pax_global_header 0000666 0000000 0000000 00000000064 15160333015 0014507 g ustar 00root root 0000000 0000000 52 comment=8d50724a7dea209a05234a445e28f97994b0a5f6
ada-url-ada-8d50724/ 0000775 0000000 0000000 00000000000 15160333015 0014036 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/.clang-format 0000664 0000000 0000000 00000000051 15160333015 0016405 0 ustar 00root root 0000000 0000000 BasedOnStyle: Google
SortIncludes: Never
ada-url-ada-8d50724/.clang-tidy 0000664 0000000 0000000 00000001041 15160333015 0016066 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-8d50724/.editorconfig 0000664 0000000 0000000 00000000143 15160333015 0016511 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-8d50724/.github/ 0000775 0000000 0000000 00000000000 15160333015 0015376 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/.github/FUNDING.yml 0000664 0000000 0000000 00000000032 15160333015 0017206 0 ustar 00root root 0000000 0000000 github: [anonrig, lemire]
ada-url-ada-8d50724/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15160333015 0017561 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/.github/ISSUE_TEMPLATE/1-bug-report.yml 0000664 0000000 0000000 00000003007 15160333015 0022530 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-8d50724/.github/ISSUE_TEMPLATE/2-feature-request.yml 0000664 0000000 0000000 00000001212 15160333015 0023560 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-8d50724/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000257 15160333015 0021555 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-8d50724/.github/actions/ 0000775 0000000 0000000 00000000000 15160333015 0017036 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/.github/actions/setup-runner/ 0000775 0000000 0000000 00000000000 15160333015 0021505 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/.github/actions/setup-runner/action.yml 0000664 0000000 0000000 00000001047 15160333015 0023507 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-8d50724/.github/dependabot.yml 0000664 0000000 0000000 00000000560 15160333015 0020227 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-8d50724/.github/workflows/ 0000775 0000000 0000000 00000000000 15160333015 0017433 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/.github/workflows/_build.yaml 0000664 0000000 0000000 00000014662 15160333015 0021566 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-8d50724/.github/workflows/cifuzz.yml 0000664 0000000 0000000 00000002145 15160333015 0021472 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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: steps.build.outcome == 'success'
with:
name: ${{ matrix.sanitizer }}-artifacts
path: ./out/artifacts
ada-url-ada-8d50724/.github/workflows/codecov.yml 0000664 0000000 0000000 00000003276 15160333015 0021610 0 ustar 00root root 0000000 0000000 name: Code Coverage
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:
coverage:
name: coverage
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Install dependencies
run: sudo apt-get update -y && sudo apt-get install -y gcovr ninja-build
- name: Configure
run: >
cmake -B build -G Ninja
-DCMAKE_CXX_COMPILER=g++-12
-DADA_TESTING=ON
-DADA_COVERAGE=ON
-DADA_DEVELOPMENT_CHECKS=ON
- name: Build
run: cmake --build build
- name: Run tests
run: ctest --output-on-failure --test-dir build
- name: Generate coverage report
run: >
gcovr
--root .
--exclude 'build/_deps/.*'
--exclude 'build/dependencies/.*'
--exclude 'dependencies/.*'
--exclude 'tools/.*'
--exclude 'singleheader/.*'
--exclude 'benchmarks/.*'
--exclude 'tests/.*'
--exclude 'include/ada/expected.h'
--gcov-executable gcov-12
--xml
--output build/coverage.xml
- name: Upload to Codecov
uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3
with:
files: build/coverage.xml
fail_ci_if_error: true
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
ada-url-ada-8d50724/.github/workflows/codeql.yml 0000664 0000000 0000000 00000002341 15160333015 0021425 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@89a39a4e59826350b863aa6b6252a07ad50cf83e # 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@89a39a4e59826350b863aa6b6252a07ad50cf83e # v2.2.5
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v2.2.5
with:
category: "/language:${{matrix.language}}"
ada-url-ada-8d50724/.github/workflows/codspeed.yml 0000664 0000000 0000000 00000001707 15160333015 0021751 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-8d50724/.github/workflows/dependency-review.yml 0000664 0000000 0000000 00000000607 15160333015 0023576 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@05fe4576374b728f0c523d6a13d64c25081e0803 # v4.8.3
ada-url-ada-8d50724/.github/workflows/documentation.yml 0000664 0000000 0000000 00000001634 15160333015 0023033 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-8d50724/.github/workflows/lint_and_format_check.yml 0000664 0000000 0000000 00000002472 15160333015 0024460 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-8d50724/.github/workflows/macos.yml 0000664 0000000 0000000 00000001375 15160333015 0021266 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-8d50724/.github/workflows/release-script-tests.yml 0000664 0000000 0000000 00000001617 15160333015 0024245 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-8d50724/.github/workflows/release_create.yml 0000664 0000000 0000000 00000003307 15160333015 0023124 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-8d50724/.github/workflows/release_prepare.yml 0000664 0000000 0000000 00000003446 15160333015 0023323 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-8d50724/.github/workflows/scorecard.yml 0000664 0000000 0000000 00000005626 15160333015 0022134 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@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.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@89a39a4e59826350b863aa6b6252a07ad50cf83e # v3.29.5
with:
sarif_file: results.sarif
ada-url-ada-8d50724/.github/workflows/ubuntu.yml 0000664 0000000 0000000 00000015442 15160333015 0021506 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-8d50724/.github/workflows/windows.yml 0000664 0000000 0000000 00000002762 15160333015 0021657 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-8d50724/.github/workflows/wpt-updater.yml 0000664 0000000 0000000 00000002427 15160333015 0022437 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-8d50724/.gitignore 0000664 0000000 0000000 00000000730 15160333015 0016026 0 ustar 00root root 0000000 0000000 # common build directory
build
build-*/
*-build-*
_fuzz_check/
# 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-*
# gcov coverage data files
*.gcov
ada-url-ada-8d50724/.python-version 0000664 0000000 0000000 00000000005 15160333015 0017036 0 ustar 00root root 0000000 0000000 3.12
ada-url-ada-8d50724/AI_USAGE_POLICY.md 0000664 0000000 0000000 00000004700 15160333015 0016655 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-8d50724/CLAUDE.md 0000664 0000000 0000000 00000021736 15160333015 0015326 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-8d50724/CMakeLists.txt 0000664 0000000 0000000 00000013633 15160333015 0016604 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.4
)
set(ADA_LIB_VERSION "3.4.4" 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-8d50724/LICENSE-APACHE 0000664 0000000 0000000 00000026116 15160333015 0015770 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-8d50724/LICENSE-MIT 0000664 0000000 0000000 00000002057 15160333015 0015476 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-8d50724/README.md 0000664 0000000 0000000 00000033501 15160333015 0015317 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-8d50724/SECURITY.md 0000664 0000000 0000000 00000000400 15160333015 0015621 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-8d50724/ada.pc.in 0000664 0000000 0000000 00000000461 15160333015 0015515 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-8d50724/benchmarks/ 0000775 0000000 0000000 00000000000 15160333015 0016153 5 ustar 00root root 0000000 0000000 ada-url-ada-8d50724/benchmarks/CMakeLists.txt 0000664 0000000 0000000 00000034354 15160333015 0020724 0 ustar 00root root 0000000 0000000 # Performance counters
CPMAddPackage(
NAME counters
GITHUB_REPOSITORY lemire/counters
GIT_TAG v3.0.0
)
# bench_protocol
add_executable(bench_protocol bench_protocol.cpp)
target_link_libraries(bench_protocol PRIVATE ada counters::counters)
# 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-8d50724/benchmarks/bbc_bench.cpp 0000664 0000000 0000000 00000002654 15160333015 0020553 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-8d50724/benchmarks/bench.cpp 0000664 0000000 0000000 00000004550 15160333015 0017742 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-8d50724/benchmarks/bench_ipv4.cpp 0000664 0000000 0000000 00000023264 15160333015 0020707 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-8d50724/benchmarks/bench_protocol.cpp 0000664 0000000 0000000 00000023626 15160333015 0021670 0 ustar 00root root 0000000 0000000 /**
* Reference:
* Daniel Lemire, "Quickly checking that a string belongs to a small set," in
* Daniel Lemire's blog, December 30, 2022,
* https://lemire.me/blog/2022/12/30/quickly-checking-that-a-string-belongs-to-a-small-set/.
*/
#include
#include
#include
#include
#include
#include
#include