From: Jérémy Lal Date: Wed, 19 Jun 2024 10:03:27 +0000 (+0200) Subject: nodejs (20.14.0+dfsg-3) unstable; urgency=medium X-Git-Tag: archive/raspbian/20.14.0+dfsg-3+rpi1^2~30 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=d242d954cf085b5b69c6a4a60da5e5636d7573f3;p=nodejs.git nodejs (20.14.0+dfsg-3) unstable; urgency=medium * test-cpu-prof-dir-absolute is flaky * Fix: skip failing test in the right suite [dgit import unpatched nodejs 20.14.0+dfsg-3] --- d242d954cf085b5b69c6a4a60da5e5636d7573f3 diff --cc ada/.clang-format index 000000000,000000000,000000000,000000000..1acba5a7b new file mode 100644 --- /dev/null +++ b/ada/.clang-format @@@@@ -1,0 -1,0 -1,0 -1,0 +1,2 @@@@@ ++++BasedOnStyle: Google ++++SortIncludes: false diff --cc ada/.editorconfig index 000000000,000000000,000000000,000000000..0b3779e53 new file mode 100644 --- /dev/null +++ b/ada/.editorconfig @@@@@ -1,0 -1,0 -1,0 -1,0 +1,5 @@@@@ ++++root = true ++++ ++++[*] ++++end_of_line = lf ++++insert_final_newline = true diff --cc ada/.github/ISSUE_TEMPLATE/1-bug-report.yml index 000000000,000000000,000000000,000000000..c11dae751 new file mode 100644 --- /dev/null +++ b/ada/.github/ISSUE_TEMPLATE/1-bug-report.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,40 @@@@@ ++++name: 🐛 Bug report ++++description: Create a report to help us improve ++++body: ++++ - type: markdown ++++ attributes: ++++ value: | ++++ Thank you for reporting an issue. ++++ ++++ Please fill in as much of the following form as you're able. ++++ - 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. diff --cc ada/.github/ISSUE_TEMPLATE/2-feature-request.yml index 000000000,000000000,000000000,000000000..f7cae8c0e new file mode 100644 --- /dev/null +++ b/ada/.github/ISSUE_TEMPLATE/2-feature-request.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,23 @@@@@ ++++name: 🚀 Feature request ++++description: Suggest an idea for this project ++++labels: [feature request] ++++body: ++++ - type: markdown ++++ attributes: ++++ value: | ++++ Thank you for suggesting an idea to make Node.js 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? diff --cc ada/.github/ISSUE_TEMPLATE/config.yml index 000000000,000000000,000000000,000000000..3159b474e new file mode 100644 --- /dev/null +++ b/ada/.github/ISSUE_TEMPLATE/config.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,5 @@@@@ ++++blank_issues_enabled: true ++++contact_links: ++++ - name: Looking for documentation? ++++ url: https://ada-url.github.io/ada ++++ about: Please navigate to our documentation website. diff --cc ada/.github/dependabot.yml index 000000000,000000000,000000000,000000000..1cfc22ead new file mode 100644 --- /dev/null +++ b/ada/.github/dependabot.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,23 @@@@@ ++++# Set update schedule for GitHub Actions ++++ ++++version: 2 ++++updates: ++++ - package-ecosystem: github-actions ++++ directory: / ++++ schedule: ++++ interval: monthly ++++ ++++ - package-ecosystem: docker ++++ directory: / ++++ schedule: ++++ interval: monthly ++++ ++++ - package-ecosystem: cargo ++++ directory: /benchmarks/competitors/servo-url ++++ schedule: ++++ interval: monthly ++++ ++++ - package-ecosystem: pip ++++ directory: /tools/release ++++ schedule: ++++ interval: monthly diff --cc ada/.github/workflows/alpine.yml index 000000000,000000000,000000000,000000000..29d058c31 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/alpine.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,45 @@@@@ ++++name: Alpine Linux ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-latest ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.5.2 ++++ - name: start docker ++++ 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: cmake ++++ run: | ++++ ./alpine.sh cmake -DADA_BENCHMARKS=ON -B build_for_alpine ++++ - name: build ++++ run: | ++++ ./alpine.sh cmake --build build_for_alpine ++++ - name: test ++++ run: | ++++ ./alpine.sh bash -c "cd build_for_alpine && ctest ." diff --cc ada/.github/workflows/cifuzz.yml index 000000000,000000000,000000000,000000000..2dcece0b4 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/cifuzz.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,41 @@@@@ ++++name: CIFuzz ++++ ++++on: ++++ pull_request: ++++ branches: ++++ - main ++++ ++++concurrency: ++++ group: ${{ github.workflow }}-${{ github.ref }} ++++ cancel-in-progress: true ++++ ++++permissions: read-all ++++ ++++jobs: ++++ Fuzzing: ++++ runs-on: ubuntu-latest ++++ strategy: ++++ fail-fast: false ++++ matrix: ++++ sanitizer: [address, undefined, memory] ++++ steps: ++++ - name: Build Fuzzers (${{ matrix.sanitizer }}) ++++ id: build ++++ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@c9485cb75e3e39a122264a45ce667d3b57188675 # 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@c9485cb75e3e39a122264a45ce667d3b57188675 # master ++++ with: ++++ oss-fuzz-project-name: 'ada-url' ++++ language: c++ ++++ fuzz-seconds: 300 ++++ sanitizer: ${{ matrix.sanitizer }} ++++ - name: Upload Crash ++++ uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 ++++ if: steps.build.outcome == 'success' ++++ with: ++++ name: ${{ matrix.sanitizer }}-artifacts ++++ path: ./out/artifacts diff --cc ada/.github/workflows/codeql.yml index 000000000,000000000,000000000,000000000..280ed75e6 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/codeql.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,47 @@@@@ ++++name: "CodeQL" ++++ ++++on: ++++ schedule: ++++ - cron: '0 0 * * 1' ++++ ++++permissions: ++++ contents: read ++++ security-events: write ++++ pull-requests: read ++++ actions: read ++++ ++++jobs: ++++ analyze: ++++ name: Analyze ++++ ++++ runs-on: ubuntu-latest ++++ ++++ permissions: ++++ actions: read ++++ contents: read ++++ security-events: write ++++ ++++ strategy: ++++ fail-fast: false ++++ matrix: ++++ language: [ 'cpp', 'python' ] ++++ ++++ steps: ++++ - name: Checkout repository ++++ uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ ++++ # Initializes the CodeQL tools for scanning. ++++ - name: Initialize CodeQL ++++ uses: github/codeql-action/init@cdcdbb579706841c47f7063dda365e292e5cad7a # 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@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.2.5 ++++ ++++ - name: Perform CodeQL Analysis ++++ uses: github/codeql-action/analyze@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.2.5 ++++ with: ++++ category: "/language:${{matrix.language}}" diff --cc ada/.github/workflows/dependency-review.yml index 000000000,000000000,000000000,000000000..332890bf0 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/dependency-review.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,14 @@@@@ ++++name: 'Dependency Review' ++++on: [pull_request] ++++ ++++permissions: ++++ contents: read ++++ ++++jobs: ++++ dependency-review: ++++ runs-on: ubuntu-latest ++++ steps: ++++ - name: 'Checkout Repository' ++++ uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: 'Dependency Review' ++++ uses: actions/dependency-review-action@5bbc3ba658137598168acb2ab73b21c432dd411b # v4.2.5 diff --cc ada/.github/workflows/documentation.yml index 000000000,000000000,000000000,000000000..5ed64cdcc new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/documentation.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,35 @@@@@ ++++name: Doxygen GitHub Pages ++++ ++++on: ++++ push: ++++ branches: ++++ - main ++++ # 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@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Install theme ++++ run: ./tools/prepare-doxygen.sh ++++ - uses: mattnotmitt/doxygen-action@e0c8cd4cd05e28b88e723b25b30188ecf2505b40 # edge ++++ with: ++++ doxyfile-path: './doxygen' ++++ - name: Deploy to GitHub Pages ++++ uses: peaceiris/actions-gh-pages@373f7f263a76c20808c831209c920827a82a2847 # v3.9.3 ++++ with: ++++ github_token: ${{ secrets.GITHUB_TOKEN }} ++++ publish_dir: docs/html diff --cc ada/.github/workflows/emscripten.yml index 000000000,000000000,000000000,000000000..301dace92 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/emscripten.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,39 @@@@@ ++++name: emscripten ++++ ++++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: ++++ runs-on: ubuntu-latest ++++ steps: ++++ - uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 ++++ - uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14 ++++ - name: Verify ++++ run: emcc -v ++++ - name: Checkout ++++ uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Configure ++++ run: emcmake cmake -B buildwasm -D ADA_TOOLS=OFF ++++ - name: Build ++++ run: cmake --build buildwasm ++++ - name: Test ++++ run: ctest --test-dir buildwasm diff --cc ada/.github/workflows/lint_and_format_check.yml index 000000000,000000000,000000000,000000000..825bd89a0 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/lint_and_format_check.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,44 @@@@@ ++++name: Lint and format ++++ ++++on: ++++ pull_request: ++++ types: [opened, synchronize, reopened, ready_for_review] ++++ paths-ignore: ++++ - '**.md' ++++ - 'docs/**' ++++ push: ++++ branches: ++++ - main ++++ paths-ignore: ++++ - '**.md' ++++ - 'docs/**' ++++ ++++permissions: ++++ contents: read ++++ ++++concurrency: ++++ group: ${{ github.workflow }}-${{ github.ref }} ++++ cancel-in-progress: true ++++ ++++jobs: ++++ lint-and-format: ++++ runs-on: ubuntu-latest ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ ++++ - name: Install clang-format ++++ run: | ++++ sudo apt update && sudo apt install clang-format-15 -y ++++ sudo ln -sf /usr/bin/clang-format-15 /usr/bin/clang-format ++++ ++++ - name: Build with Lint and Format Check ++++ run: | ++++ cmake -B build && cmake --build build ++++ env: ++++ CXX: clang++-14 ++++ LINT_AND_FORMAT_CHECK: true ++++ ++++ - uses: chartboost/ruff-action@e18ae971ccee1b2d7bbef113930f00c670b78da4 # v1.0.0 ++++ name: Lint with Ruff ++++ with: ++++ version: 0.0.263 diff --cc ada/.github/workflows/macos_install.yml index 000000000,000000000,000000000,000000000..8b5729d17 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/macos_install.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,44 @@@@@ ++++name: macos (Installation) ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: macos-latest ++++ strategy: ++++ matrix: ++++ include: ++++ - {shared: ON} ++++ - {shared: OFF} ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2 ++++ - name: Prepare ++++ run: cmake -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCMAKE_INSTALL_PREFIX:PATH=destination -B build ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Install ++++ run: cmake --install build ++++ - name: Prepare test package ++++ run: cmake -DCMAKE_INSTALL_PREFIX:PATH=../../destination -S tests/installation -B buildbabyada ++++ - name: Build test package ++++ run: cmake --build buildbabyada ++++ - name: Run example ++++ run: ./buildbabyada/main diff --cc ada/.github/workflows/release-script-tests.yml index 000000000,000000000,000000000,000000000..4365272ba new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/release-script-tests.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,40 @@@@@ ++++name: Release Script Tests ++++ ++++on: ++++ # workflow_call is used to indicate that a workflow can be called by another workflow. ++++ workflow_call: ++++ pull_request: ++++ types: [opened, synchronize, reopened, ready_for_review] ++++ paths-ignore: ++++ - '**.md' ++++ - 'docs/**' ++++ push: ++++ branches: ++++ - main ++++ paths-ignore: ++++ - '**.md' ++++ - 'docs/**' ++++ ++++permissions: ++++ contents: read ++++ ++++jobs: ++++ release-script-test: ++++ runs-on: ubuntu-latest ++++ defaults: ++++ run: ++++ working-directory: ./tools/release ++++ ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ ++++ - name: Prepare Python ++++ uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 ++++ with: ++++ cache: 'pip' # caching pip dependencies ++++ ++++ - name: Install dependencies ++++ run: pip install -r requirements.txt ++++ ++++ - name: Run tests ++++ run: pytest -v diff --cc ada/.github/workflows/release_create.yml index 000000000,000000000,000000000,000000000..07b3b21db new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/release_create.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,61 @@@@@ ++++name: Release Create ++++ ++++on: ++++ pull_request: ++++ types: [closed] ++++ ++++env: ++++ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ++++ ++++jobs: ++++ check-release-conditions: ++++ runs-on: ubuntu-latest ++++ if: | ++++ github.event.pull_request.merged == true && ++++ github.event.pull_request.base.ref == 'main' && ++++ startsWith(github.event.pull_request.head.ref, 'release/v') && ++++ startsWith(github.event.pull_request.user.login, 'github-actions') ++++ ++++ steps: ++++ - name: Check release conditions ++++ run: | ++++ echo "All conditions have been met!" ++++ ++++ release-script-test: ++++ needs: check-release-conditions ++++ uses: ./.github/workflows/release-script-tests.yml ++++ ++++ create-release: ++++ permissions: ++++ contents: write ++++ needs: release-script-test ++++ runs-on: ubuntu-latest ++++ if: ${{ needs.release-script-test.result == 'success' }} ++++ ++++ env: ++++ NEXT_RELEASE_TAG: ${{ github.event.pull_request.head.ref }} ++++ steps: ++++ - name: Checkout ++++ uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ ++++ - name: Prepare Python ++++ uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.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 diff --cc ada/.github/workflows/release_prepare.yml index 000000000,000000000,000000000,000000000..8253e841b new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/release_prepare.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,57 @@@@@ ++++name: Release Prepare ++++ ++++on: ++++ workflow_dispatch: ++++ inputs: ++++ tag: ++++ type: string ++++ required: true ++++ description: "Tag for the next release. Ex.: v5.0.0" ++++ ++++env: ++++ NEXT_RELEASE_TAG: ${{ github.event.inputs.tag }} ++++ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ++++ ++++jobs: ++++ release-script-test: ++++ uses: ./.github/workflows/release-script-tests.yml ++++ ++++ prepare-release-and-pull-request: ++++ permissions: ++++ contents: write ++++ pull-requests: write ++++ needs: release-script-test ++++ runs-on: ubuntu-latest ++++ if: ${{ needs.release-script-test.result == 'success' }} ++++ env: ++++ CXX: clang++-14 ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ ++++ - name: Prepare Python ++++ uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.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@f3a21bf3404eae73a97f65817ab35f351a1a63fe #v5.0.0 ++++ with: ++++ commit-message: "chore: release ${{ env.NEXT_RELEASE_TAG }}" ++++ branch: "release/${{ env.NEXT_RELEASE_TAG }}" ++++ title: "chore: release ${{ env.NEXT_RELEASE_TAG }}" ++++ token: ${{ env.GITHUB_TOKEN }} ++++ body: | ++++ This pull PR updates the source code version to ${{ env.NEXT_RELEASE_TAG }} ++++ delete-branch: true ++++ reviewers: "lemire,anonrig" diff --cc ada/.github/workflows/scorecard.yml index 000000000,000000000,000000000,000000000..3bf589bc7 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/scorecard.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,70 @@@@@ ++++# 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@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ with: ++++ persist-credentials: false ++++ ++++ - name: "Run analysis" ++++ uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 ++++ 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-pat. ++++ # 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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 ++++ 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@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4 ++++ with: ++++ sarif_file: results.sarif diff --cc ada/.github/workflows/ubuntu-release.yml index 000000000,000000000,000000000,000000000..16a67c4f0 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu-release.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,40 @@@@@ ++++name: Ubuntu 22.04 (Release build) ++++ ++++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: ++++ ubuntu-release-build: ++++ runs-on: ubuntu-22.04 ++++ strategy: ++++ matrix: ++++ cxx: [g++-12, clang++-14] ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -G Ninja -B build ++++ env: ++++ CXX: ${{matrix.cxx}} ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Test ++++ run: ctest --output-on-failure --test-dir build diff --cc ada/.github/workflows/ubuntu-s390x.yml index 000000000,000000000,000000000,000000000..be08e63bf new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu-s390x.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,43 @@@@@ ++++name: Ubuntu s390x (GCC 11) ++++ ++++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: ++++ runs-on: ubuntu-latest ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - uses: uraimo/run-on-arch-action@517085f0367c8256bcfa753e3e13e1550af09954 # v2.7.1 ++++ name: Test ++++ id: runcmd ++++ with: ++++ arch: s390x ++++ distro: ubuntu_latest ++++ githubToken: ${{ github.token }} ++++ install: | ++++ apt-get update -q -y ++++ apt-get install -y cmake make g++ git ++++ apt-get install -y ninja-build ++++ run: | ++++ cmake -DCMAKE_BUILD_TYPE=Release -G Ninja -B build ++++ rm -r -f dependencies ++++ cmake --build build -j=2 ++++ ctest --output-on-failure --test-dir build diff --cc ada/.github/workflows/ubuntu-sanitized.yml index 000000000,000000000,000000000,000000000..dcd98e40d new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu-sanitized.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,40 @@@@@ ++++name: Ubuntu 22.04 (GCC 12 SANITIZED) ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-22.04 ++++ strategy: ++++ matrix: ++++ shared: [ON, OFF] ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -DADA_SANITIZE=ON -DADA_DEVELOPMENT_CHECKS=ON -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build ++++ env: ++++ CXX: g++-12 ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Test ++++ run: ctest --output-on-failure --test-dir build diff --cc ada/.github/workflows/ubuntu-undef.yml index 000000000,000000000,000000000,000000000..1f582ea85 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu-undef.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,40 @@@@@ ++++name: Ubuntu 22.04 (GCC 12 SANITIZE UNDEFINED) ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-22.04 ++++ strategy: ++++ matrix: ++++ shared: [ON, OFF] ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -D ADA_SANITIZE_UNDEFINED=ON -DADA_DEVELOPMENT_CHECKS=ON -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build ++++ env: ++++ CXX: g++-12 ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Test ++++ run: ctest --output-on-failure --test-dir build diff --cc ada/.github/workflows/ubuntu.yml index 000000000,000000000,000000000,000000000..ae115094e new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,43 @@@@@ ++++name: Ubuntu 22.04 ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-22.04 ++++ strategy: ++++ matrix: ++++ shared: [ON, OFF] ++++ cxx: [g++-12, clang++-14] ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -D ADA_BENCHMARKS=ON -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build ++++ env: ++++ CXX: ${{matrix.cxx}} ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Test ++++ run: ctest --output-on-failure --test-dir build ++++ - name: Run default benchmark ++++ run: cd build && benchmarks/bench diff --cc ada/.github/workflows/ubuntu_install.yml index 000000000,000000000,000000000,000000000..f4acb5805 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu_install.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,46 @@@@@ ++++name: Ubuntu 22.04 (Installation) ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-22.04 ++++ strategy: ++++ matrix: ++++ include: ++++ - {shared: ON} ++++ - {shared: OFF} ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -G Ninja -DBUILD_SHARED_LIBS=${{matrix.shared}} -DCMAKE_INSTALL_PREFIX:PATH=destination -B build ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Install ++++ run: cmake --install build ++++ - name: Prepare test package ++++ run: cmake -DCMAKE_INSTALL_PREFIX:PATH=../../destination -S tests/installation -B buildbabyada ++++ - name: Build test package ++++ run: cmake --build buildbabyada ++++ - name: Run example ++++ run: ./buildbabyada/main diff --cc ada/.github/workflows/ubuntu_old.yml index 000000000,000000000,000000000,000000000..cddebab42 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu_old.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,41 @@@@@ ++++name: Ubuntu 20.04 ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-20.04 ++++ strategy: ++++ matrix: ++++ shared: [ON, OFF] ++++ cxx: [g++-9, clang++-10] ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build ++++ env: ++++ CXX: ${{matrix.cxx}} ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Test ++++ run: ctest --output-on-failure --test-dir build diff --cc ada/.github/workflows/ubuntu_pedantic.yml index 000000000,000000000,000000000,000000000..92fc3b9a6 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/ubuntu_pedantic.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,41 @@@@@ ++++name: Ubuntu 22.04 (GCC 12) Fails On Compiler Warnings ++++ ++++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: ++++ ubuntu-build: ++++ runs-on: ubuntu-22.04 ++++ strategy: ++++ matrix: ++++ shared: [ON, OFF] ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Setup Ninja ++++ run: sudo apt-get install ninja-build ++++ - name: Prepare ++++ run: cmake -DBUILD_SHARED_LIBS=${{matrix.shared}} -G Ninja -B build ++++ env: ++++ CXX: g++-12 ++++ CXXFLAGS: -Werror ++++ - name: Build ++++ run: cmake --build build -j=2 ++++ - name: Test ++++ run: ctest --output-on-failure --test-dir build diff --cc ada/.github/workflows/visual_studio.yml index 000000000,000000000,000000000,000000000..631ab9943 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/visual_studio.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,45 @@@@@ ++++name: VS17-CI ++++ ++++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: ++++ ci: ++++ name: windows-vs17 ++++ runs-on: windows-latest ++++ strategy: ++++ fail-fast: false ++++ matrix: ++++ include: ++++ - {gen: Visual Studio 17 2022, arch: x64, devchecks: OFF, shared: OFF, config: Release} ++++ - {gen: Visual Studio 17 2022, arch: x64, devchecks: ON, shared: OFF, config: Debug} ++++ - {gen: Visual Studio 17 2022, arch: x64, devchecks: ON, shared: ON, config: Debug} ++++ - {gen: Visual Studio 17 2022, arch: Win32, devchecks: ON, shared: OFF, config: Debug} ++++ - {gen: Visual Studio 17 2022, arch: Win32, devchecks: ON, shared: ON, config: Debug} ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Configure ++++ run: | ++++ cmake -DADA_DEVELOPMENT_CHECKS="${{matrix.devchecks}}" -G "${{matrix.gen}}" -A ${{matrix.arch}} -DBUILD_SHARED_LIBS=${{matrix.shared}} -B build ++++ - name: Build ++++ run: cmake --build build --config "${{matrix.config}}" --verbose ++++ - name: Run tests ++++ working-directory: build ++++ run: ctest -C "${{matrix.config}}" --output-on-failure diff --cc ada/.github/workflows/visual_studio_clang.yml index 000000000,000000000,000000000,000000000..b0a21a5b7 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/visual_studio_clang.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,46 @@@@@ ++++name: VS17-clang-CI ++++ ++++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: ++++ ci: ++++ name: windows-vs17 ++++ runs-on: windows-latest ++++ strategy: ++++ fail-fast: false ++++ matrix: ++++ include: ++++ - {gen: Visual Studio 17 2022, arch: x64, devchecks: ON} ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Configure ++++ run: | ++++ cmake -DADA_DEVELOPMENT_CHECKS="${{matrix.devchecks}}" -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -B build ++++ - name: Build Debug ++++ run: cmake --build build --config Debug --verbose ++++ - name: Run Debug tests ++++ working-directory: build ++++ run: ctest -C Debug --output-on-failure ++++ - name: Build Release ++++ run: cmake --build build --config Release --verbose ++++ - name: Run Release tests ++++ working-directory: build ++++ run: ctest -C Release --output-on-failure diff --cc ada/.github/workflows/wpt-updater.yml index 000000000,000000000,000000000,000000000..53d7bf339 new file mode 100644 --- /dev/null +++ b/ada/.github/workflows/wpt-updater.yml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,40 @@@@@ ++++name: Update WPT ++++ ++++on: ++++ schedule: ++++ - cron: '0 0 * * *' ++++ ++++env: ++++ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ++++ ++++concurrency: ++++ group: wpt-updater ++++ cancel-in-progress: true ++++ ++++permissions: ++++ contents: read ++++ ++++jobs: ++++ issue: ++++ runs-on: ubuntu-latest ++++ permissions: ++++ contents: write ++++ pull-requests: write ++++ steps: ++++ - uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v3.6.0 ++++ - name: Fetch tests ++++ run: tools/update-wpt.sh ++++ - name: Open pull request ++++ uses: peter-evans/create-pull-request@284f54f989303d2699d373481a0cfa13ad5a6666 #v5.0.1 ++++ with: ++++ token: ${{ secrets.GH_PAT }} ++++ commit-message: "test: update web platform tests" ++++ branch: "automatic-update-wpt" ++++ title: "Update web platform tests" ++++ 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 diff --cc ada/.gitignore index 000000000,000000000,000000000,000000000..bb1f23e33 new file mode 100644 --- /dev/null +++ b/ada/.gitignore @@@@@ -1,0 -1,0 -1,0 -1,0 +1,27 @@@@@ ++++# 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/ diff --cc ada/.python-version index 000000000,000000000,000000000,000000000..e4fba2183 new file mode 100644 --- /dev/null +++ b/ada/.python-version @@@@@ -1,0 -1,0 -1,0 -1,0 +1,1 @@@@@ ++++3.12 diff --cc ada/CMakeLists.txt index 000000000,000000000,000000000,000000000..278c23ac7 new file mode 100644 --- /dev/null +++ b/ada/CMakeLists.txt @@@@@ -1,0 -1,0 -1,0 -1,0 +1,183 @@@@@ ++++cmake_minimum_required(VERSION 3.16) ++++ ++++project(ada ++++ DESCRIPTION "Fast spec-compliant URL parser" ++++ LANGUAGES C CXX ++++ VERSION 2.7.8 ++++) ++++set(CMAKE_CXX_STANDARD 17) ++++set(ADA_LIB_VERSION "2.7.8" CACHE STRING "ada library version") ++++set(ADA_LIB_SOVERSION "2" CACHE STRING "ada library soversion") ++++ ++++include(GNUInstallDirs) ++++ ++++include (cmake/clang-format.cmake) ++++ ++++include(CTest) ++++include(cmake/ada-flags.cmake) ++++ ++++set(ADA_SOURCE_DIR src) ++++ ++++add_subdirectory(src) ++++ ++++set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/scripts/cmake) ++++ ++++option(ADA_BENCHMARKS "Build benchmarks" OFF) ++++option(ADA_TESTING "Build tests" ${BUILD_TESTING}) ++++ ++++# 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) ++++ include(cmake/CPM.cmake) ++++ # CPM requires git as an implicit dependency ++++ find_package(Git QUIET) ++++ # We use googletest in the tests ++++ if(Git_FOUND AND ADA_TESTING) ++++ CPMAddPackage( ++++ NAME GTest ++++ GITHUB_REPOSITORY google/googletest ++++ VERSION 1.14.0 ++++ OPTIONS "BUILD_GMOCK OFF" "INSTALL_GTEST OFF" ++++ ) ++++ endif() ++++ # We use simdjson in both the benchmarks and tests ++++ if(Git_FOUND AND (ADA_TESTING OR ADA_BENCHMARKS)) ++++ CPMAddPackage("gh:simdjson/simdjson@3.3.0") ++++ endif() ++++ # We use Google Benchmark, but it does not build under several 32-bit systems. ++++ if(Git_FOUND AND ADA_BENCHMARKS AND (CMAKE_SIZEOF_VOID_P EQUAL 8)) ++++ CPMAddPackage( ++++ NAME benchmark ++++ GITHUB_REPOSITORY google/benchmark ++++ GIT_TAG f91b6b4 ++++ OPTIONS "BENCHMARK_ENABLE_TESTING OFF" ++++ "BENCHMARK_ENABLE_INSTALL OFF" ++++ "BENCHMARK_ENABLE_WERROR OFF" ++++ ++++ ) ++++ endif() ++++ ++++ if (ADA_TESTING AND NOT EMSCRIPTEN) ++++ if(Git_FOUND) ++++ message(STATUS "The tests are enabled.") ++++ add_subdirectory(tests) ++++ else() ++++ message(STATUS "The tests are disabled because git was not found.") ++++ endif() ++++ else() ++++ if(is_top_project) ++++ message(STATUS "The tests are disabled.") ++++ endif() ++++ endif(ADA_TESTING AND NOT EMSCRIPTEN) ++++ ++++ If(ADA_BENCHMARKS AND NOT EMSCRIPTEN) ++++ if(Git_FOUND) ++++ message(STATUS "Ada benchmarks enabled.") ++++ add_subdirectory(benchmarks) ++++ else() ++++ message(STATUS "The benchmarks are disabled because git was not found.") ++++ endif() ++++ 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) ++++endif() ++++ ++++ ++++add_library(ada::ada ALIAS ada) ++++ ++++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) ++++ if(Git_FOUND) ++++ add_subdirectory(tools) ++++ else() ++++ message(STATUS "The tools are disabled because git was not found.") ++++ endif() ++++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 ++++) ++++ ++++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() diff --cc ada/Dockerfile index 000000000,000000000,000000000,000000000..4b1957e29 new file mode 100644 --- /dev/null +++ b/ada/Dockerfile @@@@@ -1,0 -1,0 -1,0 -1,0 +1,12 @@@@@ ++++FROM debian:12-slim@sha256:7802002798b0e351323ed2357ae6dc5a8c4d0a05a57e7f4d8f97136151d3d603 ++++ ++++RUN apt-get update && apt-get install -y \ ++++ apt-transport-https \ ++++ gcc \ ++++ clang \ ++++ clang-tools \ ++++ cmake ++++ ++++WORKDIR /repo ++++ ++++CMD ["bash", "-c", "cmake -B build && cmake --build build && cd build && ctest --output-on-failure"] diff --cc ada/LICENSE-APACHE index 000000000,000000000,000000000,000000000..1204b0aab new file mode 100644 --- /dev/null +++ b/ada/LICENSE-APACHE @@@@@ -1,0 -1,0 -1,0 -1,0 +1,201 @@@@@ ++++ 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. diff --cc ada/LICENSE-MIT index 000000000,000000000,000000000,000000000..bd2abacfc new file mode 100644 --- /dev/null +++ b/ada/LICENSE-MIT @@@@@ -1,0 -1,0 -1,0 -1,0 +1,18 @@@@@ ++++Copyright 2023 Yagiz Nizipli and Daniel Lemire ++++ ++++Permission is hereby granted, free of charge, to any person obtaining a copy of ++++this software and associated documentation files (the "Software"), to deal in ++++the Software without restriction, including without limitation the rights to ++++use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of ++++the Software, and to permit persons to whom the Software is furnished to do so, ++++subject to the following conditions: ++++ ++++The above copyright notice and this permission notice shall be included in all ++++copies or substantial portions of the Software. ++++ ++++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS ++++FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR ++++COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER ++++IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++++CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --cc ada/README.md index 000000000,000000000,000000000,000000000..24a8465b7 new file mode 100644 --- /dev/null +++ b/ada/README.md @@@@@ -1,0 -1,0 -1,0 -1,0 +1,325 @@@@@ ++++# 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. ++++ ++++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) | ++++ ++++### Requirements ++++ ++++The project is otherwise self-contained and it has no dependency. ++++A recent C++ compiler supporting C++17. We test GCC 9 or better, LLVM 10 or better and Microsoft Visual Studio 2022. ++++ ++++## 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 and Cloudflare Workers. ++++ ++++ ++++ ++++[![the ada library](http://img.youtube.com/vi/tQ-6OWRDsZg/0.jpg)](https://www.youtube.com/watch?v=tQ-6OWRDsZg)
++++ ++++## 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/v2.6.10/ada.cpp ++++ wget https://github.com/ada-url/ada/releases/download/v2.6.10/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; ++++ } ++++ ``` ++++2. Compile ++++ ``` ++++ c++ -std=c++17 -o demo demo.cpp ++++ ``` ++++3. `./demo` ++++ ++++ ``` ++++ http: ++++ www.google.com ++++ ``` ++++ ++++## Bindings of Ada ++++ ++++We provide clients for different programming languages through our C API. ++++ ++++- [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 ++++- [R](https://github.com/schochastics/adaR): R wrapper for Ada ++++ ++++## 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 UTF-8 string ++++ ++++```cpp ++++ada::result 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 ++++ada::result url = ada::parse("some bad url"); ++++url->get_href(); ++++``` ++++ ++++You should do... ++++ ++++```cpp ++++ada::result url = ada::parse("some bad url"); ++++if(url) { ++++ // next line is now safe: ++++ url->get_href(); ++++} else { ++++ // report a parsing failure ++++} ++++``` ++++ ++++For simplicity, in the examples below, we skip the check because ++++we know that parsing succeeds. ++++ ++++### Examples ++++ ++++- Get/Update credentials ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_username("username"); ++++url->set_password("password"); ++++// ada->get_href() will return "https://username:password@www.google.com/" ++++``` ++++ ++++- Get/Update Protocol ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_protocol("wss"); ++++// url->get_protocol() will return "wss:" ++++// url->get_href() will return "wss://www.google.com/" ++++``` ++++ ++++- Get/Update host ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_host("github.com"); ++++// url->get_host() will return "github.com" ++++// you can use `url.set_hostname` depending on your usage. ++++``` ++++ ++++- Get/Update port ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_port("8080"); ++++// url->get_port() will return "8080" ++++``` ++++ ++++- Get/Update pathname ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_pathname("/my-super-long-path") ++++// url->get_pathname() will return "/my-super-long-path" ++++``` ++++ ++++- Get/Update search/query ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_search("target=self"); ++++// url->get_search() will return "?target=self" ++++``` ++++ ++++- Get/Update hash/fragment ++++ ++++```cpp ++++ada::result url = ada::parse("https://www.google.com"); ++++url->set_hash("is-this-the-real-life"); ++++// url->get_hash() will return "#is-this-the-real-life" ++++``` ++++For more information about command-line options, please refer to the [CLI documentation](docs/cli.md). ++++ ++++- 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" ++++} ++++``` ++++ ++++### 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 ++++ ++++static void ada_print(ada_string string) { ++++ printf("%.*s\n", (int)string.length, string.data); ++++} ++++ ++++int main(int c, char *arg[] ) { ++++ ada_url url = ada_parse("https://username:password@www.google.com:8080/" ++++ "pathname?query=true#hash-exists"); ++++ 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"); ++++ if(!ada_is_valid(url)) { puts("failure"); return EXIT_FAILURE; } ++++ ada_set_hash(url, "new-hash"); ++++ ada_set_hostname(url, "new-host"); ++++ ada_set_host(url, "changed-host:9090"); ++++ ada_set_pathname(url, "new-pathname"); ++++ ada_set_search(url, "new-search"); ++++ ada_set_protocol(url, "wss"); ++++ 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++17 ++++cc -c demo.c ++++c++ demo.o ada.o -o cdemo ++++./cdemo ++++``` ++++ ++++### 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. ++++ ++++## Installation ++++ ++++### Homebrew ++++ ++++Ada is available through [Homebrew](https://formulae.brew.sh/formula/ada-url#default). ++++You can install Ada using `brew install ada-url`. ++++ ++++## Contributing ++++ ++++### Building ++++ ++++Ada uses cmake as a build system. It's recommended you to run the following commands to build it locally. ++++ ++++- **Build**: `cmake -B build && cmake --build build` ++++- **Test**: `ctest --output-on-failure --test-dir build` ++++ ++++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. diff --cc ada/SECURITY.md index 000000000,000000000,000000000,000000000..255f50955 new file mode 100644 --- /dev/null +++ b/ada/SECURITY.md @@@@@ -1,0 -1,0 -1,0 -1,0 +1,8 @@@@@ ++++# 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 diff --cc ada/benchmarks/CMakeLists.txt index 000000000,000000000,000000000,000000000..886ce52f5 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/CMakeLists.txt @@@@@ -1,0 -1,0 -1,0 -1,0 +1,300 @@@@@ ++++# bench_search_params ++++add_executable(bench_search_params bench_search_params.cpp) ++++target_link_libraries(bench_search_params PRIVATE ada) ++++ ++++# Bench ++++add_executable(wpt_bench wpt_bench.cpp) ++++target_link_libraries(wpt_bench PRIVATE ada) ++++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) ++++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) ++++target_include_directories(benchdata PUBLIC "$") ++++target_include_directories(benchdata PUBLIC "$") ++++target_compile_definitions(benchdata PRIVATE ADA_URL_FILE="${url-dataset_SOURCE_DIR}/out.txt") ++++ ++++ ++++# BBC Bench ++++add_executable(bbc_bench bbc_bench.cpp) ++++target_link_libraries(bbc_bench PRIVATE ada) ++++target_include_directories(bbc_bench PUBLIC "$") ++++target_include_directories(bbc_bench PUBLIC "$") ++++ ++++# Percent Encode ++++add_executable(percent_encode percent_encode.cpp) ++++target_link_libraries(percent_encode PRIVATE ada) ++++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() ++++ ++++if(CMAKE_SYSTEM_NAME MATCHES "Linux") ++++ # The model_bench program requires accurate/low-overhead performance counters. ++++ # We only have such support under Linux. ++++ add_executable(model_bench model_bench.cpp) ++++ target_link_libraries(model_bench PRIVATE ada) ++++ target_compile_definitions(model_bench PRIVATE ADA_URL_FILE="${url-dataset_SOURCE_DIR}/out.txt") ++++endif() ++++ ++++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(percent_encode PRIVATE benchmark::benchmark) ++++target_link_libraries(bench_search_params PRIVATE benchmark::benchmark) ++++ ++++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.80 ++++ 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) ++++ ++++ ++++# We want the check whether Rust is available before trying to build a crate. ++++CPMAddPackage( ++++ NAME corrosion ++++ GITHUB_REPOSITORY corrosion-rs/corrosion ++++ VERSION 0.4.4 ++++ DOWNLOAD_ONLY ON ++++ OPTIONS "Rust_FIND_QUIETLY OFF" ++++) ++++include("${corrosion_SOURCE_DIR}/cmake/FindRust.cmake") ++++ ++++ ++++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) ++++ ++++ 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(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() diff --cc ada/benchmarks/bbc_bench.cpp index 000000000,000000000,000000000,000000000..907d49189 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/bbc_bench.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,36 @@@@@ ++++#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); ++++}(); ++++ ++++#include "benchmark_template.cpp" diff --cc ada/benchmarks/bench.cpp index 000000000,000000000,000000000,000000000..8986c936f new file mode 100644 --- /dev/null +++ b/ada/benchmarks/bench.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,72 @@@@@ ++++#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(); ++++} ++++#include "benchmark_template.cpp" diff --cc ada/benchmarks/bench_search_params.cpp index 000000000,000000000,000000000,000000000..26cf53e26 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/bench_search_params.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,343 @@@@@ ++++#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()) { ++++ 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); ++++ 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(); ++++} diff --cc ada/benchmarks/benchmark_header.h index 000000000,000000000,000000000,000000000..3351e2725 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/benchmark_header.h @@@@@ -1,0 -1,0 -1,0 -1,0 +1,65 @@@@@ ++++#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 "performancecounters/event_counter.h" ++++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; ++++} diff --cc ada/benchmarks/benchmark_template.cpp index 000000000,000000000,000000000,000000000..922872aa3 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/benchmark_template.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,818 @@@@@ ++++/** ++++ * 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; ++++} ++++ ++++enum { JUST_PARSE = 1, PARSE_AND_HREF = 0 }; ++++ ++++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()) { ++++ 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); ++++ 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); ++++} ++++ ++++auto BasicBench_AdaURL_href = BasicBench_AdaURL; ++++BENCHMARK(BasicBench_AdaURL_href); ++++auto BasicBench_AdaURL_aggregator_href = ++++ BasicBench_AdaURL; ++++BENCHMARK(BasicBench_AdaURL_aggregator_href); ++++ ++++#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 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 (!just_parse) { ++++ href_size += url.href().size(); ++++ } ++++ } ++++ } ++++ } ++++ 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) { ++++ upa::url url; ++++ if (upa::success(url.parse(url_string, nullptr))) { ++++ success++; ++++ if (!just_parse) { ++++ href_size += url.href().size(); ++++ } ++++ } ++++ } ++++ 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; ++++ } ++++ (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_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 (!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()) { ++++ 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 (!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); ++++ 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 (!just_parse) { ++++ href_size += u.buffer().size(); ++++ } ++++ } catch (...) { ++++ } ++++ } ++++ } ++++ 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) { ++++ try { ++++ url u(url_string); ++++ u.normalize(); ++++ success++; ++++ if (!just_parse) { ++++ href_size += u.buffer().size(); ++++ } ++++ } catch (...) { ++++ } ++++ } ++++ 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; ++++ } ++++ (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()) { ++++ 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); ++++ 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()) { ++++ 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); ++++ 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(); ++++} diff --cc ada/benchmarks/competitors/servo-url/Cargo.lock index 000000000,000000000,000000000,000000000..262ac528f new file mode 100644 --- /dev/null +++ b/ada/benchmarks/competitors/servo-url/Cargo.lock @@@@@ -1,0 -1,0 -1,0 -1,0 +1,83 @@@@@ ++++# This file is automatically @generated by Cargo. ++++# It is not intended for manual editing. ++++version = 3 ++++ ++++[[package]] ++++name = "form_urlencoded" ++++version = "1.2.1" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" ++++dependencies = [ ++++ "percent-encoding", ++++] ++++ ++++[[package]] ++++name = "idna" ++++version = "0.5.0" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" ++++dependencies = [ ++++ "unicode-bidi", ++++ "unicode-normalization", ++++] ++++ ++++[[package]] ++++name = "libc" ++++version = "0.2.153" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" ++++ ++++[[package]] ++++name = "percent-encoding" ++++version = "2.3.1" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" ++++ ++++[[package]] ++++name = "servo-url" ++++version = "0.1.0" ++++dependencies = [ ++++ "libc", ++++ "url", ++++] ++++ ++++[[package]] ++++name = "tinyvec" ++++version = "1.6.0" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" ++++dependencies = [ ++++ "tinyvec_macros", ++++] ++++ ++++[[package]] ++++name = "tinyvec_macros" ++++version = "0.1.1" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" ++++ ++++[[package]] ++++name = "unicode-bidi" ++++version = "0.3.10" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" ++++ ++++[[package]] ++++name = "unicode-normalization" ++++version = "0.1.22" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" ++++dependencies = [ ++++ "tinyvec", ++++] ++++ ++++[[package]] ++++name = "url" ++++version = "2.5.0" ++++source = "registry+https://github.com/rust-lang/crates.io-index" ++++checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" ++++dependencies = [ ++++ "form_urlencoded", ++++ "idna", ++++ "percent-encoding", ++++] diff --cc ada/benchmarks/competitors/servo-url/Cargo.toml index 000000000,000000000,000000000,000000000..900f8787d new file mode 100644 --- /dev/null +++ b/ada/benchmarks/competitors/servo-url/Cargo.toml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,16 @@@@@ ++++[package] ++++name = "servo-url" ++++version = "0.1.0" ++++ ++++[lib] ++++path = "lib.rs" ++++crate-type = ["cdylib"] ++++ ++++[dependencies] ++++url = "2.5.0" ++++libc = "0.2" ++++ ++++[profile.release] ++++opt-level = 3 ++++debug = false ++++lto = true diff --cc ada/benchmarks/competitors/servo-url/README.md index 000000000,000000000,000000000,000000000..47a3c7823 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/competitors/servo-url/README.md @@@@@ -1,0 -1,0 -1,0 -1,0 +1,17 @@@@@ ++++## 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://michael-f-bryan.github.io/rust-ffi-guide/setting_up.html ++++ ++++### 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` diff --cc ada/benchmarks/competitors/servo-url/cbindgen.toml index 000000000,000000000,000000000,000000000..0daad0cf8 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/competitors/servo-url/cbindgen.toml @@@@@ -1,0 -1,0 -1,0 -1,0 +1,12 @@@@@ ++++autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" ++++include_version = true ++++braces = "SameLine" ++++line_length = 100 ++++tab_width = 2 ++++language = "C++" ++++namespaces = ["servo_url"] ++++include_guard = "servo_url_ffi_h" ++++ ++++[parse] ++++parse_deps = true ++++include = ["url"] diff --cc ada/benchmarks/competitors/servo-url/lib.rs index 000000000,000000000,000000000,000000000..700ebf37e new file mode 100644 --- /dev/null +++ b/ada/benchmarks/competitors/servo-url/lib.rs @@@@@ -1,0 -1,0 -1,0 -1,0 +1,44 @@@@@ ++++use url::Url; ++++use std::slice; ++++use libc::{c_char, size_t}; ++++ ++++extern crate url; ++++extern crate libc; ++++ ++++#[no_mangle] ++++pub unsafe extern "C" fn parse_url(raw_input: *const c_char, raw_input_length: size_t) -> *mut Url { ++++ let input = 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(), ++++ } ++++} ++++ ++++#[no_mangle] ++++pub unsafe extern "C" fn parse_url_to_href(raw_input: *const c_char, raw_input_length: size_t) -> *const c_char { ++++ let input = 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(), ++++ } ++++} ++++ ++++#[no_mangle] ++++pub unsafe extern "C" fn free_url(raw: *mut Url) { ++++ if raw.is_null() { ++++ return; ++++ } ++++ ++++ drop(Box::from_raw(raw)) ++++} ++++ ++++#[no_mangle] ++++pub unsafe extern fn free_string(ptr: *const c_char) { ++++ // Take the ownership back to rust and drop the owner ++++ let _ = std::ffi::CString::from_raw(ptr as *mut _); ++++} diff --cc ada/benchmarks/competitors/servo-url/servo_url.h index 000000000,000000000,000000000,000000000..406d5fc2f new file mode 100644 --- /dev/null +++ b/ada/benchmarks/competitors/servo-url/servo_url.h @@@@@ -1,0 -1,0 -1,0 -1,0 +1,30 @@@@@ ++++#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 diff --cc ada/benchmarks/model_bench.cpp index 000000000,000000000,000000000,000000000..e9d1501d9 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/model_bench.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,280 @@@@@ ++++#include ++++#include ++++#include ++++#include ++++#include ++++#include ++++ ++++#include "ada.h" ++++#include "performancecounters/event_counter.h" ++++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{}; ++++ 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); ++++ 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; ++++} diff --cc ada/benchmarks/percent_encode.cpp index 000000000,000000000,000000000,000000000..97f969f54 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/percent_encode.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,266 @@@@@ ++++#include ++++ ++++#include "ada.h" ++++#include "ada/character_sets.h" ++++#include "ada/unicode.h" ++++#include "performancecounters/event_counter.h" ++++event_collector collector; ++++size_t N = 1000; ++++ ++++#include ++++ ++++std::string examples[] = {"á|", "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()) { ++++ 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); ++++ 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()) { ++++ 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); ++++ 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()) { ++++ 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); ++++ 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()) { ++++ 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); ++++ 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()) { ++++ 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); ++++ 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(); ++++} diff --cc ada/benchmarks/performancecounters/apple_arm_events.h index 000000000,000000000,000000000,000000000..608817277 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/performancecounters/apple_arm_events.h @@@@@ -1,0 -1,0 -1,0 -1,0 +1,1110 @@@@@ ++++/* clang-format off */ ++++ ++++// Original design from: ++++// ============================================================================= ++++// XNU kperf/kpc ++++// Available for 64-bit Intel/Apple Silicon, macOS/iOS, with root privileges ++++// ++++// References: ++++// ++++// XNU source (since xnu 2422.1.72): ++++// https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/kpc.h ++++// https://github.com/apple/darwin-xnu/blob/main/bsd/kern/kern_kpc.c ++++// ++++// Lightweight PET (Profile Every Thread, since xnu 3789.1.32): ++++// https://github.com/apple/darwin-xnu/blob/main/osfmk/kperf/pet.c ++++// https://github.com/apple/darwin-xnu/blob/main/osfmk/kperf/kperf_kpc.c ++++// ++++// System Private frameworks (since macOS 10.11, iOS 8.0): ++++// /System/Library/PrivateFrameworks/kperf.framework ++++// /System/Library/PrivateFrameworks/kperfdata.framework ++++// ++++// Xcode framework (since Xcode 7.0): ++++// /Applications/Xcode.app/Contents/SharedFrameworks/DVTInstrumentsFoundation.framework ++++// ++++// CPU database (plist files) ++++// macOS (since macOS 10.11): ++++// /usr/share/kpep/.plist ++++// iOS (copied from Xcode, since iOS 10.0, Xcode 8.0): ++++// /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform ++++// /DeviceSupport//DeveloperDiskImage.dmg/usr/share/kpep/.plist ++++// ++++// ++++// Created by YaoYuan on 2021. ++++// Released into the public domain (unlicense.org). ++++// ============================================================================= ++++ ++++#ifndef M1CYCLES_H ++++#define M1CYCLES_H ++++ ++++#include ++++#include ++++#include ++++#include ++++#include ++++ ++++#include // for dlopen() and dlsym() ++++#include // for mach_absolute_time() ++++#include // for kdebug trace decode ++++#include // for sysctl() ++++#include // for usleep() ++++ ++++struct performance_counters { ++++ double cycles; ++++ double branches; ++++ double missed_branches; ++++ double instructions; ++++ performance_counters(uint64_t c, uint64_t b, uint64_t m, uint64_t i) ++++ : cycles(c), branches(b), missed_branches(m), instructions(i) {} ++++ performance_counters(double c, double b, double m, double i) ++++ : cycles(c), branches(b), missed_branches(m), instructions(i) {} ++++ performance_counters(double init) ++++ : cycles(init), ++++ branches(init), ++++ missed_branches(init), ++++ instructions(init) {} ++++ ++++ inline performance_counters &operator-=(const performance_counters &other) { ++++ cycles -= other.cycles; ++++ branches -= other.branches; ++++ missed_branches -= other.missed_branches; ++++ instructions -= other.instructions; ++++ return *this; ++++ } ++++ inline performance_counters &min(const performance_counters &other) { ++++ cycles = other.cycles < cycles ? other.cycles : cycles; ++++ branches = other.branches < branches ? other.branches : branches; ++++ missed_branches = other.missed_branches < missed_branches ++++ ? other.missed_branches ++++ : missed_branches; ++++ instructions = ++++ other.instructions < instructions ? other.instructions : instructions; ++++ return *this; ++++ } ++++ inline performance_counters &operator+=(const performance_counters &other) { ++++ cycles += other.cycles; ++++ branches += other.branches; ++++ missed_branches += other.missed_branches; ++++ instructions += other.instructions; ++++ return *this; ++++ } ++++ ++++ inline performance_counters &operator/=(double numerator) { ++++ cycles /= numerator; ++++ branches /= numerator; ++++ missed_branches /= numerator; ++++ instructions /= numerator; ++++ return *this; ++++ } ++++}; ++++ ++++inline performance_counters operator-(const performance_counters &a, ++++ const performance_counters &b) { ++++ return performance_counters(a.cycles - b.cycles, a.branches - b.branches, ++++ a.missed_branches - b.missed_branches, ++++ a.instructions - b.instructions); ++++} ++++ ++++typedef float f32; ++++typedef double f64; ++++typedef int8_t i8; ++++typedef uint8_t u8; ++++typedef int16_t i16; ++++typedef uint16_t u16; ++++typedef int32_t i32; ++++typedef uint32_t u32; ++++typedef int64_t i64; ++++typedef uint64_t u64; ++++typedef size_t usize; ++++ ++++// ----------------------------------------------------------------------------- ++++// header (reverse engineered) ++++// This framework wraps some sysctl calls to communicate with the kpc in kernel. ++++// Most functions requires root privileges, or process is "blessed". ++++// ----------------------------------------------------------------------------- ++++ ++++// Cross-platform class constants. ++++#define KPC_CLASS_FIXED (0) ++++#define KPC_CLASS_CONFIGURABLE (1) ++++#define KPC_CLASS_POWER (2) ++++#define KPC_CLASS_RAWPMU (3) ++++ ++++// Cross-platform class mask constants. ++++#define KPC_CLASS_FIXED_MASK (1u << KPC_CLASS_FIXED) // 1 ++++#define KPC_CLASS_CONFIGURABLE_MASK (1u << KPC_CLASS_CONFIGURABLE) // 2 ++++#define KPC_CLASS_POWER_MASK (1u << KPC_CLASS_POWER) // 4 ++++#define KPC_CLASS_RAWPMU_MASK (1u << KPC_CLASS_RAWPMU) // 8 ++++ ++++// PMU version constants. ++++#define KPC_PMU_ERROR (0) // Error ++++#define KPC_PMU_INTEL_V3 (1) // Intel ++++#define KPC_PMU_ARM_APPLE (2) // ARM64 ++++#define KPC_PMU_INTEL_V2 (3) // Old Intel ++++#define KPC_PMU_ARM_V2 (4) // Old ARM ++++ ++++// The maximum number of counters we could read from every class in one go. ++++// ARMV7: FIXED: 1, CONFIGURABLE: 4 ++++// ARM32: FIXED: 2, CONFIGURABLE: 6 ++++// ARM64: FIXED: 2, CONFIGURABLE: CORE_NCTRS - FIXED (6 or 8) ++++// x86: 32 ++++#define KPC_MAX_COUNTERS 32 ++++ ++++// Bits for defining what to do on an action. ++++// Defined in https://github.com/apple/darwin-xnu/blob/main/osfmk/kperf/action.h ++++#define KPERF_SAMPLER_TH_INFO (1U << 0) ++++#define KPERF_SAMPLER_TH_SNAPSHOT (1U << 1) ++++#define KPERF_SAMPLER_KSTACK (1U << 2) ++++#define KPERF_SAMPLER_USTACK (1U << 3) ++++#define KPERF_SAMPLER_PMC_THREAD (1U << 4) ++++#define KPERF_SAMPLER_PMC_CPU (1U << 5) ++++#define KPERF_SAMPLER_PMC_CONFIG (1U << 6) ++++#define KPERF_SAMPLER_MEMINFO (1U << 7) ++++#define KPERF_SAMPLER_TH_SCHEDULING (1U << 8) ++++#define KPERF_SAMPLER_TH_DISPATCH (1U << 9) ++++#define KPERF_SAMPLER_TK_SNAPSHOT (1U << 10) ++++#define KPERF_SAMPLER_SYS_MEM (1U << 11) ++++#define KPERF_SAMPLER_TH_INSCYC (1U << 12) ++++#define KPERF_SAMPLER_TK_INFO (1U << 13) ++++ ++++// Maximum number of kperf action ids. ++++#define KPERF_ACTION_MAX (32) ++++ ++++// Maximum number of kperf timer ids. ++++#define KPERF_TIMER_MAX (8) ++++ ++++// x86/arm config registers are 64-bit ++++typedef u64 kpc_config_t; ++++ ++++/// Print current CPU identification string to the buffer (same as snprintf), ++++/// such as "cpu_7_8_10b282dc_46". This string can be used to locate the PMC ++++/// database in /usr/share/kpep. ++++/// @return string's length, or negative value if error occurs. ++++/// @note This method does not requires root privileges. ++++/// @details sysctl get(hw.cputype), get(hw.cpusubtype), ++++/// get(hw.cpufamily), get(machdep.cpu.model) ++++static int (*kpc_cpu_string)(char *buf, usize buf_size); ++++ ++++/// Get the version of KPC that's being run. ++++/// @return See `PMU version constants` above. ++++/// @details sysctl get(kpc.pmu_version) ++++static u32 (*kpc_pmu_version)(void); ++++ ++++/// Get running PMC classes. ++++/// @return See `class mask constants` above, ++++/// 0 if error occurs or no class is set. ++++/// @details sysctl get(kpc.counting) ++++static u32 (*kpc_get_counting)(void); ++++ ++++/// Set PMC classes to enable counting. ++++/// @param classes See `class mask constants` above, set 0 to shutdown counting. ++++/// @return 0 for success. ++++/// @details sysctl set(kpc.counting) ++++static int (*kpc_set_counting)(u32 classes); ++++ ++++/// Get running PMC classes for current thread. ++++/// @return See `class mask constants` above, ++++/// 0 if error occurs or no class is set. ++++/// @details sysctl get(kpc.thread_counting) ++++static u32 (*kpc_get_thread_counting)(void); ++++ ++++/// Set PMC classes to enable counting for current thread. ++++/// @param classes See `class mask constants` above, set 0 to shutdown counting. ++++/// @return 0 for success. ++++/// @details sysctl set(kpc.thread_counting) ++++static int (*kpc_set_thread_counting)(u32 classes); ++++ ++++/// Get how many config registers there are for a given mask. ++++/// For example: Intel may returns 1 for `KPC_CLASS_FIXED_MASK`, ++++/// returns 4 for `KPC_CLASS_CONFIGURABLE_MASK`. ++++/// @param classes See `class mask constants` above. ++++/// @return 0 if error occurs or no class is set. ++++/// @note This method does not requires root privileges. ++++/// @details sysctl get(kpc.config_count) ++++static u32 (*kpc_get_config_count)(u32 classes); ++++ ++++/// Get config registers. ++++/// @param classes see `class mask constants` above. ++++/// @param config Config buffer to receive values, should not smaller than ++++/// kpc_get_config_count(classes) * sizeof(kpc_config_t). ++++/// @return 0 for success. ++++/// @details sysctl get(kpc.config_count), get(kpc.config) ++++static int (*kpc_get_config)(u32 classes, kpc_config_t *config); ++++ ++++/// Set config registers. ++++/// @param classes see `class mask constants` above. ++++/// @param config Config buffer, should not smaller than ++++/// kpc_get_config_count(classes) * sizeof(kpc_config_t). ++++/// @return 0 for success. ++++/// @details sysctl get(kpc.config_count), set(kpc.config) ++++static int (*kpc_set_config)(u32 classes, kpc_config_t *config); ++++ ++++/// Get how many counters there are for a given mask. ++++/// For example: Intel may returns 3 for `KPC_CLASS_FIXED_MASK`, ++++/// returns 4 for `KPC_CLASS_CONFIGURABLE_MASK`. ++++/// @param classes See `class mask constants` above. ++++/// @note This method does not requires root privileges. ++++/// @details sysctl get(kpc.counter_count) ++++static u32 (*kpc_get_counter_count)(u32 classes); ++++ ++++/// Get counter accumulations. ++++/// If `all_cpus` is true, the buffer count should not smaller than ++++/// (cpu_count * counter_count). Otherwise, the buffer count should not smaller ++++/// than (counter_count). ++++/// @see kpc_get_counter_count(), kpc_cpu_count(). ++++/// @param all_cpus true for all CPUs, false for current cpu. ++++/// @param classes See `class mask constants` above. ++++/// @param curcpu A pointer to receive current cpu id, can be NULL. ++++/// @param buf Buffer to receive counter's value. ++++/// @return 0 for success. ++++/// @details sysctl get(hw.ncpu), get(kpc.counter_count), get(kpc.counters) ++++static int (*kpc_get_cpu_counters)(bool all_cpus, u32 classes, int *curcpu, ++++ u64 *buf); ++++ ++++/// Get counter accumulations for current thread. ++++/// @param tid Thread id, should be 0. ++++/// @param buf_count The number of buf's elements (not bytes), ++++/// should not smaller than kpc_get_counter_count(). ++++/// @param buf Buffer to receive counter's value. ++++/// @return 0 for success. ++++/// @details sysctl get(kpc.thread_counters) ++++static int (*kpc_get_thread_counters)(u32 tid, u32 buf_count, u64 *buf); ++++ ++++/// Acquire/release the counters used by the Power Manager. ++++/// @param val 1:acquire, 0:release ++++/// @return 0 for success. ++++/// @details sysctl set(kpc.force_all_ctrs) ++++static int (*kpc_force_all_ctrs_set)(int val); ++++ ++++/// Get the state of all_ctrs. ++++/// @return 0 for success. ++++/// @details sysctl get(kpc.force_all_ctrs) ++++static int (*kpc_force_all_ctrs_get)(int *val_out); ++++ ++++/// Set number of actions, should be `KPERF_ACTION_MAX`. ++++/// @details sysctl set(kperf.action.count) ++++static int (*kperf_action_count_set)(u32 count); ++++ ++++/// Get number of actions. ++++/// @details sysctl get(kperf.action.count) ++++static int (*kperf_action_count_get)(u32 *count); ++++ ++++/// Set what to sample when a trigger fires an action, e.g. ++++/// `KPERF_SAMPLER_PMC_CPU`. ++++/// @details sysctl set(kperf.action.samplers) ++++static int (*kperf_action_samplers_set)(u32 actionid, u32 sample); ++++ ++++/// Get what to sample when a trigger fires an action. ++++/// @details sysctl get(kperf.action.samplers) ++++static int (*kperf_action_samplers_get)(u32 actionid, u32 *sample); ++++ ++++/// Apply a task filter to the action, -1 to disable filter. ++++/// @details sysctl set(kperf.action.filter_by_task) ++++static int (*kperf_action_filter_set_by_task)(u32 actionid, i32 port); ++++ ++++/// Apply a pid filter to the action, -1 to disable filter. ++++/// @details sysctl set(kperf.action.filter_by_pid) ++++static int (*kperf_action_filter_set_by_pid)(u32 actionid, i32 pid); ++++ ++++/// Set number of time triggers, should be `KPERF_TIMER_MAX`. ++++/// @details sysctl set(kperf.timer.count) ++++static int (*kperf_timer_count_set)(u32 count); ++++ ++++/// Get number of time triggers. ++++/// @details sysctl get(kperf.timer.count) ++++static int (*kperf_timer_count_get)(u32 *count); ++++ ++++/// Set timer number and period. ++++/// @details sysctl set(kperf.timer.period) ++++static int (*kperf_timer_period_set)(u32 actionid, u64 tick); ++++ ++++/// Get timer number and period. ++++/// @details sysctl get(kperf.timer.period) ++++static int (*kperf_timer_period_get)(u32 actionid, u64 *tick); ++++ ++++/// Set timer number and actionid. ++++/// @details sysctl set(kperf.timer.action) ++++static int (*kperf_timer_action_set)(u32 actionid, u32 timerid); ++++ ++++/// Get timer number and actionid. ++++/// @details sysctl get(kperf.timer.action) ++++static int (*kperf_timer_action_get)(u32 actionid, u32 *timerid); ++++ ++++/// Set which timer ID does PET (Profile Every Thread). ++++/// @details sysctl set(kperf.timer.pet_timer) ++++static int (*kperf_timer_pet_set)(u32 timerid); ++++ ++++/// Get which timer ID does PET (Profile Every Thread). ++++/// @details sysctl get(kperf.timer.pet_timer) ++++static int (*kperf_timer_pet_get)(u32 *timerid); ++++ ++++/// Enable or disable sampling. ++++/// @details sysctl set(kperf.sampling) ++++static int (*kperf_sample_set)(u32 enabled); ++++ ++++/// Get is currently sampling. ++++/// @details sysctl get(kperf.sampling) ++++static int (*kperf_sample_get)(u32 *enabled); ++++ ++++/// Reset kperf: stop sampling, kdebug, timers and actions. ++++/// @return 0 for success. ++++static int (*kperf_reset)(void); ++++ ++++/// Nanoseconds to CPU ticks. ++++static u64 (*kperf_ns_to_ticks)(u64 ns); ++++ ++++/// CPU ticks to nanoseconds. ++++static u64 (*kperf_ticks_to_ns)(u64 ticks); ++++ ++++/// CPU ticks frequency (mach_absolute_time). ++++static u64 (*kperf_tick_frequency)(void); ++++ ++++/// Get lightweight PET mode (not in kperf.framework). ++++static int kperf_lightweight_pet_get(u32 *enabled) { ++++ if (!enabled) return -1; ++++ usize size = 4; ++++ return sysctlbyname("kperf.lightweight_pet", enabled, &size, NULL, 0); ++++} ++++ ++++/// Set lightweight PET mode (not in kperf.framework). ++++static int kperf_lightweight_pet_set(u32 enabled) { ++++ return sysctlbyname("kperf.lightweight_pet", NULL, NULL, &enabled, 4); ++++} ++++ ++++// ----------------------------------------------------------------------------- ++++// header (reverse engineered) ++++// This framework provides some functions to access the local CPU database. ++++// These functions do not require root privileges. ++++// ----------------------------------------------------------------------------- ++++ ++++// KPEP CPU architecture constants. ++++#define KPEP_ARCH_I386 0 ++++#define KPEP_ARCH_X86_64 1 ++++#define KPEP_ARCH_ARM 2 ++++#define KPEP_ARCH_ARM64 3 ++++ ++++/// KPEP event (size: 48/28 bytes on 64/32 bit OS) ++++typedef struct kpep_event { ++++ const char *name; ///< Unique name of a event, such as "INST_RETIRED.ANY". ++++ const char *description; ///< Description for this event. ++++ const char *errata; ///< Errata, currently NULL. ++++ const char *alias; ///< Alias name, such as "Instructions", "Cycles". ++++ const char *fallback; ///< Fallback event name for fixed counter. ++++ u32 mask; ++++ u8 number; ++++ u8 umask; ++++ u8 reserved; ++++ u8 is_fixed; ++++} kpep_event; ++++ ++++/// KPEP database (size: 144/80 bytes on 64/32 bit OS) ++++typedef struct kpep_db { ++++ const char *name; ///< Database name, such as "haswell". ++++ const char *cpu_id; ///< Plist name, such as "cpu_7_8_10b282dc". ++++ const char *marketing_name; ///< Marketing name, such as "Intel Haswell". ++++ void *plist_data; ///< Plist data (CFDataRef), currently NULL. ++++ void *event_map; ///< All events (CFDict). ++++ kpep_event ++++ *event_arr; ///< Event struct buffer (sizeof(kpep_event) * events_count). ++++ kpep_event **fixed_event_arr; ///< Fixed counter events (sizeof(kpep_event *) ++++ ///< * fixed_counter_count) ++++ void *alias_map; ///< All aliases (CFDict). ++++ usize reserved_1; ++++ usize reserved_2; ++++ usize reserved_3; ++++ usize event_count; ///< All events count. ++++ usize alias_count; ++++ usize fixed_counter_count; ++++ usize config_counter_count; ++++ usize power_counter_count; ++++ u32 architecture; ///< see `KPEP CPU architecture constants` above. ++++ u32 fixed_counter_bits; ++++ u32 config_counter_bits; ++++ u32 power_counter_bits; ++++} kpep_db; ++++ ++++/// KPEP config (size: 80/44 bytes on 64/32 bit OS) ++++typedef struct kpep_config { ++++ kpep_db *db; ++++ kpep_event **ev_arr; ///< (sizeof(kpep_event *) * counter_count), init NULL ++++ usize *ev_map; ///< (sizeof(usize *) * counter_count), init 0 ++++ usize *ev_idx; ///< (sizeof(usize *) * counter_count), init -1 ++++ u32 *flags; ///< (sizeof(u32 *) * counter_count), init 0 ++++ u64 *kpc_periods; ///< (sizeof(u64 *) * counter_count), init 0 ++++ usize event_count; /// kpep_config_events_count() ++++ usize counter_count; ++++ u32 classes; ///< See `class mask constants` above. ++++ u32 config_counter; ++++ u32 power_counter; ++++ u32 reserved; ++++} kpep_config; ++++ ++++/// Error code for kpep_config_xxx() and kpep_db_xxx() functions. ++++typedef enum { ++++ KPEP_CONFIG_ERROR_NONE = 0, ++++ KPEP_CONFIG_ERROR_INVALID_ARGUMENT = 1, ++++ KPEP_CONFIG_ERROR_OUT_OF_MEMORY = 2, ++++ KPEP_CONFIG_ERROR_IO = 3, ++++ KPEP_CONFIG_ERROR_BUFFER_TOO_SMALL = 4, ++++ KPEP_CONFIG_ERROR_CUR_SYSTEM_UNKNOWN = 5, ++++ KPEP_CONFIG_ERROR_DB_PATH_INVALID = 6, ++++ KPEP_CONFIG_ERROR_DB_NOT_FOUND = 7, ++++ KPEP_CONFIG_ERROR_DB_ARCH_UNSUPPORTED = 8, ++++ KPEP_CONFIG_ERROR_DB_VERSION_UNSUPPORTED = 9, ++++ KPEP_CONFIG_ERROR_DB_CORRUPT = 10, ++++ KPEP_CONFIG_ERROR_EVENT_NOT_FOUND = 11, ++++ KPEP_CONFIG_ERROR_CONFLICTING_EVENTS = 12, ++++ KPEP_CONFIG_ERROR_COUNTERS_NOT_FORCED = 13, ++++ KPEP_CONFIG_ERROR_EVENT_UNAVAILABLE = 14, ++++ KPEP_CONFIG_ERROR_ERRNO = 15, ++++ KPEP_CONFIG_ERROR_MAX ++++} kpep_config_error_code; ++++ ++++/// Error description for kpep_config_error_code. ++++static const char *kpep_config_error_names[KPEP_CONFIG_ERROR_MAX] = { ++++ "none", ++++ "invalid argument", ++++ "out of memory", ++++ "I/O", ++++ "buffer too small", ++++ "current system unknown", ++++ "database path invalid", ++++ "database not found", ++++ "database architecture unsupported", ++++ "database version unsupported", ++++ "database corrupt", ++++ "event not found", ++++ "conflicting events", ++++ "all counters must be forced", ++++ "event unavailable", ++++ "check errno"}; ++++ ++++/// Error description. ++++static const char *kpep_config_error_desc(int code) { ++++ if (0 <= code && code < KPEP_CONFIG_ERROR_MAX) { ++++ return kpep_config_error_names[code]; ++++ } ++++ return "unknown error"; ++++} ++++ ++++/// Create a config. ++++/// @param db A kpep db, see kpep_db_create() ++++/// @param cfg_ptr A pointer to receive the new config. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_create)(kpep_db *db, kpep_config **cfg_ptr); ++++ ++++/// Free the config. ++++static void (*kpep_config_free)(kpep_config *cfg); ++++ ++++/// Add an event to config. ++++/// @param cfg The config. ++++/// @param ev_ptr A event pointer. ++++/// @param flag 0: all, 1: user space only ++++/// @param err Error bitmap pointer, can be NULL. ++++/// If return value is `CONFLICTING_EVENTS`, this bitmap contains ++++/// the conflicted event indices, e.g. "1 << 2" means index 2. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_add_event)(kpep_config *cfg, kpep_event **ev_ptr, ++++ u32 flag, u32 *err); ++++ ++++/// Remove event at index. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_remove_event)(kpep_config *cfg, usize idx); ++++ ++++/// Force all counters. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_force_counters)(kpep_config *cfg); ++++ ++++/// Get events count. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_events_count)(kpep_config *cfg, usize *count_ptr); ++++ ++++/// Get all event pointers. ++++/// @param buf A buffer to receive event pointers. ++++/// @param buf_size The buffer's size in bytes, should not smaller than ++++/// kpep_config_events_count() * sizeof(void *). ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_events)(kpep_config *cfg, kpep_event **buf, ++++ usize buf_size); ++++ ++++/// Get kpc register configs. ++++/// @param buf A buffer to receive kpc register configs. ++++/// @param buf_size The buffer's size in bytes, should not smaller than ++++/// kpep_config_kpc_count() * sizeof(kpc_config_t). ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_kpc)(kpep_config *cfg, kpc_config_t *buf, ++++ usize buf_size); ++++ ++++/// Get kpc register config count. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_kpc_count)(kpep_config *cfg, usize *count_ptr); ++++ ++++/// Get kpc classes. ++++/// @param classes See `class mask constants` above. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_kpc_classes)(kpep_config *cfg, u32 *classes_ptr); ++++ ++++/// Get the index mapping from event to counter. ++++/// @param buf A buffer to receive indexes. ++++/// @param buf_size The buffer's size in bytes, should not smaller than ++++/// kpep_config_events_count() * sizeof(kpc_config_t). ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_config_kpc_map)(kpep_config *cfg, usize *buf, usize buf_size); ++++ ++++/// Open a kpep database file in "/usr/share/kpep/" or "/usr/local/share/kpep/". ++++/// @param name File name, for example "haswell", "cpu_100000c_1_92fb37c8". ++++/// Pass NULL for current CPU. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_create)(const char *name, kpep_db **db_ptr); ++++ ++++/// Free the kpep database. ++++static void (*kpep_db_free)(kpep_db *db); ++++ ++++/// Get the database's name. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_name)(kpep_db *db, const char **name); ++++ ++++/// Get the event alias count. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_aliases_count)(kpep_db *db, usize *count); ++++ ++++/// Get all alias. ++++/// @param buf A buffer to receive all alias strings. ++++/// @param buf_size The buffer's size in bytes, ++++/// should not smaller than kpep_db_aliases_count() * sizeof(void *). ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_aliases)(kpep_db *db, const char **buf, usize buf_size); ++++ ++++/// Get counters count for given classes. ++++/// @param classes 1: Fixed, 2: Configurable. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_counters_count)(kpep_db *db, u8 classes, usize *count); ++++ ++++/// Get all event count. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_events_count)(kpep_db *db, usize *count); ++++ ++++/// Get all events. ++++/// @param buf A buffer to receive all event pointers. ++++/// @param buf_size The buffer's size in bytes, ++++/// should not smaller than kpep_db_events_count() * sizeof(void *). ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_events)(kpep_db *db, kpep_event **buf, usize buf_size); ++++ ++++/// Get one event by name. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_db_event)(kpep_db *db, const char *name, kpep_event **ev_ptr); ++++ ++++/// Get event's name. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_event_name)(kpep_event *ev, const char **name_ptr); ++++ ++++/// Get event's alias. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_event_alias)(kpep_event *ev, const char **alias_ptr); ++++ ++++/// Get event's description. ++++/// @return kpep_config_error_code, 0 for success. ++++static int (*kpep_event_description)(kpep_event *ev, const char **str_ptr); ++++ ++++// ----------------------------------------------------------------------------- ++++// load kperf/kperfdata dynamic library ++++// ----------------------------------------------------------------------------- ++++ ++++typedef struct { ++++ const char *name; ++++ void **impl; ++++} lib_symbol; ++++ ++++#define lib_nelems(x) (sizeof(x) / sizeof((x)[0])) ++++#define lib_symbol_def(name) \ ++++ { #name, (void **)&name } ++++ ++++static const lib_symbol lib_symbols_kperf[] = { ++++ lib_symbol_def(kpc_pmu_version), ++++ lib_symbol_def(kpc_cpu_string), ++++ lib_symbol_def(kpc_set_counting), ++++ lib_symbol_def(kpc_get_counting), ++++ lib_symbol_def(kpc_set_thread_counting), ++++ lib_symbol_def(kpc_get_thread_counting), ++++ lib_symbol_def(kpc_get_config_count), ++++ lib_symbol_def(kpc_get_counter_count), ++++ lib_symbol_def(kpc_set_config), ++++ lib_symbol_def(kpc_get_config), ++++ lib_symbol_def(kpc_get_cpu_counters), ++++ lib_symbol_def(kpc_get_thread_counters), ++++ lib_symbol_def(kpc_force_all_ctrs_set), ++++ lib_symbol_def(kpc_force_all_ctrs_get), ++++ lib_symbol_def(kperf_action_count_set), ++++ lib_symbol_def(kperf_action_count_get), ++++ lib_symbol_def(kperf_action_samplers_set), ++++ lib_symbol_def(kperf_action_samplers_get), ++++ lib_symbol_def(kperf_action_filter_set_by_task), ++++ lib_symbol_def(kperf_action_filter_set_by_pid), ++++ lib_symbol_def(kperf_timer_count_set), ++++ lib_symbol_def(kperf_timer_count_get), ++++ lib_symbol_def(kperf_timer_period_set), ++++ lib_symbol_def(kperf_timer_period_get), ++++ lib_symbol_def(kperf_timer_action_set), ++++ lib_symbol_def(kperf_timer_action_get), ++++ lib_symbol_def(kperf_sample_set), ++++ lib_symbol_def(kperf_sample_get), ++++ lib_symbol_def(kperf_reset), ++++ lib_symbol_def(kperf_timer_pet_set), ++++ lib_symbol_def(kperf_timer_pet_get), ++++ lib_symbol_def(kperf_ns_to_ticks), ++++ lib_symbol_def(kperf_ticks_to_ns), ++++ lib_symbol_def(kperf_tick_frequency), ++++}; ++++ ++++static const lib_symbol lib_symbols_kperfdata[] = { ++++ lib_symbol_def(kpep_config_create), ++++ lib_symbol_def(kpep_config_free), ++++ lib_symbol_def(kpep_config_add_event), ++++ lib_symbol_def(kpep_config_remove_event), ++++ lib_symbol_def(kpep_config_force_counters), ++++ lib_symbol_def(kpep_config_events_count), ++++ lib_symbol_def(kpep_config_events), ++++ lib_symbol_def(kpep_config_kpc), ++++ lib_symbol_def(kpep_config_kpc_count), ++++ lib_symbol_def(kpep_config_kpc_classes), ++++ lib_symbol_def(kpep_config_kpc_map), ++++ lib_symbol_def(kpep_db_create), ++++ lib_symbol_def(kpep_db_free), ++++ lib_symbol_def(kpep_db_name), ++++ lib_symbol_def(kpep_db_aliases_count), ++++ lib_symbol_def(kpep_db_aliases), ++++ lib_symbol_def(kpep_db_counters_count), ++++ lib_symbol_def(kpep_db_events_count), ++++ lib_symbol_def(kpep_db_events), ++++ lib_symbol_def(kpep_db_event), ++++ lib_symbol_def(kpep_event_name), ++++ lib_symbol_def(kpep_event_alias), ++++ lib_symbol_def(kpep_event_description), ++++}; ++++ ++++#define lib_path_kperf "/System/Library/PrivateFrameworks/kperf.framework/kperf" ++++#define lib_path_kperfdata \ ++++ "/System/Library/PrivateFrameworks/kperfdata.framework/kperfdata" ++++ ++++static bool lib_inited = false; ++++static bool lib_has_err = false; ++++static char lib_err_msg[256]; ++++ ++++static void *lib_handle_kperf = NULL; ++++static void *lib_handle_kperfdata = NULL; ++++ ++++static void lib_deinit(void) { ++++ lib_inited = false; ++++ lib_has_err = false; ++++ if (lib_handle_kperf) dlclose(lib_handle_kperf); ++++ if (lib_handle_kperfdata) dlclose(lib_handle_kperfdata); ++++ lib_handle_kperf = NULL; ++++ lib_handle_kperfdata = NULL; ++++ for (usize i = 0; i < lib_nelems(lib_symbols_kperf); i++) { ++++ const lib_symbol *symbol = &lib_symbols_kperf[i]; ++++ *symbol->impl = NULL; ++++ } ++++ for (usize i = 0; i < lib_nelems(lib_symbols_kperfdata); i++) { ++++ const lib_symbol *symbol = &lib_symbols_kperfdata[i]; ++++ *symbol->impl = NULL; ++++ } ++++} ++++ ++++static bool lib_init(void) { ++++#define return_err() \ ++++ do { \ ++++ lib_deinit(); \ ++++ lib_inited = true; \ ++++ lib_has_err = true; \ ++++ return false; \ ++++ } while (false) ++++ ++++ if (lib_inited) return !lib_has_err; ++++ ++++ // load dynamic library ++++ lib_handle_kperf = dlopen(lib_path_kperf, RTLD_LAZY); ++++ if (!lib_handle_kperf) { ++++ snprintf(lib_err_msg, sizeof(lib_err_msg), ++++ "Failed to load kperf.framework, message: %s.", dlerror()); ++++ return_err(); ++++ } ++++ lib_handle_kperfdata = dlopen(lib_path_kperfdata, RTLD_LAZY); ++++ if (!lib_handle_kperfdata) { ++++ snprintf(lib_err_msg, sizeof(lib_err_msg), ++++ "Failed to load kperfdata.framework, message: %s.", dlerror()); ++++ return_err(); ++++ } ++++ ++++ // load symbol address from dynamic library ++++ for (usize i = 0; i < lib_nelems(lib_symbols_kperf); i++) { ++++ const lib_symbol *symbol = &lib_symbols_kperf[i]; ++++ *symbol->impl = dlsym(lib_handle_kperf, symbol->name); ++++ if (!*symbol->impl) { ++++ snprintf(lib_err_msg, sizeof(lib_err_msg), ++++ "Failed to load kperf function: %s.", symbol->name); ++++ return_err(); ++++ } ++++ } ++++ for (usize i = 0; i < lib_nelems(lib_symbols_kperfdata); i++) { ++++ const lib_symbol *symbol = &lib_symbols_kperfdata[i]; ++++ *symbol->impl = dlsym(lib_handle_kperfdata, symbol->name); ++++ if (!*symbol->impl) { ++++ snprintf(lib_err_msg, sizeof(lib_err_msg), ++++ "Failed to load kperfdata function: %s.", symbol->name); ++++ return_err(); ++++ } ++++ } ++++ ++++ lib_inited = true; ++++ lib_has_err = false; ++++ return true; ++++ ++++#undef return_err ++++} ++++ ++++// ----------------------------------------------------------------------------- ++++// kdebug private structs ++++// https://github.com/apple/darwin-xnu/blob/main/bsd/sys_private/kdebug_private.h ++++// ----------------------------------------------------------------------------- ++++ ++++/* ++++ * Ensure that both LP32 and LP64 variants of arm64 use the same kd_buf ++++ * structure. ++++ */ ++++#if defined(__arm64__) ++++typedef uint64_t kd_buf_argtype; ++++#else ++++typedef uintptr_t kd_buf_argtype; ++++#endif ++++ ++++typedef struct { ++++ uint64_t timestamp; ++++ kd_buf_argtype arg1; ++++ kd_buf_argtype arg2; ++++ kd_buf_argtype arg3; ++++ kd_buf_argtype arg4; ++++ kd_buf_argtype arg5; /* the thread ID */ ++++ uint32_t debugid; /* see */ ++++ ++++/* ++++ * Ensure that both LP32 and LP64 variants of arm64 use the same kd_buf ++++ * structure. ++++ */ ++++#if defined(__LP64__) || defined(__arm64__) ++++ uint32_t cpuid; /* cpu index, from 0 */ ++++ kd_buf_argtype unused; ++++#endif ++++} kd_buf; ++++ ++++/* bits for the type field of kd_regtype */ ++++#define KDBG_CLASSTYPE 0x10000 ++++#define KDBG_SUBCLSTYPE 0x20000 ++++#define KDBG_RANGETYPE 0x40000 ++++#define KDBG_TYPENONE 0x80000 ++++#define KDBG_CKTYPES 0xF0000 ++++ ++++/* only trace at most 4 types of events, at the code granularity */ ++++#define KDBG_VALCHECK 0x00200000U ++++ ++++typedef struct { ++++ unsigned int type; ++++ unsigned int value1; ++++ unsigned int value2; ++++ unsigned int value3; ++++ unsigned int value4; ++++} kd_regtype; ++++ ++++typedef struct { ++++ /* number of events that can fit in the buffers */ ++++ int nkdbufs; ++++ /* set if trace is disabled */ ++++ int nolog; ++++ /* kd_ctrl_page.flags */ ++++ unsigned int flags; ++++ /* number of threads in thread map */ ++++ int nkdthreads; ++++ /* the owning pid */ ++++ int bufid; ++++} kbufinfo_t; ++++ ++++// ----------------------------------------------------------------------------- ++++// kdebug utils ++++// ----------------------------------------------------------------------------- ++++ ++++/// Clean up trace buffers and reset ktrace/kdebug/kperf. ++++/// @return 0 on success. ++++static int kdebug_reset(void) { ++++ int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDREMOVE}; ++++ return sysctl(mib, 3, NULL, NULL, NULL, 0); ++++} ++++ ++++/// Disable and reinitialize the trace buffers. ++++/// @return 0 on success. ++++static int kdebug_reinit(void) { ++++ int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETUP}; ++++ return sysctl(mib, 3, NULL, NULL, NULL, 0); ++++} ++++ ++++/// Set debug filter. ++++static int kdebug_setreg(kd_regtype *kdr) { ++++ int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETREG}; ++++ usize size = sizeof(kd_regtype); ++++ return sysctl(mib, 3, kdr, &size, NULL, 0); ++++} ++++ ++++/// Set maximum number of trace entries (kd_buf). ++++/// Only allow allocation up to half the available memory (sane_size). ++++/// @return 0 on success. ++++static int kdebug_trace_setbuf(int nbufs) { ++++ int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETBUF, nbufs}; ++++ return sysctl(mib, 4, NULL, NULL, NULL, 0); ++++} ++++ ++++/// Enable or disable kdebug trace. ++++/// Trace buffer must already be initialized. ++++/// @return 0 on success. ++++static int kdebug_trace_enable(bool enable) { ++++ int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDENABLE, enable}; ++++ return sysctl(mib, 4, NULL, 0, NULL, 0); ++++} ++++ ++++/// Retrieve trace buffer information from kernel. ++++/// @return 0 on success. ++++static int kdebug_get_bufinfo(kbufinfo_t *info) { ++++ if (!info) return -1; ++++ int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDGETBUF}; ++++ size_t needed = sizeof(kbufinfo_t); ++++ return sysctl(mib, 3, info, &needed, NULL, 0); ++++} ++++ ++++/// Retrieve trace buffers from kernel. ++++/// @param buf Memory to receive buffer data, array of `kd_buf`. ++++/// @param len Length of `buf` in bytes. ++++/// @param count Number of trace entries (kd_buf) obtained. ++++/// @return 0 on success. ++++static int kdebug_trace_read(void *buf, usize len, usize *count) { ++++ if (count) *count = 0; ++++ if (!buf || !len) return -1; ++++ ++++ // Note: the input and output units are not the same. ++++ // input: bytes ++++ // output: number of kd_buf ++++ int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDREADTR}; ++++ int ret = sysctl(mib, 3, buf, &len, NULL, 0); ++++ if (ret != 0) return ret; ++++ *count = len; ++++ return 0; ++++} ++++ ++++/// Block until there are new buffers filled or `timeout_ms` have passed. ++++/// @param timeout_ms timeout milliseconds, 0 means wait forever. ++++/// @param suc set true if new buffers filled. ++++/// @return 0 on success. ++++static int kdebug_wait(usize timeout_ms, bool *suc) { ++++ if (timeout_ms == 0) return -1; ++++ int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDBUFWAIT}; ++++ usize val = timeout_ms; ++++ int ret = sysctl(mib, 3, NULL, &val, NULL, 0); ++++ if (suc) *suc = !!val; ++++ return ret; ++++} ++++ ++++// ----------------------------------------------------------------------------- ++++// Demo ++++// ----------------------------------------------------------------------------- ++++ ++++#define EVENT_NAME_MAX 8 ++++typedef struct { ++++ const char *alias; /// name for print ++++ const char *names[EVENT_NAME_MAX]; /// name from pmc db ++++} event_alias; ++++ ++++/// Event names from /usr/share/kpep/.plist ++++static const event_alias profile_events[] = { ++++ {"cycles", ++++ { ++++ "FIXED_CYCLES", // Apple A7-A15 ++++ "CPU_CLK_UNHALTED.THREAD", // Intel Core 1th-10th ++++ "CPU_CLK_UNHALTED.CORE", // Intel Yonah, Merom ++++ }}, ++++ {"instructions", ++++ { ++++ "FIXED_INSTRUCTIONS", // Apple A7-A15 ++++ "INST_RETIRED.ANY" // Intel Yonah, Merom, Core 1th-10th ++++ }}, ++++ {"branches", ++++ { ++++ "INST_BRANCH", // Apple A7-A15 ++++ "BR_INST_RETIRED.ALL_BRANCHES", // Intel Core 1th-10th ++++ "INST_RETIRED.ANY", // Intel Yonah, Merom ++++ }}, ++++ {"branch-misses", ++++ { ++++ "BRANCH_MISPRED_NONSPEC", // Apple A7-A15, since iOS 15, macOS 12 ++++ "BRANCH_MISPREDICT", // Apple A7-A14 ++++ "BR_MISP_RETIRED.ALL_BRANCHES", // Intel Core 2th-10th ++++ "BR_INST_RETIRED.MISPRED", // Intel Yonah, Merom ++++ }}, ++++}; ++++ ++++static kpep_event *get_event(kpep_db *db, const event_alias *alias) { ++++ for (usize j = 0; j < EVENT_NAME_MAX; j++) { ++++ const char *name = alias->names[j]; ++++ if (!name) break; ++++ kpep_event *ev = NULL; ++++ if (kpep_db_event(db, name, &ev) == 0) { ++++ return ev; ++++ } ++++ } ++++ return NULL; ++++} ++++ ++++struct AppleEvents { ++++ kpc_config_t regs[KPC_MAX_COUNTERS] = {0}; ++++ usize counter_map[KPC_MAX_COUNTERS] = {0}; ++++ u64 counters_0[KPC_MAX_COUNTERS] = {0}; ++++ u64 counters_1[KPC_MAX_COUNTERS] = {0}; ++++ static constexpr usize ev_count = ++++ sizeof(profile_events) / sizeof(profile_events[0]); ++++ bool init = false; ++++ bool worked = false; ++++ inline bool setup_performance_counters() { ++++ if (init) { ++++ return worked; ++++ } ++++ init = true; ++++ ++++ // load dylib ++++ if (!lib_init()) { ++++ printf("Error: %s\n", lib_err_msg); ++++ return (worked = false); ++++ } ++++ ++++ // check permission ++++ int force_ctrs = 0; ++++ if (kpc_force_all_ctrs_get(&force_ctrs)) { ++++ printf("Permission denied, xnu/kpc requires root privileges.\n"); ++++ return (worked = false); ++++ } ++++ int ret; ++++ // load pmc db ++++ kpep_db *db = NULL; ++++ if ((ret = kpep_db_create(NULL, &db))) { ++++ printf("Error: cannot load pmc database: %d.\n", ret); ++++ return (worked = false); ++++ } ++++ printf("loaded db: %s (%s)\n", db->name, db->marketing_name); ++++ // printf("number of fixed counters: %zu\n", db->fixed_counter_count); ++++ // printf("number of configurable counters: %zu\n", ++++ // db->config_counter_count); ++++ ++++ // create a config ++++ kpep_config *cfg = NULL; ++++ if ((ret = kpep_config_create(db, &cfg))) { ++++ printf("Failed to create kpep config: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ if ((ret = kpep_config_force_counters(cfg))) { ++++ printf("Failed to force counters: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ ++++ // get events ++++ kpep_event *ev_arr[ev_count] = {0}; ++++ for (usize i = 0; i < ev_count; i++) { ++++ const event_alias *alias = profile_events + i; ++++ ev_arr[i] = get_event(db, alias); ++++ if (!ev_arr[i]) { ++++ printf("Cannot find event: %s.\n", alias->alias); ++++ return (worked = false); ++++ } ++++ } ++++ ++++ // add event to config ++++ for (usize i = 0; i < ev_count; i++) { ++++ kpep_event *ev = ev_arr[i]; ++++ if ((ret = kpep_config_add_event(cfg, &ev, 0, NULL))) { ++++ printf("Failed to add event: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ } ++++ ++++ // prepare buffer and config ++++ u32 classes = 0; ++++ usize reg_count = 0; ++++ if ((ret = kpep_config_kpc_classes(cfg, &classes))) { ++++ printf("Failed get kpc classes: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ if ((ret = kpep_config_kpc_count(cfg, ®_count))) { ++++ printf("Failed get kpc count: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ if ((ret = kpep_config_kpc_map(cfg, counter_map, sizeof(counter_map)))) { ++++ printf("Failed get kpc map: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ if ((ret = kpep_config_kpc(cfg, regs, sizeof(regs)))) { ++++ printf("Failed get kpc registers: %d (%s).\n", ret, ++++ kpep_config_error_desc(ret)); ++++ return (worked = false); ++++ } ++++ ++++ // set config to kernel ++++ if ((ret = kpc_force_all_ctrs_set(1))) { ++++ printf("Failed force all ctrs: %d.\n", ret); ++++ return (worked = false); ++++ } ++++ if ((classes & KPC_CLASS_CONFIGURABLE_MASK) && reg_count) { ++++ if ((ret = kpc_set_config(classes, regs))) { ++++ printf("Failed set kpc config: %d.\n", ret); ++++ return (worked = false); ++++ } ++++ } ++++ ++++ // start counting ++++ if ((ret = kpc_set_counting(classes))) { ++++ printf("Failed set counting: %d.\n", ret); ++++ return (worked = false); ++++ } ++++ if ((ret = kpc_set_thread_counting(classes))) { ++++ printf("Failed set thread counting: %d.\n", ret); ++++ return (worked = false); ++++ } ++++ ++++ return (worked = true); ++++ } ++++ ++++ inline performance_counters get_counters() { ++++ static bool warned = false; ++++ int ret; ++++ // get counters before ++++ if ((ret = kpc_get_thread_counters(0, KPC_MAX_COUNTERS, counters_0))) { ++++ if (!warned) { ++++ printf("Failed get thread counters before: %d.\n", ret); ++++ warned = true; ++++ } ++++ return 1; ++++ } ++++ /* ++++ // We could print it out this way if we wanted to: ++++ printf("counters value:\n"); ++++ for (usize i = 0; i < ev_count; i++) { ++++ const event_alias *alias = profile_events + i; ++++ usize idx = counter_map[i]; ++++ u64 val = counters_1[idx] - counters_0[idx]; ++++ printf("%14s: %llu\n", alias->alias, val); ++++ }*/ ++++ return performance_counters{ ++++ counters_0[counter_map[0]], counters_0[counter_map[2]], ++++ counters_0[counter_map[2]], counters_0[counter_map[1]]}; ++++ } ++++}; ++++ ++++#endif diff --cc ada/benchmarks/performancecounters/event_counter.h index 000000000,000000000,000000000,000000000..4a1e5eca3 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/performancecounters/event_counter.h @@@@@ -1,0 -1,0 -1,0 -1,0 +1,155 @@@@@ ++++#ifndef __EVENT_COUNTER_H ++++#define __EVENT_COUNTER_H ++++ ++++#include ++++#ifndef _MSC_VER ++++#include ++++#endif ++++#include ++++ ++++#include ++++ ++++#include ++++#include ++++ ++++#include "linux-perf-events.h" ++++#ifdef __linux__ ++++#include ++++#endif ++++ ++++#if __APPLE__ && __aarch64__ ++++#include "apple_arm_events.h" ++++#endif ++++ ++++struct event_count { ++++ std::chrono::duration elapsed; ++++ std::vector event_counts; ++++ event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {} ++++ event_count(const std::chrono::duration _elapsed, ++++ const std::vector _event_counts) ++++ : elapsed(_elapsed), event_counts(_event_counts) {} ++++ event_count(const event_count& other) ++++ : elapsed(other.elapsed), event_counts(other.event_counts) {} ++++ ++++ // The types of counters (so we can read the getter more easily) ++++ enum event_counter_types { ++++ CPU_CYCLES, ++++ INSTRUCTIONS, ++++ BRANCH_MISSES = 2, ++++ BRANCH = 4 ++++ }; ++++ ++++ double elapsed_sec() const { ++++ return std::chrono::duration(elapsed).count(); ++++ } ++++ double elapsed_ns() const { ++++ return std::chrono::duration(elapsed).count(); ++++ } ++++ double cycles() const { ++++ return static_cast(event_counts[CPU_CYCLES]); ++++ } ++++ double instructions() const { ++++ return static_cast(event_counts[INSTRUCTIONS]); ++++ } ++++ double branches() const { return static_cast(event_counts[BRANCH]); } ++++ double branch_misses() const { ++++ return static_cast(event_counts[BRANCH_MISSES]); ++++ } ++++ event_count& operator=(const event_count& other) { ++++ this->elapsed = other.elapsed; ++++ this->event_counts = other.event_counts; ++++ return *this; ++++ } ++++ event_count operator+(const event_count& other) const { ++++ return event_count(elapsed + other.elapsed, ++++ { ++++ event_counts[0] + other.event_counts[0], ++++ event_counts[1] + other.event_counts[1], ++++ event_counts[2] + other.event_counts[2], ++++ event_counts[3] + other.event_counts[3], ++++ event_counts[4] + other.event_counts[4], ++++ }); ++++ } ++++ ++++ void operator+=(const event_count& other) { *this = *this + other; } ++++}; ++++ ++++struct event_aggregate { ++++ bool has_events = false; ++++ int iterations = 0; ++++ event_count total{}; ++++ event_count best{}; ++++ event_count worst{}; ++++ ++++ event_aggregate() = default; ++++ ++++ void operator<<(const event_count& other) { ++++ if (iterations == 0 || other.elapsed < best.elapsed) { ++++ best = other; ++++ } ++++ if (iterations == 0 || other.elapsed > worst.elapsed) { ++++ worst = other; ++++ } ++++ iterations++; ++++ total += other; ++++ } ++++ ++++ double elapsed_sec() const { return total.elapsed_sec() / iterations; } ++++ double elapsed_ns() const { return total.elapsed_ns() / iterations; } ++++ double cycles() const { return total.cycles() / iterations; } ++++ double instructions() const { return total.instructions() / iterations; } ++++}; ++++ ++++struct event_collector { ++++ event_count count{}; ++++ std::chrono::time_point start_clock{}; ++++ ++++#if defined(__linux__) ++++ LinuxEvents linux_events; ++++ event_collector() ++++ : linux_events(std::vector{ ++++ PERF_COUNT_HW_CPU_CYCLES, ++++ PERF_COUNT_HW_INSTRUCTIONS, ++++ }) {} ++++ bool has_events() { return linux_events.is_working(); } ++++#elif __APPLE__ && __aarch64__ ++++ AppleEvents apple_events; ++++ performance_counters diff; ++++ event_collector() : diff(0) { apple_events.setup_performance_counters(); } ++++ bool has_events() { return apple_events.setup_performance_counters(); } ++++#else ++++ event_collector() {} ++++ bool has_events() { return false; } ++++#endif ++++ ++++ inline void start() { ++++#if defined(__linux) ++++ linux_events.start(); ++++#elif __APPLE__ && __aarch64__ ++++ if (has_events()) { ++++ diff = apple_events.get_counters(); ++++ } ++++#endif ++++ start_clock = std::chrono::steady_clock::now(); ++++ } ++++ inline event_count& end() { ++++ const auto end_clock = std::chrono::steady_clock::now(); ++++#if defined(__linux) ++++ linux_events.end(count.event_counts); ++++#elif __APPLE__ && __aarch64__ ++++ if (has_events()) { ++++ performance_counters end = apple_events.get_counters(); ++++ diff = end - diff; ++++ } ++++ count.event_counts[0] = diff.cycles; ++++ count.event_counts[1] = diff.instructions; ++++ count.event_counts[2] = diff.missed_branches; ++++ count.event_counts[3] = 0; ++++ count.event_counts[4] = diff.branches; ++++#endif ++++ count.elapsed = end_clock - start_clock; ++++ return count; ++++ } ++++}; ++++ ++++#endif diff --cc ada/benchmarks/performancecounters/linux-perf-events.h index 000000000,000000000,000000000,000000000..7ed0f8d24 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/performancecounters/linux-perf-events.h @@@@@ -1,0 -1,0 -1,0 -1,0 +1,105 @@@@@ ++++#pragma once ++++#ifdef __linux__ ++++ ++++#include // for __NR_perf_event_open ++++#include // for perf event constants ++++#include // for ioctl ++++#include // for syscall ++++ ++++#include // for errno ++++#include // for memset ++++#include ++++ ++++#include ++++#include ++++ ++++template ++++class LinuxEvents { ++++ int fd; ++++ bool working; ++++ perf_event_attr attribs{}; ++++ size_t num_events{}; ++++ std::vector temp_result_vec{}; ++++ std::vector ids{}; ++++ ++++ public: ++++ explicit LinuxEvents(std::vector config_vec) : fd(0), working(true) { ++++ memset(&attribs, 0, sizeof(attribs)); ++++ attribs.type = TYPE; ++++ attribs.size = sizeof(attribs); ++++ attribs.disabled = 1; ++++ attribs.exclude_kernel = 1; ++++ attribs.exclude_hv = 1; ++++ ++++ attribs.sample_period = 0; ++++ attribs.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID; ++++ const int pid = 0; // the current process ++++ const int cpu = -1; // all CPUs ++++ const unsigned long flags = 0; ++++ ++++ int group = -1; // no group ++++ num_events = config_vec.size(); ++++ ids.resize(config_vec.size()); ++++ uint32_t i = 0; ++++ for (auto config : config_vec) { ++++ attribs.config = config; ++++ int _fd = static_cast( ++++ syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags)); ++++ if (_fd == -1) { ++++ report_error("perf_event_open"); ++++ } ++++ ioctl(_fd, PERF_EVENT_IOC_ID, &ids[i++]); ++++ if (group == -1) { ++++ group = _fd; ++++ fd = _fd; ++++ } ++++ } ++++ ++++ temp_result_vec.resize(num_events * 2 + 1); ++++ } ++++ ++++ ~LinuxEvents() { ++++ if (fd != -1) { ++++ close(fd); ++++ } ++++ } ++++ ++++ inline void start() { ++++ if (fd != -1) { ++++ if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { ++++ report_error("ioctl(PERF_EVENT_IOC_RESET)"); ++++ } ++++ ++++ if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { ++++ report_error("ioctl(PERF_EVENT_IOC_ENABLE)"); ++++ } ++++ } ++++ } ++++ ++++ inline void end(std::vector &results) { ++++ if (fd != -1) { ++++ if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { ++++ report_error("ioctl(PERF_EVENT_IOC_DISABLE)"); ++++ } ++++ ++++ if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) { ++++ report_error("read"); ++++ } ++++ } ++++ // our actual results are in slots 1,3,5, ... of this structure ++++ for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) { ++++ results[i / 2] = temp_result_vec[i]; ++++ } ++++ for (uint32_t i = 2; i < temp_result_vec.size(); i += 2) { ++++ if (ids[i / 2 - 1] != temp_result_vec[i]) { ++++ report_error("event mismatch"); ++++ } ++++ } ++++ } ++++ ++++ bool is_working() { return working; } ++++ ++++ private: ++++ void report_error(const std::string &) { working = false; } ++++}; ++++#endif diff --cc ada/benchmarks/wpt_bench.cpp index 000000000,000000000,000000000,000000000..2b37db119 new file mode 100644 --- /dev/null +++ b/ada/benchmarks/wpt_bench.cpp @@@@@ -1,0 -1,0 -1,0 -1,0 +1,227 @@@@@ ++++#include "benchmark_header.h" ++++#include "simdjson.h" ++++ ++++using namespace simdjson; ++++ ++++double url_examples_bytes{}; ++++ ++++std::vector> url_examples; ++++ ++++size_t init_data(const char *source) { ++++ 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); ++++ 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"); ++++ } ++++ std::string_view base; ++++ if (element["base"].get_string(true).get(base) != simdjson::SUCCESS) { ++++ } ++++ url_examples.push_back({std::string(input), std::string(base)}); ++++ url_examples_bytes += input.size() + base.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 += url->get_href().size(); ++++ } ++++ } ++++ } ++++ 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 (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 += url->get_href().size(); ++++ } ++++ } ++++ 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); ++++} ++++auto BasicBench_AdaURL_url = BasicBench_AdaURL; ++++BENCHMARK(BasicBench_AdaURL_url); ++++auto BasicBench_AdaURL_url_aggregator = BasicBench_AdaURL; ++++BENCHMARK(BasicBench_AdaURL_url_aggregator); ++++ ++++#if ADA_url_whatwg_ENABLED ++++ ++++#include ++++ ++++static void 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++; ++++ } ++++ } ++++ } ++++ 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 (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++; ++++ } ++++ } ++++ 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_whatwg); ++++#endif // ADA_url_whatwg_ENABLED ++++ ++++int main(int argc, char **argv) { ++++ if (argc == 1 || !init_data(argv[1])) { ++++ 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; ++++ 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(); ++++} diff --cc ada/clang-format-ignore.txt index 000000000,000000000,000000000,000000000..e69de29bb new file mode 100644 --- /dev/null +++ b/ada/clang-format-ignore.txt diff --cc ada/cmake/CPM.cmake index 000000000,000000000,000000000,000000000..ad6b74a8b new file mode 100644 --- /dev/null +++ b/ada/cmake/CPM.cmake @@@@@ -1,0 -1,0 -1,0 -1,0 +1,24 @@@@@ ++++# SPDX-License-Identifier: MIT ++++# ++++# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors ++++ ++++set(CPM_DOWNLOAD_VERSION 0.38.6) ++++set(CPM_HASH_SUM "11c3fa5f1ba14f15d31c2fb63dbc8628ee133d81c8d764caad9a8db9e0bacb07") ++++ ++++if(CPM_SOURCE_CACHE) ++++ set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") ++++elseif(DEFINED ENV{CPM_SOURCE_CACHE}) ++++ set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") ++++else() ++++ set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") ++++endif() ++++ ++++# Expand relative path. This is important if the provided path contains a tilde (~) ++++get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) ++++ ++++file(DOWNLOAD ++++ https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ++++ ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} ++++) ++++ ++++include(${CPM_DOWNLOAD_LOCATION}) diff --cc ada/cmake/ada-config.cmake.in index 000000000,000000000,000000000,000000000..0c5d540b1 new file mode 100644 --- /dev/null +++ b/ada/cmake/ada-config.cmake.in @@@@@ -1,0 -1,0 -1,0 -1,0 +1,1 @@@@@ ++++include("${CMAKE_CURRENT_LIST_DIR}/ada_targets.cmake") diff --cc ada/cmake/ada-flags.cmake index 000000000,000000000,000000000,000000000..43fdcaeb6 new file mode 100644 --- /dev/null +++ b/ada/cmake/ada-flags.cmake @@@@@ -1,0 -1,0 -1,0 -1,0 +1,59 @@@@@ ++++option(ADA_LOGGING "verbose output (useful for debugging)" OFF) ++++option(ADA_DEVELOPMENT_CHECKS "development checks (useful for debugging)" OFF) ++++option(ADA_SANITIZE "Sanitize addresses" OFF) ++++if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") ++++ option(ADA_SANITIZE_BOUNDS_STRICT "Sanitize bounds (strict): only for GCC" OFF) ++++endif() ++++option(ADA_SANITIZE_UNDEFINED "Sanitize undefined behaviour" OFF) ++++if(ADA_SANITIZE) ++++ message(STATUS "Address sanitizer enabled.") ++++endif() ++++if(ADA_SANITIZE_UNDEFINED) ++++ message(STATUS "Undefined sanitizer enabled.") ++++endif() ++++option(ADA_COVERAGE "Compute coverage" OFF) ++++option(ADA_TOOLS "Build cli tools (adaparse)" 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_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 17) ++++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) diff --cc ada/cmake/add-cpp-test.cmake index 000000000,000000000,000000000,000000000..3d1e78556 new file mode 100644 --- /dev/null +++ b/ada/cmake/add-cpp-test.cmake @@@@@ -1,0 -1,0 -1,0 -1,0 +1,67 @@@@@ ++++# 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_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}) ++++ 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() ++++ add_test(${TEST_NAME} ${TEST_NAME}) ++++ ++++ # Add to