pax_global_header 0000666 0000000 0000000 00000000064 15147206404 0014515 g ustar 00root root 0000000 0000000 52 comment=3c84d730f6c6395355d94c620e96e886e7026474
ada-url-ada-3c84d73/ 0000775 0000000 0000000 00000000000 15147206404 0014126 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/.clang-format 0000664 0000000 0000000 00000000051 15147206404 0016475 0 ustar 00root root 0000000 0000000 BasedOnStyle: Google
SortIncludes: Never
ada-url-ada-3c84d73/.clang-tidy 0000664 0000000 0000000 00000001041 15147206404 0016156 0 ustar 00root root 0000000 0000000 Checks: >
bugprone-*,
-bugprone-easily-swappable-parameters,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-narrowing-conversions,
-bugprone-suspicious-include,
-bugprone-unhandled-exception-at-new,
clang-analyzer-*,
-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,
# Turn all the warnings from the checks above into errors.
WarningsAsErrors: '*'
# Check first-party (non-system, non-vendored) headers.
HeaderFilterRegex: '.*'
ExcludeHeaderFilterRegex: 'build/_deps/'
SystemHeaders: false
ada-url-ada-3c84d73/.editorconfig 0000664 0000000 0000000 00000000143 15147206404 0016601 0 ustar 00root root 0000000 0000000 root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_size = 2
indent_style = space
ada-url-ada-3c84d73/.github/ 0000775 0000000 0000000 00000000000 15147206404 0015466 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/.github/FUNDING.yml 0000664 0000000 0000000 00000000032 15147206404 0017276 0 ustar 00root root 0000000 0000000 github: [anonrig, lemire]
ada-url-ada-3c84d73/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15147206404 0017651 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/.github/ISSUE_TEMPLATE/1-bug-report.yml 0000664 0000000 0000000 00000003007 15147206404 0022620 0 ustar 00root root 0000000 0000000 name: 🐛 Bug report
description: Create a report to help us improve
body:
- type: markdown
attributes:
value: |
Thank you for reporting an issue.
Please fill in as much of the following form as you're able.
Contributors are encouraged to read our AI Usage Policy, see AI_USAGE_POLICY.md.
- type: input
attributes:
label: Version
description: Which Ada version are you referring to?
- type: input
attributes:
label: Platform
description: |
UNIX: output of `uname -a`
Windows: output of `"$([Environment]::OSVersion.VersionString) $(('x86', 'x64')[[Environment]::Is64BitOperatingSystem])"` in PowerShell console
- type: textarea
attributes:
label: What steps will reproduce the bug?
description: Enter details about your bug, preferably a simple code snippet that can be run directly without installing third-party dependencies.
- type: textarea
attributes:
label: How often does it reproduce? Is there a required condition?
- type: textarea
attributes:
label: What is the expected behavior?
description: If possible please provide textual output instead of screenshots.
- type: textarea
attributes:
label: What do you see instead?
description: If possible please provide textual output instead of screenshots.
validations:
required: true
- type: textarea
attributes:
label: Additional information
description: Tell us anything else you think we should know.
ada-url-ada-3c84d73/.github/ISSUE_TEMPLATE/2-feature-request.yml 0000664 0000000 0000000 00000001212 15147206404 0023650 0 ustar 00root root 0000000 0000000 name: 🚀 Feature request
description: Suggest an idea for this project
labels: [feature request]
body:
- type: markdown
attributes:
value: |
Thank you for suggesting an idea to make ada better.
Please fill in as much of the following form as you're able.
- type: textarea
attributes:
label: What is the problem this feature will solve?
validations:
required: true
- type: textarea
attributes:
label: What is the feature you are proposing to solve the problem?
validations:
required: true
- type: textarea
attributes:
label: What alternatives have you considered?
ada-url-ada-3c84d73/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000257 15147206404 0021645 0 ustar 00root root 0000000 0000000 blank_issues_enabled: true
contact_links:
- name: Looking for documentation?
url: https://ada-url.github.io/ada
about: Please navigate to our documentation website.
ada-url-ada-3c84d73/.github/actions/ 0000775 0000000 0000000 00000000000 15147206404 0017126 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/.github/actions/setup-runner/ 0000775 0000000 0000000 00000000000 15147206404 0021575 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/.github/actions/setup-runner/action.yml 0000664 0000000 0000000 00000001047 15147206404 0023577 0 ustar 00root root 0000000 0000000 name: 'Setup runner environment'
description: 'Sets up runner environment with Ninja and proper toolchain for building Ada'
runs:
using: 'composite'
steps:
- name: Setup Linux
if: runner.os == 'Linux'
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y ninja-build
- name: Setup macOS
if: runner.os == 'macOS'
shell: bash
run: |
brew install ninja
- name: Setup Windows
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install ninja
ada-url-ada-3c84d73/.github/dependabot.yml 0000664 0000000 0000000 00000000560 15147206404 0020317 0 ustar 00root root 0000000 0000000 # Set update schedule for GitHub Actions
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: cargo
directory: /benchmarks/competitors/servo-url
schedule:
interval: monthly
- package-ecosystem: pip
directory: /tools/release
schedule:
interval: monthly
ada-url-ada-3c84d73/.github/workflows/ 0000775 0000000 0000000 00000000000 15147206404 0017523 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/.github/workflows/_build.yaml 0000664 0000000 0000000 00000014662 15147206404 0021656 0 ustar 00root root 0000000 0000000 name: 'Build'
on:
workflow_call:
inputs:
# Runner configuration
runner:
type: string
required: true
# Build configuration
cxx:
type: string
required: false
default: ''
cxxflags:
type: string
required: false
default: ''
shared:
type: string
required: false
default: 'OFF'
simdutf:
type: string
required: false
default: 'OFF'
build_type:
type: string
required: false
default: ''
# CMake options
cmake_args:
type: string
required: false
default: ''
testing:
type: string
required: false
default: 'ON'
benchmarks:
type: string
required: false
default: 'OFF'
# Windows-specific
generator:
type: string
required: false
default: 'Ninja'
arch:
type: string
required: false
default: ''
toolset:
type: string
required: false
default: ''
# Post-build actions
run_tests:
type: boolean
required: false
default: true
run_benchmarks:
type: boolean
required: false
default: false
run_install:
type: boolean
required: false
default: false
# Cross-compilation
toolchain_file:
type: string
required: false
default: ''
qemu_cpu:
type: string
required: false
default: ''
qemu_ld_prefix:
type: string
required: false
default: ''
setup_script:
type: string
required: false
default: ''
# run-on-arch-action (for s390x, etc.)
use_run_on_arch:
type: boolean
required: false
default: false
run_on_arch_arch:
type: string
required: false
default: ''
run_on_arch_distro:
type: string
required: false
default: ''
run_on_arch_install:
type: string
required: false
default: ''
run_on_arch_run:
type: string
required: false
default: ''
permissions:
contents: read
jobs:
build:
runs-on: ${{ inputs.runner }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Runner
if: ${{ !inputs.use_run_on_arch }}
uses: ./.github/actions/setup-runner
- name: Setup environment
if: ${{ !inputs.use_run_on_arch && inputs.setup_script != '' }}
shell: bash
run: ${{ inputs.setup_script }}
- name: Configure
if: ${{ !inputs.use_run_on_arch }}
shell: bash
run: |
CMAKE_ARGS="-G \"${{ inputs.generator }}\" -B build"
CMAKE_ARGS="$CMAKE_ARGS -DADA_TESTING=${{ inputs.testing }}"
CMAKE_ARGS="$CMAKE_ARGS -DADA_BENCHMARKS=${{ inputs.benchmarks }}"
CMAKE_ARGS="$CMAKE_ARGS -DBUILD_SHARED_LIBS=${{ inputs.shared }}"
CMAKE_ARGS="$CMAKE_ARGS -DADA_USE_SIMDUTF=${{ inputs.simdutf }}"
# For multi-config generators (Visual Studio), don't set CMAKE_BUILD_TYPE
# For single-config generators (Ninja, Make), set it
if [[ "${{ inputs.generator }}" != *"Visual Studio"* ]] && [ -n "${{ inputs.build_type }}" ]; then
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=${{ inputs.build_type }}"
fi
# Visual Studio generator: add architecture
if [ -n "${{ inputs.arch }}" ]; then
CMAKE_ARGS="$CMAKE_ARGS -A ${{ inputs.arch }}"
fi
if [ -n "${{ inputs.toolchain_file }}" ]; then
CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${{ inputs.toolchain_file }}"
fi
if [ -n "${{ inputs.cmake_args }}" ]; then
CMAKE_ARGS="$CMAKE_ARGS ${{ inputs.cmake_args }}"
fi
if [ -n "${{ inputs.qemu_ld_prefix }}" ]; then
export QEMU_LD_PREFIX="${{ inputs.qemu_ld_prefix }}"
fi
if [ -n "${{ inputs.qemu_cpu }}" ]; then
export QEMU_CPU="${{ inputs.qemu_cpu }}"
fi
echo "Running: cmake $CMAKE_ARGS"
eval cmake $CMAKE_ARGS
env:
CXX: ${{ inputs.cxx }}
CXXFLAGS: ${{ inputs.cxxflags }}
- name: Build
if: ${{ !inputs.use_run_on_arch }}
shell: bash
run: |
BUILD_ARGS="--build build -j=4"
# For multi-config generators (Visual Studio), specify config at build time
if [[ "${{ inputs.generator }}" == *"Visual Studio"* ]] && [ -n "${{ inputs.build_type }}" ]; then
BUILD_ARGS="$BUILD_ARGS --config ${{ inputs.build_type }}"
fi
cmake $BUILD_ARGS
- name: Test
if: ${{ !inputs.use_run_on_arch && inputs.run_tests }}
shell: bash
run: |
if [ -n "${{ inputs.qemu_ld_prefix }}" ]; then
export QEMU_LD_PREFIX="${{ inputs.qemu_ld_prefix }}"
fi
if [ -n "${{ inputs.qemu_cpu }}" ]; then
export QEMU_CPU="${{ inputs.qemu_cpu }}"
fi
TEST_ARGS="--output-on-failure --test-dir build"
# For multi-config generators (Visual Studio), specify config at test time
if [[ "${{ inputs.generator }}" == *"Visual Studio"* ]] && [ -n "${{ inputs.build_type }}" ]; then
TEST_ARGS="$TEST_ARGS -C ${{ inputs.build_type }}"
fi
ctest $TEST_ARGS
- name: Run benchmarks
if: ${{ !inputs.use_run_on_arch && inputs.run_benchmarks }}
shell: bash
run: cd build && benchmarks/bench
- name: Install and test
if: ${{ !inputs.use_run_on_arch && inputs.run_install }}
shell: bash
run: |
cmake --install build
cmake -DCMAKE_INSTALL_PREFIX:PATH=../../destination -S tests/installation -B build_install_test
cmake --build build_install_test
./build_install_test/main
# run-on-arch path (for s390x and similar architectures)
- uses: uraimo/run-on-arch-action@d94c13912ea685de38fccc1109385b83fd79427d # v3.0.1
if: ${{ inputs.use_run_on_arch }}
name: Build and Test
with:
arch: ${{ inputs.run_on_arch_arch }}
distro: ${{ inputs.run_on_arch_distro }}
githubToken: ${{ github.token }}
install: ${{ inputs.run_on_arch_install }}
run: ${{ inputs.run_on_arch_run }}
ada-url-ada-3c84d73/.github/workflows/cifuzz.yml 0000664 0000000 0000000 00000002145 15147206404 0021562 0 ustar 00root root 0000000 0000000 name: CIFuzz
on:
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions: read-all
jobs:
Fuzzing:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
sanitizer: [address, undefined, memory]
steps:
- name: Build Fuzzers (${{ matrix.sanitizer }})
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'ada-url'
language: c++
sanitizer: ${{ matrix.sanitizer }}
- name: Run Fuzzers (${{ matrix.sanitizer }})
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'ada-url'
language: c++
fuzz-seconds: 600
sanitizer: ${{ matrix.sanitizer }}
- name: Upload Crash
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
if: steps.build.outcome == 'success'
with:
name: ${{ matrix.sanitizer }}-artifacts
path: ./out/artifacts
ada-url-ada-3c84d73/.github/workflows/codeql.yml 0000664 0000000 0000000 00000002341 15147206404 0021515 0 ustar 00root root 0000000 0000000 name: "CodeQL"
on:
schedule:
- cron: '0 0 * * 1'
permissions:
contents: read
security-events: write
pull-requests: read
actions: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'cpp', 'python' ]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v2.2.5
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v2.2.5
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v2.2.5
with:
category: "/language:${{matrix.language}}"
ada-url-ada-3c84d73/.github/workflows/codspeed.yml 0000664 0000000 0000000 00000001707 15147206404 0022041 0 ustar 00root root 0000000 0000000 name: CodSpeed Benchmarks
on:
push:
branches:
- "main"
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
# `workflow_dispatch` allows CodSpeed to trigger backtest
# performance analysis in order to generate initial data.
workflow_dispatch:
permissions:
contents: read
jobs:
benchmarks:
name: Run benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Build the benchmark target(s)
run: |
cmake -DCMAKE_BUILD_TYPE=Release -DADA_TESTING=OFF -DADA_DEVELOPMENT_CHECKS=OFF -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DADA_BENCHMARKS=ON -DCODSPEED_MODE=simulation -G Ninja -B build
cmake --build build -j
env:
CXX: g++-12
- name: Run the benchmarks
uses: CodSpeedHQ/action@v4
with:
mode: simulation
run: cmake --build build --target run_all_benchmarks
ada-url-ada-3c84d73/.github/workflows/dependency-review.yml 0000664 0000000 0000000 00000000607 15147206404 0023666 0 ustar 00root root 0000000 0000000 name: 'Dependency Review'
on: [pull_request]
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: 'Dependency Review'
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
ada-url-ada-3c84d73/.github/workflows/documentation.yml 0000664 0000000 0000000 00000001634 15147206404 0023123 0 ustar 00root root 0000000 0000000 name: Doxygen GitHub Pages
on:
release:
types: [created]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
deploy:
permissions:
contents: write
pages: write
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install theme
run: ./tools/prepare-doxygen.sh
- uses: mattnotmitt/doxygen-action@ded75d963c260fd8489801611a5079d149ebcc07 # edge
with:
doxyfile-path: './doxygen'
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: docs/html
ada-url-ada-3c84d73/.github/workflows/lint_and_format_check.yml 0000664 0000000 0000000 00000002472 15147206404 0024550 0 ustar 00root root 0000000 0000000 name: Lint and format
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**.md'
- 'docs/**'
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-and-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run clang-format
uses: jidicula/clang-format-action@6cd220de46c89139a0365edae93eee8eb30ca8fe # v4.16.0
with:
clang-format-version: '17'
fallback-style: 'Google'
- uses: chartboost/ruff-action@e18ae971ccee1b2d7bbef113930f00c670b78da4 # v1.0.0
name: Lint with Ruff
with:
version: 0.6.0
- name: Install clang-tidy and libc++
run: sudo apt-get update && sudo apt-get install -y clang-tidy-20 libc++-20-dev libc++abi-20-dev
- name: Run clang-tidy
run: >
cmake -B build -DADA_TESTING=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_CXX_CLANG_TIDY=clang-tidy-20 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_CXX_FLAGS="-stdlib=libc++" &&
cmake --build build -j=4
env:
CXX: clang++-20
ada-url-ada-3c84d73/.github/workflows/macos.yml 0000664 0000000 0000000 00000001375 15147206404 0021356 0 ustar 00root root 0000000 0000000 name: macOS
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**.md'
- 'docs/**'
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
fail-fast: false
matrix:
runner: [macos-14, macos-15, macos-26]
shared: [OFF]
name: build (${{ matrix.runner }}, shared=${{ matrix.shared }})
uses: ./.github/workflows/_build.yaml
with:
runner: ${{ matrix.runner }}
shared: ${{ matrix.shared }}
cmake_args: '-DCMAKE_INSTALL_PREFIX:PATH=destination'
run_install: true
ada-url-ada-3c84d73/.github/workflows/release-script-tests.yml 0000664 0000000 0000000 00000001617 15147206404 0024335 0 ustar 00root root 0000000 0000000 name: Release Script Tests
on:
# workflow_call is used to indicate that a workflow can be called by another workflow.
workflow_call:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**.md'
- 'docs/**'
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
jobs:
release-script-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./tools/release
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Prepare Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
run: pytest -v
ada-url-ada-3c84d73/.github/workflows/release_create.yml 0000664 0000000 0000000 00000003307 15147206404 0023214 0 ustar 00root root 0000000 0000000 name: Release Create
on:
pull_request:
types: [closed]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
check-release-conditions:
runs-on: ubuntu-latest
if: |
github.event.pull_request.merged == true &&
github.event.pull_request.base.ref == 'main' &&
startsWith(github.event.pull_request.head.ref, 'release/v') &&
startsWith(github.event.pull_request.user.login, 'github-actions')
steps:
- name: Check release conditions
run: |
echo "All conditions have been met!"
release-script-test:
needs: check-release-conditions
uses: ./.github/workflows/release-script-tests.yml
create-release:
permissions:
contents: write
needs: release-script-test
runs-on: ubuntu-latest
if: ${{ needs.release-script-test.result == 'success' }}
env:
NEXT_RELEASE_TAG: ${{ github.event.pull_request.head.ref }}
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Prepare Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: pip install -r ./tools/release/requirements.txt
- name: Extract Tag from branch name
run: |
NEXT_RELEASE_TAG=$(echo $NEXT_RELEASE_TAG | sed 's/^release\///')
echo "NEXT_RELEASE_TAG=${NEXT_RELEASE_TAG}" >> $GITHUB_ENV
- name: Target release Tag
run: echo "New tag $NEXT_RELEASE_TAG"
- name: Amalgamation
run: ./singleheader/amalgamate.py
- name: "Create release"
run: ./tools/release/create_release.py
ada-url-ada-3c84d73/.github/workflows/release_prepare.yml 0000664 0000000 0000000 00000003446 15147206404 0023413 0 ustar 00root root 0000000 0000000 name: Release Prepare
on:
workflow_dispatch:
inputs:
tag:
type: string
required: true
description: "Tag for the next release. Ex.: v5.0.0"
env:
NEXT_RELEASE_TAG: ${{ github.event.inputs.tag }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
release-script-test:
uses: ./.github/workflows/release-script-tests.yml
prepare-release-and-pull-request:
permissions:
contents: write
pull-requests: write
needs: release-script-test
runs-on: ubuntu-22.04-arm
if: ${{ needs.release-script-test.result == 'success' }}
env:
CXX: clang++-14
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Prepare Python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
cache: 'pip' # caching pip dependencies
- name: Install dependencies
run: pip install -r ./tools/release/requirements.txt
- name: Update source code versions
run: ./tools/release/update_versions.py
- name: Ada Build
run: cmake -B build && cmake --build build
- name: Ada Test
run: ctest --output-on-failure --test-dir build
- name: Create PR with code updates for new release
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 #v8.1.0
with:
commit-message: "chore: release ${{ env.NEXT_RELEASE_TAG }}"
branch: "release/${{ env.NEXT_RELEASE_TAG }}"
title: "chore: release ${{ env.NEXT_RELEASE_TAG }}"
body: |
This pull PR updates the source code version to ${{ env.NEXT_RELEASE_TAG }}
delete-branch: true
reviewers: "lemire,anonrig"
assignees: "lemire,anonrig"
sign-commits: true
ada-url-ada-3c84d73/.github/workflows/scorecard.yml 0000664 0000000 0000000 00000005626 15147206404 0022224 0 ustar 00root root 0000000 0000000 # This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '0 0 * * 1'
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-fine-grained-pat-optional.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3.29.5
with:
sarif_file: results.sarif
ada-url-ada-3c84d73/.github/workflows/ubuntu.yml 0000664 0000000 0000000 00000015442 15147206404 0021576 0 ustar 00root root 0000000 0000000 name: Ubuntu
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**.md'
- 'docs/**'
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# Main build matrix (Ubuntu 22.04 x86_64 and ARM64)
build:
strategy:
fail-fast: false
matrix:
runner: [ubuntu-22.04, ubuntu-22.04-arm]
shared: [ON, OFF]
cxx: [g++-12, clang++-15]
simdutf: [OFF, ON]
name: build (${{ matrix.runner }}, ${{ matrix.cxx }}, shared=${{ matrix.shared }}, simdutf=${{ matrix.simdutf }})
uses: ./.github/workflows/_build.yaml
with:
runner: ${{ matrix.runner }}
cxx: ${{ matrix.cxx }}
shared: ${{ matrix.shared }}
simdutf: ${{ matrix.simdutf }}
benchmarks: 'ON'
run_benchmarks: true
# Installation test (Ubuntu 24.04)
install:
strategy:
fail-fast: false
matrix:
shared: [ON, OFF]
cxx: [g++-12, clang++]
name: install (${{ matrix.cxx }}, shared=${{ matrix.shared }})
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-24.04
cxx: ${{ matrix.cxx }}
shared: ${{ matrix.shared }}
cmake_args: '-DCMAKE_INSTALL_PREFIX:PATH=destination'
run_install: true
# Pedantic build - fails on compiler warnings
pedantic:
strategy:
fail-fast: false
matrix:
shared: [ON, OFF]
name: pedantic (shared=${{ matrix.shared }})
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-22.04
cxx: g++-12
cxxflags: '-Werror -Wextra -Wno-unused-parameter -Wimplicit-fallthrough'
shared: ${{ matrix.shared }}
# Release build
release:
strategy:
fail-fast: false
matrix:
cxx: [g++-12, clang++-14]
name: release (${{ matrix.cxx }})
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-22.04
cxx: ${{ matrix.cxx }}
build_type: Release
testing: 'OFF'
# Address Sanitizer
sanitized:
strategy:
fail-fast: false
matrix:
shared: [ON, OFF]
name: asan (shared=${{ matrix.shared }})
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-22.04
cxx: g++-12
shared: ${{ matrix.shared }}
cmake_args: '-DADA_SANITIZE=ON -DADA_DEVELOPMENT_CHECKS=ON'
# Undefined Behavior Sanitizer
sanitize-undefined:
strategy:
fail-fast: false
matrix:
shared: [ON, OFF]
name: ubsan (shared=${{ matrix.shared }})
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-22.04
cxx: g++-12
shared: ${{ matrix.shared }}
cmake_args: '-DADA_SANITIZE_UNDEFINED=ON -DADA_DEVELOPMENT_CHECKS=ON'
# s390x via QEMU
s390x:
name: s390x
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-latest
use_run_on_arch: true
run_on_arch_arch: s390x
run_on_arch_distro: ubuntu_latest
run_on_arch_install: |
apt-get update -q -y
apt-get install -y cmake make g++-12 gcc-12 git ninja-build
run_on_arch_run: |
CC=gcc-12 CXX=g++-12 cmake -D ADA_TESTING=ON -DCMAKE_BUILD_TYPE=Release -G Ninja -B build
rm -r -f dependencies
CC=gcc-12 CXX=g++-12 cmake --build build -j=4
ctest --output-on-failure --test-dir build
# LoongArch64 via cross-compilation + QEMU
loongarch64:
name: loongarch64
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-24.04
toolchain_file: cmake/toolchains-dev/loongarch64.cmake
simdutf: 'ON'
build_type: Release
qemu_ld_prefix: '/usr/loongarch64-linux-gnu'
qemu_cpu: 'la464'
setup_script: |
sudo apt-get update -y
sudo apt-get install -y cmake curl ninja-build \
g++-14-loongarch64-linux-gnu \
gcc-14-loongarch64-linux-gnu-base \
gcc-14-loongarch64-linux-gnu
sudo curl -L https://github.com/loongson/build-tools/releases/download/2025.06.06/qemu-loongarch64 --output /usr/local/bin/qemu-loongarch64
sudo chmod +x /usr/local/bin/qemu-loongarch64
# RISC-V Vector Extension via cross-compilation + QEMU
riscv64-rvv:
name: riscv64-rvv
uses: ./.github/workflows/_build.yaml
with:
runner: ubuntu-24.04
toolchain_file: cmake/toolchains-dev/riscv64-rvv.cmake
simdutf: 'ON'
build_type: Release
qemu_ld_prefix: '/usr/riscv64-linux-gnu'
qemu_cpu: 'rv64,v=on,vlen=128'
setup_script: |
sudo apt-get update -y
sudo apt-get install -y cmake curl ninja-build \
g++-riscv64-linux-gnu \
gcc-riscv64-linux-gnu \
qemu-user-static qemu-user
# Emscripten (WebAssembly)
emscripten:
name: emscripten
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify
run: emcc -v
- name: Configure
run: emcmake cmake -B build -DADA_TESTING=ON -DADA_TOOLS=OFF
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build
# Alpine Linux (musl libc)
alpine:
name: alpine
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Setup Alpine container
run: |
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest
echo 'docker exec alpine "$@";' > ./alpine.sh
chmod +x ./alpine.sh
- name: Install packages
run: |
./alpine.sh apk update
./alpine.sh apk add build-base cmake g++ linux-headers git bash icu-dev
- name: Configure
run: ./alpine.sh cmake -DADA_TESTING=ON -DADA_BENCHMARKS=ON -B build
- name: Build
run: ./alpine.sh cmake --build build
- name: Test
run: ./alpine.sh ctest --output-on-failure --test-dir build
# Debian pkg-config test
pkg-config:
name: pkg-config
runs-on: ubuntu-latest
container:
image: debian:12
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install dependencies
run: |
apt -y update
apt -y --no-install-recommends install g++ cmake make pkg-config
- name: Build and install
run: |
cmake -B build
cmake --build build
cmake --install build
- name: Test pkg-config
run: pkg-config --cflags --libs ada
ada-url-ada-3c84d73/.github/workflows/windows.yml 0000664 0000000 0000000 00000002762 15147206404 0021747 0 ustar 00root root 0000000 0000000 name: Windows
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths-ignore:
- '**.md'
- 'docs/**'
push:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# MSVC builds (use Visual Studio generator for native MSVC support)
msvc:
strategy:
fail-fast: false
matrix:
include:
- { build_type: Release, shared: OFF, arch: x64, cmake_args: '' }
- { build_type: Debug, shared: OFF, arch: x64, cmake_args: '-DADA_DEVELOPMENT_CHECKS=ON' }
- { build_type: Debug, shared: ON, arch: x64, cmake_args: '-DADA_DEVELOPMENT_CHECKS=ON' }
name: msvc (${{ matrix.build_type }}, shared=${{ matrix.shared }})
uses: ./.github/workflows/_build.yaml
with:
runner: windows-2025
generator: 'Visual Studio 17 2022'
arch: ${{ matrix.arch }}
shared: ${{ matrix.shared }}
build_type: ${{ matrix.build_type }}
cmake_args: ${{ matrix.cmake_args }}
# ClangCL builds
clangcl:
strategy:
fail-fast: false
matrix:
build_type: [Debug, Release]
name: clangcl (${{ matrix.build_type }})
uses: ./.github/workflows/_build.yaml
with:
runner: windows-2025
build_type: ${{ matrix.build_type }}
cmake_args: '-DADA_DEVELOPMENT_CHECKS=ON -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl'
ada-url-ada-3c84d73/.github/workflows/wpt-updater.yml 0000664 0000000 0000000 00000002427 15147206404 0022527 0 ustar 00root root 0000000 0000000 name: Update WPT
on:
schedule:
- cron: '0 0 * * *'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
concurrency:
group: wpt-updater
cancel-in-progress: true
permissions:
contents: read
jobs:
issue:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
strategy:
fail-fast: false
matrix:
module: [url, urlpattern]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Fetch tests
run: tools/update-wpt.sh ${{matrix.module}}
- name: Open pull request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 #v8.1.0
with:
token: ${{secrets.GH_PAT}}
commit-message: "test: update web platform tests"
branch: automatic-update-wpt-${{matrix.module}}
title: Update web platform tests (${{matrix.module}})
body: |
This is an automated pull request for updating the WPT.
- [Web Platform Tests](https://github.com/web-platform-tests/wpt/tree/master/url)
- [Commit History](https://github.com/web-platform-tests/wpt/commits/master/url/resources)
cc @anonrig @lemire
team-reviewers: core
delete-branch: true
ada-url-ada-3c84d73/.gitignore 0000664 0000000 0000000 00000000637 15147206404 0016124 0 ustar 00root root 0000000 0000000 # common build directory
build
*-build-*
# Python cache
__pycache__
venv
cmake-build-debug
.cache
docs/html
docs/theme
# Generated using only the Github workflow
benchmark_result.json
singleheader/ada.h
singleheader/ada_c.h
singleheader/ada.cpp
singleheader/singleheader.zip
benchmarks/competitors/servo-url/debug
benchmarks/competitors/servo-url/target
#ignore VScode
.vscode/
.idea
# bazel output
bazel-*
ada-url-ada-3c84d73/.python-version 0000664 0000000 0000000 00000000005 15147206404 0017126 0 ustar 00root root 0000000 0000000 3.12
ada-url-ada-3c84d73/AI_USAGE_POLICY.md 0000664 0000000 0000000 00000004700 15147206404 0016745 0 ustar 00root root 0000000 0000000 # AI Usage Policy
Contributors can use whatever tools they would like to
craft their contributions, but there must be a **human in the loop**.
**Contributors must read and review all LLM-generated code or text before they
ask other project members to review it.** The contributor is always the author
and is fully accountable for their contributions. Contributors should be
sufficiently confident that the contribution is high enough quality that asking
for a review is a good use of scarce maintainer time, and they should be **able
to answer questions about their work** during review.
We expect that new contributors will be less confident in their contributions,
and our guidance to them is to **start with small contributions** that they can
fully understand to build confidence. We aspire to be a welcoming community
that helps new contributors grow their expertise, but learning involves taking
small steps, getting feedback, and iterating. Passing maintainer feedback to an
LLM doesn't help anyone grow, and does not sustain our community.
This policy includes, but is not limited to, the following kinds of
contributions:
- Code, usually in the form of a pull request
- Issues or security vulnerabilities
- Comments and feedback on pull requests
## Extractive Contributions
The reason for our "human-in-the-loop" contribution policy is that processing
patches, PRs, RFCs, and comments is not free -- it takes a lot of
maintainer time and energy to review those contributions! Sending the
unreviewed output of an LLM to open source project maintainers *extracts* work
from them in the form of design and code review, so we call this kind of
contribution an "extractive contribution".
## Copyright
Artificial intelligence systems raise many questions around copyright that have
yet to be answered. Our policy on AI tools is similar to our copyright policy:
Contributors are responsible for ensuring that they have the right to
contribute code under the terms of our license, typically meaning that either
they, their employer, or their collaborators hold the copyright. Using AI tools
to regenerate copyrighted material does not remove the copyright, and
contributors are responsible for ensuring that such material does not appear in
their contributions. Contributions found to violate this policy will be removed
just like any other offending contribution.
## Reference
- [LLVM AI Tool Use Policy](https://discourse.llvm.org/t/rfc-llvm-ai-tool-policy-human-in-the-loop/89159)
ada-url-ada-3c84d73/CLAUDE.md 0000664 0000000 0000000 00000021736 15147206404 0015416 0 ustar 00root root 0000000 0000000 # Ada Development Guide for Claude
This guide provides instructions for building, testing, and benchmarking the Ada URL parser library using CMake.
## Quick Reference
```bash
# Build library only (no tests, no benchmarks)
cmake -B build && cmake --build build
# Build with tests (development checks ENABLED)
cmake -B build -DADA_TESTING=ON && cmake --build build
ctest --output-on-failure --test-dir build
# Build with benchmarks (development checks DISABLED for accurate performance)
cmake -B build -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build
./build/benchmarks/benchdata # Run main benchmark
# FASTER BUILDS: Use Ninja instead of Make
cmake -B build -G Ninja -DADA_TESTING=ON && cmake --build build
cmake -B build -G Ninja -DADA_BENCHMARKS=ON -DADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build
```
## Requirements
- C++20 compatible compiler (GCC 12+, LLVM 14+, MSVC 2022+)
- CMake 3.15+
- Git (for fetching test dependencies)
- Ninja (optional, for faster builds): `sudo apt install ninja-build` on Ubuntu
## Building the Library
### Basic Build (Library Only)
For a minimal build with just the library:
```bash
cmake -B build
cmake --build build
```
This creates the Ada library without tests or benchmarks.
### Build with Tests
To build with tests enabled:
```bash
cmake -B build -DADA_TESTING=ON
cmake --build build
```
**Important:** When `ADA_TESTING=ON`, development checks are automatically enabled unless you explicitly build in Release mode with `NDEBUG` defined. Development checks include assertions (`ADA_ASSERT_TRUE`, `ADA_ASSERT_EQUAL`) that validate internal state.
### Build with Benchmarks
To build benchmarks for performance testing:
```bash
cmake -B build -DADA_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
```
**Critical:** Always build benchmarks in Release mode (`-DCMAKE_BUILD_TYPE=Release`) to disable development checks. Development assertions significantly impact performance and will give misleading benchmark results.
### Using Local Packages
If you have dependencies (like GoogleTest, Google Benchmark) already installed locally:
```bash
cmake -B build -DADA_TESTING=ON -DCPM_USE_LOCAL_PACKAGES=ON
cmake --build build
```
## CMake Build Options
| Option | Default | Description |
|--------|---------|-------------|
| `ADA_TESTING` | OFF | Enable building tests |
| `ADA_BENCHMARKS` | OFF | Enable building benchmarks (requires 64-bit) |
| `ADA_TOOLS` | OFF | Enable building command-line tools |
| `ADA_BUILD_SINGLE_HEADER_LIB` | OFF | Build from single-header amalgamated files |
| `ADA_USE_SIMDUTF` | OFF | Enable SIMD-accelerated Unicode via simdutf |
| `CMAKE_BUILD_TYPE` | - | Set to `Release` for optimized builds, `Debug` for development |
## Running Tests
After building with `-DADA_TESTING=ON`:
```bash
# Run all tests
ctest --output-on-failure --test-dir build
# Run specific test executable
./build/tests/basic_tests
# Run tests with verbose output
ctest --verbose --test-dir build
```
### Development Checks in Tests
Tests run with development checks **enabled by default** (unless built with `-DCMAKE_BUILD_TYPE=Release -DNDEBUG`). This means:
- Assertions are active (`ADA_ASSERT_TRUE`, `ADA_ASSERT_EQUAL`)
- Internal state validation occurs
- Performance is slower but catches bugs early
This is the **recommended mode for development**.
## Running Benchmarks
After building with `-DADA_BENCHMARKS=ON`:
```bash
# Main benchmark comparing against competitors
./build/benchmarks/benchdata
# Specific benchmarks
./build/benchmarks/bench # Basic URL parsing benchmarks
./build/benchmarks/bbc_bench # BBC URLs benchmark
./build/benchmarks/wpt_bench # Web Platform Tests benchmark
./build/benchmarks/percent_encode # Percent encoding benchmarks
```
### Development Checks in Benchmarks
**Always disable development checks for benchmarks** by building in Release mode:
```bash
# CORRECT: Benchmarks with development checks disabled
cmake -B build -DADA_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
./build/benchmarks/benchdata
# WRONG: Don't benchmark with development checks enabled
cmake -B build -DADA_BENCHMARKS=ON # Missing Release mode!
```
Development checks add significant overhead that skews performance measurements. The `ADA_DEVELOPMENT_CHECKS` macro is automatically disabled when:
- Building with `-DCMAKE_BUILD_TYPE=Release`
- `NDEBUG` is defined
- Explicitly set `ADA_DEVELOPMENT_CHECKS=0`
## Complete Development Workflow
### 1. Initial Setup
```bash
# Clone and enter directory
cd /path/to/ada
# Create build directory for tests
cmake -B build -DADA_TESTING=ON
cmake --build build
```
### 2. Development Cycle (with tests)
```bash
# Make code changes...
# Rebuild (only rebuilds changed files)
cmake --build build
# Run tests to verify correctness
ctest --output-on-failure --test-dir build
# Or run specific test
./build/tests/basic_tests
```
### 3. Performance Validation (with benchmarks)
```bash
# Create separate benchmark build
cmake -B build-release -DADA_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build-release
# Run benchmarks
./build-release/benchmarks/benchdata
# Compare before/after optimizations
# (stash changes, rebuild, run benchmark, restore, rebuild, run again)
```
### 4. Clean Rebuild
```bash
# Remove build directory and start fresh
rm -rf build
cmake -B build -DADA_TESTING=ON
cmake --build build
```
## Understanding Development Checks
### What are Development Checks?
Development checks are compile-time assertions that validate:
- Function preconditions and postconditions
- Internal invariants (e.g., `validate()` on URL objects)
- Argument validity
### When are they Enabled?
Automatically enabled when:
- `ADA_TESTING=ON` (unless overridden with Release mode)
- Debug build (`CMAKE_BUILD_TYPE=Debug`)
- `NDEBUG` is not defined
Automatically disabled when:
- `CMAKE_BUILD_TYPE=Release`
- `NDEBUG` is defined
- Production builds
### Manual Control
```bash
# Force enable development checks (even in Release)
cmake -B build -DADA_DEVELOPMENT_CHECKS=1
# Force disable development checks (even in Debug)
cmake -B build -DNDEBUG=1
```
## Running Clang-Tidy
Clang-tidy is used for static analysis. There are two ways to run it:
### During Build (Recommended)
Run clang-tidy automatically during compilation:
```bash
cmake -B build -DADA_TESTING=ON \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_CXX_CLANG_TIDY=clang-tidy \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build
```
**Important:** You must use clang++ as the compiler when running clang-tidy during build. Using GCC will cause errors because clang-tidy doesn't understand GCC-specific flags like `-mno-avx256-split-unaligned-load`.
### Standalone with compile_commands.json
First, generate the compilation database:
```bash
cmake -B build -DADA_TESTING=ON \
-DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
cmake --build build
```
Then run clang-tidy on specific files:
```bash
clang-tidy -p build src/ada.cpp
clang-tidy -p build src/ada_idna.cpp
```
The `-p build` flag tells clang-tidy to use the `compile_commands.json` from the build directory.
### Clang-Tidy Configuration
The `.clang-tidy` file in the project root configures which checks are enabled. Current configuration enables:
- `bugprone-*` checks (with some exclusions)
- `clang-analyzer-*` checks
All warnings are treated as errors (`WarningsAsErrors: '*'`).
## Platform-Specific Notes
### Windows
Specify configuration during build:
```bash
cmake -B build -DADA_TESTING=ON
cmake --build build --config Release
ctest --output-on-failure --test-dir build --config Release
```
### macOS/Linux
Standard commands work as documented above.
## Troubleshooting
### Benchmarks are unexpectedly slow
**Cause:** Development checks are enabled.
**Solution:** Rebuild with Release mode:
```bash
cmake -B build -DADA_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build
```
### Tests are failing with assertion errors
**Expected behavior** - development checks are catching bugs. Review the assertion message and fix the underlying issue.
### Can't find benchmark executable
**Cause:** Benchmarks not built (32-bit system or not enabled).
**Solution:**
```bash
cmake -B build -DADA_BENCHMARKS=ON
cmake --build build
ls build/benchmarks/ # Check what was built
```
## Additional Resources
- **README.md**: General project overview and API usage
- **docs/cli.md**: Command-line interface documentation
- **benchmarks/**: Benchmark source code
- **tests/**: Test source code
- **include/ada/**: Library headers
## Summary
| Task | Command | Development Checks |
|------|---------|-------------------|
| Library only | `cmake -B build && cmake --build build` | N/A |
| Testing | `cmake -B build -DADA_TESTING=ON && cmake --build build` | ✅ Enabled |
| Benchmarking | `cmake -B build -DADA_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build` | ❌ Disabled |
| Development | `cmake -B build -DADA_TESTING=ON -DCMAKE_BUILD_TYPE=Debug && cmake --build build` | ✅ Enabled |
ada-url-ada-3c84d73/CMakeLists.txt 0000664 0000000 0000000 00000013633 15147206404 0016674 0 ustar 00root root 0000000 0000000 cmake_minimum_required(VERSION 3.16)
project(ada
DESCRIPTION "Fast spec-compliant URL parser"
LANGUAGES C CXX
VERSION 3.4.3
)
set(ADA_LIB_VERSION "3.4.3" CACHE STRING "ada library version")
set(ADA_LIB_SOVERSION "3" CACHE STRING "ada library soversion")
include(GNUInstallDirs)
include(CTest)
include(cmake/ada-flags.cmake)
add_subdirectory(src)
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts/cmake)
option(ADA_TESTING "Whether to build tests." OFF)
option(ADA_BENCHMARKS "Whether to build benchmarks." OFF)
option(ADA_TOOLS "Whether to build tools." OFF)
option(ADA_BUILD_SINGLE_HEADER_LIB "Whether to build the lib from the single-header files" OFF)
option(ADA_USE_SIMDUTF "Whether to use SIMDUTF for IDNA" OFF)
# There are cases where when embedding ada as a dependency for other CMake
# projects as submodules or subdirectories (via FetchContent) can lead to
# errors due to CPM, so this is here to support disabling all the testing
# and tooling for ada if one only wishes to use the ada library.
if(ADA_TESTING OR ADA_BENCHMARKS OR ADA_TOOLS OR ADA_USE_SIMDUTF)
include(cmake/CPM.cmake)
# CPM requires git as an implicit dependency
# We use googletest in the tests
if(ADA_TESTING)
CPMAddPackage(
NAME GTest
GITHUB_REPOSITORY google/googletest
VERSION 1.15.2
OPTIONS "BUILD_GMOCK OFF" "INSTALL_GTEST OFF"
)
endif()
# We use simdjson in both the benchmarks and tests
if(ADA_TESTING OR ADA_BENCHMARKS)
CPMAddPackage("gh:simdjson/simdjson@3.10.1")
endif()
# We use Google Benchmark, but it does not build under several 32-bit systems.
if(ADA_BENCHMARKS AND (CMAKE_SIZEOF_VOID_P EQUAL 8))
if(DEFINED CODSPEED_MODE)
message(STATUS "Using CodSpeed-instrumented Google Benchmark")
CPMAddPackage(
NAME benchmark
GITHUB_REPOSITORY CodSpeedHQ/codspeed-cpp
VERSION 2.0.0
SOURCE_SUBDIR google_benchmark
OPTIONS "BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
"BENCHMARK_ENABLE_WERROR OFF"
)
else()
message(STATUS "Using standard Google Benchmark")
CPMAddPackage(
NAME benchmark
GITHUB_REPOSITORY google/benchmark
VERSION 1.9.0
OPTIONS "BENCHMARK_ENABLE_TESTING OFF"
"BENCHMARK_ENABLE_INSTALL OFF"
"BENCHMARK_ENABLE_WERROR OFF"
)
endif()
endif()
if (ADA_TESTING AND NOT EMSCRIPTEN)
set(CTEST_TEST_TIMEOUT 5)
set(ADA_USE_UNSAFE_STD_REGEX_PROVIDER ON)
message(STATUS "The tests are enabled.")
add_subdirectory(tests)
else()
if(is_top_project)
message(STATUS "The tests are disabled.")
endif()
endif(ADA_TESTING AND NOT EMSCRIPTEN)
If(ADA_BENCHMARKS AND NOT EMSCRIPTEN)
message(STATUS "Ada benchmarks enabled.")
add_subdirectory(benchmarks)
else(ADA_BENCHMARKS AND NOT EMSCRIPTEN)
if(is_top_project)
message(STATUS "Ada benchmarks disabled. Set ADA_BENCHMARKS=ON to enable them.")
endif()
endif(ADA_BENCHMARKS AND NOT EMSCRIPTEN)
if (ADA_TESTING AND EMSCRIPTEN)
add_subdirectory(tests/wasm)
endif(ADA_TESTING AND EMSCRIPTEN)
if(ADA_USE_SIMDUTF)
CPMAddPackage(
NAME simdutf
GITHUB_REPOSITORY simdutf/simdutf
VERSION 7.3.2
OPTIONS "SIMDUTF_TESTS OFF" "SIMDUTF_TOOLS OFF"
)
endif()
endif()
add_library(ada::ada ALIAS ada)
if(ADA_TESTING OR ADA_USE_UNSAFE_STD_REGEX_PROVIDER)
# IMPORTANT!
#
# We enable std_regex_provider for testing purposes
# It is not recommended to enable this flag and use std::regex under
# production environments due to several security issues.
#
target_compile_definitions(ada PUBLIC ADA_USE_UNSAFE_STD_REGEX_PROVIDER=ON)
endif()
set_target_properties(
ada PROPERTIES
VERSION "${ADA_LIB_VERSION}"
SOVERSION "${ADA_LIB_SOVERSION}"
WINDOWS_EXPORT_ALL_SYMBOLS YES
)
include(CMakePackageConfigHelpers)
include(GNUInstallDirs)
if(NOT ADA_COVERAGE AND NOT EMSCRIPTEN)
add_subdirectory(singleheader)
endif()
if(ADA_TOOLS)
add_subdirectory(tools)
endif()
install(
FILES include/ada.h include/ada_c.h
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT ada_development
)
install(
DIRECTORY include/ada
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
COMPONENT ada_development
)
install(
TARGETS ada
EXPORT ada_targets
RUNTIME COMPONENT ada_runtime
LIBRARY COMPONENT ada_runtime
NAMELINK_COMPONENT ada_development
ARCHIVE COMPONENT ada_development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
configure_file(cmake/ada-config.cmake.in ada-config.cmake @ONLY)
write_basic_package_version_file(
ada-config-version.cmake
COMPATIBILITY SameMinorVersion
)
set(
ADA_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/ada"
CACHE STRING "CMake package config location relative to the install prefix"
)
mark_as_advanced(ADA_INSTALL_CMAKEDIR)
install(
FILES
"${PROJECT_BINARY_DIR}/ada-config.cmake"
"${PROJECT_BINARY_DIR}/ada-config-version.cmake"
DESTINATION "${ADA_INSTALL_CMAKEDIR}"
COMPONENT ada_development
)
install(
EXPORT ada_targets
NAMESPACE ada::
DESTINATION "${ADA_INSTALL_CMAKEDIR}"
COMPONENT ada_development
)
install(
EXPORT ada_targets
NAMESPACE ada::
DESTINATION "${ADA_INSTALL_CMAKEDIR}"
COMPONENT example_development
)
# pkg-config
include(cmake/JoinPaths.cmake)
join_paths(PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
join_paths(PKGCONFIG_LIBDIR "\${prefix}" "${CMAKE_INSTALL_LIBDIR}")
configure_file("ada.pc.in" "ada.pc" @ONLY)
install(
FILES "${CMAKE_CURRENT_BINARY_DIR}/ada.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
)
if(is_top_project)
set(CPACK_PACKAGE_VENDOR "Ada Authors")
set(CPACK_PACKAGE_CONTACT "yagiz@nizipli.com")
set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE-MIT")
set(CPACK_RPM_PACKAGE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE-MIT")
set(CPACK_RESOURCE_FILE_README "${PROJECT_SOURCE_DIR}/README.md")
set(CPACK_SOURCE_GENERATOR "TGZ;ZIP")
include(CPack)
endif()
ada-url-ada-3c84d73/LICENSE-APACHE 0000664 0000000 0000000 00000026116 15147206404 0016060 0 ustar 00root root 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2023 Yagiz Nizipli and Daniel Lemire
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
ada-url-ada-3c84d73/LICENSE-MIT 0000664 0000000 0000000 00000002057 15147206404 0015566 0 ustar 00root root 0000000 0000000 Copyright 2023 Yagiz Nizipli and Daniel Lemire
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.
ada-url-ada-3c84d73/README.md 0000664 0000000 0000000 00000033501 15147206404 0015407 0 ustar 00root root 0000000 0000000 # Ada
[](https://securityscorecards.dev/viewer/?uri=github.com/ada-url/ada)
[](https://bestpractices.coreinfrastructure.org/projects/7085)
[](https://github.com/ada-url/ada/actions/workflows/ubuntu.yml)
[](https://github.com/ada-url/ada/actions/workflows/visual_studio.yml)
[](https://github.com/ada-url/ada/actions/workflows/visual_studio_clang.yml)
[](https://github.com/ada-url/ada/actions/workflows/ubuntu-s390x.yml)
Ada is a fast and spec-compliant URL parser written in C++.
Specification for URL parser can be found from the
[WHATWG](https://url.spec.whatwg.org/#url-parsing) website.
Ada library also includes a [URLPattern](https://url.spec.whatwg.org/#urlpattern) implementation
that is compatible with the [web-platform tests](https://github.com/web-platform-tests/wpt/tree/master/urlpattern).
The Ada library passes the full range of tests from the specification,
across a wide range of platforms (e.g., Windows, Linux, macOS). It fully
supports the relevant [Unicode Technical Standard](https://www.unicode.org/reports/tr46/#ToUnicode).
A common use of a URL parser is to take a URL string and normalize it.
The WHATWG URL specification has been adopted by most browsers. Other tools, such as curl and many
standard libraries, follow the RFC 3986. The following table illustrates possible differences in practice
(encoding of the host, encoding of the path):
| string source | string value |
|:------------------------|:------------------------------------------------------------|
| input string | https://www.7‑Eleven.com/Home/Privacy/Montréal |
| ada's normalized string | https://www.xn--7eleven-506c.com/Home/Privacy/Montr%C3%A9al |
| curl 7.87 | (returns the original unchanged) |
## Ada is fast.
On a benchmark where we need to validate and normalize [thousands URLs found
on popular websites](https://github.com/ada-url/url-various-datasets/tree/main/top100),
we find that ada can be several times faster than popular competitors (system: Apple MacBook 2022
with LLVM 14).
```
ada ▏ 188 ns/URL ███▏
servo url ▏ 664 ns/URL ███████████▎
CURL ▏ 1471 ns/URL █████████████████████████
```
Ada has improved the performance of the popular JavaScript environment Node.js:
> Since Node.js 18, a new URL parser dependency was added to Node.js — Ada. This addition bumped the Node.js performance when parsing URLs to a new level. Some results could reach up to an improvement of **400%**. ([State of Node.js Performance 2023](https://blog.rafaelgss.dev/state-of-nodejs-performance-2023))
The Ada library is used by important systems besides Node.js such as Redpanda, Kong, Telegram, DataDog, and Cloudflare Workers.
[](https://www.youtube.com/watch?v=tQ-6OWRDsZg)
### Requirements
The project is otherwise self-contained and it has no dependency.
A recent C++ compiler supporting C++20. We test GCC 12 or better, LLVM 14 or better and Microsoft Visual Studio 2022.
## Installation
Binary packages for the following systems are currently available:
[](https://repology.org/project/ada/versions)
## Quick Start
Linux or macOS users might follow the following instructions if they have a recent C++ compiler installed and a standard utility (`wget`)
1. Pull the library in a directory
```
wget https://github.com/ada-url/ada/releases/download/v3.0.0/ada.cpp
wget https://github.com/ada-url/ada/releases/download/v3.0.0/ada.h
```
2. Create a new file named `demo.cpp` with this content:
```C++
#include "ada.cpp"
#include "ada.h"
#include
int main(int, char *[]) {
auto url = ada::parse("https://www.google.com");
if (!url) {
std::cout << "failure" << std::endl;
return EXIT_FAILURE;
}
url->set_protocol("http");
std::cout << url->get_protocol() << std::endl;
std::cout << url->get_host() << std::endl;
return EXIT_SUCCESS;
}
```
3. Compile
```
c++ -std=c++20 -o demo demo.cpp
```
4. `./demo`
```
http:
www.google.com
```
## Bindings of Ada
The following libraries are maintained by the Ada team and available under [Ada GitHub organization](https://github.com/ada-url).
- [Rust](https://github.com/ada-url/rust): Rust bindings for Ada
- [Go](https://github.com/ada-url/goada): Go bindings for Ada
- [Python](https://github.com/ada-url/python): Python bindings for Ada
### Community maintained
- [R](https://github.com/schochastics/adaR): R wrapper for Ada
- [PHP](https://github.com/lnear-dev/ada-url): PHP Wrapper for Ada URL
- [LuaJIT](https://github.com/bungle/lua-resty-ada): LuaJIT FFI bindings for Ada
- [Zig](https://github.com/braheezy/ada-zig): Unofficial Zig bindings for Ada
- [Python](https://github.com/TkTech/can_ada): Python bindings for Ada
- [React Native](https://github.com/KusStar/react-native-fast-url): A Fast URL and URLSearchParams polyfill for React Native.
- [D](https://github.com/kassane/ada-d): D bindings for Ada, `@nogc`, `nothrow` and `@safe` compat.
- [Nim](https://github.com/ferus-web/nim-ada): High-level Nim abstraction over Ada, uses ORC move semantics to safely and efficiently handle memory.
## Usage
Ada supports two types of URL instances, `ada::url` and `ada::url_aggregator`. The usage is
the same in either case: we have an parsing function template `ada::parse` which can return
either a result of type `ada::result` or of type `ada::result`
depending on your needs. The `ada::url_aggregator` class is smaller and it is backed by a precomputed
serialized URL string. The `ada::url` class is made of several separate strings for the various
components (path, host, and so forth).
### Parsing & Validation
- Parse and validate a URL from an ASCII or a valid UTF-8 string.
```cpp
auto url = ada::parse("https://www.google.com");
if (url) { /* URL is valid */ }
```
After calling 'parse', you *must* check that the result is valid before
accessing it when you are not sure that it will succeed. The following
code is unsafe:
```cpp
auto> url = ada::parse("some bad url");
url->get_href();
```
For simplicity, in the examples below, we skip the check because
we know that parsing succeeds. All strings are assumed to be valid
UTF-8 strings.
## Examples
## URL Parser
```c++
auto url = ada::parse("https://www.google.com");
url->set_username("username"); // Update credentials
url->set_password("password");
// ada->get_href() will return "https://username:password@www.google.com/"
url->set_protocol("wss"); // Update protocol
// url->get_protocol() will return "wss:"
url->set_host("github.com"); // Update host
// url->get_host() will return "github.com"
url->set_port("8080"); // Update port
// url->get_port() will return "8080"
url->set_pathname("/my-super-long-path"); // Update pathname
// url->get_pathname() will return "/my-super-long-path"
url->set_search("target=self"); // Update search
// url->get_search() will return "?target=self"
url->set_hash("is-this-the-real-life"); // Update hash/fragment
// url->get_hash() will return "#is-this-the-real-life"
```
### URL Search Params
```cpp
ada::url_search_params search_params("a=b&c=d&e=f");
search_params.append("g=h");
search_params.get("g"); // will return "h"
auto keys = search_params.get_keys();
while (keys.has_next()) {
auto key = keys.next(); // "a", "c", "e", "g"
}
```
### URLPattern
Our implementation doesn't provide a regex engine and leaves the decision of choosing the right engine to the user.
This is done as a security measure since the default std::regex engine is not safe and open to DDOS attacks.
Runtimes like Node.js and Cloudflare Workers use the V8 regex engine, which is safe and performant.
```cpp
// Define a regex engine that conforms to the following interface
// For example we will use v8 regex engine
class v8_regex_provider {
public:
v8_regex_provider() = default;
using regex_type = v8::Global;
static std::optional create_instance(std::string_view pattern,
bool ignore_case);
static std::optional>> regex_search(
std::string_view input, const regex_type& pattern);
static bool regex_match(std::string_view input, const regex_type& pattern);
};
// Define a URLPattern
auto pattern = ada::parse_url_pattern("/books/:id(\\d+)", "https://example.com");
// Check validity
if (!pattern) { return EXIT_FAILURE; }
// Match a URL
auto match = pattern->match("https://example.com/books/123");
// Test a URL
auto matched = pattern->test("https://example.com/books/123");
```
### C wrapper
See the file `include/ada_c.h` for our C interface. We expect ASCII or UTF-8 strings.
```C
#include "ada_c.h"
#include
#include
#include
#include
static void ada_print(ada_string string) {
printf("%.*s\n", (int)string.length, string.data);
}
int main(int c, char *arg[] ) {
const char* input =
"https://username:password@www.google.com:8080/"
"pathname?query=true#hash-exists";
ada_url url = ada_parse(input, strlen(input));
if(!ada_is_valid(url)) { puts("failure"); return EXIT_FAILURE; }
ada_print(ada_get_href(url)); // prints https://username:password@host:8080/pathname?query=true#hash-exists
ada_print(ada_get_protocol(url)); // prints https:
ada_print(ada_get_username(url)); // prints username
ada_set_href(url, "https://www.yagiz.co", strlen("https://www.yagiz.co"));
if(!ada_is_valid(url)) { puts("failure"); return EXIT_FAILURE; }
ada_set_hash(url, "new-hash", strlen("new-hash"));
ada_set_hostname(url, "new-host", strlen("new-host"));
ada_set_host(url, "changed-host:9090", strlen("changed-host:9090"));
ada_set_pathname(url, "new-pathname", strlen("new-pathname"));
ada_set_search(url, "new-search", strlen("new-search"));
ada_set_protocol(url, "wss", 3);
ada_print(ada_get_href(url)); // will print wss://changed-host:9090/new-pathname?new-search#new-hash
// Manipulating search params
ada_string search = ada_get_search(url);
ada_url_search_params search_params =
ada_parse_search_params(search.data, search.length);
ada_search_params_append(search_params, "a", 1, "b", 1);
ada_owned_string result = ada_search_params_to_string(search_params);
ada_set_search(url, result.data, result.length);
ada_free_owned_string(result);
ada_free_search_params(search_params);
ada_free(url);
return EXIT_SUCCESS;
}
```
When linking against the ada library from C++, be minding that ada requires access to the standard
C++ library. E.g., you may link with the C++ compiler.
E.g., if you grab our single-header C++ files (`ada.cpp` and `ada.h`), as well as the C header (`ada_c.h`),
you can often compile a C program (`demo.c`) as follows under Linux/macOS systems:
```
c++ -c ada.cpp -std=c++20
cc -c demo.c
c++ demo.o ada.o -o cdemo
./cdemo
```
### Command-line interface
For more information about command-line options, please refer to the [CLI documentation](docs/cli.md).
### CMake dependency
See the file `tests/installation/CMakeLists.txt` for an example of how you might use ada from your own
CMake project, after having installed ada on your system.
## Contributing
Contributors are encouraged to read our [AI Tool Policy](AI_USAGE_POLICY.md).
### Building
Ada uses cmake as a build system, but also supports Bazel. It's recommended you to run the following
commands to build it locally.
Without tests:
- **Build**: `cmake -B build && cmake --build build`
With tests (requires git):
- **Build**: `cmake -B build -DADA_TESTING=ON && cmake --build build`
- **Test**: `ctest --output-on-failure --test-dir build`
With tests (requires available local packages):
- **Build**: `cmake -B build -DADA_TESTING=ON -D CPM_USE_LOCAL_PACKAGES=ON && cmake --build build`
- **Test**: `ctest --output-on-failure --test-dir build`
### Build options
Ada provides several CMake options to customize the build:
- `ADA_USE_SIMDUTF`: Enables SIMD-accelerated Unicode processing via simdutf (default: OFF)
Windows users need additional flags to specify the build configuration, e.g. `--config Release`.
The project can also be built via docker using default docker file of repository with following commands.
`docker build -t ada-builder . && docker run --rm -it -v ${PWD}:/repo ada-builder`
### Amalgamation
You may amalgamate all source files into only two files (`ada.h` and `ada.cpp`) by typing executing the Python
3 script `singleheader/amalgamate.py`. By default, the files are created in the `singleheader` directory.
### License
This code is made available under the Apache License 2.0 as well as the MIT license.
Our tests include third-party code and data. The benchmarking code includes third-party code: it is provided for research purposes only and not part of the library.
### Further reading
* Yagiz Nizipli, Daniel Lemire, [Parsing Millions of URLs per Second](https://doi.org/10.1002/spe.3296), Software: Practice and Experience 54(5) May 2024.
## Stars
[](https://www.star-history.com/#ada-url/ada&Date)
ada-url-ada-3c84d73/SECURITY.md 0000664 0000000 0000000 00000000400 15147206404 0015711 0 ustar 00root root 0000000 0000000 # Security Policy
## Reporting a Vulnerability
Please use the following contact information for reporting a vulnerability:
- [Daniel Lemire](https://github.com/lemire) - daniel@lemire.me
- [Yagiz Nizipli](https://github.com/anonrig) - yagiz@nizipli.com
ada-url-ada-3c84d73/ada.pc.in 0000664 0000000 0000000 00000000461 15147206404 0015605 0 ustar 00root root 0000000 0000000 prefix=@CMAKE_INSTALL_PREFIX@
includedir=@PKGCONFIG_INCLUDEDIR@
libdir=@PKGCONFIG_LIBDIR@
Name: @PROJECT_NAME@
Description: @PROJECT_DESCRIPTION@
URL: @PROJECT_HOMEPAGE_URL@
Version: @PROJECT_VERSION@
Cflags: -I${includedir} @PKGCONFIG_CFLAGS@
Libs: -L${libdir} -l@PROJECT_NAME@
@PKGCONFIG_LIBS_PRIVATE@
ada-url-ada-3c84d73/benchmarks/ 0000775 0000000 0000000 00000000000 15147206404 0016243 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/benchmarks/CMakeLists.txt 0000664 0000000 0000000 00000034143 15147206404 0021010 0 ustar 00root root 0000000 0000000 # Performance counters
CPMAddPackage(
NAME counters
GITHUB_REPOSITORY lemire/counters
GIT_TAG v2.2.0
)
# bench_search_params
add_executable(bench_search_params bench_search_params.cpp)
target_link_libraries(bench_search_params PRIVATE ada counters::counters)
add_executable(urlpattern urlpattern.cpp)
target_link_libraries(urlpattern PRIVATE ada counters::counters)
target_include_directories(urlpattern PUBLIC "$")
target_include_directories(urlpattern PUBLIC "$")
# Bench
add_executable(wpt_bench wpt_bench.cpp)
target_link_libraries(wpt_bench PRIVATE ada counters::counters)
target_link_libraries(wpt_bench PRIVATE simdjson)
target_include_directories(wpt_bench PUBLIC "$")
target_include_directories(wpt_bench PUBLIC "$")
# Bench
add_executable(bench bench.cpp)
target_link_libraries(bench PRIVATE ada counters::counters)
target_include_directories(bench PUBLIC "$")
target_include_directories(bench PUBLIC "$")
# Benchdata
CPMAddPackage("gh:ada-url/url-dataset#9749b92c13e970e70409948fa862461191504ccc")
add_executable(benchdata bench.cpp)
target_link_libraries(benchdata PRIVATE ada counters::counters)
target_include_directories(benchdata PUBLIC "$")
target_include_directories(benchdata PUBLIC "$")
target_compile_definitions(benchdata PRIVATE ADA_URL_FILE="${url-dataset_SOURCE_DIR}/out.txt")
target_compile_definitions(benchdata PRIVATE BENCHMARK_PREFIX=BenchData_)
target_compile_definitions(benchdata PRIVATE BENCHMARK_PREFIX_STR="BenchData_")
# BBC Bench
add_executable(bbc_bench bbc_bench.cpp)
target_link_libraries(bbc_bench PRIVATE ada counters::counters)
target_include_directories(bbc_bench PUBLIC "$")
target_include_directories(bbc_bench PUBLIC "$")
# bench_ipv4
add_executable(bench_ipv4 bench_ipv4.cpp)
target_link_libraries(bench_ipv4 PRIVATE ada counters::counters)
target_include_directories(bench_ipv4 PUBLIC "$")
target_include_directories(bench_ipv4 PUBLIC "$")
target_compile_definitions(bench_ipv4 PRIVATE ADA_URL_FILE="${url-dataset_SOURCE_DIR}/out.txt")
# Percent Encode
add_executable(percent_encode percent_encode.cpp)
target_link_libraries(percent_encode PRIVATE ada counters::counters)
target_include_directories(percent_encode PUBLIC "$")
target_include_directories(percent_encode PUBLIC "$")
if(MSVC AND BUILD_SHARED_LIBS)
# Copy the ada dll into the directory
add_custom_command(TARGET percent_encode POST_BUILD # Adds a post-build event
COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake -E copy_if_different..."
"$" # <--this is in-file
"$") # <--this is out-file path
endif()
add_executable(model_bench model_bench.cpp)
target_link_libraries(model_bench PRIVATE ada counters::counters)
target_compile_definitions(model_bench PRIVATE ADA_URL_FILE="${url-dataset_SOURCE_DIR}/out.txt")
target_link_libraries(wpt_bench PRIVATE benchmark::benchmark)
target_link_libraries(bench PRIVATE benchmark::benchmark)
target_link_libraries(benchdata PRIVATE benchmark::benchmark)
target_link_libraries(bbc_bench PRIVATE benchmark::benchmark)
target_link_libraries(bench_ipv4 PRIVATE benchmark::benchmark)
target_link_libraries(percent_encode PRIVATE benchmark::benchmark)
target_link_libraries(bench_search_params PRIVATE benchmark::benchmark)
target_link_libraries(urlpattern PRIVATE benchmark::benchmark)
set(BENCHMARKS wpt_bench bench benchdata bbc_bench bench_ipv4 percent_encode bench_search_params urlpattern)
add_custom_target(run_all_benchmarks
COMMAND ${CMAKE_COMMAND} -E echo "Running all benchmarks..."
)
foreach(benchmark IN LISTS BENCHMARKS)
add_custom_command(
TARGET run_all_benchmarks
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Running ${benchmark}..."
COMMAND $ --benchmark_min_time=1s
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
endforeach()
option(ADA_COMPETITION "Whether to install various competitors." OFF)
# We only build url_whatwg if ICU is found, so we need to make
# finding ICU easy.
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Apple system detected.")
# People who run macOS often use brew.
if(EXISTS /opt/homebrew/opt/icu4c)
message(STATUS "icu is provided by homebrew at /opt/homebrew/opt/icu4c.")
## This is a bit awkward, but it is a lot better than asking the
## user to figure that out.
list(APPEND CMAKE_PREFIX_PATH "/opt/homebrew/opt/icu4c/include")
list(APPEND CMAKE_LIBRARY_PATH "/opt/homebrew/opt/icu4c/lib")
elseif(EXISTS /usr/local/opt/icu4c)
message(STATUS "icu is provided by homebrew at /usr/local/opt/icu4c.")
list(APPEND CMAKE_PREFIX_PATH "/usr/local/opt/icu4c/include")
list(APPEND CMAKE_LIBRARY_PATH "/usr/local/opt/icu4c/lib")
endif()
endif()
find_package(ICU COMPONENTS uc i18n)
### If the user does not have ICU, let us help them with instructions:
if(NOT ICU_FOUND)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
if(EXISTS /opt/homebrew)
message(STATUS "Under macOS, you may install ICU with brew, using 'brew install icu4c'.")
else()
message(STATUS "Under macOS, you should install brew (see https://brew.sh) and then icu4c ('brew install icu4c').")
endif()
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install ICU with a command such as 'apt-get install libicu-dev'." )
endif()
endif(NOT ICU_FOUND)
if(ICU_FOUND)
CPMAddPackage(
NAME url_whatwg
GITHUB_REPOSITORY rmisev/url_whatwg
GIT_TAG 72bcabf
OPTIONS "URL_BUILD_TESTS OFF" "URL_USE_LIBS OFF"
)
add_library(url_whatwg_lib STATIC "${url_whatwg_SOURCE_DIR}/src/url.cpp"
"${url_whatwg_SOURCE_DIR}/src/url_idna.cpp"
"${url_whatwg_SOURCE_DIR}/src/url_ip.cpp"
"${url_whatwg_SOURCE_DIR}/src/url_percent_encode.cpp"
"${url_whatwg_SOURCE_DIR}/src/url_search_params.cpp"
"${url_whatwg_SOURCE_DIR}/src/url_utf.cpp"
"${url_whatwg_SOURCE_DIR}/src/url.cpp")
target_include_directories(url_whatwg_lib PUBLIC "${url_whatwg_SOURCE_DIR}/include")
target_link_libraries(url_whatwg_lib PRIVATE ICU::uc ICU::i18n)
target_link_libraries(bench PRIVATE url_whatwg_lib)
target_link_libraries(benchdata PRIVATE url_whatwg_lib)
target_link_libraries(bbc_bench PRIVATE url_whatwg_lib)
target_link_libraries(wpt_bench PRIVATE url_whatwg_lib)
target_include_directories(bench PUBLIC "${url_whatwg_SOURCE_DIR}")
target_include_directories(benchdata PUBLIC "${url_whatwg_SOURCE_DIR}")
target_include_directories(bbc_bench PUBLIC "${url_whatwg_SOURCE_DIR}")
target_include_directories(wpt_bench PUBLIC "${url_whatwg_SOURCE_DIR}")
target_compile_definitions(bench PRIVATE ADA_url_whatwg_ENABLED=1)
target_compile_definitions(benchdata PRIVATE ADA_url_whatwg_ENABLED=1)
target_compile_definitions(bbc_bench PRIVATE ADA_url_whatwg_ENABLED=1)
target_compile_definitions(wpt_bench PRIVATE ADA_url_whatwg_ENABLED=1)
endif(ICU_FOUND)
if(ADA_COMPETITION)
# URI Parser
CPMAddPackage(
NAME uriparser
GITHUB_REPOSITORY uriparser/uriparser
GIT_TAG 634b678
OPTIONS "URIPARSER_BUILD_TESTS OFF" "URIPARSER_BUILD_DOCS OFF"
)
target_link_libraries(bench PRIVATE uriparser)
target_link_libraries(bbc_bench PRIVATE uriparser)
# URL Parser
CPMAddPackage(
NAME urlparser
GITHUB_REPOSITORY netmindms/urlparser
GIT_TAG 69c09ed
)
add_library(urlparser STATIC "${urlparser_SOURCE_DIR}/src/EdUrlParser.cpp")
target_include_directories(urlparser PUBLIC "${urlparser_SOURCE_DIR}/src")
target_link_libraries(bench PRIVATE urlparser)
target_link_libraries(bbc_bench PRIVATE urlparser)
# HTTP Parser
CPMAddPackage(
NAME httpparser
GITHUB_REPOSITORY nodejs/http-parser
VERSION 2.9.4
)
add_library(httpparser STATIC "${httpparser_SOURCE_DIR}/http_parser.c")
set_source_files_properties("${httpparser_SOURCE_DIR}/http_parser.c" PROPERTIES LANGUAGE C)
target_include_directories(httpparser PUBLIC "${httpparser_SOURCE_DIR}")
target_link_libraries(bench PRIVATE httpparser)
target_link_libraries(bbc_bench PRIVATE httpparser)
target_compile_definitions(bench PRIVATE ADA_VARIOUS_COMPETITION_ENABLED=1)
target_compile_definitions(bbc_bench PRIVATE ADA_VARIOUS_COMPETITION_ENABLED=1)
endif(ADA_COMPETITION)
# CURL
find_package(CURL)
if(CURL_FOUND)
message(STATUS "curl version " ${CURL_VERSION_STRING})
if (CURL_VERSION_STRING VERSION_LESS "7.62.0")
message(STATUS "curl is too old, we need version 7.62.0 or better")
else()
include_directories(${CURL_INCLUDE_DIRS})
if(NOT CURL_LIBRARIES)
target_link_libraries(bench PRIVATE CURL::libcurl)
target_link_libraries(benchdata PRIVATE CURL::libcurl)
target_link_libraries(bbc_bench PRIVATE CURL::libcurl)
else()
target_link_libraries(bench PRIVATE ${CURL_LIBRARIES})
target_link_libraries(benchdata PRIVATE ${CURL_LIBRARIES})
target_link_libraries(bbc_bench PRIVATE ${CURL_LIBRARIES})
endif()
target_compile_definitions(bench PRIVATE ADA_CURL_ENABLED=1)
target_compile_definitions(benchdata PRIVATE ADA_CURL_ENABLED=1)
target_compile_definitions(bbc_bench PRIVATE ADA_CURL_ENABLED=1)
endif()
else(CURL_FOUND)
message(STATUS "Curl not found! Please install the curl library.")
endif(CURL_FOUND)
option(ADA_BOOST_URL "Whether to install boost URL." OFF)
message(STATUS "Compiler is " ${CMAKE_CXX_COMPILER_ID})
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
message(STATUS "Compiler version " ${CMAKE_CXX_COMPILER_VERSION})
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9)
message(STATUS "Compiler is too old, disabling boost url.")
SET(ADA_BOOST_URL OFF CACHE BOOL "Whether to install boost URL." FORCE)
endif()
endif()
# Boost
if(ADA_BOOST_URL)
find_package(
Boost 1.86
COMPONENTS system
)
endif(ADA_BOOST_URL)
if(Boost_FOUND)
CPMAddPackage(
NAME boost_url
GITHUB_REPOSITORY boostorg/url
GIT_TAG boost-1.81.0
)
add_library(boost_url INTERFACE)
target_include_directories(boost_url INTERFACE
"${boost_url_SOURCE_DIR}/include")
target_link_libraries(bench PRIVATE Boost::system)
target_link_libraries(bench PRIVATE boost_url)
target_compile_definitions(bench PRIVATE ADA_BOOST_ENABLED=1)
target_link_libraries(benchdata PRIVATE Boost::system)
target_link_libraries(benchdata PRIVATE boost_url)
target_compile_definitions(benchdata PRIVATE ADA_BOOST_ENABLED=1)
target_link_libraries(bbc_bench PRIVATE Boost::system)
target_link_libraries(bbc_bench PRIVATE boost_url)
target_compile_definitions(bbc_bench PRIVATE ADA_BOOST_ENABLED=1)
else(Boost_FOUND)
if(ADA_BOOST_URL)
message(STATUS "Boost 1.80 or better was not found, please install it for benchmarking purposes.")
endif(ADA_BOOST_URL)
endif(Boost_FOUND)
# Zuri
find_package(ZURI QUIET)
if(ZURI_FOUND)
message(STATUS "Zuri found")
target_link_libraries(bench PRIVATE zuri)
target_link_libraries(benchdata PRIVATE zuri)
target_link_libraries(bbc_bench PRIVATE zuri)
target_compile_definitions(bench PRIVATE ADA_ZURI_ENABLED=1)
target_compile_definitions(benchdata PRIVATE ADA_ZURI_ENABLED=1)
target_compile_definitions(bbc_bench PRIVATE ADA_ZURI_ENABLED=1)
else(ZURI_FOUND)
message(STATUS "Zuri not found! Please install to include in benchmark.")
endif(ZURI_FOUND)
if(NOT WIN32)
# We want the check whether Rust is available before trying to build a crate.
CPMAddPackage(
NAME corrosion
GITHUB_REPOSITORY corrosion-rs/corrosion
VERSION 0.5.0
DOWNLOAD_ONLY ON
OPTIONS "Rust_FIND_QUIETLY OFF"
)
include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake")
endif()
if(RUST_FOUND)
message(STATUS "Rust found: " ${Rust_VERSION} )
add_subdirectory("${corrosion_SOURCE_DIR}" "${PROJECT_BINARY_DIR}/_deps/corrosion" EXCLUDE_FROM_ALL)
# Important: we want to build in release mode!
corrosion_import_crate(MANIFEST_PATH "competitors/servo-url/Cargo.toml" NO_LINKER_OVERRIDE PROFILE release)
# Check if servo-url target was created successfully
if(TARGET servo_url)
message(STATUS "servo_url target was created. Linking benchmarks and servo_url. ")
target_link_libraries(bench PRIVATE servo_url)
target_compile_definitions(bench PRIVATE ADA_RUST_VERSION="${Rust_VERSION}")
target_link_libraries(benchdata PRIVATE servo_url)
target_compile_definitions(benchdata PRIVATE ADA_RUST_VERSION="${Rust_VERSION}")
target_link_libraries(bbc_bench PRIVATE servo_url)
target_compile_definitions(bbc_bench PRIVATE ADA_RUST_VERSION="${Rust_VERSION}")
target_link_libraries(percent_encode PRIVATE servo_url)
target_compile_definitions(percent_encode PRIVATE ADA_RUST_VERSION="${Rust_VERSION}")
target_link_libraries(wpt_bench PRIVATE servo_url)
target_compile_definitions(wpt_bench PRIVATE ADA_RUST_VERSION="${Rust_VERSION}")
else()
message(SEND_ERROR "Rust servo-url target was not created successfully. Likely due to a bug.")
endif()
else()
message(STATUS "Rust/Cargo is unavailable." )
message(STATUS "We will not benchmark servo-url." )
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
message(STATUS "Under macOS, you may be able to install rust with")
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "Under Linux, you may be able to install rust with a command such as")
message(STATUS "apt-get install cargo" )
message(STATUS "or" )
message(STATUS "curl https://sh.rustup.rs -sSf | sh")
endif()
endif()
ada-url-ada-3c84d73/benchmarks/bbc_bench.cpp 0000664 0000000 0000000 00000002654 15147206404 0020643 0 ustar 00root root 0000000 0000000 #include "benchmark_header.h"
/**
* Realistic URL examples collected from the BBC homepage.
*/
std::string url_examples[] = {
"https://static.files.bbci.co.uk/orbit/737a4ee2bed596eb65afc4d2ce9af568/js/"
"polyfills.js",
"https://static.files.bbci.co.uk/orbit/737a4ee2bed596eb65afc4d2ce9af568/"
"css/orbit-v5-ltr.min.css",
"https://static.files.bbci.co.uk/orbit/737a4ee2bed596eb65afc4d2ce9af568/js/"
"require.min.js",
"https://static.files.bbci.co.uk/fonts/reith/2.512/BBCReithSans_W_Rg.woff2",
"https://nav.files.bbci.co.uk/searchbox/c8bfe8595e453f2b9483fda4074e9d15/"
"css/box.css",
"https://static.files.bbci.co.uk/cookies/d3bb303e79f041fec95388e04f84e716/"
"cookie-banner/cookie-library.bundle.js",
"https://static.files.bbci.co.uk/account/id-cta/597/style/id-cta.css",
"https://gn-web-assets.api.bbc.com/wwhp/"
"20220908-1153-091014d07889c842a7bdc06e00fa711c9e04f049/responsive/css/"
"old-ie.min.css",
"https://gn-web-assets.api.bbc.com/wwhp/"
"20220908-1153-091014d07889c842a7bdc06e00fa711c9e04f049/modules/vendor/"
"bower/modernizr/modernizr.js"};
void init_data(const char* v = nullptr) {}
double url_examples_bytes = []() -> double {
size_t bytes{0};
for (std::string& url_string : url_examples) {
bytes += url_string.size();
}
return double(bytes);
}();
#define BENCHMARK_PREFIX BBC_
#define BENCHMARK_PREFIX_STR "BBC_"
#include "benchmark_template.cpp"
ada-url-ada-3c84d73/benchmarks/bench.cpp 0000664 0000000 0000000 00000004550 15147206404 0020032 0 ustar 00root root 0000000 0000000 #include "benchmark_header.h"
/**
* Realistic URL examples collected on the actual web.
*/
std::string url_examples_default[] = {
"https://www.google.com/"
"webhp?hl=en&ictx=2&sa=X&ved=0ahUKEwil_"
"oSxzJj8AhVtEFkFHTHnCGQQPQgI",
"https://support.google.com/websearch/"
"?p=ws_results_help&hl=en-CA&fg=1",
"https://en.wikipedia.org/wiki/Dog#Roles_with_humans",
"https://www.tiktok.com/@aguyandagolden/video/7133277734310038830",
"https://business.twitter.com/en/help/troubleshooting/"
"how-twitter-ads-work.html?ref=web-twc-ao-gbl-adsinfo&utm_source=twc&utm_"
"medium=web&utm_campaign=ao&utm_content=adsinfo",
"https://images-na.ssl-images-amazon.com/images/I/"
"41Gc3C8UysL.css?AUIClients/AmazonGatewayAuiAssets",
"https://www.reddit.com/?after=t3_zvz1ze",
"https://www.reddit.com/login/?dest=https%3A%2F%2Fwww.reddit.com%2F",
"postgresql://other:9818274x1!!@localhost:5432/"
"otherdb?connect_timeout=10&application_name=myapp",
"http://192.168.1.1", // ipv4
"http://[2606:4700:4700::1111]", // ipv6
};
std::vector url_examples;
double url_examples_bytes = []() -> double {
size_t bytes{0};
for (std::string& url_string : url_examples) {
bytes += url_string.size();
}
return double(bytes);
}();
#ifdef ADA_URL_FILE
const char* default_file = ADA_URL_FILE;
#else
const char* default_file = nullptr;
#endif
size_t init_data(const char* input = default_file) {
// compute the number of bytes.
auto compute = []() -> double {
size_t bytes{0};
for (std::string& url_string : url_examples) {
bytes += url_string.size();
}
return double(bytes);
};
if (input == nullptr) {
for (const std::string& s : url_examples_default) {
url_examples.emplace_back(s);
}
url_examples_bytes = compute();
return url_examples.size();
}
if (!file_exists(input)) {
std::cout << "File not found !" << input << std::endl;
for (const std::string& s : url_examples_default) {
url_examples.emplace_back(s);
}
} else {
std::cout << "Loading " << input << std::endl;
url_examples = split_string(read_file(input));
}
url_examples_bytes = compute();
return url_examples.size();
}
#ifndef BENCHMARK_PREFIX
#define BENCHMARK_PREFIX Bench_
#define BENCHMARK_PREFIX_STR "Bench_"
#endif
#include "benchmark_template.cpp"
ada-url-ada-3c84d73/benchmarks/bench_ipv4.cpp 0000664 0000000 0000000 00000023264 15147206404 0020777 0 ustar 00root root 0000000 0000000 #include "benchmark_header.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace {
const std::string_view kIpv4NonDecimalUrls[] = {
"http://0x7f.0x0.0x0.0x1",
"http://0177.000.000.001",
"http://0x7f.1.2.03",
"http://0x7f.000.00.000",
"http://000.000.000.000",
"http://0x.0x.0x.0x",
"http://0300.0250.0001.0001",
"http://0xc0.0xa8.0x01.0x01",
"http://3232235777",
"http://0xc0a80101",
"http://030052000401",
"http://127.1",
"http://127.0.1",
"http://0x7f.1",
"http://0177.1",
"http://0300.0xa8.1.1",
"http://192.168.0x1.01",
"http://0x0.0x0.0x0.0x0",
"http://0.0.0.0x0",
"http://022.022.022.022",
"http://0x12.0x12.0x12.0x12",
"http://0xff.0xff.0xff.0xff",
"http://0377.0377.0377.0377",
"http://4294967295",
"http://0xffffffff",
"http://0x00.0x00.0x00.0x00",
"http://00000.00000.00000.00000",
"http://1.0x2.03.4",
"http://0x1.2.0x3.4",
"http://0.01.0x02.3"};
const std::string_view kDnsFallbackUrls[] = {
"http://example.com", "http://www.google.com",
"http://localhost", "http://foo.bar",
"http://github.com", "http://microsoft.com",
"http://aws.amazon.com", "http://adaparser.com",
"http://www.wikipedia.org", "http://www.apple.com",
"http://www.amazon.com", "http://www.facebook.com",
"http://www.twitter.com", "http://www.instagram.com",
"http://www.linkedin.com", "http://www.reddit.com",
"http://www.netflix.com", "http://www.youtube.com",
"http://www.bing.com", "http://www.yahoo.com"};
#ifdef ADA_URL_FILE
const char* default_dns_file = ADA_URL_FILE;
#else
const char* default_dns_file = nullptr;
#endif
double bytes_for(const std::vector& urls) {
size_t bytes = 0;
for (auto url : urls) {
bytes += url.size();
}
return double(bytes);
}
std::vector make_permutation(size_t count, uint64_t seed) {
std::vector order(count);
std::iota(order.begin(), order.end(), 0);
if (count < 2) return order;
std::mt19937_64 rng(seed);
std::shuffle(order.begin(), order.end(), rng);
return order;
}
std::vector make_strides(size_t count) {
std::vector strides;
if (count > 1) {
for (size_t s = 1; s < std::min(count, size_t(100)); ++s) {
if (std::gcd(s, count) == 1) strides.push_back(s);
}
}
if (strides.empty()) strides.push_back(1);
return strides;
}
bool file_exists(const char* filename) {
std::ifstream file(filename);
return file.good();
}
std::string read_file(const char* filename) {
std::ifstream file(filename);
if (!file.is_open()) {
return "";
}
return std::string((std::istreambuf_iterator(file)),
std::istreambuf_iterator());
}
std::vector split_string(const std::string& s,
char delimiter = '\n') {
std::vector tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter)) {
if (!token.empty()) {
tokens.push_back(token);
}
}
return tokens;
}
template
void run_benchmark(benchmark::State& state,
const std::vector& urls) {
if (urls.empty()) return;
double bytes = bytes_for(urls);
size_t count = urls.size();
auto order = make_permutation(count, 0x12345678);
auto strides = make_strides(count);
size_t iter = 0;
volatile size_t success = 0;
for (auto _ : state) {
size_t stride = strides[iter % strides.size()];
size_t pos = iter % count;
for (size_t i = 0; i < count; ++i) {
auto result = ada::parse(urls[order[pos]]);
if (result) {
success++;
}
benchmark::DoNotOptimize(result);
pos += stride;
if (pos >= count) pos -= count;
}
benchmark::ClobberMemory();
++iter;
}
(void)success;
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
size_t stride = strides[i % strides.size()];
size_t pos = i % count;
for (size_t j = 0; j < count; ++j) {
auto result = ada::parse(urls[order[pos]]);
if (result) {
success++;
}
benchmark::DoNotOptimize(result);
pos += stride;
if (pos >= count) pos -= count;
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["branch_misses/url"] =
aggregate.best.branch_misses() / count;
state.counters["branches/url"] = aggregate.best.branches() / count;
state.counters["cycles/url"] = aggregate.best.cycles() / count;
state.counters["instructions/url"] = aggregate.best.instructions() / count;
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] = aggregate.best.instructions() / bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] = aggregate.best.elapsed_ns() / count;
state.counters["cycle/byte"] = aggregate.best.cycles() / bytes;
}
state.counters["time/byte"] =
benchmark::Counter(bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] = benchmark::Counter(
double(count), benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] =
benchmark::Counter(bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] = benchmark::Counter(
double(count), benchmark::Counter::kIsIterationInvariantRate);
}
struct DataGenerator {
std::vector storage;
std::vector views;
static const std::vector& GetDecimalWorkload() {
static DataGenerator instance = []() {
DataGenerator gen;
constexpr size_t count = 5000;
std::mt19937 rng(42);
std::uniform_int_distribution octet(0, 255);
gen.storage.reserve(count);
gen.views.reserve(count);
for (size_t i = 0; i < count; ++i) {
std::string ip = "http://" + std::to_string(octet(rng)) + "." +
std::to_string(octet(rng)) + "." +
std::to_string(octet(rng)) + "." +
std::to_string(octet(rng));
gen.storage.push_back(std::move(ip));
gen.views.push_back(gen.storage.back());
}
return gen;
}();
return instance.views;
}
static const std::vector& GetNonDecimalWorkload() {
static DataGenerator instance = []() {
DataGenerator gen;
constexpr size_t count = 5000;
size_t src_len = std::size(kIpv4NonDecimalUrls);
gen.views.reserve(count);
for (size_t i = 0; i < count; ++i) {
gen.views.push_back(kIpv4NonDecimalUrls[i % src_len]);
}
return gen;
}();
return instance.views;
}
static const std::vector& GetDnsWorkload() {
static DataGenerator instance = []() {
DataGenerator gen;
// Try to load from file
if (default_dns_file && file_exists(default_dns_file)) {
std::cout << "# Loading DNS data from: " << default_dns_file
<< std::endl;
std::string content = read_file(default_dns_file);
gen.storage = split_string(content);
gen.views.reserve(gen.storage.size());
for (const auto& s : gen.storage) {
gen.views.push_back(s);
}
}
// Fallback if file load failed or empty
if (gen.views.empty()) {
std::cout << "# Loading built-in DNS fallback data" << std::endl;
size_t count = 2000;
size_t src_len = std::size(kDnsFallbackUrls);
gen.views.reserve(count);
for (size_t i = 0; i < count; ++i) {
gen.views.push_back(kDnsFallbackUrls[i % src_len]);
}
}
return gen;
}();
return instance.views;
}
};
} // namespace
static void Bench_IPv4_Decimal_AdaURL(benchmark::State& state) {
run_benchmark(state, DataGenerator::GetDecimalWorkload());
}
BENCHMARK(Bench_IPv4_Decimal_AdaURL);
static void Bench_IPv4_Decimal_Aggregator(benchmark::State& state) {
run_benchmark(state,
DataGenerator::GetDecimalWorkload());
}
BENCHMARK(Bench_IPv4_Decimal_Aggregator);
static void Bench_IPv4_NonDecimal_AdaURL(benchmark::State& state) {
run_benchmark(state, DataGenerator::GetNonDecimalWorkload());
}
BENCHMARK(Bench_IPv4_NonDecimal_AdaURL);
static void Bench_IPv4_NonDecimal_Aggregator(benchmark::State& state) {
run_benchmark(state,
DataGenerator::GetNonDecimalWorkload());
}
BENCHMARK(Bench_IPv4_NonDecimal_Aggregator);
static void Bench_DNS_AdaURL(benchmark::State& state) {
run_benchmark(state, DataGenerator::GetDnsWorkload());
}
BENCHMARK(Bench_DNS_AdaURL);
static void Bench_DNS_Aggregator(benchmark::State& state) {
run_benchmark(state, DataGenerator::GetDnsWorkload());
}
BENCHMARK(Bench_DNS_Aggregator);
int main(int argc, char** argv) {
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
}
ada-url-ada-3c84d73/benchmarks/bench_search_params.cpp 0000664 0000000 0000000 00000042650 15147206404 0022725 0 ustar 00root root 0000000 0000000 #include "benchmark_header.h"
/**
* Realistic URL examples collected from Indeed.com, see
* https://github.com/ada-url/ada/pull/459#issuecomment-1624187633
*/
std::string url_examples_default[] = {
"https://secure.indeed.com/"
"auth?continue=https%3A%2F%2Fm5.apply.indeed.com%2Fbeta%2Findeedapply%"
"2Fresumeapply%3FdraftId%3Dd2f89678-c675-4dd6-8776-c7de2df808cc-Y21o%"
"26draftDc%3Dcmh%26postUrl%3Dhttp%253A%252F%252Fmuffit%252Fprocess-"
"indeedapply%26jk%3D4ce8c8f85737012d%26mob%3D0%26referer%3Dhttps%253A%252F%"
"252Fwww.indeed.com%252F%26formParent%3D%26hl%3Den_US%26jobTitle%"
"3DEmbedded%2BSoftware%2BEngineer%26questions%3Diq%253A%252F%"
"252F5a5f158dfd632ec505eb%253Fv%253D1%26twoPaneVjAllocId%3D%"
"26onappliedstatus%3D_updateIndeedApplyStatus%26preload%3D0%26autoString%"
"3Dnone%26iip%3D1%26recentsearchquery%3D%257B%2522what%2522%253A%"
"2522software%2Bengineer%2522%252C%2522where%2522%253A%2522austin%252C%"
"2Btx%2522%257D%26isCreateIAJobApiSuccess%3Dfalse%26onclose%"
"3DindeedApplyHandleModalClose%26onContinueClick%"
"3DindeedApplyHandleModalClose%26jobUrl%3Dhttps%253A%252F%252Fwww.indeed."
"com%252Fviewjob%253Fjk%253D4ce8c8f85737012d%26onready%3D_onButtonReady%"
"26onapplied%3DindeedApplyHandleApply%26href%3Dhttps%253A%252F%252Fwww."
"indeed.com%252Fviewjob%253Fjk%253D4ce8c8f85737012d%2526from%253Dmobhp_"
"jobfeed_auto%2526tk%253D1h4m9jbiui7lq801%2526viewtype%253Dembedded%"
"2526advn%253D2919294681304046%2526adid%253D409899006%2526xkcb%253DSoCq-_"
"M3NWbCoeUCiZ0LbzkdCdPP%2526topwindowlocation%253D%25252F%26coverletter%"
"3DOPTIONAL%26resume%3Drequired%26twoPaneAllocId%3D%26jobMeta%3D%257B%"
"2526quot%253Bvtk%2526quot%253B%253A%2526quot%253B1h4m9jddo28q3001%"
"2526quot%253B%252C%2526quot%253Btk%2526quot%253B%253A%2526quot%"
"253B1h4m9jbiui7lq801%2526quot%253B%257D%26src%3Didd%26ms%3D1688670424981%"
"26jobCompany%3DSigmaSense%252C%2BLLC%26onclick%"
"3DindeedApplyHandleButtonClick%26pingbackUrl%3Dhttps%253A%252F%252Fgdc."
"indeed.com%252Fconv%252ForgIndApp%253Fco%253DUS%2526vjtk%"
"253D1h4m9jddo28q3001%2526jk%253D4ce8c8f85737012d%2526mvj%253D0%2526tk%"
"253D1h4m9jbiui7lq801%2526trk.origin%253Djobsearch%2526sj%253D1%2526vjfrom%"
"253Dmobhp_jobfeed_auto%2526advn%253D2919294681304046%2526adid%"
"253D409899006%2526ad%253D-6NYlbfkN0BLmp7eN89U-"
"imdIS3k1HPy83nFSQVS0CyWSe3vCO57TwIlXkEWIh-"
"pJhJKr5e0ECbg2AnsbYecK2l6IQRkcmJAo04wMd0HwXw9frAU8JSwJ1mjwcEN4QeCXiILN_"
"wIA4Wr_ywZCGdozVPXXsoaJzqbyZBeGNAHJQuiHvWOxPzh1LKLSr_"
"pFbOxn1NmCOkmvvMW36P569CcM6K7a7vOkj32OJUAg8NT_"
"oipaaUGwXpvKlH6ebfTW6B3WWuJtZ9tsQNwH330zZOVkF1mhjr837W2e-OaEjikG0Nrqh-"
"9DFBdDUmSLosfcp0hGtARFGYWfp7xU-897-fsivVLte1sPZhzSqWn9P_"
"D9hHnfmG2LZnTVBp3Jx6QcGng4-U5K8v9KFx7XN9GjcqQum735VDirUpQ61ZT-"
"WOT5Ilm1xI3nNocOcUQJELhqt6WiAgSIyvTKw7SAfCj2fzp0DshQHzxqVdhe-"
"iJ9apJI0JWZa195l_ZNFYvu8-rusj79RaBev9_"
"LPbejUXOZON2MDA37bFHRZsyWNXOCCKl0tswubGZku70sD7HVHm5aYYINKdL_"
"uKogRuW4r7C99AU69eZMUJF78gl%2526xkcb%253DSoCq-_M3NWbCoeUCiZ0LbzkdCdPP%"
"2526astse%253Dad9474a7b6ec862d%2526assa%253D8360%26co%3DUS%26advNum%"
"3D2919294681304046%26noButtonUI%3Dtrue%26iaUid%3D1h4m9je9qjcbf800%26spn%"
"3D1%26jobId%3D5a5f158dfd632ec505eb%26isITA%3D0%26apiToken%"
"3Daa102235a5ccb18bd3668c0e14aa3ea7e2503cfac2a7a9bf3d6549899e125af4%"
"26jobLocation%3DAustin%252C%2BTX%2B78758%26twoPaneGroup%3D-1%"
"26indeedcsrftoken%3D7bG1QaY6YSlr3rfgMbu9YRVPyk1v2TF0%26phone%3DOPTIONAL%"
"26jobApplies%3D-1%26twoPaneVjGroup%3D-1%26returnToJobSearchUrl%3Dhttp%"
"253A%252F%252Fwww.indeed.com%252F%26indeedApplyableJobApiURI%3D&cfb=2&obo="
"http%3A%2F%2Fwww.indeed.com%2F&hl=en_US&from=indapply-login-SmartApply&"
"branding=indeed-apply",
//
"https://secure.indeed.com/"
"auth?continue=https%3A%2F%2Fm5.apply.indeed.com%2Fbeta%2Findeedapply%"
"2Fresumeapply%3FdraftId%3Dcd45b794-ede7-48a2-a143-6023319e90a4-Y21o%"
"26draftDc%3Dcmh%26postUrl%3Dhttps%253A%252F%252Fapply.workable.com%"
"252Fapi%252Fv1%252Fjobboards%252Findeed%252Fjobs%252FEC33BF8806%252Fapply%"
"26jk%3D0ffb6f7ed64d3bae%26mob%3D0%26referer%3Dhttps%253A%252F%252Fwww."
"indeed.com%252F%26formParent%3D%26hl%3Den_US%26jobTitle%3DEmbedded%"
"2BSoftware%2BEngineer%26questions%3Dhttps%253A%252F%252Fapply.workable."
"com%252Fapi%252Fv1%252Fjobboards%252Findeed%252Fjobs%252FEC33BF8806%"
"252Fquestions%26twoPaneVjAllocId%3D%26onappliedstatus%3D_"
"updateIndeedApplyStatus%26preload%3D0%26autoString%3Dnone%26iip%3D1%"
"26recentsearchquery%3D%257B%2522what%2522%253A%2522software%2Bengineer%"
"2522%252C%2522where%2522%253A%2522austin%252C%2Btx%2522%257D%"
"26isCreateIAJobApiSuccess%3Dfalse%26onclose%3DindeedApplyHandleModalClose%"
"26onContinueClick%3DindeedApplyHandleModalClose%26jobUrl%3Dhttps%253A%"
"252F%252Fwww.indeed.com%252Fviewjob%253Fjk%253D0ffb6f7ed64d3bae%26onready%"
"3D_onButtonReady%26onapplied%3DindeedApplyHandleApply%26href%3Dhttps%253A%"
"252F%252Fwww.indeed.com%252Fviewjob%253Fjk%253D0ffb6f7ed64d3bae%2526from%"
"253Dhp%2526tk%253D1h4m9jbiui7lq801%2526viewtype%253Dembedded%2526advn%"
"253D2169897021852324%2526adid%253D412530207%2526xkcb%253DSoDv-_"
"M3NWbCoe0CiZ0LbzkdCdPP%2526topwindowlocation%253D%25252F%26coverletter%3D%"
"26twoPaneAllocId%3D%26src%3Didd%26ms%3D1688670502027%26jobCompany%3DShift%"
"2BRobotics%26onclick%3DindeedApplyHandleButtonClick%26pingbackUrl%3Dhttps%"
"253A%252F%252Fgdc.indeed.com%252Fconv%252ForgIndApp%253Fco%253DUS%"
"2526vjtk%253D1h4m9ltcgii2t800%2526jk%253D0ffb6f7ed64d3bae%2526mvj%253D0%"
"2526tk%253D1h4m9jbiui7lq801%2526trk.origin%253Djobsearch%2526sj%253D1%"
"2526vjfrom%253Dhp%2526advn%253D2169897021852324%2526adid%253D412530207%"
"2526ad%253D-6NYlbfkN0ADTLHW1lVcttxG1n9WEfcRI1-"
"ixIWqaQXrnishWQ6BGJjne4HH5OGRzbL9TFjFzxuxk65rhcUupJlJ21QkpPLqd89n0B4cMJw-"
"xmaYdF9-dzypunDDP4jQEuuhT-tpejJCNc8jlBI6FGBAtkAXuipq96Z-"
"vOtd24jCWqboqknQBia2fKh5sYbqLv3E7C6vlBmxO2FH4-qm1_"
"vkeeUq1lsktOtkKCFK2RSR5V5xbkBHcu0hkuZAShjpg2ro3F4e9VbP5_"
"tC3BKSqdL9un4SibeC59V880-mAhOnU_"
"yhuURbniZCCFxjEH66D3euJEOSBZDVnpK0jsbAbxwAnx9dtEdC_"
"HG3BG2PgUf9uwPA8SgdtHuhTAkToYjDBF1l5ENrF3WSXIMTCANToEbE3FpgMwNgOkTDf_"
"4E0Zf-vZ5LjmNY_8q8gL9SwhL6dAsnb-iH5Nm9OGEI32LTlhl9KtszAFZ99UGlzmRjo_"
"iD7ienJa3zd_Ebh_NZWkb_4pEKal6--pSAPlVPbC6azvhPiBzQgMhzpUS9Z-7YYhU%25253D%"
"2526xkcb%253DSoDv-_M3NWbCoe0CiZ0LbzkdCdPP%2526astse%253Dc630be9cfe791df9%"
"2526assa%253D240%26co%3DUS%26advNum%3D2169897021852324%26noButtonUI%"
"3Dtrue%26iaUid%3D1h4m9lujpkblm800%26spn%3D1%26jobId%3D5F6DD26C1B%26isITA%"
"3D0%26apiToken%"
"3D3a51613a4d8b9799d352130065868b0c34bce36cee7f4dffa3ed16b0c7936634%"
"26jobLocation%3DAustin%252C%2BTexas%252C%2BUnited%2BStates%26twoPaneGroup%"
"3D-1%26indeedcsrftoken%3D7bG1QaY6YSlr3rfgMbu9YRVPyk1v2TF0%26phone%"
"3Doptional%26jobApplies%3D-1%26twoPaneVjGroup%3D-1%26returnToJobSearchUrl%"
"3Dhttp%253A%252F%252Fwww.indeed.com%252F%26indeedApplyableJobApiURI%3D&"
"cfb=2&obo=http%3A%2F%2Fwww.indeed.com%2F&hl=en_US&from=indapply-login-"
"SmartApply&branding=indeed-apply",
//
"https://secure.indeed.com/"
"auth?hl=en_US&co=US&continue=https%3A%2F%2Fwww.indeed.com%"
"2Fthirdpartysignin%3Fjk%3D67557c870d9debaf%26from%3Dhp%26from%3Djsfe-"
"3pintercept-viewjob%26tk%3D1h4m9jbiui7lq801%26viewtype%3Dembedded%26advn%"
"3D8187210054516026%26adid%3D378267801%26ad%3D-6NYlbfkN0CfpH2aSe_"
"yWN7pjV6WFrWU4hEZi9Btn9eCdDUBIhjK5M5mY81rEexvugfeSup1QuHOvw9d5hvgsJ79xiL2b"
"Cis9Y8r23bY8qvwxN3cXtMQH5eaPpn4zk1QcFRVOjQFg-"
"0YX6StKUcjnJroSlWw3vVqor9zKJ4mUJ-Ksql7DBTYyyZGXojbnMo-"
"neBlW1zDoHnAAl1ZZZa38U8p1jl35T8o9uwhvY3mVw2XDdmKpKawVuyFfiNGl3_"
"jyLBWarAGLeTBHVsVlBONBK8GK4zH1pVL31V4M43uQUjWUhjRqH4lnq92jt7uCHE97bhKm2hMo"
"6dpJ6I-"
"1REKDf9gE0gloVW3r2lBI2TpIWbePg2zuBg4CnvYaRAm7elrbL8hYuiPYtB3hjTkldS_IYH3-"
"NgunawHQ-"
"LwIxAO35DyDhaY1DrGuFWaTQj6f1JlddpnImKhUaKP3jgV0q9uKoQxvyyFhLOlLGDxfMsVecGZ"
"B4lwuUK0TE74Qix1iR26X1QtEguPk8yp8DQZ-AfOqT_"
"S7A0PtcI2eI0sLM1y3BHB3p0KdpYJUsDv02t7UYO_gNEmMOmcsr5gLsmE-cu52BF_"
"n2lEDE3kKpIKqMu91dFTmI25H393tb-"
"PfCUfVAVaUveXuO2hjWSctjtFCo9RPl6ix3ilDs1QgKt08BtT4IUb5I24JlxIJXNvkHhkH75vw"
"PH9SHKr5XfuN32rOCTUr9JWLmVEcQ4x5A0pHUXQRyz8OxdfsifIibHB8SpDYTtyY50lSL4sAe3"
"M4PDq0d54xfqWuSQqhGqo0lE944k8JjiQue8M1cIcqpssOOqE8SIi-"
"hDdv1KG0G1kQuLBIYMzzrGCJ6WDZm_KbLiyK0wTrPf2cWfHIyU1JI1pdWKbK6fop_"
"kuNd3OBEAl00YETNwOrg4HrZdK8NXEkG_QWXA-A0nYxFWz58uoHND5rkyVDO0o%26xkcb%"
"3DSoBZ-_M3NWbCoZUCiZ0LbzkdCdPP%26topwindowlocation%3D%252F%253Fadvn%"
"253D2169897021852324%2526vjk%253D0ffb6f7ed64d3bae%26vjtk%"
"3D1h4m9npiq21a4002&from=jsfe-3pintercept-viewjob&branding=third-party-"
"applies",
//
"https://secure.indeed.com/"
"auth?continue=https%3A%2F%2Fm5.apply.indeed.com%2Fbeta%2Findeedapply%"
"2Fresumeapply%3FdraftId%3Dde4f06da-7b31-465c-96d2-80f791a85bf7-Y21o%"
"26draftDc%3Dcmh%26postUrl%3Dhttp%253A%252F%252Fmuffit%252Fprocess-"
"indeedapply%26jk%3D7590bdb1fe928d49%26mob%3D0%26referer%3Dhttps%253A%252F%"
"252Fwww.indeed.com%252F%253Fvjk%253D4ce8c8f85737012d%2526advn%"
"253D2919294681304046%26formParent%3D%26hl%3Den_US%26jobTitle%3DSenior%"
"2BSoftware%2BDeveloper%2B%2528onsite%2529%26questions%3Diq%253A%252F%"
"252F0efc2325f6b4a2c5bc27%253Fv%253D1%26twoPaneVjAllocId%3D%"
"26onappliedstatus%3D_updateIndeedApplyStatus%26preload%3D0%26autoString%"
"3Dnone%26iip%3D1%26recentsearchquery%3D%257B%2522what%2522%253A%"
"2522software%2Bengineer%2522%252C%2522where%2522%253A%2522austin%252C%"
"2Btx%2522%257D%26isCreateIAJobApiSuccess%3Dfalse%26onclose%"
"3DindeedApplyHandleModalClose%26onContinueClick%"
"3DindeedApplyHandleModalClose%26jobUrl%3Dhttps%253A%252F%252Fwww.indeed."
"com%252Fviewjob%253Fjk%253D7590bdb1fe928d49%26onready%3D_onButtonReady%"
"26onapplied%3DindeedApplyHandleApply%26href%3Dhttps%253A%252F%252Fwww."
"indeed.com%252Fviewjob%253Fjk%253D7590bdb1fe928d49%2526from%253Dhp%2526tk%"
"253D1h4m9jbiui7lq801%2526viewtype%253Dembedded%2526advn%"
"253D5522285726153717%2526adid%253D414206073%2526xkcb%253DSoDt-_"
"M3NWbCoZUCiZ0KbzkdCdPP%2526topwindowlocation%253D%25252F%25253Fvjk%"
"25253D4ce8c8f85737012d%252526advn%25253D2919294681304046%26coverletter%"
"3DOPTIONAL%26resume%3Drequired%26twoPaneAllocId%3D%26jobMeta%3D%257B%"
"2526quot%253Bvtk%2526quot%253B%253A%2526quot%253B1h4m9oh7mirks800%"
"2526quot%253B%252C%2526quot%253Btk%2526quot%253B%253A%2526quot%"
"253B1h4m9jbiui7lq801%2526quot%253B%257D%26src%3Didd%26ms%3D1688670587917%"
"26jobCompany%3DCitizens%2BInc%26onclick%3DindeedApplyHandleButtonClick%"
"26pingbackUrl%3Dhttps%253A%252F%252Fgdc.indeed.com%252Fconv%252ForgIndApp%"
"253Fco%253DUS%2526vjtk%253D1h4m9oh7mirks800%2526jk%253D7590bdb1fe928d49%"
"2526mvj%253D0%2526tk%253D1h4m9jbiui7lq801%2526trk.origin%253Djobsearch%"
"2526sj%253D1%2526vjfrom%253Dhp%2526advn%253D5522285726153717%2526adid%"
"253D414206073%2526ad%253D-"
"6NYlbfkN0CHSAkotDdvvZVbhOqFdbxXOHJMhXe1DXuaBPnaU5fYte-"
"aud5Z0lqoqFyp33jrJfy1DYFhCWCqBjAqfX3PBXom-d5E4gy3cqbwZuMtWn4flXO-"
"Fd9DkMZrQjqK002kTnGqvqfkH0ftIspK3hwJPRmAEy7EY87A9OOFRyFmxA9AdiimsdRWyksA-"
"nCQ0w1VI28XDuVMu7qO_D46dH-"
"dtW5jWIG4jTe8HCv21447lFobYgFb9oJdF8NrjyCNP4fdGeojlELmcjS5cvC5dKfXi8IZm4sWW"
"-7b5SBQKvBMmSVDjiTsgYZS6lb8B-"
"a3YF1Lny7hpNfClmOcLe49wiZAG9LWJ7uRUEfzOPrUCwxdHNQK-vEo3ZhDK4AeER-"
"LfOUabNSjrKz7_91l8sQjBNOR-FJ25ioX0sqoNByLfJC7cWzjDxqvW-l82GsWQR2O_"
"6Khe2oq91fjVXMAFQdSQWdr_DWCf_"
"e2FYtN69Qql9maXH550XNcfynxCicTL71xLstYfWqbSMpADJhrW_"
"0pf4x58zLVfYLBJ7MPQaW15uKzbFn68lAlyF5GXDqWxowOm58EyeS7OmQkBdGyxYanZ6452m6O"
"%2526xkcb%253DSoDt-_M3NWbCoZUCiZ0KbzkdCdPP%2526astse%253Db4f6f6ed591bacca%"
"2526assa%253D6102%26co%3DUS%26advNum%3D5522285726153717%26noButtonUI%"
"3Dtrue%26iaUid%3D1h4m9oi2qj4h4800%26spn%3D1%26jobId%"
"3D0efc2325f6b4a2c5bc27%26isITA%3D0%26apiToken%"
"3Daa102235a5ccb18bd3668c0e14aa3ea7e2503cfac2a7a9bf3d6549899e125af4%"
"26jobLocation%3DAustin%252C%2BTX%2B78758%26twoPaneGroup%3D-1%"
"26indeedcsrftoken%3D7bG1QaY6YSlr3rfgMbu9YRVPyk1v2TF0%26phone%3DOPTIONAL%"
"26jobApplies%3D-1%26twoPaneVjGroup%3D-1%26returnToJobSearchUrl%3Dhttp%"
"253A%252F%252Fwww.indeed.com%252F%253Fvjk%253D4ce8c8f85737012d%2526advn%"
"253D2919294681304046%26indeedApplyableJobApiURI%3D&cfb=2&obo=http%3A%2F%"
"2Fwww.indeed.com%2F&hl=en_US&from=indapply-login-SmartApply&branding="
"indeed-apply"};
std::vector url_examples;
double url_examples_bytes = []() -> double {
size_t bytes{0};
for (std::string& url_string : url_examples) {
bytes += url_string.size();
}
return double(bytes);
}();
#ifdef ADA_URL_FILE
const char* default_file = ADA_URL_FILE;
#else
const char* default_file = nullptr;
#endif
size_t init_data(const char* input = default_file) {
// compute the number of bytes.
auto compute = []() -> double {
size_t bytes{0};
for (std::string& url_string : url_examples) {
bytes += url_string.size();
}
return double(bytes);
};
if (input == nullptr) {
for (const std::string& s : url_examples_default) {
url_examples.emplace_back(s);
}
url_examples_bytes = compute();
return url_examples.size();
}
if (!file_exists(input)) {
std::cout << "File not found !" << input << std::endl;
for (const std::string& s : url_examples_default) {
url_examples.emplace_back(s);
}
} else {
std::cout << "Loading " << input << std::endl;
url_examples = split_string(read_file(input));
}
url_examples_bytes = compute();
return url_examples.size();
}
size_t count_ada_invalid() {
size_t how_many = 0;
for (std::string& url_string : url_examples) {
auto url = ada::parse(url_string);
if (!url) {
how_many++;
}
}
return how_many;
}
template
static void BasicBench_AdaURL(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t param_count = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
ada::result url = ada::parse(url_string);
if (url) {
auto params = ada::url_search_params{url->get_search()};
param_count += params.size();
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
ada::result url = ada::parse(url_string);
if (url) {
auto params = ada::url_search_params{url->get_search()};
param_count += params.size();
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)param_count;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
auto url_search_params_AdaURL = BasicBench_AdaURL;
BENCHMARK(url_search_params_AdaURL);
int main(int argc, char** argv) {
if (argc > 1 && file_exists(argv[1])) {
init_data(argv[1]);
} else {
init_data();
}
#if (__APPLE__ && __aarch64__) || defined(__linux__)
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters",
"No privileged access (sudo may help).");
}
#else
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Unsupported system.");
}
#endif
benchmark::AddCustomContext("input bytes",
std::to_string(size_t(url_examples_bytes)));
benchmark::AddCustomContext("number of URLs",
std::to_string(std::size(url_examples)));
benchmark::AddCustomContext(
"bytes/URL",
std::to_string(url_examples_bytes / std::size(url_examples)));
if (collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Enabled");
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
}
ada-url-ada-3c84d73/benchmarks/benchmark_header.h 0000664 0000000 0000000 00000003127 15147206404 0021661 0 ustar 00root root 0000000 0000000 #include
#include
#include
#include
#include
#include
#if ADA_VARIOUS_COMPETITION_ENABLED
#include
#include
#include
#endif
#if ADA_url_whatwg_ENABLED
#include
#endif
#include "ada.h"
#include "counters/event_counter.h"
counters::event_collector collector;
size_t N = 1000;
#include
bool file_exists(const char* filename) {
namespace fs = std::filesystem;
std::filesystem::path f{filename};
if (std::filesystem::exists(filename)) {
return true;
} else {
return false;
}
}
std::string read_file(std::string filename) {
constexpr size_t read_size = 4096;
auto stream = std::ifstream(filename.c_str());
stream.exceptions(std::ios_base::badbit);
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
out.append(buf, 0, size_t(stream.gcount()));
}
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
std::vector split_string(const std::string& str) {
std::vector result;
std::stringstream ss{str};
for (std::string line; std::getline(ss, line, '\n');) {
std::string_view view = line;
// Some parsers like boost/url will refuse to parse a URL with trailing
// whitespace.
while (!view.empty() && std::isspace(view.back())) {
view.remove_suffix(1);
}
while (!view.empty() && std::isspace(view.front())) {
view.remove_prefix(1);
}
if (!view.empty()) {
result.emplace_back(view);
}
}
return result;
}
ada-url-ada-3c84d73/benchmarks/benchmark_template.cpp 0000664 0000000 0000000 00000103753 15147206404 0022605 0 ustar 00root root 0000000 0000000 /**
* The main benchmark is to take an input string, and convert it into a
* normalized URL (or 'href').
*/
size_t count_ada_invalid() {
size_t how_many = 0;
for (std::string& url_string : url_examples) {
auto url = ada::parse(url_string);
if (!url) {
how_many++;
}
}
return how_many;
}
template
static void BasicBench_AdaURL(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
volatile size_t href_size = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
ada::result url = ada::parse(url_string);
if (url) {
success++;
if constexpr (!just_parse) {
href_size += url->get_href().size();
}
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
ada::result url = ada::parse(url_string);
if (url) {
success++;
if constexpr (!just_parse) {
href_size += url->get_href().size();
}
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
#ifndef BENCHMARK_PREFIX
#define BENCHMARK_PREFIX
#endif
#ifndef BENCHMARK_PREFIX_STR
#define BENCHMARK_PREFIX_STR ""
#endif
#define CONCAT_IMPL(x, y) x##y
#define CONCAT(x, y) CONCAT_IMPL(x, y)
#define BENCHMARK_NAME(name) CONCAT(BENCHMARK_PREFIX, name)
auto BENCHMARK_NAME(BasicBench_AdaURL_href) =
BasicBench_AdaURL;
static auto* CONCAT(benchmark_register_,
BENCHMARK_NAME(BasicBench_AdaURL_href)) =
::benchmark::RegisterBenchmark(BENCHMARK_PREFIX_STR
"BasicBench_AdaURL_href",
BENCHMARK_NAME(BasicBench_AdaURL_href));
auto BENCHMARK_NAME(BasicBench_AdaURL_aggregator_href) =
BasicBench_AdaURL;
static auto* CONCAT(benchmark_register_,
BENCHMARK_NAME(BasicBench_AdaURL_aggregator_href)) =
::benchmark::RegisterBenchmark(
BENCHMARK_PREFIX_STR "BasicBench_AdaURL_aggregator_href",
BENCHMARK_NAME(BasicBench_AdaURL_aggregator_href));
static void BENCHMARK_NAME(BasicBench_AdaURL_CanParse)(
benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
bool can_parse = ada::can_parse(url_string);
if (can_parse) {
success++;
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
bool can_parse = ada::can_parse(url_string);
if (can_parse) {
success++;
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
static auto* CONCAT(benchmark_register_,
BENCHMARK_NAME(BasicBench_AdaURL_CanParse)) =
::benchmark::RegisterBenchmark(BENCHMARK_PREFIX_STR
"BasicBench_AdaURL_CanParse",
BENCHMARK_NAME(BasicBench_AdaURL_CanParse));
#if ADA_url_whatwg_ENABLED
size_t count_whatwg_invalid() {
size_t how_many = 0;
for (std::string& url_string : url_examples) {
upa::url url;
if (!upa::success(url.parse(url_string, nullptr))) {
how_many++;
}
}
return how_many;
}
template
static void BENCHMARK_NAME(BasicBench_whatwg)(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
volatile size_t href_size = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
upa::url url;
if (upa::success(url.parse(url_string, nullptr))) {
success++;
if constexpr (!just_parse) {
href_size += url.href().size();
}
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
upa::url url;
if (upa::success(url.parse(url_string, nullptr))) {
success++;
if constexpr (!just_parse) {
href_size += url.href().size();
}
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
(void)href_size;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate);
}
static auto* CONCAT(benchmark_register_, BENCHMARK_NAME(BasicBench_whatwg)) =
::benchmark::RegisterBenchmark(BENCHMARK_PREFIX_STR "BasicBench_whatwg",
BENCHMARK_NAME(BasicBench_whatwg));
// There is no need for BasicBench_whatwg_just_parse because whatwg appears to
// provide the href at a minimal cost, probably because it is already
// materialized. auto BasicBench_whatwg_just_parse =
// BasicBench_whatwg; BENCHMARK(BasicBench_whatwg_just_parse);
#endif // ADA_url_whatwg_ENABLED
#if ADA_CURL_ENABLED
#include
size_t count_curl_invalid() {
size_t how_many = 0;
CURLU* url = curl_url();
for (std::string& url_string : url_examples) {
CURLUcode rc = curl_url_set(url, CURLUPART_URL, url_string.c_str(), 0);
// Returns a CURLUcode error value, which is (0) if everything went fine.
if (rc != 0) {
how_many++;
}
}
curl_url_cleanup(url);
return how_many;
}
// curl follows RFC3986+
template
static void BasicBench_CURL(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
volatile size_t href_size = 0;
CURLU* url = curl_url();
for (auto _ : state) {
for (std::string& url_string : url_examples) {
CURLUcode rc = curl_url_set(url, CURLUPART_URL, url_string.c_str(), 0);
// Returns a CURLUcode error value, which is (0) if everything went fine.
if (rc == 0) {
success++;
if constexpr (!just_parse) {
char* buffer;
// When asked to return the full URL, curl_url_get will return a
// normalized and possibly cleaned up version of what was previously
// parsed.
rc = curl_url_get(url, CURLUPART_URL, &buffer, 0);
if (rc == 0) {
href_size += strlen(buffer);
curl_free(buffer);
}
}
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
CURLUcode rc = curl_url_set(url, CURLUPART_URL, url_string.c_str(), 0);
// Returns a CURLUcode error value, which is (0) if everything went
// fine.
if constexpr (!just_parse) {
char* buffer;
rc = curl_url_get(url, CURLUPART_URL, &buffer, 0);
if (rc == 0) {
href_size += strlen(buffer);
curl_free(buffer);
}
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
curl_url_cleanup(url);
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_CURL);
// 'just parsing' is faster with curl, but maybe not so important for us.
// auto BasicBench_CURL_just_parse = BasicBench_CURL;
// BENCHMARK(BasicBench_CURL_just_parse);
#endif
#if ADA_BOOST_ENABLED
#include
using namespace boost::urls;
size_t count_boosturl_invalid() {
size_t how_many = 0;
for (std::string& url_string : url_examples) {
try {
url u(url_string);
u.normalize();
} catch (...) {
how_many++;
}
}
return how_many;
}
// Boost URL follows RFC3986
template
static void BasicBench_BoostURL(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
volatile size_t href_size = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
try {
url u(url_string);
u.normalize();
success++;
if constexpr (!just_parse) {
href_size += u.buffer().size();
}
} catch (...) {
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
try {
url u(url_string);
u.normalize();
success++;
if constexpr (!just_parse) {
href_size += u.buffer().size();
}
} catch (...) {
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
(void)href_size;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_BoostURL);
// There is no need for 'just_parse' because BoostURL materializes the href.
// auto BasicBench_BoostURL_just_parse = BasicBench_BoostURL;
// BENCHMARK(BasicBench_BoostURL_just_parse);
#endif // ADA_BOOST_ENABLED
#if ADA_ZURI_ENABLED
#include
size_t count_zuri_invalid() {
size_t how_many = 0;
for (std::string& url_string : url_examples) {
struct zuri2k uri;
zuri_error err = zuri_parse2k(&uri, url_string.c_str());
if (err) how_many++;
}
return how_many;
}
// ZURI follows RFC3986
template
static void BasicBench_ZURI(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
volatile size_t href_size = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
struct zuri2k uri;
benchmark::DoNotOptimize(uri);
zuri_error err = zuri_parse2k(&uri, url_string.c_str());
if (!err) {
success++;
if constexpr (!just_parse) {
char buf[2048];
benchmark::DoNotOptimize(href_size +=
zuri_read2k(&uri, &buf[0], sizeof(buf)));
}
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
struct zuri2k uri;
benchmark::DoNotOptimize(uri);
zuri_error err = zuri_parse2k(&uri, url_string.c_str());
if (!err) {
success++;
if constexpr (!just_parse) {
char buf[2048];
benchmark::DoNotOptimize(href_size +=
zuri_read2k(&uri, &buf[0], sizeof(buf)));
}
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
(void)href_size;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] = benchmark::Counter(
std::size(url_examples), benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_ZURI);
#endif // ADA_ZURI_ENABLED
#if ADA_VARIOUS_COMPETITION_ENABLED
static void BasicBench_uriparser_just_parse(benchmark::State& state) {
// volatile to prevent optimizations.
volatile bool is_valid = true;
const char* errorPos;
UriUriA uri;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
is_valid &= (uriParseSingleUriA(&uri, url_string.c_str(), &errorPos) ==
URI_SUCCESS);
}
}
if (!is_valid) {
std::cout << "uri-parser: invalid? " << std::endl;
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
is_valid &= (uriParseSingleUriA(&uri, url_string.c_str(), &errorPos) ==
URI_SUCCESS);
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
uriFreeUriMembersA(&uri);
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_uriparser_just_parse);
#endif // ADA_VARIOUS_COMPETITION_ENABLED
#if ADA_VARIOUS_COMPETITION_ENABLED
static void BasicBench_urlparser_just_parse(benchmark::State& state) {
// volatile to prevent optimizations.
for (auto _ : state) {
for (std::string& url_string : url_examples) {
std::unique_ptr url(EdUrlParser::parseUrl(url_string));
}
}
if (collector.has_events()) {
event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
std::unique_ptr url(EdUrlParser::parseUrl(url_string));
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_urlparser_just_parse);
#endif // ADA_VARIOUS_COMPETITION_ENABLED
#if ADA_VARIOUS_COMPETITION_ENABLED
static void BasicBench_http_parser_just_parse(benchmark::State& state) {
volatile bool is_valid{true};
struct http_parser_url u;
http_parser_url_init(&u);
for (auto _ : state) {
for (std::string& url_string : url_examples) {
is_valid &=
!http_parser_parse_url(url_string.data(), url_string.size(), 0, &u);
}
}
if (collector.has_events()) {
event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
is_valid &=
!http_parser_parse_url(url_string.data(), url_string.size(), 0, &u);
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
if (!is_valid) {
std::cout << "http_parser: invalid? " << std::endl;
}
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_http_parser_just_parse);
#endif // ADA_VARIOUS_COMPETITION_ENABLED
#if defined(ADA_RUST_VERSION)
#include "competitors/servo-url/servo_url.h"
size_t count_rust_invalid() {
size_t how_many = 0;
for (std::string& url_string : url_examples) {
servo_url::Url* url =
servo_url::parse_url(url_string.c_str(), url_string.length());
servo_url::free_url(url);
if (!url) {
how_many++;
}
}
return how_many;
}
// Emilio from Mozilla recommended that using an opaque-pointer will improve the
// performance of this benchmark. It has indeed improved but with the cost of
// validating the output. Reference:
// https://twitter.com/ecbos_/status/1627494441656238082?s=61&t=vCdcfSGWHH056CBdklWfCg
static void BasicBench_ServoUrl(benchmark::State& state) {
// Other benchmarks copy the 'standard url' to a structure.
// We try to mimic the effect.
volatile size_t success = 0;
for (auto _ : state) {
for (std::string& url_string : url_examples) {
// benchmark::DoNotOptimize is unnecessary and potentially misleading.
const char* url_href =
servo_url::parse_url_to_href(url_string.c_str(), url_string.length());
if (url_href) {
// if you'd like you could print it: printf("%s\n", url_href);
success++;
servo_url::free_string(url_href);
}
}
}
if (collector.has_events()) {
event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : url_examples) {
const char* url_href = servo_url::parse_url_to_href(
url_string.c_str(), url_string.length());
if (url_href) {
success++;
servo_url::free_string(url_href);
}
}
std::atomic_thread_fence(std::memory_order_release);
event_count allocate_count = collector.end();
aggregate << allocate_count;
}
(void)success;
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_ServoUrl);
#endif // ADA_RUST
int main(int argc, char** argv) {
if (argc > 1 && file_exists(argv[1])) {
init_data(argv[1]);
} else {
init_data();
}
benchmark::AddCustomContext("ada spec", "Ada follows whatwg/url");
size_t ada_bad_url = count_ada_invalid();
#if ADA_url_whatwg_ENABLED
size_t whatwg_bad_url = count_whatwg_invalid();
#endif
#if defined(ADA_RUST_VERSION)
benchmark::AddCustomContext("rust version ", ADA_RUST_VERSION);
size_t servo_bad_url = count_rust_invalid();
#endif
#if ADA_CURL_ENABLED
// the curl dependency will depend on the system.
benchmark::AddCustomContext("curl version ", LIBCURL_VERSION);
benchmark::AddCustomContext("curl spec",
"Curl follows RFC3986, not whatwg/url");
size_t curl_bad_url = count_curl_invalid();
#else
benchmark::AddCustomContext("curl ", "OMITTED");
#endif
#if ADA_BOOST_ENABLED
benchmark::AddCustomContext("boost-url spec",
"Boost URL follows RFC3986, not whatwg/url");
size_t boost_bad_url = count_boosturl_invalid();
#endif
#if ADA_ZURI_ENABLED
benchmark::AddCustomContext("zuri spec",
"Zuri follows RFC3986, not whatwg/url");
size_t zuri_bad_url = count_zuri_invalid();
#else
benchmark::AddCustomContext("zuri ", "OMITTED");
#endif
#if (__APPLE__ && __aarch64__) || defined(__linux__)
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters",
"No privileged access (sudo may help).");
}
#else
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Unsupported system.");
}
#endif
benchmark::AddCustomContext("input bytes",
std::to_string(size_t(url_examples_bytes)));
benchmark::AddCustomContext("number of URLs",
std::to_string(std::size(url_examples)));
benchmark::AddCustomContext(
"bytes/URL",
std::to_string(url_examples_bytes / std::size(url_examples)));
#if ADA_VARIOUS_COMPETITION_ENABLED
benchmark::AddCustomContext("WARNING",
"BasicBench_urlparser and BasicBench_uriparser "
"do not use a normalized task.");
#endif
if (collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Enabled");
}
std::stringstream badcounts;
badcounts << "---------------------\n";
badcounts << "ada---count of bad URLs " << std::to_string(ada_bad_url)
<< "\n";
#if defined(ADA_RUST_VERSION)
badcounts << "servo/url---count of bad URLs " << std::to_string(servo_bad_url)
<< "\n";
#endif
#if ADA_url_whatwg_ENABLED
badcounts << "whatwg---count of bad URLs "
<< std::to_string(whatwg_bad_url) << "\n";
#endif
#if ADA_CURL_ENABLED
badcounts << "curl---count of bad URLs " << std::to_string(curl_bad_url)
<< "\n";
#endif
#if ADA_BOOST_ENABLED
badcounts << "boost-url---count of bad URLs " << std::to_string(boost_bad_url)
<< "\n";
#endif
#if ADA_ZURI_ENABLED
badcounts << "zuri---count of bad URLs " << std::to_string(zuri_bad_url)
<< "\n";
#endif
badcounts << "-------------------------------\n";
benchmark::AddCustomContext("bad urls", badcounts.str());
if (size_t(url_examples_bytes) > 1000000) {
N = 10;
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
}
ada-url-ada-3c84d73/benchmarks/competitors/ 0000775 0000000 0000000 00000000000 15147206404 0020613 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/benchmarks/competitors/servo-url/ 0000775 0000000 0000000 00000000000 15147206404 0022551 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/benchmarks/competitors/servo-url/Cargo.lock 0000664 0000000 0000000 00000021215 15147206404 0024457 0 ustar 00root root 0000000 0000000 # This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "icu_collections"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43"
dependencies = [
"displaydoc",
"potential_utf",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a"
[[package]]
name = "icu_properties"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99"
dependencies = [
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899"
[[package]]
name = "icu_provider"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "libc"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "litemap"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "potential_utf"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77"
dependencies = [
"zerovec",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "servo-url"
version = "0.1.0"
dependencies = [
"libc",
"url",
]
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "syn"
version = "2.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinystr"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "writeable"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "yoke"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerofrom"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerotrie"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
ada-url-ada-3c84d73/benchmarks/competitors/servo-url/Cargo.toml 0000664 0000000 0000000 00000000324 15147206404 0024500 0 ustar 00root root 0000000 0000000 [package]
name = "servo-url"
version = "0.1.0"
edition = "2021"
[lib]
path = "lib.rs"
crate-type = ["cdylib"]
[dependencies]
url = "2.5.8"
libc = "0.2"
[profile.release]
opt-level = 3
debug = false
lto = true
ada-url-ada-3c84d73/benchmarks/competitors/servo-url/README.md 0000664 0000000 0000000 00000001016 15147206404 0024026 0 ustar 00root root 0000000 0000000 ## Servo URL FFI
This folder includes FFI bindings for servo/url.
### Links
- https://github.com/eqrion/cbindgen/blob/master/docs.md
- https://gist.github.com/zbraniecki/b251714d77ffebbc73c03447f2b2c69f
- https://github.com/Michael-F-Bryan/rust-ffi-guide/blob/master/book/setting_up.md
### Building
- Generating cbindgen output
- Install dependencies with `brew install cbindgen`
- Generate with `cbindgen --config cbindgen.toml --crate servo-url --output servo_url.h`
- Building
- Run with `cargo build --release`
ada-url-ada-3c84d73/benchmarks/competitors/servo-url/cbindgen.toml 0000664 0000000 0000000 00000000455 15147206404 0025223 0 ustar 00root root 0000000 0000000 autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
include_version = true
braces = "SameLine"
line_length = 100
tab_width = 2
language = "C++"
namespaces = ["servo_url"]
include_guard = "servo_url_ffi_h"
[parse]
parse_deps = true
include = ["url"]
ada-url-ada-3c84d73/benchmarks/competitors/servo-url/lib.rs 0000664 0000000 0000000 00000003222 15147206404 0023664 0 ustar 00root root 0000000 0000000 use url::Url;
use std::slice;
use libc::{c_char, size_t};
extern crate url;
extern crate libc;
#[unsafe(no_mangle)]
pub extern "C" fn parse_url(raw_input: *const c_char, raw_input_length: size_t) -> *mut Url {
let input = unsafe { std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length)) };
// This code would assume that the URL is parsed successfully:
// let result = Url::parse(input).unwrap();
// Box::into_raw(Box::new(result))
// But we might get an invalid input. So we want to return null in case of
// error. We can do it in such a manner:
match Url::parse(input) {
Ok(result) => Box::into_raw(Box::new(result)),
Err(_) => std::ptr::null_mut(),
}
}
#[unsafe(no_mangle)]
pub extern "C" fn parse_url_to_href(raw_input: *const c_char, raw_input_length: size_t) -> *const c_char {
let input = unsafe { std::str::from_utf8_unchecked(slice::from_raw_parts(raw_input as *const u8, raw_input_length)) };
match Url::parse(input) {
Ok(result) => std::ffi::CString::new(result.as_str()).unwrap().into_raw(),
Err(_) => std::ptr::null_mut(),
}
}
/// # Safety
///
/// This function is unsafe because it takes ownership of the pointer and drops it.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_url(raw: *mut Url) {
if raw.is_null() {
return;
}
unsafe { drop(Box::from_raw(raw)) }
}
/// # Safety
///
/// This function is unsafe because it takes ownership of the pointer and drops it.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn free_string(ptr: *const c_char) {
// Take the ownership back to rust and drop the owner
let _ = unsafe { std::ffi::CString::from_raw(ptr as *mut _) };
}
ada-url-ada-3c84d73/benchmarks/competitors/servo-url/servo_url.h 0000664 0000000 0000000 00000001023 15147206404 0024736 0 ustar 00root root 0000000 0000000 #ifndef servo_url_ffi_h
#define servo_url_ffi_h
/* This file was modified manually. */
#include
#include
#include
#include
#include
namespace servo_url {
/// A parsed URL record.
struct Url;
extern "C" {
Url *parse_url(const char *raw_input, size_t raw_input_length);
void free_url(Url *raw);
const char *parse_url_to_href(const char *raw_input, size_t raw_input_length);
void free_string(const char *);
} // extern "C"
} // namespace servo_url
#endif // servo_url_ffi_h
ada-url-ada-3c84d73/benchmarks/model_bench.cpp 0000664 0000000 0000000 00000021122 15147206404 0021204 0 ustar 00root root 0000000 0000000 #include
#include
#include
#include
#include
#include
#include "ada.h"
#include "counters/event_counter.h"
counters::event_collector collector;
bool file_exists(const char* filename) {
namespace fs = std::filesystem;
std::filesystem::path f{filename};
if (std::filesystem::exists(filename)) {
return true;
} else {
std::cout << " file missing: " << filename << std::endl;
return false;
}
}
std::string read_file(std::string filename) {
constexpr size_t read_size = 4096;
std::ifstream stream(filename.c_str());
stream.exceptions(std::ios_base::badbit);
std::string out;
std::string buf(read_size, '\0');
while (stream.read(&buf[0], read_size)) {
out.append(buf, 0, size_t(stream.gcount()));
}
out.append(buf, 0, size_t(stream.gcount()));
return out;
}
std::vector split_string(const std::string& str) {
auto result = std::vector{};
std::stringstream ss{str};
for (std::string line; std::getline(ss, line, '\n');) {
std::string_view view = line;
// Some parsers like boost/url will refuse to parse a URL with trailing
// whitespace.
while (!view.empty() && std::isspace(view.back())) {
view.remove_suffix(1);
}
while (!view.empty() && std::isspace(view.front())) {
view.remove_prefix(1);
}
if (!view.empty()) {
result.emplace_back(view);
}
}
return result;
}
struct stat_numbers {
std::string url_string{};
std::string href{};
ada::url_components components{};
counters::event_aggregate counters{};
bool is_valid = true;
bool has_port = false;
bool has_credentials = false;
bool has_fragment = false;
bool has_search = false;
};
size_t count_ascii_bytes(const std::string& s) {
size_t counter = 0;
for (uint8_t c : s) {
if (c < 128) {
counter++;
}
}
return counter;
}
template
std::vector collect_values(
const std::vector& url_examples, size_t trials) {
std::vector numbers(url_examples.size());
for (size_t i = 0; i < url_examples.size(); i++) {
numbers[i].url_string = url_examples[i];
ada::result url = ada::parse(url_examples[i]);
if (url) {
numbers[i].is_valid = true;
numbers[i].href = url->get_href();
numbers[i].components = url->get_components();
numbers[i].has_port = url->has_port();
numbers[i].has_credentials = url->has_credentials();
numbers[i].has_fragment = url->has_hash();
numbers[i].has_search = url->has_search();
} else {
numbers[i].is_valid = false;
}
}
volatile size_t href_size = 0;
for (size_t i = 0; i < trials; i++) {
for (stat_numbers& n : numbers) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
ada::result url = ada::parse(n.url_string);
if (url) {
href_size += url->get_href().size();
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
n.counters << allocate_count;
}
}
return numbers;
}
#ifdef ADA_URL_FILE
const char* default_file = ADA_URL_FILE;
#else
const char* default_file = nullptr;
#endif
std::vector init_data(const char* input = default_file) {
std::vector input_urls;
if (input == nullptr) {
return input_urls;
}
if (!file_exists(input)) {
std::cout << "File not found !" << input << std::endl;
return input_urls;
} else {
std::cout << "# Loading " << input << std::endl;
input_urls = split_string(read_file(input));
}
return input_urls;
}
void print(const stat_numbers& n) {
std::cout << std::setw(15) << n.url_string.size() << ",";
std::cout << std::setw(15) << n.counters.best.cycles() << "," << std::setw(15)
<< size_t(n.counters.cycles()) << ",";
std::cout << std::setw(15) << n.counters.best.instructions() << ","
<< std::setw(15) << n.counters.instructions() << ",";
std::cout << std::setw(15) << n.is_valid << ",";
// hash size
std::cout << std::setw(15) << n.href.size() << ",";
size_t end = n.href.size();
if (n.components.hash_start != ada::url_components::omitted) {
std::cout << std::setw(15) << (end - n.components.hash_start) << ",";
end = n.components.hash_start;
} else {
std::cout << std::setw(15) << 0 << ",";
}
// search size
if (n.components.search_start != ada::url_components::omitted) {
std::cout << std::setw(15) << (end - n.components.search_start) << ",";
end = n.components.search_start;
} else {
std::cout << std::setw(15) << 0 << ",";
}
// path size
std::cout << std::setw(15) << (end - n.components.pathname_start) << ",";
end = n.components.pathname_start;
// port size
std::cout << std::setw(15) << (end - n.components.host_end) << ",";
end = n.components.host_end;
// host size
std::cout << std::setw(15) << (end - n.components.host_start) << ",";
end = n.components.host_start;
// user/pass size
std::cout << std::setw(15) << (end - n.components.protocol_end) << ",";
end = n.components.protocol_end;
// protocol type
ada::result url =
ada::parse(n.url_string);
if (url) {
std::cout << std::setw(15) << int(url->type);
} else {
std::cout << std::setw(15) << -1;
}
std::cout << ",";
std::cout << std::setw(15) << n.has_port << ",";
std::cout << std::setw(15) << n.has_credentials << ",";
std::cout << std::setw(15) << n.has_fragment << ",";
std::cout << std::setw(15) << n.has_search << ",";
std::cout << std::setw(15)
<< (n.url_string.size() - count_ascii_bytes(n.url_string)) << ",";
std::cout << std::setw(15) << (n.href.size() - count_ascii_bytes(n.href))
<< ",";
std::cout << std::setw(15)
<< (count_ascii_bytes(n.url_string) == n.url_string.size()) << ",";
std::cout << std::setw(15) << (n.href == n.url_string);
}
void print(const std::vector numbers) {
std::cout << std::setw(15) << "input_size"
<< ",";
std::cout << std::setw(15) << "best_cycles"
<< ",";
std::cout << std::setw(15) << "mean_cycles"
<< ",";
std::cout << std::setw(15) << "best_instr"
<< ",";
std::cout << std::setw(15) << "mean_instr"
<< ",";
std::cout << std::setw(15) << "is_valid"
<< ",";
std::cout << std::setw(15) << "href_size"
<< ",";
std::cout << std::setw(15) << "hash_size"
<< ",";
std::cout << std::setw(15) << "search_size"
<< ",";
std::cout << std::setw(15) << "path_size"
<< ",";
std::cout << std::setw(15) << "port_size"
<< ",";
std::cout << std::setw(15) << "host_size"
<< ",";
std::cout << std::setw(15) << "credential_size"
<< ",";
std::cout << std::setw(15) << "protocol_type"
<< ",";
std::cout << std::setw(15) << "has_port"
<< ",";
std::cout << std::setw(15) << "has_authority"
<< ",";
std::cout << std::setw(15) << "has_fragment"
<< ",";
std::cout << std::setw(15) << "has_search"
<< ",";
std::cout << std::setw(15) << "non_ascii_bytes"
<< ",";
std::cout << std::setw(15) << "href_non_ascii_bytes"
<< ",";
std::cout << std::setw(15) << "is_ascii"
<< ",";
std::cout << std::setw(15) << "input_is_href";
std::cout << std::endl;
for (const stat_numbers& n : numbers) {
print(n);
std::cout << std::endl;
}
}
int main(int argc, char** argv) {
std::vector input_urls;
if (argc == 1) {
input_urls = init_data();
} else {
input_urls = init_data(argv[1]);
}
if (input_urls.empty()) {
std::cout << "pass the path to a file containing a list of URL (one per "
"line) as a parameter."
<< std::endl;
return EXIT_FAILURE;
}
if (!collector.has_events()) {
std::cout << "We require access to performance counters. (Try sudo.)"
<< std::endl;
return EXIT_FAILURE;
}
std::string empty;
// We always start with a null URL for calibration.
input_urls.insert(input_urls.begin(), empty);
bool use_ada_url = (getenv("USE_URL") != nullptr);
size_t trials = 100;
std::cout << "# trials " << trials << std::endl;
if (use_ada_url) {
std::cout << "# ada::url" << std::endl;
print(collect_values(input_urls, trials));
} else {
std::cout << "# ada::url_aggregator" << std::endl;
print(collect_values(input_urls, trials));
}
return EXIT_SUCCESS;
}
ada-url-ada-3c84d73/benchmarks/percent_encode.cpp 0000664 0000000 0000000 00000025163 15147206404 0021733 0 ustar 00root root 0000000 0000000 #include
#include "ada.h"
#include "ada/character_sets.h"
#include "ada/unicode.h"
#include "counters/event_counter.h"
counters::event_collector collector;
size_t N = 1000;
#include
std::string examples[] = {"\xE1|", "other:9818274x1!!",
"ref=web-twc-ao-gbl-adsinfo&utm_source=twc&utm_",
"connect_timeout=10&application_name=myapp"};
void init_data() {}
double examples_bytes = []() -> double {
size_t bytes{0};
for (std::string& url_string : examples) {
bytes += url_string.size();
}
return double(bytes);
}();
static void Fragment(benchmark::State& state) {
for (auto _ : state) {
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::FRAGMENT_PERCENT_ENCODE));
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::FRAGMENT_PERCENT_ENCODE));
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(examples);
state.counters["instructions/cycle"] =
aggregate.total.instructions() / aggregate.total.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / examples_bytes;
state.counters["GHz"] =
aggregate.total.cycles() / aggregate.total.elapsed_ns();
}
state.counters["time/byte"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(Fragment);
static void Query(benchmark::State& state) {
for (auto _ : state) {
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::QUERY_PERCENT_ENCODE));
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::QUERY_PERCENT_ENCODE));
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(examples);
state.counters["instructions/cycle"] =
aggregate.total.instructions() / aggregate.total.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / examples_bytes;
state.counters["GHz"] =
aggregate.total.cycles() / aggregate.total.elapsed_ns();
}
state.counters["time/byte"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(Query);
static void SpecialQuery(benchmark::State& state) {
for (auto _ : state) {
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::FRAGMENT_PERCENT_ENCODE));
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::SPECIAL_QUERY_PERCENT_ENCODE));
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(examples);
state.counters["instructions/cycle"] =
aggregate.total.instructions() / aggregate.total.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / examples_bytes;
state.counters["GHz"] =
aggregate.total.cycles() / aggregate.total.elapsed_ns();
}
state.counters["time/byte"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(SpecialQuery);
static void UserInfo(benchmark::State& state) {
for (auto _ : state) {
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::USERINFO_PERCENT_ENCODE));
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::USERINFO_PERCENT_ENCODE));
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(examples);
state.counters["instructions/cycle"] =
aggregate.total.instructions() / aggregate.total.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / examples_bytes;
state.counters["GHz"] =
aggregate.total.cycles() / aggregate.total.elapsed_ns();
}
state.counters["time/byte"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(UserInfo);
static void C0Control(benchmark::State& state) {
for (auto _ : state) {
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::C0_CONTROL_PERCENT_ENCODE));
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_string : examples) {
benchmark::DoNotOptimize(ada::unicode::percent_encode(
url_string, ada::character_sets::C0_CONTROL_PERCENT_ENCODE));
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(examples);
state.counters["instructions/cycle"] =
aggregate.total.instructions() / aggregate.total.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / examples_bytes;
state.counters["GHz"] =
aggregate.total.cycles() / aggregate.total.elapsed_ns();
}
state.counters["time/byte"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(C0Control);
int main(int argc, char** argv) {
#if defined(ADA_RUST_VERSION)
benchmark::AddCustomContext("rust version ", ADA_RUST_VERSION);
#endif
#if (__APPLE__ && __aarch64__) || defined(__linux__)
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters",
"No privileged access (sudo may help).");
}
#else
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Unsupported system.");
}
#endif
if (collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Enabled");
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
}
ada-url-ada-3c84d73/benchmarks/urlpattern.cpp 0000664 0000000 0000000 00000030751 15147206404 0021155 0 ustar 00root root 0000000 0000000 #include "benchmark_header.h"
std::vector> url_pattern_examples = {
{"https://example.com/foo/bar", "/foo/bar"},
{"https://example.com/foo/bar/baz", "/foo/bar"},
{"https://example.com/foo.html", ":name.html"},
{"https://sub.example.com/foo/bar",
"http{s}?://{*.}?example.com/:product/:endpoint"},
{"https://example.com/?foo", "https://example.com?foo"},
{"https://example.com:8080/?foo", "https://example.com:8080?foo"},
{"https://example.com/?foo", "https://example.com/*\\?foo"},
{"https://example.com/bar?foo", "https://example.com/:name?foo"}};
std::string url_examples[] = {
"https://www.google.com/"
"webhp?hl=en&ictx=2&sa=X&ved=0ahUKEwil_"
"oSxzJj8AhVtEFkFHTHnCGQQPQgI",
"https://support.google.com/websearch/"
"?p=ws_results_help&hl=en-CA&fg=1",
"https://en.wikipedia.org/wiki/Dog#Roles_with_humans",
"https://www.tiktok.com/@aguyandagolden/video/7133277734310038830",
"https://business.twitter.com/en/help/troubleshooting/"
"how-twitter-ads-work.html?ref=web-twc-ao-gbl-adsinfo&utm_source=twc&utm_"
"medium=web&utm_campaign=ao&utm_content=adsinfo",
"https://images-na.ssl-images-amazon.com/images/I/"
"41Gc3C8UysL.css?AUIClients/AmazonGatewayAuiAssets",
"https://www.reddit.com/?after=t3_zvz1ze",
"https://www.reddit.com/login/?dest=https%3A%2F%2Fwww.reddit.com%2F",
"postgresql://other:9818274x1!!@localhost:5432/"
"otherdb?connect_timeout=10&application_name=myapp",
"http://192.168.1.1", // ipv4
"http://[2606:4700:4700::1111]", // ipv6
};
double url_examples_bytes;
size_t init_data() {
// compute the number of bytes.
auto compute = []() -> double {
size_t bytes{0};
for (const auto& [base, input] : url_pattern_examples) {
bytes += base.size() + input.size();
}
return double(bytes);
};
url_examples_bytes = compute();
return url_pattern_examples.size();
}
size_t count_urlpattern_parse_invalid() {
size_t how_many = 0;
for (const auto& [base, input] : url_pattern_examples) {
auto result =
ada::parse_url_pattern(
input, &base, nullptr);
if (!result) {
how_many++;
}
}
return how_many;
}
size_t count_urlpattern_exec_invalid() {
size_t how_many = 0;
auto pattern =
ada::parse_url_pattern(
"https://*example.com/*");
if (!pattern) {
return std::size(url_examples);
}
for (const std::string& url_example : url_examples) {
auto result = pattern->exec(url_example);
if (!result) {
how_many++;
}
}
return how_many;
}
size_t count_urlpattern_test_invalid() {
size_t how_many = 0;
auto pattern =
ada::parse_url_pattern(
"https://*example.com/*");
if (!pattern) {
return std::size(url_examples);
}
for (const std::string& url_example : url_examples) {
auto result = pattern->test(url_example);
if (!result) {
how_many++;
}
}
return how_many;
}
static void BasicBench_AdaURL_URLPattern_Parse(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t success = 0;
for (auto _ : state) {
for (const auto& [base, input] : url_pattern_examples) {
auto result =
ada::parse_url_pattern(
input, &base, nullptr);
if (result) {
success++;
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (const auto& [base, input] : url_pattern_examples) {
auto result =
ada::parse_url_pattern(
input, &base, nullptr);
if (result) {
success++;
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_pattern_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_pattern_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_pattern_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_pattern_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_pattern_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_AdaURL_URLPattern_Parse);
static void BasicBench_AdaURL_URLPattern_Exec(benchmark::State& state) {
auto pattern =
ada::parse_url_pattern(
"https://*example.com/*");
if (!pattern) {
state.SkipWithError("Failed to parse test pattern");
return;
}
// volatile to prevent optimizations.
volatile size_t success = 0;
for (auto _ : state) {
for (std::string& url_example : url_examples) {
auto result = pattern->exec(url_example);
if (result) {
success++;
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_example : url_examples) {
auto result = pattern->exec(url_example);
if (result) {
success++;
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_pattern_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_pattern_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_pattern_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_pattern_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_pattern_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_AdaURL_URLPattern_Exec);
static void BasicBench_AdaURL_URLPattern_Test(benchmark::State& state) {
auto pattern =
ada::parse_url_pattern(
"https://*example.com/*");
if (!pattern) {
state.SkipWithError("Failed to parse test pattern");
return;
}
// volatile to prevent optimizations.
volatile size_t success = 0;
for (auto _ : state) {
for (std::string& url_example : url_examples) {
auto result = pattern->test(url_example);
if (result) {
success++;
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (std::string& url_example : url_examples) {
auto result = pattern->test(url_example);
if (result) {
success++;
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
(void)success;
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(BasicBench_AdaURL_URLPattern_Test);
int main(int argc, char** argv) {
init_data();
size_t urlpattern_parse_bad_urls = count_urlpattern_parse_invalid();
size_t urlpattern_exec_bad_urls = count_urlpattern_exec_invalid();
size_t urlpattern_test_bad_urls = count_urlpattern_test_invalid();
#if (__APPLE__ && __aarch64__) || defined(__linux__)
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters",
"No privileged access (sudo may help).");
}
#else
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Unsupported system.");
}
#endif
benchmark::AddCustomContext("input bytes",
std::to_string(size_t(url_examples_bytes)));
benchmark::AddCustomContext("number of URLs",
std::to_string(std::size(url_pattern_examples)));
benchmark::AddCustomContext(
"bytes/URL",
std::to_string(url_examples_bytes / std::size(url_pattern_examples)));
std::stringstream badcounts;
badcounts << "---------------------\n";
badcounts << "urlpattern-parse---count of bad URLs "
<< std::to_string(urlpattern_parse_bad_urls) << "\n";
badcounts << "urlpattern-exec---count of bad URLs "
<< std::to_string(urlpattern_exec_bad_urls) << "\n";
badcounts << "urlpattern-test---count of bad URLs "
<< std::to_string(urlpattern_test_bad_urls) << "\n";
benchmark::AddCustomContext("bad url patterns", badcounts.str());
if (collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Enabled");
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
}
ada-url-ada-3c84d73/benchmarks/wpt_bench.cpp 0000664 0000000 0000000 00000023051 15147206404 0020721 0 ustar 00root root 0000000 0000000 #include "benchmark_header.h"
#include "simdjson.h"
using namespace simdjson;
double url_examples_bytes{};
std::vector> url_examples;
enum {
ALL_URLS = -1,
};
size_t init_data(const char* source, int which_url) {
ondemand::parser parser;
std::vector> answer;
if (!file_exists(source)) {
return 0;
}
padded_string json = padded_string::load(source);
ondemand::document doc = parser.iterate(json);
if (which_url == ALL_URLS) {
for (auto element : doc.get_array()) {
if (element.type() == ondemand::json_type::object) {
std::string_view input;
if (element["input"].get_string(true).get(input) != simdjson::SUCCESS) {
printf("missing input.\n");
continue;
}
std::string_view base;
if (element["base"].get_string(true).get(base) != simdjson::SUCCESS) {
// missing base is ok?
}
url_examples.push_back({std::string(input), std::string(base)});
url_examples_bytes += input.size() + base.size();
}
}
} else {
size_t count = 0;
for (auto element : doc.get_array()) {
if (element.type() == ondemand::json_type::object) {
std::string_view input;
if (element["input"].get_string(true).get(input) != simdjson::SUCCESS) {
printf("missing input.\n");
continue;
}
std::string_view base;
if (element["base"].get_string(true).get(base) != simdjson::SUCCESS) {
// missing base is ok?
}
if (count++ == which_url) {
url_examples.push_back({std::string(input), std::string(base)});
url_examples_bytes += input.size() + base.size();
break;
}
}
}
if (url_examples.size() == 0) {
printf("# There are %zu urls in the file, index is %d.\n", count,
which_url);
}
}
printf("# recovered %zu urls.\n", url_examples.size());
return url_examples.size();
}
template
static void BasicBench_AdaURL(benchmark::State& state) {
// volatile to prevent optimizations.
volatile size_t href_size = 0;
for (auto _ : state) {
for (const std::pair& url_strings :
url_examples) {
ada::result base;
result* base_ptr = nullptr;
if (!url_strings.second.empty()) {
base = ada::parse(url_strings.second);
if (base) {
base_ptr = &*base;
} else {
continue;
}
}
auto url = ada::parse(url_strings.first, base_ptr);
if (url) {
href_size = href_size + url->get_href().size();
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (const std::pair& url_strings :
url_examples) {
ada::result base;
result* base_ptr = nullptr;
if (!url_strings.second.empty()) {
base = ada::parse(url_strings.second);
if (base) {
base_ptr = &*base;
} else {
continue;
}
}
auto url = ada::parse(url_strings.first, base_ptr);
if (url) {
href_size = href_size + url->get_href().size();
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
auto WptBench_BasicBench_AdaURL_url = BasicBench_AdaURL;
BENCHMARK(WptBench_BasicBench_AdaURL_url);
auto WptBench_BasicBench_AdaURL_url_aggregator =
BasicBench_AdaURL;
BENCHMARK(WptBench_BasicBench_AdaURL_url_aggregator);
#if ADA_url_whatwg_ENABLED
#include
static void WptBench_BasicBench_whatwg(benchmark::State& state) {
volatile size_t success{};
for (auto _ : state) {
for (const std::pair& url_strings :
url_examples) {
upa::url base;
upa::url* base_ptr = nullptr;
if (!url_strings.second.empty()) {
if (upa::success(base.parse(url_strings.second, nullptr))) {
base_ptr = &base;
}
}
upa::url url;
if (upa::success(url.parse(url_strings.first, base_ptr))) {
success = success + 1;
}
}
}
if (collector.has_events()) {
counters::event_aggregate aggregate{};
for (size_t i = 0; i < N; i++) {
std::atomic_thread_fence(std::memory_order_acquire);
collector.start();
for (const std::pair& url_strings :
url_examples) {
upa::url base;
upa::url* base_ptr = nullptr;
if (!url_strings.second.empty()) {
if (upa::success(base.parse(url_strings.second, nullptr))) {
base_ptr = &base;
}
}
upa::url url;
if (upa::success(url.parse(url_strings.first, base_ptr))) {
success = success + 1;
}
}
std::atomic_thread_fence(std::memory_order_release);
counters::event_count allocate_count = collector.end();
aggregate << allocate_count;
}
(void)success;
state.counters["cycles/url"] =
aggregate.best.cycles() / std::size(url_examples);
state.counters["instructions/url"] =
aggregate.best.instructions() / std::size(url_examples);
state.counters["instructions/cycle"] =
aggregate.best.instructions() / aggregate.best.cycles();
state.counters["instructions/byte"] =
aggregate.best.instructions() / url_examples_bytes;
state.counters["instructions/ns"] =
aggregate.best.instructions() / aggregate.best.elapsed_ns();
state.counters["GHz"] =
aggregate.best.cycles() / aggregate.best.elapsed_ns();
state.counters["ns/url"] =
aggregate.best.elapsed_ns() / std::size(url_examples);
state.counters["cycle/byte"] = aggregate.best.cycles() / url_examples_bytes;
}
state.counters["time/byte"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["time/url"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate |
benchmark::Counter::kInvert);
state.counters["speed"] = benchmark::Counter(
url_examples_bytes, benchmark::Counter::kIsIterationInvariantRate);
state.counters["url/s"] =
benchmark::Counter(double(std::size(url_examples)),
benchmark::Counter::kIsIterationInvariantRate);
}
BENCHMARK(WptBench_BasicBench_whatwg);
#endif // ADA_url_whatwg_ENABLED
int main(int argc, char** argv) {
int which_url = ALL_URLS;
if (argc > 3 && std::string_view(argv[2]) == "--select") {
which_url = std::atoi(argv[3]);
printf("# Selecting url %d.\n", which_url);
}
if (argc == 1 || !init_data(argv[1], which_url)) {
std::cout
<< "pass the path to the file wpt/urltestdata.json as a parameter."
<< std::endl;
std::cout
<< "E.g., './build/benchmarks/wpt_bench tests/wpt/urltestdata.json'"
<< std::endl;
std::cout << "You can also select a single URL by passing --select ."
<< std::endl;
std::cout << "E.g., './build/benchmarks/wpt_bench "
"tests/wpt/urltestdata.json --select 0'"
<< std::endl;
return EXIT_SUCCESS;
}
#if defined(ADA_RUST_VERSION)
benchmark::AddCustomContext("rust version ", ADA_RUST_VERSION);
#endif
#if (__APPLE__ && __aarch64__) || defined(__linux__)
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters",
"No privileged access (sudo may help).");
}
#else
if (!collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Unsupported system.");
}
#endif
if (collector.has_events()) {
benchmark::AddCustomContext("performance counters", "Enabled");
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
benchmark::Shutdown();
}
ada-url-ada-3c84d73/cmake/ 0000775 0000000 0000000 00000000000 15147206404 0015206 5 ustar 00root root 0000000 0000000 ada-url-ada-3c84d73/cmake/CPM.cmake 0000664 0000000 0000000 00000127651 15147206404 0016643 0 ustar 00root root 0000000 0000000 # CPM.cmake - CMake's missing package manager
# ===========================================
# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions.
#
# MIT License
# -----------
#[[
Copyright (c) 2019-2023 Lars Melchior and contributors
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.
]]
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
# Initialize logging prefix
if(NOT CPM_INDENT)
set(CPM_INDENT
"CPM:"
CACHE INTERNAL ""
)
endif()
if(NOT COMMAND cpm_message)
function(cpm_message)
message(${ARGV})
endfunction()
endif()
if(DEFINED EXTRACTED_CPM_VERSION)
set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}")
else()
set(CURRENT_CPM_VERSION 0.42.0)
endif()
get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
if(CPM_DIRECTORY)
if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY)
if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION)
message(
AUTHOR_WARNING
"${CPM_INDENT} \
A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \
It is recommended to upgrade CPM to the most recent version. \
See https://github.com/cpm-cmake/CPM.cmake for more information."
)
endif()
if(${CMAKE_VERSION} VERSION_LESS "3.17.0")
include(FetchContent)
endif()
return()
endif()
get_property(
CPM_INITIALIZED GLOBAL ""
PROPERTY CPM_INITIALIZED
SET
)
if(CPM_INITIALIZED)
return()
endif()
endif()
if(CURRENT_CPM_VERSION MATCHES "development-version")
message(
WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \
Please update to a recent release if possible. \
See https://github.com/cpm-cmake/CPM.cmake for details."
)
endif()
set_property(GLOBAL PROPERTY CPM_INITIALIZED true)
macro(cpm_set_policies)
# the policy allows us to change options without caching
cmake_policy(SET CMP0077 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
# the policy allows us to change set(CACHE) without caching
if(POLICY CMP0126)
cmake_policy(SET CMP0126 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0126 NEW)
endif()
# The policy uses the download time for timestamp, instead of the timestamp in the archive. This
# allows for proper rebuilds when a projects url changes
if(POLICY CMP0135)
cmake_policy(SET CMP0135 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0135 NEW)
endif()
# treat relative git repository paths as being relative to the parent project's remote
if(POLICY CMP0150)
cmake_policy(SET CMP0150 NEW)
set(CMAKE_POLICY_DEFAULT_CMP0150 NEW)
endif()
endmacro()
cpm_set_policies()
option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies"
$ENV{CPM_USE_LOCAL_PACKAGES}
)
option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies"
$ENV{CPM_LOCAL_PACKAGES_ONLY}
)
option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL})
option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package"
$ENV{CPM_DONT_UPDATE_MODULE_PATH}
)
option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path"
$ENV{CPM_DONT_CREATE_PACKAGE_LOCK}
)
option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK
"Add all packages added through CPM.cmake to the package lock"
$ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK}
)
option(CPM_USE_NAMED_CACHE_DIRECTORIES
"Use additional directory of package name in cache on the most nested level."
$ENV{CPM_USE_NAMED_CACHE_DIRECTORIES}
)
set(CPM_VERSION
${CURRENT_CPM_VERSION}
CACHE INTERNAL ""
)
set(CPM_DIRECTORY
${CPM_CURRENT_DIRECTORY}
CACHE INTERNAL ""
)
set(CPM_FILE
${CMAKE_CURRENT_LIST_FILE}
CACHE INTERNAL ""
)
set(CPM_PACKAGES
""
CACHE INTERNAL ""
)
set(CPM_DRY_RUN
OFF
CACHE INTERNAL "Don't download or configure dependencies (for testing)"
)
if(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE})
else()
set(CPM_SOURCE_CACHE_DEFAULT OFF)
endif()
set(CPM_SOURCE_CACHE
${CPM_SOURCE_CACHE_DEFAULT}
CACHE PATH "Directory to download CPM dependencies"
)
if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
set(CPM_MODULE_PATH
"${CMAKE_BINARY_DIR}/CPM_modules"
CACHE INTERNAL ""
)
# remove old modules
file(REMOVE_RECURSE ${CPM_MODULE_PATH})
file(MAKE_DIRECTORY ${CPM_MODULE_PATH})
# locally added CPM modules should override global packages
set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}")
endif()
if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
set(CPM_PACKAGE_LOCK_FILE
"${CMAKE_BINARY_DIR}/cpm-package-lock.cmake"
CACHE INTERNAL ""
)
file(WRITE ${CPM_PACKAGE_LOCK_FILE}
"# CPM Package Lock\n# This file should be committed to version control\n\n"
)
endif()
include(FetchContent)
# Try to infer package name from git repository uri (path or url)
function(cpm_package_name_from_git_uri URI RESULT)
if("${URI}" MATCHES "([^/:]+)/?.git/?$")
set(${RESULT}
${CMAKE_MATCH_1}
PARENT_SCOPE
)
else()
unset(${RESULT} PARENT_SCOPE)
endif()
endfunction()
# Find the shortest hash that can be used eg, if origin_hash is
# cccb77ae9609d2768ed80dd42cec54f77b1f1455 the following files will be checked, until one is found
# that is either empty (allowing us to assign origin_hash), or whose contents matches ${origin_hash}
#
# * .../cccb.hash
# * .../cccb77ae.hash
# * .../cccb77ae9609.hash
# * .../cccb77ae9609d276.hash
# * etc
#
# We will be able to use a shorter path with very high probability, but in the (rare) event that the
# first couple characters collide, we will check longer and longer substrings.
function(cpm_get_shortest_hash source_cache_dir origin_hash short_hash_output_var)
# for compatibility with caches populated by a previous version of CPM, check if a directory using
# the full hash already exists
if(EXISTS "${source_cache_dir}/${origin_hash}")
set(${short_hash_output_var}
"${origin_hash}"
PARENT_SCOPE
)
return()
endif()
foreach(len RANGE 4 40 4)
string(SUBSTRING "${origin_hash}" 0 ${len} short_hash)
set(hash_lock ${source_cache_dir}/${short_hash}.lock)
set(hash_fp ${source_cache_dir}/${short_hash}.hash)
# Take a lock, so we don't have a race condition with another instance of cmake. We will release
# this lock when we can, however, if there is an error, we want to ensure it gets released on
# it's own on exit from the function.
file(LOCK ${hash_lock} GUARD FUNCTION)
# Load the contents of .../${short_hash}.hash
file(TOUCH ${hash_fp})
file(READ ${hash_fp} hash_fp_contents)
if(hash_fp_contents STREQUAL "")
# Write the origin hash
file(WRITE ${hash_fp} ${origin_hash})
file(LOCK ${hash_lock} RELEASE)
break()
elseif(hash_fp_contents STREQUAL origin_hash)
file(LOCK ${hash_lock} RELEASE)
break()
else()
file(LOCK ${hash_lock} RELEASE)
endif()
endforeach()
set(${short_hash_output_var}
"${short_hash}"
PARENT_SCOPE
)
endfunction()
# Try to infer package name and version from a url
function(cpm_package_name_and_ver_from_url url outName outVer)
if(url MATCHES "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|zip|ZIP)(\\?|/|$)")
# We matched an archive
set(filename "${CMAKE_MATCH_1}")
if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)")
# We matched - (ie foo-1.2.3)
set(${outName}
"${CMAKE_MATCH_1}"
PARENT_SCOPE
)
set(${outVer}
"${CMAKE_MATCH_2}"
PARENT_SCOPE
)
elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)")
# We couldn't find a name, but we found a version
#
# In many cases (which we don't handle here) the url would look something like
# `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly
# distinguish the package name from the irrelevant bits. Moreover if we try to match the
# package name from the filename, we'd get bogus at best.
unset(${outName} PARENT_SCOPE)
set(${outVer}
"${CMAKE_MATCH_1}"
PARENT_SCOPE
)
else()
# Boldly assume that the file name is the package name.
#
# Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but
# such cases should be quite rare. No popular service does this... we think.
set(${outName}
"${filename}"
PARENT_SCOPE
)
unset(${outVer} PARENT_SCOPE)
endif()
else()
# No ideas yet what to do with non-archives
unset(${outName} PARENT_SCOPE)
unset(${outVer} PARENT_SCOPE)
endif()
endfunction()
function(cpm_find_package NAME VERSION)
string(REPLACE " " ";" EXTRA_ARGS "${ARGN}")
find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET)
if(${CPM_ARGS_NAME}_FOUND)
if(DEFINED ${CPM_ARGS_NAME}_VERSION)
set(VERSION ${${CPM_ARGS_NAME}_VERSION})
endif()
cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}")
CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}")
set(CPM_PACKAGE_FOUND
YES
PARENT_SCOPE
)
else()
set(CPM_PACKAGE_FOUND
NO
PARENT_SCOPE
)
endif()
endfunction()
# Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from
# finding the system library
function(cpm_create_module_file Name)
if(NOT CPM_DONT_UPDATE_MODULE_PATH)
if(DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR)
# Redirect find_package calls to the CPM package. This is what FetchContent does when you set
# OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG
# mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined
# in script mode, or in CMake < 3.24.
# https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples
string(TOLOWER ${Name} NameLower)
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config.cmake
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" OPTIONAL)\n"
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n"
)
file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake
"set(PACKAGE_VERSION_COMPATIBLE TRUE)\n" "set(PACKAGE_VERSION_EXACT TRUE)\n"
)
else()
file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake
"include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)"
)
endif()
endif()
endfunction()
# Find a package locally or fallback to CPMAddPackage
function(CPMFindPackage)
set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS)
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN})
if(NOT DEFINED CPM_ARGS_VERSION)
if(DEFINED CPM_ARGS_GIT_TAG)
cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
endif()
endif()
set(downloadPackage ${CPM_DOWNLOAD_ALL})
if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME})
set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}})
elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}})
set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}})
endif()
if(downloadPackage)
CPMAddPackage(${ARGN})
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS})
if(NOT CPM_PACKAGE_FOUND)
CPMAddPackage(${ARGN})
cpm_export_variables(${CPM_ARGS_NAME})
endif()
endfunction()
# checks if a package has been added before
function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION)
if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES)
CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION)
if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}")
message(
WARNING
"${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})."
)
endif()
cpm_get_fetch_properties(${CPM_ARGS_NAME})
set(${CPM_ARGS_NAME}_ADDED NO)
set(CPM_PACKAGE_ALREADY_ADDED
YES
PARENT_SCOPE
)
cpm_export_variables(${CPM_ARGS_NAME})
else()
set(CPM_PACKAGE_ALREADY_ADDED
NO
PARENT_SCOPE
)
endif()
endfunction()
# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of
# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted
# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3
function(cpm_parse_add_package_single_arg arg outArgs)
# Look for a scheme
if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$")
string(TOLOWER "${CMAKE_MATCH_1}" scheme)
set(uri "${CMAKE_MATCH_2}")
# Check for CPM-specific schemes
if(scheme STREQUAL "gh")
set(out "GITHUB_REPOSITORY;${uri}")
set(packageType "git")
elseif(scheme STREQUAL "gl")
set(out "GITLAB_REPOSITORY;${uri}")
set(packageType "git")
elseif(scheme STREQUAL "bb")
set(out "BITBUCKET_REPOSITORY;${uri}")
set(packageType "git")
# A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine
# type
elseif(arg MATCHES ".git/?(@|#|$)")
set(out "GIT_REPOSITORY;${arg}")
set(packageType "git")
else()
# Fall back to a URL
set(out "URL;${arg}")
set(packageType "archive")
# We could also check for SVN since FetchContent supports it, but SVN is so rare these days.
# We just won't bother with the additional complexity it will induce in this function. SVN is
# done by multi-arg
endif()
else()
if(arg MATCHES ".git/?(@|#|$)")
set(out "GIT_REPOSITORY;${arg}")
set(packageType "git")
else()
# Give up
message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'")
endif()
endif()
# For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs
# containing '@' can be used
string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}")
# Parse the rest according to package type
if(packageType STREQUAL "git")
# For git repos we interpret #... as a tag or branch or commit hash
string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}")
elseif(packageType STREQUAL "archive")
# For archives we interpret #... as a URL hash.
string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}")
# We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url
# should do this at a later point
else()
# We should never get here. This is an assertion and hitting it means there's a problem with the
# code above. A packageType was set, but not handled by this if-else.
message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'")
endif()
set(${outArgs}
${out}
PARENT_SCOPE
)
endfunction()
# Check that the working directory for a git repo is clean
function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean)
find_package(Git REQUIRED)
if(NOT GIT_EXECUTABLE)
# No git executable, assume directory is clean
set(${isClean}
TRUE
PARENT_SCOPE
)
return()
endif()
# check for uncommitted changes
execute_process(
COMMAND ${GIT_EXECUTABLE} status --porcelain
RESULT_VARIABLE resultGitStatus
OUTPUT_VARIABLE repoStatus
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET
WORKING_DIRECTORY ${repoPath}
)
if(resultGitStatus)
# not supposed to happen, assume clean anyway
message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed")
set(${isClean}
TRUE
PARENT_SCOPE
)
return()
endif()
if(NOT "${repoStatus}" STREQUAL "")
set(${isClean}
FALSE
PARENT_SCOPE
)
return()
endif()
# check for committed changes
execute_process(
COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag}
RESULT_VARIABLE resultGitDiff
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET
WORKING_DIRECTORY ${repoPath}
)
if(${resultGitDiff} EQUAL 0)
set(${isClean}
TRUE
PARENT_SCOPE
)
else()
set(${isClean}
FALSE
PARENT_SCOPE
)
endif()
endfunction()
# Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN
# then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended
# to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`.
function(cpm_add_patches)
# Return if no patch files are supplied.
if(NOT ARGN)
return()
endif()
# Find the patch program.
find_program(PATCH_EXECUTABLE patch)
if(CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE)
# The Windows git executable is distributed with patch.exe. Find the path to the executable, if
# it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe.
find_package(Git QUIET)
if(GIT_EXECUTABLE)
get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY)
get_filename_component(extra_search_path_1up ${extra_search_path} DIRECTORY)
get_filename_component(extra_search_path_2up ${extra_search_path_1up} DIRECTORY)
find_program(
PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up}/usr/bin"
"${extra_search_path_2up}/usr/bin"
)
endif()
endif()
if(NOT PATCH_EXECUTABLE)
message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.")
endif()
# Create a temporary
set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS})
# Ensure each file exists (or error out) and add it to the list.
set(first_item True)
foreach(PATCH_FILE ${ARGN})
# Make sure the patch file exists, if we can't find it, try again in the current directory.
if(NOT EXISTS "${PATCH_FILE}")
if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}")
message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'")
endif()
set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}")
endif()
# Convert to absolute path for use with patch file command.
get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE)
# The first patch entry must be preceded by "PATCH_COMMAND" while the following items are
# preceded by "&&".
if(first_item)
set(first_item False)
list(APPEND temp_list "PATCH_COMMAND")
else()
list(APPEND temp_list "&&")
endif()
# Add the patch command to the list
list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}")
endforeach()
# Move temp out into parent scope.
set(CPM_ARGS_UNPARSED_ARGUMENTS
${temp_list}
PARENT_SCOPE
)
endfunction()
# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload
# FetchContent calls. As these are internal cmake properties, this method should be used carefully
# and may need modification in future CMake versions. Source:
# https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152
function(cpm_override_fetchcontent contentName)
cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "")
if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "")
message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}")
endif()
string(TOLOWER ${contentName} contentNameLower)
set(prefix "_FetchContent_${contentNameLower}")
set(propertyName "${prefix}_sourceDir")
define_property(
GLOBAL
PROPERTY ${propertyName}
BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
)
set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}")
set(propertyName "${prefix}_binaryDir")
define_property(
GLOBAL
PROPERTY ${propertyName}
BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
)
set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}")
set(propertyName "${prefix}_populated")
define_property(
GLOBAL
PROPERTY ${propertyName}
BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()"
FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}"
)
set_property(GLOBAL PROPERTY ${propertyName} TRUE)
endfunction()
# Download and add a package from source
function(CPMAddPackage)
cpm_set_policies()
set(oneValueArgs
NAME
FORCE
VERSION
GIT_TAG
DOWNLOAD_ONLY
GITHUB_REPOSITORY
GITLAB_REPOSITORY
BITBUCKET_REPOSITORY
GIT_REPOSITORY
SOURCE_DIR
FIND_PACKAGE_ARGUMENTS
NO_CACHE
SYSTEM
GIT_SHALLOW
EXCLUDE_FROM_ALL
SOURCE_SUBDIR
CUSTOM_CACHE_KEY
)
set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES)
list(LENGTH ARGN argnLength)
# Parse single shorthand argument
if(argnLength EQUAL 1)
cpm_parse_add_package_single_arg("${ARGN}" ARGN)
# The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM
set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;")
# Parse URI shorthand argument
elseif(argnLength GREATER 1 AND "${ARGV0}" STREQUAL "URI")
list(REMOVE_AT ARGN 0 1) # remove "URI gh:<...>@version#tag"
cpm_parse_add_package_single_arg("${ARGV1}" ARGV0)
set(ARGN "${ARGV0};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;${ARGN}")
endif()
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}")
# Set default values for arguments
if(NOT DEFINED CPM_ARGS_VERSION)
if(DEFINED CPM_ARGS_GIT_TAG)
cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION)
endif()
endif()
if(CPM_ARGS_DOWNLOAD_ONLY)
set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY})
else()
set(DOWNLOAD_ONLY NO)
endif()
if(DEFINED CPM_ARGS_GITHUB_REPOSITORY)
set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git")
elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY)
set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git")
elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY)
set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git")
endif()
if(DEFINED CPM_ARGS_GIT_REPOSITORY)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY})
if(NOT DEFINED CPM_ARGS_GIT_TAG)
set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION})
endif()
# If a name wasn't provided, try to infer it from the git repo
if(NOT DEFINED CPM_ARGS_NAME)
cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME)
endif()
endif()
set(CPM_SKIP_FETCH FALSE)
if(DEFINED CPM_ARGS_GIT_TAG)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG})
# If GIT_SHALLOW is explicitly specified, honor the value.
if(DEFINED CPM_ARGS_GIT_SHALLOW)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW})
endif()
endif()
if(DEFINED CPM_ARGS_URL)
# If a name or version aren't provided, try to infer them from the URL
list(GET CPM_ARGS_URL 0 firstUrl)
cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl)
# If we fail to obtain name and version from the first URL, we could try other URLs if any.
# However multiple URLs are expected to be quite rare, so for now we won't bother.
# If the caller provided their own name and version, they trump the inferred ones.
if(NOT DEFINED CPM_ARGS_NAME)
set(CPM_ARGS_NAME ${nameFromUrl})
endif()
if(NOT DEFINED CPM_ARGS_VERSION)
set(CPM_ARGS_VERSION ${verFromUrl})
endif()
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}")
endif()
# Check for required arguments
if(NOT DEFINED CPM_ARGS_NAME)
message(
FATAL_ERROR
"${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'"
)
endif()
# Check if package has been added before
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
if(CPM_PACKAGE_ALREADY_ADDED)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
# Check for manual overrides
if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "")
set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE})
set(CPM_${CPM_ARGS_NAME}_SOURCE "")
CPMAddPackage(
NAME "${CPM_ARGS_NAME}"
SOURCE_DIR "${PACKAGE_SOURCE}"
EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}"
SYSTEM "${CPM_ARGS_SYSTEM}"
PATCHES "${CPM_ARGS_PATCHES}"
OPTIONS "${CPM_ARGS_OPTIONS}"
SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}"
DOWNLOAD_ONLY "${DOWNLOAD_ONLY}"
FORCE True
)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
# Check for available declaration
if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "")
set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}})
set(CPM_DECLARATION_${CPM_ARGS_NAME} "")
CPMAddPackage(${declaration})
cpm_export_variables(${CPM_ARGS_NAME})
# checking again to ensure version and option compatibility
cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}")
return()
endif()
if(NOT CPM_ARGS_FORCE)
if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY)
cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS})
if(CPM_PACKAGE_FOUND)
cpm_export_variables(${CPM_ARGS_NAME})
return()
endif()
if(CPM_LOCAL_PACKAGES_ONLY)
message(
SEND_ERROR
"${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})"
)
endif()
endif()
endif()
CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}")
if(DEFINED CPM_ARGS_GIT_TAG)
set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}")
elseif(DEFINED CPM_ARGS_SOURCE_DIR)
set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}")
else()
set(PACKAGE_INFO "${CPM_ARGS_VERSION}")
endif()
if(DEFINED FETCHCONTENT_BASE_DIR)
# respect user's FETCHCONTENT_BASE_DIR if set
set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR})
else()
set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps)
endif()
cpm_add_patches(${CPM_ARGS_PATCHES})
if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND})
elseif(DEFINED CPM_ARGS_SOURCE_DIR)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR})
if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR})
# Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work
# for relative paths.
get_filename_component(
source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR}
)
else()
set(source_directory ${CPM_ARGS_SOURCE_DIR})
endif()
if(NOT EXISTS ${source_directory})
string(TOLOWER ${CPM_ARGS_NAME} lower_case_name)
# remove timestamps so CMake will re-download the dependency
file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild")
endif()
elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE)
string(TOLOWER ${CPM_ARGS_NAME} lower_case_name)
set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS})
list(SORT origin_parameters)
if(CPM_ARGS_CUSTOM_CACHE_KEY)
# Application set a custom unique directory name
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY})
elseif(CPM_USE_NAMED_CACHE_DIRECTORIES)
string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG")
cpm_get_shortest_hash(
"${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory
"${origin_hash}" # Input hash
origin_hash # Computed hash
)
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME})
else()
string(SHA1 origin_hash "${origin_parameters}")
cpm_get_shortest_hash(
"${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory
"${origin_hash}" # Input hash
origin_hash # Computed hash
)
set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash})
endif()
# Expand `download_directory` relative path. This is important because EXISTS doesn't work for
# relative paths.
get_filename_component(download_directory ${download_directory} ABSOLUTE)
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory})
if(CPM_SOURCE_CACHE)
file(LOCK ${download_directory}/../cmake.lock)
endif()
if(EXISTS ${download_directory})
if(CPM_SOURCE_CACHE)
file(LOCK ${download_directory}/../cmake.lock RELEASE)
endif()
cpm_store_fetch_properties(
${CPM_ARGS_NAME} "${download_directory}"
"${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build"
)
cpm_get_fetch_properties("${CPM_ARGS_NAME}")
if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS))
# warn if cache has been changed since checkout
cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN)
if(NOT ${IS_CLEAN})
message(
WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty"
)
endif()
endif()
cpm_add_subdirectory(
"${CPM_ARGS_NAME}"
"${DOWNLOAD_ONLY}"
"${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}"
"${${CPM_ARGS_NAME}_BINARY_DIR}"
"${CPM_ARGS_EXCLUDE_FROM_ALL}"
"${CPM_ARGS_SYSTEM}"
"${CPM_ARGS_OPTIONS}"
)
set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}")
# As the source dir is already cached/populated, we override the call to FetchContent.
set(CPM_SKIP_FETCH TRUE)
cpm_override_fetchcontent(
"${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}"
BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}"
)
else()
# Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but
# it should guarantee no commit hash get mis-detected.
if(NOT DEFINED CPM_ARGS_GIT_SHALLOW)
cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH)
if(NOT ${IS_HASH})
list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE)
endif()
endif()
# remove timestamps so CMake will re-download the dependency
file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild)
set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}")
endif()
endif()
if(NOT "${DOWNLOAD_ONLY}")
cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")")
endif()
if(CPM_PACKAGE_LOCK_ENABLED)
if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK)
cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}")
elseif(CPM_ARGS_SOURCE_DIR)
cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory")
else()
cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}")
endif()
endif()
cpm_message(
STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})"
)
if(NOT CPM_SKIP_FETCH)
# CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare.
# Calling FetchContent_MakeAvailable will then internally forward these options to
# add_subdirectory. Up until these changes, we had to call FetchContent_Populate and
# add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30.
# A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3.
set(fetchContentDeclareExtraArgs "")
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3")
if(${CPM_ARGS_EXCLUDE_FROM_ALL})
list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL)
endif()
if(${CPM_ARGS_SYSTEM})
list(APPEND fetchContentDeclareExtraArgs SYSTEM)
endif()
if(DEFINED CPM_ARGS_SOURCE_SUBDIR)
list(APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR})
endif()
# For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory
if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY)
foreach(OPTION ${CPM_ARGS_OPTIONS})
cpm_parse_option("${OPTION}")
set(${OPTION_KEY} "${OPTION_VALUE}")
endforeach()
endif()
endif()
cpm_declare_fetch(
"${CPM_ARGS_NAME}" ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS}"
)
cpm_fetch_package("${CPM_ARGS_NAME}" ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS})
if(CPM_SOURCE_CACHE AND download_directory)
file(LOCK ${download_directory}/../cmake.lock RELEASE)
endif()
if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3")
cpm_add_subdirectory(
"${CPM_ARGS_NAME}"
"${DOWNLOAD_ONLY}"
"${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}"
"${${CPM_ARGS_NAME}_BINARY_DIR}"
"${CPM_ARGS_EXCLUDE_FROM_ALL}"
"${CPM_ARGS_SYSTEM}"
"${CPM_ARGS_OPTIONS}"
)
endif()
cpm_get_fetch_properties("${CPM_ARGS_NAME}")
endif()
set(${CPM_ARGS_NAME}_ADDED YES)
cpm_export_variables("${CPM_ARGS_NAME}")
endfunction()
# Fetch a previously declared package
macro(CPMGetPackage Name)
if(DEFINED "CPM_DECLARATION_${Name}")
CPMAddPackage(NAME ${Name})
else()
message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available")
endif()
endmacro()
# export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set
macro(cpm_export_variables name)
set(${name}_SOURCE_DIR
"${${name}_SOURCE_DIR}"
PARENT_SCOPE
)
set(${name}_BINARY_DIR
"${${name}_BINARY_DIR}"
PARENT_SCOPE
)
set(${name}_ADDED
"${${name}_ADDED}"
PARENT_SCOPE
)
set(CPM_LAST_PACKAGE_NAME
"${name}"
PARENT_SCOPE
)
endmacro()
# declares a package, so that any call to CPMAddPackage for the package name will use these
# arguments instead. Previous declarations will not be overridden.
macro(CPMDeclarePackage Name)
if(NOT DEFINED "CPM_DECLARATION_${Name}")
set("CPM_DECLARATION_${Name}" "${ARGN}")
endif()
endmacro()
function(cpm_add_to_package_lock Name)
if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN})
file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n")
endif()
endfunction()
function(cpm_add_comment_to_package_lock Name)
if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN})
file(APPEND ${CPM_PACKAGE_LOCK_FILE}
"# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n"
)
endif()
endfunction()
# includes the package lock file if it exists and creates a target `cpm-update-package-lock` to
# update it
macro(CPMUsePackageLock file)
if(NOT CPM_DONT_CREATE_PACKAGE_LOCK)
get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE)
if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH})
endif()
if(NOT TARGET cpm-update-package-lock)
add_custom_target(
cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE}
${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}
)
endif()
set(CPM_PACKAGE_LOCK_ENABLED true)
endif()
endmacro()
# registers a package that has been added to CPM
function(CPMRegisterPackage PACKAGE VERSION)
list(APPEND CPM_PACKAGES ${PACKAGE})
set(CPM_PACKAGES
${CPM_PACKAGES}
CACHE INTERNAL ""
)
set("CPM_PACKAGE_${PACKAGE}_VERSION"
${VERSION}
CACHE INTERNAL ""
)
endfunction()
# retrieve the current version of the package to ${OUTPUT}
function(CPMGetPackageVersion PACKAGE OUTPUT)
set(${OUTPUT}
"${CPM_PACKAGE_${PACKAGE}_VERSION}"
PARENT_SCOPE
)
endfunction()
# declares a package in FetchContent_Declare
function(cpm_declare_fetch PACKAGE)
if(${CPM_DRY_RUN})
cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)")
return()
endif()
FetchContent_Declare(${PACKAGE} ${ARGN})
endfunction()
# returns properties for a package previously defined by cpm_declare_fetch
function(cpm_get_fetch_properties PACKAGE)
if(${CPM_DRY_RUN})
return()
endif()
set(${PACKAGE}_SOURCE_DIR
"${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}"
PARENT_SCOPE
)
set(${PACKAGE}_BINARY_DIR
"${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}"
PARENT_SCOPE
)
endfunction()
function(cpm_store_fetch_properties PACKAGE source_dir binary_dir)
if(${CPM_DRY_RUN})
return()
endif()
set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR
"${source_dir}"
CACHE INTERNAL ""
)
set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR
"${binary_dir}"
CACHE INTERNAL ""
)
endfunction()
# adds a package as a subdirectory if viable, according to provided options
function(
cpm_add_subdirectory
PACKAGE
DOWNLOAD_ONLY
SOURCE_DIR
BINARY_DIR
EXCLUDE
SYSTEM
OPTIONS
)
if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt)
set(addSubdirectoryExtraArgs "")
if(EXCLUDE)
list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL)
endif()
if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25")
# https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM
list(APPEND addSubdirectoryExtraArgs SYSTEM)
endif()
if(OPTIONS)
foreach(OPTION ${OPTIONS})
cpm_parse_option("${OPTION}")
set(${OPTION_KEY} "${OPTION_VALUE}")
endforeach()
endif()
set(CPM_OLD_INDENT "${CPM_INDENT}")
set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:")
add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs})
set(CPM_INDENT "${CPM_OLD_INDENT}")
endif()
endfunction()
# downloads a previously declared package via FetchContent and exports the variables
# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope
function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated)
set(${populated}
FALSE
PARENT_SCOPE
)
if(${CPM_DRY_RUN})
cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)")
return()
endif()
FetchContent_GetProperties(${PACKAGE})
string(TOLOWER "${PACKAGE}" lower_case_name)
if(NOT ${lower_case_name}_POPULATED)
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3")
if(DOWNLOAD_ONLY)
# MakeAvailable will call add_subdirectory internally which is not what we want when
# DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the
# build
FetchContent_Populate(
${PACKAGE}
SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-src"
BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build"
SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild"
${ARGN}
)
else()
FetchContent_MakeAvailable(${PACKAGE})
endif()
else()
FetchContent_Populate(${PACKAGE})
endif()
set(${populated}
TRUE
PARENT_SCOPE
)
endif()
cpm_store_fetch_properties(
${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR}
)
set(${PACKAGE}_SOURCE_DIR
${${lower_case_name}_SOURCE_DIR}
PARENT_SCOPE
)
set(${PACKAGE}_BINARY_DIR
${${lower_case_name}_BINARY_DIR}
PARENT_SCOPE
)
endfunction()
# splits a package option
function(cpm_parse_option OPTION)
string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}")
string(LENGTH "${OPTION}" OPTION_LENGTH)
string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH)
if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH)
# no value for key provided, assume user wants to set option to "ON"
set(OPTION_VALUE "ON")
else()
math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1")
string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE)
endif()
set(OPTION_KEY
"${OPTION_KEY}"
PARENT_SCOPE
)
set(OPTION_VALUE
"${OPTION_VALUE}"
PARENT_SCOPE
)
endfunction()
# guesses the package version from a git tag
function(cpm_get_version_from_git_tag GIT_TAG RESULT)
string(LENGTH ${GIT_TAG} length)
if(length EQUAL 40)
# GIT_TAG is probably a git hash
set(${RESULT}
0
PARENT_SCOPE
)
else()
string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG})
set(${RESULT}
${CMAKE_MATCH_1}
PARENT_SCOPE
)
endif()
endfunction()
# guesses if the git tag is a commit hash or an actual tag or a branch name.
function(cpm_is_git_tag_commit_hash GIT_TAG RESULT)
string(LENGTH "${GIT_TAG}" length)
# full hash has 40 characters, and short hash has at least 7 characters.
if(length LESS 7 OR length GREATER 40)
set(${RESULT}
0
PARENT_SCOPE
)
else()
if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$")
set(${RESULT}
1
PARENT_SCOPE
)
else()
set(${RESULT}
0
PARENT_SCOPE
)
endif()
endif()
endfunction()
function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT)
set(oneValueArgs
NAME
FORCE
VERSION
GIT_TAG
DOWNLOAD_ONLY
GITHUB_REPOSITORY
GITLAB_REPOSITORY
BITBUCKET_REPOSITORY
GIT_REPOSITORY
SOURCE_DIR
FIND_PACKAGE_ARGUMENTS
NO_CACHE
SYSTEM
GIT_SHALLOW
EXCLUDE_FROM_ALL
SOURCE_SUBDIR
)
set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND)
cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
foreach(oneArgName ${oneValueArgs})
if(DEFINED CPM_ARGS_${oneArgName})
if(${IS_IN_COMMENT})
string(APPEND PRETTY_OUT_VAR "#")
endif()
if(${oneArgName} STREQUAL "SOURCE_DIR")
string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName}
${CPM_ARGS_${oneArgName}}
)
endif()
string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n")
endif()
endforeach()
foreach(multiArgName ${multiValueArgs})
if(DEFINED CPM_ARGS_${multiArgName})
if(${IS_IN_COMMENT})
string(APPEND PRETTY_OUT_VAR "#")
endif()
string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n")
foreach(singleOption ${CPM_ARGS_${multiArgName}})
if(${IS_IN_COMMENT})
string(APPEND PRETTY_OUT_VAR "#")
endif()
string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n")
endforeach()
endif()
endforeach()
if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "")
if(${IS_IN_COMMENT})
string(APPEND PRETTY_OUT_VAR "#")
endif()
string(APPEND PRETTY_OUT_VAR " ")
foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS})
string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}")
endforeach()
string(APPEND PRETTY_OUT_VAR "\n")
endif()
set(${OUT_VAR}
${PRETTY_OUT_VAR}
PARENT_SCOPE
)
endfunction()
ada-url-ada-3c84d73/cmake/JoinPaths.cmake 0000664 0000000 0000000 00000000673 15147206404 0020115 0 ustar 00root root 0000000 0000000 function(join_paths joined_path first_path_segment)
set(temp_path "${first_path_segment}")
foreach(current_segment IN LISTS ARGN)
if(NOT ("${current_segment}" STREQUAL ""))
if(IS_ABSOLUTE "${current_segment}")
set(temp_path "${current_segment}")
else()
set(temp_path "${temp_path}/${current_segment}")
endif()
endif()
endforeach()
set(${joined_path} "${temp_path}" PARENT_SCOPE)
endfunction()
ada-url-ada-3c84d73/cmake/ada-config.cmake.in 0000664 0000000 0000000 00000000067 15147206404 0020610 0 ustar 00root root 0000000 0000000 include("${CMAKE_CURRENT_LIST_DIR}/ada_targets.cmake")
ada-url-ada-3c84d73/cmake/ada-flags.cmake 0000664 0000000 0000000 00000005422 15147206404 0020032 0 ustar 00root root 0000000 0000000 option(ADA_LOGGING "verbose output (useful for debugging)" OFF)
option(ADA_DEVELOPMENT_CHECKS "development checks (useful for debugging)" OFF)
option(ADA_SANITIZE "Sanitize addresses" OFF)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
option(ADA_SANITIZE_BOUNDS_STRICT "Sanitize bounds (strict): only for GCC" OFF)
endif()
option(ADA_SANITIZE_UNDEFINED "Sanitize undefined behaviour" OFF)
if(ADA_SANITIZE)
message(STATUS "Address sanitizer enabled.")
endif()
if(ADA_SANITIZE_WITHOUT_LEAKS)
message(STATUS "Address sanitizer (but not leak) enabled.")
endif()
if(ADA_SANITIZE_UNDEFINED)
message(STATUS "Undefined sanitizer enabled.")
endif()
option(ADA_COVERAGE "Compute coverage" OFF)
option(ADA_TOOLS "Build cli tools (adaparse)" OFF)
option(ADA_BENCHMARKS "Build benchmarks" OFF)
option(ADA_TESTING "Build tests" OFF)
option(ADA_USE_UNSAFE_STD_REGEX_PROVIDER "Enable unsafe regex provider that uses std::regex" OFF)
option(ADA_INCLUDE_URL_PATTERN "Include URL pattern implementation" ON)
if (ADA_COVERAGE)
message(STATUS "You want to compute coverage. We assume that you have installed gcovr.")
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
endif()
#######################
# You need to install gcovr. Under macos, you may do so with brew.
# brew install gcovr
# Then build...
# cmake -D ADA_COVERAGE=ON -B buildcoverage
# cmake --build buildcoverage
# cmake --build buildcoverage --target ada_coverage
#
# open buildcoverage/ada_coverage/index.html
#####################
include(${PROJECT_SOURCE_DIR}/cmake/codecoverage.cmake)
APPEND_COVERAGE_COMPILER_FLAGS()
setup_target_for_coverage_gcovr_html(NAME ada_coverage EXECUTABLE ctest EXCLUDE "${PROJECT_SOURCE_DIR}/dependencies/*" "${PROJECT_SOURCE_DIR}/tools/*" "${PROJECT_SOURCE_DIR}/singleheader/*" ${PROJECT_SOURCE_DIR}/include/ada/common_defs.h)
endif()
if (NOT CMAKE_BUILD_TYPE)
if(ADA_SANITIZE OR ADA_SANITIZE_WITHOUT_LEAKS OR ADA_SANITIZE_BOUNDS_STRICT OR ADA_SANITIZE_UNDEFINED)
message(STATUS "No build type selected, default to Debug because you have sanitizers.")
set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
else()
message(STATUS "No build type selected, default to Release")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
endif()
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
message(STATUS "Ccache found using it as compiler launcher.")
set(CMAKE_C_COMPILER_LAUNCHER ccache)
set(CMAKE_CXX_COMPILER_LAUNCHER ccache)
endif(CCACHE_FOUND)
ada-url-ada-3c84d73/cmake/add-cpp-test.cmake 0000664 0000000 0000000 00000005376 15147206404 0020510 0 ustar 00root root 0000000 0000000 # Helper so we don't have to repeat ourselves so much
# Usage: add_cpp_test(testname [COMPILE_ONLY] [SOURCES a.cpp b.cpp ...] [LABELS acceptance per_implementation ...])
# SOURCES defaults to testname.cpp if not specified.
function(add_cpp_test TEST_NAME)
# Parse arguments
cmake_parse_arguments(PARSE_ARGV 1 ARGS "COMPILE_ONLY;LIBRARY;WILL_FAIL" "" "SOURCES;LABELS;DEPENDENCY_OF")
if (NOT ARGS_SOURCES)
list(APPEND ARGS_SOURCES ${TEST_NAME}.cpp)
endif()
if (ARGS_COMPILE_ONLY)
list(APPEND ${ARGS_LABELS} compile_only)
endif()
if(ADA_SANITIZE)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all)
add_compile_definitions(ASAN_OPTIONS=detect_leaks=1)
endif()
if(ADA_SANITIZE_WITHOUT_LEAKS)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer -fno-sanitize-recover=all)
endif()
if(ADA_SANITIZE_BOUNDS_STRICT)
add_compile_options(-fsanitize=bounds-strict -fno-sanitize-recover=all)
add_link_options(-fsanitize=bounds-strict)
endif()
if(ADA_SANITIZE_UNDEFINED)
add_compile_options(-fsanitize=undefined -fno-sanitize-recover=all)
add_link_options(-fsanitize=undefined)
endif()
# Add the compile target
if (ARGS_LIBRARY)
add_library(${TEST_NAME} STATIC ${ARGS_SOURCES})
else(ARGS_LIBRARY)
add_executable(${TEST_NAME} ${ARGS_SOURCES})
set_source_files_properties(${ARGS_SOURCES} PROPERTIES SKIP_LINTING ON)
endif(ARGS_LIBRARY)
# Add test
if (ARGS_COMPILE_ONLY OR ARGS_LIBRARY)
add_test(
NAME ${TEST_NAME}
COMMAND ${CMAKE_COMMAND} --build . --target ${TEST_NAME} --config $
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
)
set_target_properties(${TEST_NAME} PROPERTIES EXCLUDE_FROM_ALL TRUE EXCLUDE_FROM_DEFAULT_BUILD TRUE)
else()
if (CMAKE_CROSSCOMPILING_EMULATOR)
add_test(${TEST_NAME} ${CMAKE_CROSSCOMPILING_EMULATOR} ${TEST_NAME})
else()
add_test(${TEST_NAME} ${TEST_NAME})
endif()
# Add to