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