diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 0000000..9a9bade --- /dev/null +++ b/.bazelignore @@ -0,0 +1,4 @@ +bazel-radiant-cpp +bazel-bin +bazel-out +bazel-testlogs diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..b5b20e0 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,9 @@ +# Enables c++ toolchain selection via the new "platforms" mechanism +# This flag will likely not be needed anymore when bazel 7.0.0 releases +build --incompatible_enable_cc_toolchain_resolution + +# For access to cc_shared_library (experimental for years. default enabled 2023 Q1) +build --experimental_cc_shared_library + +# Remove error output limit +build --experimental_ui_max_stdouterr_bytes=-1 diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 0000000..21c8c7b --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +7.1.1 diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..5a3bff4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,65 @@ +--- +BasedOnStyle: Microsoft +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 80 +ReflowComments: true +AccessModifierOffset: -4 +SortIncludes: Never +UseCRLF: false + +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: true + AfterClass: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + AfterExternBlock: true + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: true + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakInheritanceList: AfterComma +SeparateDefinitionBlocks: Always +AlwaysBreakTemplateDeclarations: Yes + +BinPackArguments: false +BinPackParameters: false +AllowAllArgumentsOnNextLine: false +PointerAlignment: Left +ReferenceAlignment: Left +AlignAfterOpenBracket: Align +AlignArrayOfStructures: Right +AlignConsecutiveBitFields: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true +AlignConsecutiveMacros: + Enabled: true + AcrossEmptyLines: false + AcrossComments: true +AlignEscapedNewlines: Right +AlignOperands: Align +IndentCaseBlocks: false +IndentCaseLabels: true +IndentExternBlock: false +IndentGotoLabels: false +Cpp11BracedListStyle: false +SpaceBeforeCpp11BracedList: false +EmptyLineAfterAccessModifier: Always +LambdaBodyIndentation: Signature +NamespaceIndentation: None +PackConstructorInitializers: Never +SortUsingDeclarations: false +QualifierAlignment: Custom +QualifierOrder: ['static', 'constexpr', 'inline', 'type'] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..0efde22 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +*.h linguist-language=cpp +*.hpp linguist-language=cpp diff --git a/.github/ISSUE_TEMPLATE/01-bug-report.yaml b/.github/ISSUE_TEMPLATE/01-bug-report.yaml new file mode 100644 index 0000000..21a56f2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01-bug-report.yaml @@ -0,0 +1,40 @@ +--- +name: Bug Report +description: Report bugs or other issues. +labels: ["bug"] +body: + - type: textarea + attributes: + label: Brief description of your issue + placeholder: Briefly describe your issue here. + validations: + required: true + - type: textarea + attributes: + label: Steps to reproduce (optional) + placeholder: How to reproduce the issue? + validations: + required: false + - type: textarea + attributes: + label: Expected behavior (optional) + placeholder: What did you expect to happen? + validations: + required: false + - type: textarea + attributes: + label: Actual behavior (optional) + placeholder: What is currently happening? + validations: + required: false + - type: textarea + attributes: + label: Environment (optional) + placeholder: | + Operating System version(s) + Compiler version(s) + C++ standard version(s) + Any other information? + render: shell + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/02-feature-request.yaml b/.github/ISSUE_TEMPLATE/02-feature-request.yaml new file mode 100644 index 0000000..5792b89 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02-feature-request.yaml @@ -0,0 +1,16 @@ +--- +name: Feature Request +description: Suggest features, modifications, ideas, or suggestions. +labels: ["enhancement"] +body: + - type: textarea + attributes: + label: Description of the feature, modification, idea, or suggestion. + validations: + required: true + - type: textarea + attributes: + label: Proposed implementation details (optional) + placeholder: Suggest how to implement the feature. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 0000000..ea0c17f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,12 @@ +--- +blank_issues_enabled: true +contact_links: + - name: General Questions + url: https://github.com/archonitelabs/radiant-cpp/discussions/new + about: Have a question? Start a new discussion. + - name: Review Open Issues + url: https://github.com/archonitelabs/radiant-cpp/issues + about: Check existing enhancments or bug reports. + - name: Create a Blank Issue + url: https://github.com/archonitelabs/radiant-cpp/issues/new + about: Create a blank issue for anything else. diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..6ae52eb --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,40 @@ +--- +name: Build + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + +jobs: + Release: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.8.2 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + - name: Build and Run Unit Tests + run: bazel build -c opt //... + NoSTD: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.8.2 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + - name: Build and Run Unit Tests + run: bazel build --copt=-DRAD_NO_STD //... diff --git a/.github/workflows/cache-cleanup.yaml b/.github/workflows/cache-cleanup.yaml new file mode 100644 index 0000000..d793a40 --- /dev/null +++ b/.github/workflows/cache-cleanup.yaml @@ -0,0 +1,35 @@ +--- +name: Cleanup Branch Caches + +on: + pull_request: + types: + - closed + workflow_dispatch: + +# yamllint disable rule: +jobs: + Cleanup: + runs-on: ubuntu-latest + steps: + - name: Cleanup Caches + # yamllint disable rule:line-length + run: | + gh extension install actions/gh-actions-cache + + echo "Fetching list of cache key" + cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 ) + + ## Setting this to not fail the workflow while deleting cache keys. + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeysForPR + do + gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm + done + echo "Done" + # yamllint enable rule:line-length + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: refs/pull/${{ github.event.pull_request.number }}/merge diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml new file mode 100644 index 0000000..4a4e28e --- /dev/null +++ b/.github/workflows/cla.yaml @@ -0,0 +1,38 @@ +--- +name: "CLA Assistant" + +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +permissions: + actions: write + checks: read + contents: read + issues: read + discussions: read + pull-requests: write + statuses: read + +# yamllint disable rule:line-length +jobs: + CLAssistant: + if: ${{ github.event_name == 'pull_request_target' || github.event.issue.pull_request }} + runs-on: ubuntu-latest + steps: + - name: "CLA Assistant" + if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' + uses: cla-assistant/github-action@dbc1c64d82d3aad5072007a41fff2828ae6d23ec + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_ASSISTANT_TOKEN }} + with: + path-to-signatures: 'radiant-cpp/cla-signatures.json' + path-to-document: 'https://github.com/archonitelabs/radiant-cpp/blob/main/CLA.md' + branch: 'main' + remote-organization-name: 'archonitelabs' + remote-repository-name: 'archonitelabs-cla' + lock-pullrequest-aftermerge: false +# yamllint enable rule:line-length diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml new file mode 100644 index 0000000..57fe37c --- /dev/null +++ b/.github/workflows/code-coverage.yaml @@ -0,0 +1,43 @@ +--- +name: Code Coverage + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + +jobs: + CodeCoverage: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.8.2 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: ./tools/rad/setup.py + - name: Install Rad Tool + run: pip install -e ./tools/rad + - name: Generate Coverage + run: rad coverage --output-xml ./coverage.xml -vv + - name: Upload Coverage + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + slug: archonitelabs/radiant-cpp + fail_ci_if_error: true + verbose: true + files: ./coverage.xml diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml new file mode 100644 index 0000000..d841c7f --- /dev/null +++ b/.github/workflows/lint.yaml @@ -0,0 +1,29 @@ +--- +name: Lint + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + +jobs: + Lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + cache: 'pip' + cache-dependency-path: ./tools/rad/setup.py + - name: Install Rad Tool + run: pip install -e ./tools/rad + - name: Run Linter + run: rad lint --all-files --skip no-commit-to-branch diff --git a/.github/workflows/unit-tests.yaml b/.github/workflows/unit-tests.yaml new file mode 100644 index 0000000..d33e767 --- /dev/null +++ b/.github/workflows/unit-tests.yaml @@ -0,0 +1,55 @@ +--- +name: Unit Tests + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + workflow_dispatch: + +permissions: + contents: read + +jobs: + Linux: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.8.2 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + - name: Build and Run Unit Tests + run: bazel test --test_output=all //... + Mac: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.8.2 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + - name: Build and Run Unit Tests + run: bazel test --test_output=all //... + Windows: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@0.8.2 + with: + bazelisk-cache: true + disk-cache: ${{ github.workflow }} + repository-cache: true + - name: Build and Run Unit Tests + # extra slash is an intentional hack, powershell converts a // to / + run: bazel test --test_output=all ///... + shell: bash diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad2ca6e --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +*.orig +/compile_commands.json + +# vscode +/.vscode/c_cpp_properties.json +/.vscode/launch.json + +# python +__pycache__/ +.env +.venv +env/ +venv/ +env.bak/ +venv.bak/ + +# bazel +/bazel-* +/external diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..fbd4d5f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +--- +fail_fast: false +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: c4a0b883114b00d8d76b479c820ce7950211c99b # v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: mixed-line-ending + - id: no-commit-to-branch + args: [--branch, main] + - repo: https://github.com/pocc/pre-commit-hooks + rev: 9a9bbc00895bbdb7670231c2565d4f1309c42905 # v1.3.5 + hooks: + - id: clang-format + - repo: https://github.com/psf/black + rev: ec91a2be3c44d88e1a3960a4937ad6ed3b63464e # 23.12.1 + hooks: + - id: black + - repo: https://github.com/adrienverge/yamllint + rev: 81e9f98ffd059efe8aa9c1b1a42e5cce61b640c6 # v1.35.1 + hooks: + - id: yamllint + - repo: local + hooks: + - id: pylint + name: pylint + entry: pylint + language: system + types: [python] + require_serial: true diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d39b3fe --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "ms-vscode.cpptools", + "matepek.vscode-catch2-test-adapter", + "bazelbuild.vscode-bazel", + "ryanluker.vscode-coverage-gutters", + "github.vscode-pull-request-github", + "xaver.clang-format", + "ms-python.python", + "ms-python.pylint" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..2f80252 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,24 @@ +{ + "editor.rulers": [ + 80, + 100 + ], + "C_Cpp.default.compileCommands": "${workspaceFolder}/compile_commands.json", + "testMate.cpp.test.executables": "bazel-bin/test/*_test*", + "coverage-gutters.customizable.status-bar-toggler-watchCoverageAndVisibleEditors-enabled": true, + "coverage-gutters.showGutterCoverage": false, + "coverage-gutters.showLineCoverage": true, + "coverage-gutters.coverageFileNames": [ + "bazel-out/coverage.xml", + "bazel-out/_coverage/_coverage_report.dat" + ], + "[c]": { + "editor.defaultFormatter": "xaver.clang-format" + }, + "[cpp]": { + "editor.defaultFormatter": "xaver.clang-format" + }, + "[python]": { + "editor.defaultFormatter": "ms-python.black-formatter" + } +} diff --git a/.yamllint.yaml b/.yamllint.yaml new file mode 100644 index 0000000..c8b5f42 --- /dev/null +++ b/.yamllint.yaml @@ -0,0 +1,5 @@ +--- +extends: default + +rules: + truthy: disable diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..672060c --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +# This is the list of Radiant authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Archonite Labs diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 0000000..e1b96dd --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,79 @@ +package(default_visibility = ["//visibility:public"]) + +exports_files([ + "AUTHORS", + "LICENSE", +]) + +config_setting( + name = "msvc", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "msvc-cl", + }, + visibility = [":__subpackages__"], +) + +config_setting( + name = "gcc", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "gcc", + }, + visibility = [":__subpackages__"], +) + +config_setting( + name = "clang", + flag_values = { + "@bazel_tools//tools/cpp:compiler": "clang", + }, + visibility = [":__subpackages__"], +) + +filegroup( + name = "radiant-hdrs", + srcs = glob([ + "radiant/**/*.h", + "radiant/**/*.hpp", + ]), +) + +cc_library( + name = "radiant", + hdrs = [":radiant-hdrs"], +) + +platform( + name = "windows_x86", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:x86_32", + ], + visibility = [":__subpackages__"], +) + +platform( + name = "windows_x64", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:x86_64", + ], + visibility = [":__subpackages__"], +) + +platform( + name = "windows_arm", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:arm", + ], + visibility = [":__subpackages__"], +) + +platform( + name = "windows_arm64", + constraint_values = [ + "@platforms//os:windows", + "@platforms//cpu:arm64", + ], + visibility = [":__subpackages__"], +) diff --git a/CLA.md b/CLA.md new file mode 100644 index 0000000..6e562f5 --- /dev/null +++ b/CLA.md @@ -0,0 +1,89 @@ +# Contribution License Agreement + +This Contribution License Agreement (“Agreement”) is agreed to by the party signing (“You”), and +conveys certain license rights to Archonite Labs and its affiliates for Your contributions to +Archonite Labs open source projects. This Agreement is effective as of the latest signature date. + +1. **Definitions**. “**Code**” means the computer software code, whether in human-readable or +machine-executable form, that is delivered by You to Archonite Labs under this Agreement. +“**Project**” means the Radiant project owned or managed by Archonite Labs and offered under the +terms of the following license(s) Apache-2.0 (including any right to adopt any future version of a +license if permitted). “**Submit**” is the act of uploading, submitting, transmitting, or +distributing code or other content to any Project, 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 Project for the purpose of discussing and improving that Project, but +excluding communication that is conspicuously marked or otherwise designated in writing by You as +“Not a Submission.” “**Submission**” means the Code and any other copyrightable material Submitted +by You, including any associated comments and +documentation. + +2. **Your Submission**. You must agree to the terms of this Agreement before making a Submission +to any Project. This Agreement covers any and all Submissions that You, now or in the future +(except as described in Section 4 below), Submit to any Project. + +3. **Originality of Work**. You represent that each of Your Submissions is entirely Your original +work. Should You wish to Submit materials that are not Your original work, You may Submit them +separately to the Project if You (a) retain all copyright and license information that was in the +materials as You received them, (b) in the description accompanying Your Submission, include the +phrase “Submission containing materials of a third party:” followed by the names of the third +party and any licenses or other restrictions of which You are aware, and (c) follow any other +instructions in the Project’s written guidelines concerning Submissions. + +4. **Your Employer**. References to “employer” in this Agreement include Your employer or anyone +else for whom You are acting in making Your Submission, e.g. as a contractor, vendor, or agent. +If Your Submission is made in the course of Your work for an employer or Your employer has +intellectual property rights in Your Submission by contract or applicable law, You must secure +permission from Your employer to make the Submission before signing this Agreement. In that case, +the term “You” in this Agreement will refer to You and the employer collectively. If You change +employers in the future and desire to Submit additional Submissions for the new employer, then +You agree to sign a new Agreement and secure permission from the new employer before Submitting +those Submissions. + +5. **Licenses**. + + - **Copyright License**. You grant Archonite Labs, and those who receive the Submission directly + or indirectly from Archonite Labs, a perpetual, worldwide, non-exclusive, royalty-free, + irrevocable license in the Submission to reproduce, prepare derivative works of, publicly + display, publicly perform, and distribute the Submission and such derivative works, and to + sublicense any or all of the foregoing rights to third parties. + + - **Patent License**. You grant Archonite Labs, and those who receive the Submission directly or + indirectly from Archonite Labs, a perpetual, worldwide, non-exclusive, royalty-free, irrevocable + license under Your patent claims that are necessarily infringed by the Submission or the + combination of the Submission with the Project to which it was Submitted to make, have made, + use, offer to sell, sell and import or otherwise dispose of the Submission alone or with the + Project. + + - **Other Rights Reserved**. Each party reserves all rights not expressly granted in this + Agreement. No additional licenses or rights whatsoever (including, without limitation, any + implied licenses) are granted by implication, exhaustion, estoppel or otherwise. + +6. **Representations and Warranties**. You represent that You are legally entitled to grant the +above licenses. You represent that each of Your Submissions is entirely Your original work (except +as You may have disclosed under Section 3). You represent that You have secured permission from +Your employer to make the Submission in cases where Your Submission is made in the course of Your +work for Your employer or Your employer has intellectual property rights in Your Submission by +contract or applicable law. If You are signing this Agreement on behalf of Your employer, You +represent and warrant that You have the necessary authority to bind the listed employer to the +obligations contained in this Agreement. You are not expected to provide support for Your +Submission, unless You choose to do so. UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, +AND EXCEPT FOR THE WARRANTIES EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE SUBMISSION PROVIDED +UNDER THIS AGREEMENT IS PROVIDED WITHOUT WARRANTY OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY +WARRANTY OF NONINFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. + +7. **Notice to Archonite Labs**. You agree to notify Archonite Labs in writing of any facts or +circumstances of which You later become aware that would make Your representations in this Agreement +inaccurate in any respect. + +8. **Information about Submissions**. You agree that contributions to Projects and information +about contributions may be maintained indefinitely and disclosed publicly, including Your name +and other information that You submit with Your Submission. + +9. **Governing Law/Jurisdiction**. This Agreement and all disputes, claims, actions, suits or +other proceedings arising out of this agreement or relating in any way to it shall be governed by +the laws of United States of America excluding its private international law provisions. + +10. **Entire Agreement/Assignment**. This Agreement is the entire agreement between the parties, +and supersedes any and all prior agreements, understandings or communications, written or oral, +between the parties relating to the subject matter hereof. This Agreement may be assigned by +Archonite Labs. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8399c8e --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,132 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +johnny.shaw@live.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..df36e38 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# How to contribute to Radiant + +Please read the [Code of Conduct](CODE_OF_CONDUCT.md). + +## Reporting Bugs + +* Do not open a GitHub issue if the bug is a security vulnerability in Radiant, + and instead refer to our [security policy](SECURITY.md). +* Ensure the bug was not already reported by searching + [issues][github.issues.bug]. +* If you're unable to find an open issue addressing the problem, + [open one][github.issues.bug.new]. + +## Requesting Features + +* Feature requests are welcome. But take a moment to find out whether your idea + fits with the scope of the project. Features always deserve discussions before + implementation. We strongly encourage opening or joining discussions on + existing requests. +* Ensure the feature request ("enhancement") was not already requested by + searching [issues][github.issues.enhancement] and does not + have an active [discussion][github.discussions]. +* If you're unable to find an open issue or discussion: + * Consider, [starting a discussion][github.discussions] (optional). + * Otherwise, [open a feature request][github.issues.enhancement.new]. + +## Development Setup + +* Fork the repository on GitHub and clone it. +* Radiant uses Bazel as the primary build system. Install Bazel + ([official instructions][bazel.install]). [Bazelisk][bazel.bazelisk] is + recommended. +* Python is required for tooling. Install Python + ([official instructions][python.install]). +* Radiant uses python package to aid in the remaining setup and subsequent + workflows. Install it into your python environment by running: + `python -m pip install -e ./tools/rad`. +* Initialize the development environment: `rad init`. +* Build the project: `rad build`. +* Run the tests: `rad test`. +* Check out `rad --help` for more information. 😎 🎉 + +## Pull Requests + +* After opening a pull request you will be required to sign the + [Contributor License Agreement](CLA.md). +* The changes must be tested and covered by unit tests, as appropriate. +* The pull request must pass the required checks. +* Open a new pull request with the patch. + * Ensure the pull request clearly describes the subject/problem and solution. + * Include the relevant issue number if applicable. + +[//]: # (Hyperlink IDs) +[github.issues.bug]: https://github.com/archonitelabs/radiant-cpp/issues?q=is%3Aissue+label%3Abug+ +[github.issues.bug.new]: https://github.com/archonitelabs/radiant-cpp/issues/new?assignees=&labels=bug&projects=&template=01-bug-report.yml +[github.issues.enhancement]: https://github.com/archonitelabs/radiant-cpp/issues?q=is%3Aissue+label%3Aenhancement +[github.issues.enhancement.new]: https://github.com/archonitelabs/radiant-cpp/issues/new?assignees=&labels=enhancement&projects=&template=02-feature-request.yml +[github.discussions]: https://github.com/archonitelabs/radiant-cpp/discussions +[bazel.install]: https://docs.bazel.build/versions/main/install.html +[bazel.bazelisk]: https://bazel.build/install/bazelisk +[python.install]: https://www.python.org/downloads/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,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 [yyyy] [name of copyright owner] + + 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 --git a/MODULE.bazel b/MODULE.bazel new file mode 100644 index 0000000..6994b3c --- /dev/null +++ b/MODULE.bazel @@ -0,0 +1,29 @@ +# Copyright 2023 The Radiant Authors. +# +# 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. + +# https://bazel.build/external/overview#bzlmod + +module(name = "radiant") + +bazel_dep(name = "googletest", version = "1.14.0", dev_dependency = True) +bazel_dep(name = "google_benchmark", version = "1.8.3", dev_dependency = True) + +# Hedron's Compile Commands Extractor for Bazel +# https://github.com/hedronvision/bazel-compile-commands-extractor +bazel_dep(name = "hedron_compile_commands", dev_dependency = True) +git_override( + module_name = "hedron_compile_commands", + remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git", + commit = "a14ad3a64e7bf398ab48105aaa0348e032ac87f8", +) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock new file mode 100644 index 0000000..a6ec07b --- /dev/null +++ b/MODULE.bazel.lock @@ -0,0 +1,2133 @@ +{ + "lockFileVersion": 6, + "moduleFileHash": "275108e4d79dafea614d4d2ebe0a1f5bd2f49d8465e91bd5f5b9cb7cd268de5c", + "flags": { + "cmdRegistries": [ + "https://bcr.bazel.build/" + ], + "cmdModuleOverrides": {}, + "allowedYankedVersions": [], + "envVarAllowedYankedVersions": "", + "ignoreDevDependency": false, + "directDependenciesMode": "WARNING", + "compatibilityMode": "ERROR" + }, + "localOverrideHashes": { + "bazel_tools": "1ae69322ac3823527337acf02016e8ee95813d8d356f47060255b8956fa642f0" + }, + "moduleDepGraph": { + "": { + "name": "radiant", + "version": "", + "key": "", + "repoName": "radiant", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "googletest": "googletest@1.14.0", + "google_benchmark": "google_benchmark@1.8.3", + "hedron_compile_commands": "hedron_compile_commands@_", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + } + }, + "googletest@1.14.0": { + "name": "googletest", + "version": "1.14.0", + "key": "googletest@1.14.0", + "repoName": "googletest", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "com_google_absl": "abseil-cpp@20230125.1", + "platforms": "platforms@0.0.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/google/googletest/archive/refs/tags/v1.14.0.tar.gz" + ], + "integrity": "sha256-itWYxzrXluDYKAsILOvYKmMNc+c808cAV5OKZQG7pdc=", + "strip_prefix": "googletest-1.14.0", + "remote_patches": { + "https://bcr.bazel.build/modules/googletest/1.14.0/patches/module_dot_bazel.patch": "sha256-CSomzvti38LCuURDG5EEoa3O1tQF3cKKt/mknnP1qcc=" + }, + "remote_patch_strip": 0 + } + } + }, + "google_benchmark@1.8.3": { + "name": "google_benchmark", + "version": "1.8.3", + "key": "google_benchmark@1.8.3", + "repoName": "google_benchmark", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "platforms": "platforms@0.0.7", + "rules_foreign_cc": "rules_foreign_cc@0.9.0", + "rules_cc": "rules_cc@0.0.9", + "libpfm": "libpfm@4.11.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/google/benchmark/archive/refs/tags/v1.8.3.tar.gz" + ], + "integrity": "sha256-a8GApX0j1NlRVRn5KwyD1hsFtbqxiJYfNqx7BrDZ6c4=", + "strip_prefix": "benchmark-1.8.3", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "hedron_compile_commands@_": { + "name": "hedron_compile_commands", + "version": "", + "key": "hedron_compile_commands@_", + "repoName": "hedron_compile_commands", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@hedron_compile_commands//:workspace_setup.bzl", + "extensionName": "hedron_compile_commands_extension", + "usingModule": "hedron_compile_commands@_", + "location": { + "file": "@@hedron_compile_commands~//:MODULE.bazel", + "line": 3, + "column": 18 + }, + "imports": {}, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@hedron_compile_commands//:workspace_setup_transitive.bzl", + "extensionName": "hedron_compile_commands_extension", + "usingModule": "hedron_compile_commands@_", + "location": { + "file": "@@hedron_compile_commands~//:MODULE.bazel", + "line": 4, + "column": 19 + }, + "imports": {}, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@hedron_compile_commands//:workspace_setup_transitive_transitive.bzl", + "extensionName": "hedron_compile_commands_extension", + "usingModule": "hedron_compile_commands@_", + "location": { + "file": "@@hedron_compile_commands~//:MODULE.bazel", + "line": 5, + "column": 20 + }, + "imports": {}, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@hedron_compile_commands//:workspace_setup_transitive_transitive_transitive.bzl", + "extensionName": "hedron_compile_commands_extension", + "usingModule": "hedron_compile_commands@_", + "location": { + "file": "@@hedron_compile_commands~//:MODULE.bazel", + "line": 6, + "column": 21 + }, + "imports": {}, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + } + }, + "bazel_tools@_": { + "name": "bazel_tools", + "version": "", + "key": "bazel_tools@_", + "repoName": "bazel_tools", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@local_config_cc_toolchains//:all", + "@local_config_sh//:local_sh_toolchain" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl", + "extensionName": "cc_configure_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 18, + "column": 29 + }, + "imports": { + "local_config_cc": "local_config_cc", + "local_config_cc_toolchains": "local_config_cc_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/osx:xcode_configure.bzl", + "extensionName": "xcode_configure_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 22, + "column": 32 + }, + "imports": { + "local_config_xcode": "local_config_xcode" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@rules_java//java:extensions.bzl", + "extensionName": "toolchains", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 25, + "column": 32 + }, + "imports": { + "local_jdk": "local_jdk", + "remote_java_tools": "remote_java_tools", + "remote_java_tools_linux": "remote_java_tools_linux", + "remote_java_tools_windows": "remote_java_tools_windows", + "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64", + "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/sh:sh_configure.bzl", + "extensionName": "sh_configure_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 36, + "column": 39 + }, + "imports": { + "local_config_sh": "local_config_sh" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/test:extensions.bzl", + "extensionName": "remote_coverage_tools_extension", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 40, + "column": 48 + }, + "imports": { + "remote_coverage_tools": "remote_coverage_tools" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@bazel_tools//tools/android:android_extensions.bzl", + "extensionName": "remote_android_tools_extensions", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 43, + "column": 42 + }, + "imports": { + "android_gmaven_r8": "android_gmaven_r8", + "android_tools": "android_tools" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@buildozer//:buildozer_binary.bzl", + "extensionName": "buildozer_binary", + "usingModule": "bazel_tools@_", + "location": { + "file": "@@bazel_tools//:MODULE.bazel", + "line": 47, + "column": 33 + }, + "imports": { + "buildozer_binary": "buildozer_binary" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "rules_cc": "rules_cc@0.0.9", + "rules_java": "rules_java@7.4.0", + "rules_license": "rules_license@0.0.7", + "rules_proto": "rules_proto@5.3.0-21.7", + "rules_python": "rules_python@0.22.1", + "buildozer": "buildozer@6.4.0.2", + "platforms": "platforms@0.0.7", + "com_google_protobuf": "protobuf@21.7", + "zlib": "zlib@1.3", + "build_bazel_apple_support": "apple_support@1.5.0", + "local_config_platform": "local_config_platform@_" + } + }, + "local_config_platform@_": { + "name": "local_config_platform", + "version": "", + "key": "local_config_platform@_", + "repoName": "local_config_platform", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_" + } + }, + "abseil-cpp@20230125.1": { + "name": "abseil-cpp", + "version": "20230125.1", + "key": "abseil-cpp@20230125.1", + "repoName": "abseil-cpp", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "rules_cc": "rules_cc@0.0.9", + "platforms": "platforms@0.0.7", + "bazel_skylib": "bazel_skylib@1.4.1", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/abseil/abseil-cpp/archive/refs/tags/20230125.1.tar.gz" + ], + "integrity": "sha256-gTEcF1mbNxIGne0gzKCaYqsL8qid+haZN4bIeCt+0UU=", + "strip_prefix": "abseil-cpp-20230125.1", + "remote_patches": { + "https://bcr.bazel.build/modules/abseil-cpp/20230125.1/patches/module_dot_bazel.patch": "sha256-L1wChhBmDOnRbPbD4MENVXHjOBT2KFrDxT6D+aoThxk=" + }, + "remote_patch_strip": 0 + } + } + }, + "platforms@0.0.7": { + "name": "platforms", + "version": "0.0.7", + "key": "platforms@0.0.7", + "repoName": "platforms", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "rules_license": "rules_license@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/platforms/releases/download/0.0.7/platforms-0.0.7.tar.gz" + ], + "integrity": "sha256-OlYcmee9vpFzqmU/1Xn+hJ8djWc5V4CrR3Cx84FDHVE=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_cc@0.0.9": { + "name": "rules_cc", + "version": "0.0.9", + "key": "rules_cc@0.0.9", + "repoName": "rules_cc", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@local_config_cc_toolchains//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@bazel_tools//tools/cpp:cc_configure.bzl", + "extensionName": "cc_configure_extension", + "usingModule": "rules_cc@0.0.9", + "location": { + "file": "https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel", + "line": 9, + "column": 29 + }, + "imports": { + "local_config_cc_toolchains": "local_config_cc_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_cc/releases/download/0.0.9/rules_cc-0.0.9.tar.gz" + ], + "integrity": "sha256-IDeHW5pEVtzkp50RKorohbvEqtlo5lh9ym5k86CQDN8=", + "strip_prefix": "rules_cc-0.0.9", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_cc/0.0.9/patches/module_dot_bazel_version.patch": "sha256-mM+qzOI0SgAdaJBlWOSMwMPKpaA9b7R37Hj/tp5bb4g=" + }, + "remote_patch_strip": 0 + } + } + }, + "bazel_skylib@1.4.1": { + "name": "bazel_skylib", + "version": "1.4.1", + "key": "bazel_skylib@1.4.1", + "repoName": "bazel_skylib", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "//toolchains/unittest:cmd_toolchain", + "//toolchains/unittest:bash_toolchain" + ], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.1/bazel-skylib-1.4.1.tar.gz" + ], + "integrity": "sha256-uKFSeQF3QYCvx5iusoxGNL3M8ZxNmOe90c550f6aqtc=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_foreign_cc@0.9.0": { + "name": "rules_foreign_cc", + "version": "0.9.0", + "key": "rules_foreign_cc@0.9.0", + "repoName": "rules_foreign_cc", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@rules_foreign_cc_framework_toolchain_freebsd//:toolchain", + "@rules_foreign_cc_framework_toolchain_linux//:toolchain", + "@rules_foreign_cc_framework_toolchain_macos//:toolchain", + "@rules_foreign_cc_framework_toolchain_windows//:toolchain", + "@rules_foreign_cc//toolchains:built_make_toolchain", + "@rules_foreign_cc//toolchains:preinstalled_autoconf_toolchain", + "@rules_foreign_cc//toolchains:preinstalled_automake_toolchain", + "@rules_foreign_cc//toolchains:preinstalled_m4_toolchain", + "@rules_foreign_cc//toolchains:preinstalled_pkgconfig_toolchain", + "@cmake_3.23.2_toolchains//:all", + "@ninja_1.11.0_toolchains//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_foreign_cc//foreign_cc:extensions.bzl", + "extensionName": "ext", + "usingModule": "rules_foreign_cc@0.9.0", + "location": { + "file": "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel", + "line": 13, + "column": 20 + }, + "imports": { + "cmake_3.23.2_toolchains": "cmake_3.23.2_toolchains", + "rules_foreign_cc_framework_toolchain_freebsd": "rules_foreign_cc_framework_toolchain_freebsd", + "rules_foreign_cc_framework_toolchain_linux": "rules_foreign_cc_framework_toolchain_linux", + "rules_foreign_cc_framework_toolchain_macos": "rules_foreign_cc_framework_toolchain_macos", + "rules_foreign_cc_framework_toolchain_windows": "rules_foreign_cc_framework_toolchain_windows", + "cmake_src": "cmake_src", + "gnumake_src": "gnumake_src", + "ninja_build_src": "ninja_build_src", + "ninja_1.11.0_toolchains": "ninja_1.11.0_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_foreign_cc/archive/refs/tags/0.9.0.tar.gz" + ], + "integrity": "sha256-Kk0HzWSwcZs5p8EiGKPlB2crgql7mMaonThWWJTPfFE=", + "strip_prefix": "rules_foreign_cc-0.9.0", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/patches/examples.patch": "sha256-RxT7rVHxO30W350sYu7ybi4rStwoB8b8mr34ZU9ciIk=", + "https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/patches/module_dot_bazel.patch": "sha256-VTNnq8ySdeo9pI4rrJ+EXa/9ZACgQQ4baUwoQpljzCM=" + }, + "remote_patch_strip": 1 + } + } + }, + "libpfm@4.11.0": { + "name": "libpfm", + "version": "4.11.0", + "key": "libpfm@4.11.0", + "repoName": "libpfm", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "rules_foreign_cc": "rules_foreign_cc@0.9.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://sourceforge.net/projects/perfmon2/files/libpfm4/libpfm-4.11.0.tar.gz" + ], + "integrity": "sha256-XaX4hyveFLNjTJaI2YD2i9ootRAmhyPMEpc+7bq5/sw=", + "strip_prefix": "libpfm-4.11.0", + "remote_patches": { + "https://bcr.bazel.build/modules/libpfm/4.11.0/patches/module_dot_bazel.patch": "sha256-G0wQJ2mVEoW/L5LGzmbNfuZaxI2+9NDuWJtqvCpM1pc=", + "https://bcr.bazel.build/modules/libpfm/4.11.0/patches/add_build_file.patch": "sha256-E61d/qQgmeOcUliWaveHPp1EZoOjkvZJsqhGhHofqUg=" + }, + "remote_patch_strip": 0 + } + } + }, + "rules_java@7.4.0": { + "name": "rules_java", + "version": "7.4.0", + "key": "rules_java@7.4.0", + "repoName": "rules_java", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "//toolchains:all", + "@local_jdk//:runtime_toolchain_definition", + "@local_jdk//:bootstrap_runtime_toolchain_definition", + "@remotejdk11_linux_toolchain_config_repo//:all", + "@remotejdk11_linux_aarch64_toolchain_config_repo//:all", + "@remotejdk11_linux_ppc64le_toolchain_config_repo//:all", + "@remotejdk11_linux_s390x_toolchain_config_repo//:all", + "@remotejdk11_macos_toolchain_config_repo//:all", + "@remotejdk11_macos_aarch64_toolchain_config_repo//:all", + "@remotejdk11_win_toolchain_config_repo//:all", + "@remotejdk11_win_arm64_toolchain_config_repo//:all", + "@remotejdk17_linux_toolchain_config_repo//:all", + "@remotejdk17_linux_aarch64_toolchain_config_repo//:all", + "@remotejdk17_linux_ppc64le_toolchain_config_repo//:all", + "@remotejdk17_linux_s390x_toolchain_config_repo//:all", + "@remotejdk17_macos_toolchain_config_repo//:all", + "@remotejdk17_macos_aarch64_toolchain_config_repo//:all", + "@remotejdk17_win_toolchain_config_repo//:all", + "@remotejdk17_win_arm64_toolchain_config_repo//:all", + "@remotejdk21_linux_toolchain_config_repo//:all", + "@remotejdk21_linux_aarch64_toolchain_config_repo//:all", + "@remotejdk21_macos_toolchain_config_repo//:all", + "@remotejdk21_macos_aarch64_toolchain_config_repo//:all", + "@remotejdk21_win_toolchain_config_repo//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_java//java:extensions.bzl", + "extensionName": "toolchains", + "usingModule": "rules_java@7.4.0", + "location": { + "file": "https://bcr.bazel.build/modules/rules_java/7.4.0/MODULE.bazel", + "line": 19, + "column": 27 + }, + "imports": { + "remote_java_tools": "remote_java_tools", + "remote_java_tools_linux": "remote_java_tools_linux", + "remote_java_tools_windows": "remote_java_tools_windows", + "remote_java_tools_darwin_x86_64": "remote_java_tools_darwin_x86_64", + "remote_java_tools_darwin_arm64": "remote_java_tools_darwin_arm64", + "local_jdk": "local_jdk", + "remotejdk11_linux_toolchain_config_repo": "remotejdk11_linux_toolchain_config_repo", + "remotejdk11_linux_aarch64_toolchain_config_repo": "remotejdk11_linux_aarch64_toolchain_config_repo", + "remotejdk11_linux_ppc64le_toolchain_config_repo": "remotejdk11_linux_ppc64le_toolchain_config_repo", + "remotejdk11_linux_s390x_toolchain_config_repo": "remotejdk11_linux_s390x_toolchain_config_repo", + "remotejdk11_macos_toolchain_config_repo": "remotejdk11_macos_toolchain_config_repo", + "remotejdk11_macos_aarch64_toolchain_config_repo": "remotejdk11_macos_aarch64_toolchain_config_repo", + "remotejdk11_win_toolchain_config_repo": "remotejdk11_win_toolchain_config_repo", + "remotejdk11_win_arm64_toolchain_config_repo": "remotejdk11_win_arm64_toolchain_config_repo", + "remotejdk17_linux_toolchain_config_repo": "remotejdk17_linux_toolchain_config_repo", + "remotejdk17_linux_aarch64_toolchain_config_repo": "remotejdk17_linux_aarch64_toolchain_config_repo", + "remotejdk17_linux_ppc64le_toolchain_config_repo": "remotejdk17_linux_ppc64le_toolchain_config_repo", + "remotejdk17_linux_s390x_toolchain_config_repo": "remotejdk17_linux_s390x_toolchain_config_repo", + "remotejdk17_macos_toolchain_config_repo": "remotejdk17_macos_toolchain_config_repo", + "remotejdk17_macos_aarch64_toolchain_config_repo": "remotejdk17_macos_aarch64_toolchain_config_repo", + "remotejdk17_win_toolchain_config_repo": "remotejdk17_win_toolchain_config_repo", + "remotejdk17_win_arm64_toolchain_config_repo": "remotejdk17_win_arm64_toolchain_config_repo", + "remotejdk21_linux_toolchain_config_repo": "remotejdk21_linux_toolchain_config_repo", + "remotejdk21_linux_aarch64_toolchain_config_repo": "remotejdk21_linux_aarch64_toolchain_config_repo", + "remotejdk21_macos_toolchain_config_repo": "remotejdk21_macos_toolchain_config_repo", + "remotejdk21_macos_aarch64_toolchain_config_repo": "remotejdk21_macos_aarch64_toolchain_config_repo", + "remotejdk21_win_toolchain_config_repo": "remotejdk21_win_toolchain_config_repo" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "platforms": "platforms@0.0.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_skylib": "bazel_skylib@1.4.1", + "rules_proto": "rules_proto@5.3.0-21.7", + "rules_license": "rules_license@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_java/releases/download/7.4.0/rules_java-7.4.0.tar.gz" + ], + "integrity": "sha256-l27wi0nJKXQfIBeQ5Z44B8cq2B9CjIvJU82+/1/tFes=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_license@0.0.7": { + "name": "rules_license", + "version": "0.0.7", + "key": "rules_license@0.0.7", + "repoName": "rules_license", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_license/releases/download/0.0.7/rules_license-0.0.7.tar.gz" + ], + "integrity": "sha256-RTHezLkTY5ww5cdRKgVNXYdWmNrrddjPkPKEN1/nw2A=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_proto@5.3.0-21.7": { + "name": "rules_proto", + "version": "5.3.0-21.7", + "key": "rules_proto@5.3.0-21.7", + "repoName": "rules_proto", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "com_google_protobuf": "protobuf@21.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz" + ], + "integrity": "sha256-3D+yBqLLNEG0heseQjFlsjEjWh6psDG0Qzz3vB+kYN0=", + "strip_prefix": "rules_proto-5.3.0-21.7", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_python@0.22.1": { + "name": "rules_python", + "version": "0.22.1", + "key": "rules_python@0.22.1", + "repoName": "rules_python", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@bazel_tools//tools/python:autodetecting_toolchain" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_python//python/extensions/private:internal_deps.bzl", + "extensionName": "internal_deps", + "usingModule": "rules_python@0.22.1", + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel", + "line": 14, + "column": 30 + }, + "imports": { + "pypi__build": "pypi__build", + "pypi__click": "pypi__click", + "pypi__colorama": "pypi__colorama", + "pypi__importlib_metadata": "pypi__importlib_metadata", + "pypi__installer": "pypi__installer", + "pypi__more_itertools": "pypi__more_itertools", + "pypi__packaging": "pypi__packaging", + "pypi__pep517": "pypi__pep517", + "pypi__pip": "pypi__pip", + "pypi__pip_tools": "pypi__pip_tools", + "pypi__setuptools": "pypi__setuptools", + "pypi__tomli": "pypi__tomli", + "pypi__wheel": "pypi__wheel", + "pypi__zipp": "pypi__zipp", + "pypi__coverage_cp310_aarch64-apple-darwin": "pypi__coverage_cp310_aarch64-apple-darwin", + "pypi__coverage_cp310_aarch64-unknown-linux-gnu": "pypi__coverage_cp310_aarch64-unknown-linux-gnu", + "pypi__coverage_cp310_x86_64-apple-darwin": "pypi__coverage_cp310_x86_64-apple-darwin", + "pypi__coverage_cp310_x86_64-unknown-linux-gnu": "pypi__coverage_cp310_x86_64-unknown-linux-gnu", + "pypi__coverage_cp311_aarch64-unknown-linux-gnu": "pypi__coverage_cp311_aarch64-unknown-linux-gnu", + "pypi__coverage_cp311_x86_64-apple-darwin": "pypi__coverage_cp311_x86_64-apple-darwin", + "pypi__coverage_cp311_x86_64-unknown-linux-gnu": "pypi__coverage_cp311_x86_64-unknown-linux-gnu", + "pypi__coverage_cp38_aarch64-apple-darwin": "pypi__coverage_cp38_aarch64-apple-darwin", + "pypi__coverage_cp38_aarch64-unknown-linux-gnu": "pypi__coverage_cp38_aarch64-unknown-linux-gnu", + "pypi__coverage_cp38_x86_64-apple-darwin": "pypi__coverage_cp38_x86_64-apple-darwin", + "pypi__coverage_cp38_x86_64-unknown-linux-gnu": "pypi__coverage_cp38_x86_64-unknown-linux-gnu", + "pypi__coverage_cp39_aarch64-apple-darwin": "pypi__coverage_cp39_aarch64-apple-darwin", + "pypi__coverage_cp39_aarch64-unknown-linux-gnu": "pypi__coverage_cp39_aarch64-unknown-linux-gnu", + "pypi__coverage_cp39_x86_64-apple-darwin": "pypi__coverage_cp39_x86_64-apple-darwin", + "pypi__coverage_cp39_x86_64-unknown-linux-gnu": "pypi__coverage_cp39_x86_64-unknown-linux-gnu" + }, + "devImports": [], + "tags": [ + { + "tagName": "install", + "attributeValues": {}, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel", + "line": 15, + "column": 22 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@rules_python//python/extensions:python.bzl", + "extensionName": "python", + "usingModule": "rules_python@0.22.1", + "location": { + "file": "https://bcr.bazel.build/modules/rules_python/0.22.1/MODULE.bazel", + "line": 50, + "column": 23 + }, + "imports": { + "pythons_hub": "pythons_hub" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "platforms": "platforms@0.0.7", + "bazel_skylib": "bazel_skylib@1.4.1", + "rules_proto": "rules_proto@5.3.0-21.7", + "com_google_protobuf": "protobuf@21.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_python/releases/download/0.22.1/rules_python-0.22.1.tar.gz" + ], + "integrity": "sha256-pWQP3dS+sD6MH95e1xYMC6a9R359BIZhwwwGk2om/WM=", + "strip_prefix": "rules_python-0.22.1", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_python/0.22.1/patches/module_dot_bazel_version.patch": "sha256-3+VLDH9gYDzNI4eOW7mABC/LKxh1xqF6NhacLbNTucs=" + }, + "remote_patch_strip": 1 + } + } + }, + "buildozer@6.4.0.2": { + "name": "buildozer", + "version": "6.4.0.2", + "key": "buildozer@6.4.0.2", + "repoName": "buildozer", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@buildozer//:buildozer_binary.bzl", + "extensionName": "buildozer_binary", + "usingModule": "buildozer@6.4.0.2", + "location": { + "file": "https://bcr.bazel.build/modules/buildozer/6.4.0.2/MODULE.bazel", + "line": 7, + "column": 33 + }, + "imports": { + "buildozer_binary": "buildozer_binary" + }, + "devImports": [], + "tags": [ + { + "tagName": "buildozer", + "attributeValues": { + "sha256": { + "darwin-amd64": "d29e347ecd6b5673d72cb1a8de05bf1b06178dd229ff5eb67fad5100c840cc8e", + "darwin-arm64": "9b9e71bdbec5e7223871e913b65d12f6d8fa026684daf991f00e52ed36a6978d", + "linux-amd64": "8dfd6345da4e9042daa738d7fdf34f699c5dfce4632f7207956fceedd8494119", + "linux-arm64": "6559558fded658c8fa7432a9d011f7c4dcbac6b738feae73d2d5c352e5f605fa", + "windows-amd64": "e7f05bf847f7c3689dd28926460ce6e1097ae97380ac8e6ae7147b7b706ba19b" + }, + "version": "6.4.0" + }, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/buildozer/6.4.0.2/MODULE.bazel", + "line": 8, + "column": 27 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/fmeum/buildozer/releases/download/v6.4.0.2/buildozer-v6.4.0.2.tar.gz" + ], + "integrity": "sha256-k7tFKQMR2AygxpmZfH0yEPnQmF3efFgD9rBPkj+Yz/8=", + "strip_prefix": "buildozer-6.4.0.2", + "remote_patches": { + "https://bcr.bazel.build/modules/buildozer/6.4.0.2/patches/module_dot_bazel_version.patch": "sha256-gKANF2HMilj7bWmuXs4lbBIAAansuWC4IhWGB/CerjU=" + }, + "remote_patch_strip": 1 + } + } + }, + "protobuf@21.7": { + "name": "protobuf", + "version": "21.7", + "key": "protobuf@21.7", + "repoName": "protobuf", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_jvm_external//:extensions.bzl", + "extensionName": "maven", + "usingModule": "protobuf@21.7", + "location": { + "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel", + "line": 22, + "column": 22 + }, + "imports": { + "maven": "maven" + }, + "devImports": [], + "tags": [ + { + "tagName": "install", + "attributeValues": { + "name": "maven", + "artifacts": [ + "com.google.code.findbugs:jsr305:3.0.2", + "com.google.code.gson:gson:2.8.9", + "com.google.errorprone:error_prone_annotations:2.3.2", + "com.google.j2objc:j2objc-annotations:1.3", + "com.google.guava:guava:31.1-jre", + "com.google.guava:guava-testlib:31.1-jre", + "com.google.truth:truth:1.1.2", + "junit:junit:4.13.2", + "org.mockito:mockito-core:4.3.1" + ] + }, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/protobuf/21.7/MODULE.bazel", + "line": 24, + "column": 14 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "rules_python": "rules_python@0.22.1", + "rules_cc": "rules_cc@0.0.9", + "rules_proto": "rules_proto@5.3.0-21.7", + "rules_java": "rules_java@7.4.0", + "rules_pkg": "rules_pkg@0.7.0", + "com_google_abseil": "abseil-cpp@20230125.1", + "zlib": "zlib@1.3", + "upb": "upb@0.0.0-20220923-a547704", + "rules_jvm_external": "rules_jvm_external@4.4.2", + "com_google_googletest": "googletest@1.14.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/protocolbuffers/protobuf/releases/download/v21.7/protobuf-all-21.7.zip" + ], + "integrity": "sha256-VJOiH17T/FAuZv7GuUScBqVRztYwAvpIkDxA36jeeko=", + "strip_prefix": "protobuf-21.7", + "remote_patches": { + "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel.patch": "sha256-q3V2+eq0v2XF0z8z+V+QF4cynD6JvHI1y3kI/+rzl5s=", + "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_module_dot_bazel_for_examples.patch": "sha256-O7YP6s3lo/1opUiO0jqXYORNHdZ/2q3hjz1QGy8QdIU=", + "https://bcr.bazel.build/modules/protobuf/21.7/patches/relative_repo_names.patch": "sha256-RK9RjW8T5UJNG7flIrnFiNE9vKwWB+8uWWtJqXYT0w4=", + "https://bcr.bazel.build/modules/protobuf/21.7/patches/add_missing_files.patch": "sha256-Hyne4DG2u5bXcWHNxNMirA2QFAe/2Cl8oMm1XJdkQIY=" + }, + "remote_patch_strip": 1 + } + } + }, + "zlib@1.3": { + "name": "zlib", + "version": "1.3", + "key": "zlib@1.3", + "repoName": "zlib", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "platforms": "platforms@0.0.7", + "rules_cc": "rules_cc@0.0.9", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz" + ], + "integrity": "sha256-/wukwpIBPbwnUws6geH5qBPNOd4Byl4Pi/NVcC76WT4=", + "strip_prefix": "zlib-1.3", + "remote_patches": { + "https://bcr.bazel.build/modules/zlib/1.3/patches/add_build_file.patch": "sha256-Ei+FYaaOo7A3jTKunMEodTI0Uw5NXQyZEcboMC8JskY=", + "https://bcr.bazel.build/modules/zlib/1.3/patches/module_dot_bazel.patch": "sha256-fPWLM+2xaF/kuy+kZc1YTfW6hNjrkG400Ho7gckuyJk=" + }, + "remote_patch_strip": 0 + } + } + }, + "apple_support@1.5.0": { + "name": "apple_support", + "version": "1.5.0", + "key": "apple_support@1.5.0", + "repoName": "build_bazel_apple_support", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [ + "@local_config_apple_cc_toolchains//:all" + ], + "extensionUsages": [ + { + "extensionBzlFile": "@build_bazel_apple_support//crosstool:setup.bzl", + "extensionName": "apple_cc_configure_extension", + "usingModule": "apple_support@1.5.0", + "location": { + "file": "https://bcr.bazel.build/modules/apple_support/1.5.0/MODULE.bazel", + "line": 17, + "column": 35 + }, + "imports": { + "local_config_apple_cc": "local_config_apple_cc", + "local_config_apple_cc_toolchains": "local_config_apple_cc_toolchains" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/apple_support/releases/download/1.5.0/apple_support.1.5.0.tar.gz" + ], + "integrity": "sha256-miM41vja0yRPgj8txghKA+TQ+7J8qJLclw5okNW0gYQ=", + "strip_prefix": "", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "rules_pkg@0.7.0": { + "name": "rules_pkg", + "version": "0.7.0", + "key": "rules_pkg@0.7.0", + "repoName": "rules_pkg", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "rules_python": "rules_python@0.22.1", + "bazel_skylib": "bazel_skylib@1.4.1", + "rules_license": "rules_license@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz" + ], + "integrity": "sha256-iimOgydi7aGDBZfWT+fbWBeKqEzVkm121bdE1lWJQcI=", + "strip_prefix": "", + "remote_patches": { + "https://bcr.bazel.build/modules/rules_pkg/0.7.0/patches/module_dot_bazel.patch": "sha256-4OaEPZwYF6iC71ZTDg6MJ7LLqX7ZA0/kK4mT+4xKqiE=" + }, + "remote_patch_strip": 0 + } + } + }, + "upb@0.0.0-20220923-a547704": { + "name": "upb", + "version": "0.0.0-20220923-a547704", + "key": "upb@0.0.0-20220923-a547704", + "repoName": "upb", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "rules_proto": "rules_proto@5.3.0-21.7", + "com_google_protobuf": "protobuf@21.7", + "com_google_absl": "abseil-cpp@20230125.1", + "platforms": "platforms@0.0.7", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/protocolbuffers/upb/archive/a5477045acaa34586420942098f5fecd3570f577.tar.gz" + ], + "integrity": "sha256-z39x6v+QskwaKLSWRan/A6mmwecTQpHOcJActj5zZLU=", + "strip_prefix": "upb-a5477045acaa34586420942098f5fecd3570f577", + "remote_patches": { + "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/patches/module_dot_bazel.patch": "sha256-wH4mNS6ZYy+8uC0HoAft/c7SDsq2Kxf+J8dUakXhaB0=" + }, + "remote_patch_strip": 0 + } + } + }, + "rules_jvm_external@4.4.2": { + "name": "rules_jvm_external", + "version": "4.4.2", + "key": "rules_jvm_external@4.4.2", + "repoName": "rules_jvm_external", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [ + { + "extensionBzlFile": "@rules_jvm_external//:non-module-deps.bzl", + "extensionName": "non_module_deps", + "usingModule": "rules_jvm_external@4.4.2", + "location": { + "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel", + "line": 9, + "column": 32 + }, + "imports": { + "io_bazel_rules_kotlin": "io_bazel_rules_kotlin" + }, + "devImports": [], + "tags": [], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + }, + { + "extensionBzlFile": "@rules_jvm_external//:extensions.bzl", + "extensionName": "maven", + "usingModule": "rules_jvm_external@4.4.2", + "location": { + "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel", + "line": 16, + "column": 22 + }, + "imports": { + "rules_jvm_external_deps": "rules_jvm_external_deps" + }, + "devImports": [], + "tags": [ + { + "tagName": "install", + "attributeValues": { + "name": "rules_jvm_external_deps", + "artifacts": [ + "com.google.cloud:google-cloud-core:1.93.10", + "com.google.cloud:google-cloud-storage:1.113.4", + "com.google.code.gson:gson:2.9.0", + "org.apache.maven:maven-artifact:3.8.6", + "software.amazon.awssdk:s3:2.17.183" + ], + "lock_file": "@rules_jvm_external//:rules_jvm_external_deps_install.json" + }, + "devDependency": false, + "location": { + "file": "https://bcr.bazel.build/modules/rules_jvm_external/4.4.2/MODULE.bazel", + "line": 18, + "column": 14 + } + } + ], + "hasDevUseExtension": false, + "hasNonDevUseExtension": true + } + ], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "io_bazel_stardoc": "stardoc@0.5.1", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/rules_jvm_external/archive/refs/tags/4.4.2.zip" + ], + "integrity": "sha256-c1YC9QgT6y6pPKP15DsZWb2AshO4NqB6YqKddXZwt3s=", + "strip_prefix": "rules_jvm_external-4.4.2", + "remote_patches": {}, + "remote_patch_strip": 0 + } + } + }, + "stardoc@0.5.1": { + "name": "stardoc", + "version": "0.5.1", + "key": "stardoc@0.5.1", + "repoName": "stardoc", + "executionPlatformsToRegister": [], + "toolchainsToRegister": [], + "extensionUsages": [], + "deps": { + "bazel_skylib": "bazel_skylib@1.4.1", + "rules_java": "rules_java@7.4.0", + "bazel_tools": "bazel_tools@_", + "local_config_platform": "local_config_platform@_" + }, + "repoSpec": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/bazelbuild/stardoc/releases/download/0.5.1/stardoc-0.5.1.tar.gz" + ], + "integrity": "sha256-qoFNrgrEALurLoiB+ZFcb0fElmS/CHxAmhX5BDjSwj4=", + "strip_prefix": "", + "remote_patches": { + "https://bcr.bazel.build/modules/stardoc/0.5.1/patches/module_dot_bazel.patch": "sha256-UAULCuTpJE7SG0YrR9XLjMfxMRmbP+za3uW9ONZ5rjI=" + }, + "remote_patch_strip": 0 + } + } + } + }, + "moduleExtensions": { + "@@apple_support~//crosstool:setup.bzl%apple_cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "pMLFCYaRPkgXPQ8vtuNkMfiHfPmRBy6QJfnid4sWfv0=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_apple_cc": { + "bzlFile": "@@apple_support~//crosstool:setup.bzl", + "ruleClassName": "_apple_cc_autoconf", + "attributes": {} + }, + "local_config_apple_cc_toolchains": { + "bzlFile": "@@apple_support~//crosstool:setup.bzl", + "ruleClassName": "_apple_cc_autoconf_toolchains", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "apple_support~", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@bazel_tools//tools/cpp:cc_configure.bzl%cc_configure_extension": { + "general": { + "bzlTransitiveDigest": "PHpT2yqMGms2U4L3E/aZ+WcQalmZWm+ILdP3yiLsDhA=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_cc": { + "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl", + "ruleClassName": "cc_autoconf", + "attributes": {} + }, + "local_config_cc_toolchains": { + "bzlFile": "@@bazel_tools//tools/cpp:cc_configure.bzl", + "ruleClassName": "cc_autoconf_toolchains", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [ + [ + "bazel_tools", + "bazel_tools", + "bazel_tools" + ] + ] + } + }, + "@@bazel_tools//tools/osx:xcode_configure.bzl%xcode_configure_extension": { + "general": { + "bzlTransitiveDigest": "Qh2bWTU6QW6wkrd87qrU4YeY+SG37Nvw3A0PR4Y0L2Y=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_xcode": { + "bzlFile": "@@bazel_tools//tools/osx:xcode_configure.bzl", + "ruleClassName": "xcode_autoconf", + "attributes": { + "xcode_locator": "@bazel_tools//tools/osx:xcode_locator.m", + "remote_xcode": "" + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@bazel_tools//tools/sh:sh_configure.bzl%sh_configure_extension": { + "general": { + "bzlTransitiveDigest": "hp4NgmNjEg5+xgvzfh6L83bt9/aiiWETuNpwNuF1MSU=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "local_config_sh": { + "bzlFile": "@@bazel_tools//tools/sh:sh_configure.bzl", + "ruleClassName": "sh_config", + "attributes": {} + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@bazel_tools//tools/test:extensions.bzl%remote_coverage_tools_extension": { + "general": { + "bzlTransitiveDigest": "l5mcjH2gWmbmIycx97bzI2stD0Q0M5gpDc0aLOHKIm8=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "remote_coverage_tools": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "7006375f6756819b7013ca875eab70a541cf7d89142d9c511ed78ea4fefa38af", + "urls": [ + "https://mirror.bazel.build/bazel_coverage_output_generator/releases/coverage_output_generator-v2.6.zip" + ] + } + } + }, + "recordedRepoMappingEntries": [] + } + }, + "@@rules_foreign_cc~//foreign_cc:extensions.bzl%ext": { + "general": { + "bzlTransitiveDigest": "QbxK92//k6c63fpMer2Lkk6224s9gwYoVFFS6mdkucI=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "cmake-3.23.2-linux-aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-aarch64.tar.gz" + ], + "sha256": "f2654bf780b53f170bbbec44d8ac67d401d24788e590faa53036a89476efa91e", + "strip_prefix": "cmake-3.23.2-linux-aarch64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "rules_foreign_cc_framework_toolchain_macos": { + "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", + "ruleClassName": "framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:macos_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:macos" + ], + "target_compatible_with": [] + } + }, + "ninja_1.11.0_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-linux.zip" + ], + "sha256": "9726e730d5b8599f82654dc80265e64a10a8a817552c34153361ed0c017f9f02", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "gnumake_src": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "patches": [ + "@@rules_foreign_cc~//toolchains:make-reproducible-bootstrap.patch" + ], + "sha256": "e05fdde47c5f7ca45cb697e973894ff4f5d79e13b750ed57d7b66d8defc78e19", + "strip_prefix": "make-4.3", + "urls": [ + "https://mirror.bazel.build/ftpmirror.gnu.org/gnu/make/make-4.3.tar.gz", + "http://ftpmirror.gnu.org/gnu/make/make-4.3.tar.gz" + ] + } + }, + "ninja_1.11.0_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-win.zip" + ], + "sha256": "d0ee3da143211aa447e750085876c9b9d7bcdd637ab5b2c5b41349c617f22f3b", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja.exe\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "cmake_3.23.2_toolchains": { + "bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl", + "ruleClassName": "prebuilt_toolchains_repository", + "attributes": { + "repos": { + "cmake-3.23.2-linux-aarch64": [ + "@platforms//cpu:aarch64", + "@platforms//os:linux" + ], + "cmake-3.23.2-linux-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "cmake-3.23.2-macos-universal": [ + "@platforms//os:macos" + ], + "cmake-3.23.2-windows-i386": [ + "@platforms//cpu:x86_32", + "@platforms//os:windows" + ], + "cmake-3.23.2-windows-x86_64": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "cmake" + } + }, + "cmake_src": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "f316b40053466f9a416adf981efda41b160ca859e97f6a484b447ea299ff26aa", + "strip_prefix": "cmake-3.23.2", + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2.tar.gz" + ] + } + }, + "bazel_skylib": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.2.1/bazel-skylib-1.2.1.tar.gz" + ], + "sha256": "f7be3474d42aae265405a592bb7da8e171919d74c16f082a5457840f06054728" + } + }, + "cmake-3.23.2-macos-universal": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-macos-universal.tar.gz" + ], + "sha256": "853a0f9af148c5ef47282ffffee06c4c9f257be2635936755f39ca13c3286c88", + "strip_prefix": "cmake-3.23.2-macos-universal/CMake.app/Contents", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "rules_foreign_cc_framework_toolchain_freebsd": { + "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", + "ruleClassName": "framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:freebsd_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:freebsd" + ], + "target_compatible_with": [] + } + }, + "rules_foreign_cc_framework_toolchain_linux": { + "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", + "ruleClassName": "framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:linux_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:linux" + ], + "target_compatible_with": [] + } + }, + "rules_python": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "5fa3c738d33acca3b97622a13a741129f67ef43f5fdfcec63b29374cc0574c29", + "strip_prefix": "rules_python-0.9.0", + "url": "https://github.com/bazelbuild/rules_python/archive/refs/tags/0.9.0.tar.gz" + } + }, + "ninja_1.11.0_mac": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/ninja-build/ninja/releases/download/v1.11.0/ninja-mac.zip" + ], + "sha256": "21915277db59756bfc61f6f281c1f5e3897760b63776fd3d360f77dd7364137f", + "strip_prefix": "", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"ninja_bin\",\n srcs = [\"ninja\"],\n)\n\nnative_tool_toolchain(\n name = \"ninja_tool\",\n env = {\"NINJA\": \"$(execpath :ninja_bin)\"},\n path = \"$(execpath :ninja_bin)\",\n target = \":ninja_bin\",\n)\n" + } + }, + "ninja_build_src": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "filegroup(\n name = \"all_srcs\",\n srcs = glob([\"**\"]),\n visibility = [\"//visibility:public\"],\n)\n", + "sha256": "3c6ba2e66400fe3f1ae83deb4b235faf3137ec20bd5b08c29bfc368db143e4c6", + "strip_prefix": "ninja-1.11.0", + "urls": [ + "https://github.com/ninja-build/ninja/archive/v1.11.0.tar.gz" + ] + } + }, + "cmake-3.23.2-windows-i386": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-i386.zip" + ], + "sha256": "6a4fcd6a2315b93cb23c93507efccacc30c449c2bf98f14d6032bb226c582e07", + "strip_prefix": "cmake-3.23.2-windows-i386", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-linux-x86_64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-linux-x86_64.tar.gz" + ], + "sha256": "aaced6f745b86ce853661a595bdac6c5314a60f8181b6912a0a4920acfa32708", + "strip_prefix": "cmake-3.23.2-linux-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake\",\n target = \":cmake_data\",\n)\n" + } + }, + "cmake-3.23.2-windows-x86_64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "urls": [ + "https://github.com/Kitware/CMake/releases/download/v3.23.2/cmake-3.23.2-windows-x86_64.zip" + ], + "sha256": "2329387f3166b84c25091c86389fb891193967740c9bcf01e7f6d3306f7ffda0", + "strip_prefix": "cmake-3.23.2-windows-x86_64", + "build_file_content": "load(\"@rules_foreign_cc//toolchains/native_tools:native_tools_toolchain.bzl\", \"native_tool_toolchain\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nfilegroup(\n name = \"cmake_data\",\n srcs = glob(\n [\n \"**\",\n ],\n exclude = [\n \"WORKSPACE\",\n \"WORKSPACE.bazel\",\n \"BUILD\",\n \"BUILD.bazel\",\n ],\n ),\n)\n\nnative_tool_toolchain(\n name = \"cmake_tool\",\n path = \"bin/cmake.exe\",\n target = \":cmake_data\",\n)\n" + } + }, + "rules_foreign_cc_framework_toolchain_windows": { + "bzlFile": "@@rules_foreign_cc~//foreign_cc/private/framework:toolchain.bzl", + "ruleClassName": "framework_toolchain_repository", + "attributes": { + "commands_src": "@rules_foreign_cc//foreign_cc/private/framework/toolchains:windows_commands.bzl", + "exec_compatible_with": [ + "@platforms//os:windows" + ], + "target_compatible_with": [] + } + }, + "ninja_1.11.0_toolchains": { + "bzlFile": "@@rules_foreign_cc~//toolchains:prebuilt_toolchains_repository.bzl", + "ruleClassName": "prebuilt_toolchains_repository", + "attributes": { + "repos": { + "ninja_1.11.0_linux": [ + "@platforms//cpu:x86_64", + "@platforms//os:linux" + ], + "ninja_1.11.0_mac": [ + "@platforms//cpu:x86_64", + "@platforms//os:macos" + ], + "ninja_1.11.0_win": [ + "@platforms//cpu:x86_64", + "@platforms//os:windows" + ] + }, + "tool": "ninja" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_foreign_cc~", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_foreign_cc~", + "rules_foreign_cc", + "rules_foreign_cc~" + ] + ] + } + }, + "@@rules_java~//java:extensions.bzl%toolchains": { + "general": { + "bzlTransitiveDigest": "tJHbmWnq7m+9eUBnUdv7jZziQ26FmcGL9C5/hU3Q9UQ=", + "recordedFileInputs": {}, + "recordedDirentsInputs": {}, + "envVariables": {}, + "generatedRepoSpecs": { + "remotejdk21_linux_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux//:jdk\",\n)\n" + } + }, + "remotejdk17_linux_s390x_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_s390x//:jdk\",\n)\n" + } + }, + "remotejdk17_macos_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos//:jdk\",\n)\n" + } + }, + "remotejdk21_macos_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk17_linux_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk21_macos_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "e8260516de8b60661422a725f1df2c36ef888f6fb35393566b00e7325db3d04e", + "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-macosx_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_aarch64.tar.gz" + ] + } + }, + "remotejdk17_linux_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux//:jdk\",\n)\n" + } + }, + "remotejdk17_macos_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "314b04568ec0ae9b36ba03c9cbd42adc9e1265f74678923b19297d66eb84dcca", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_aarch64.tar.gz" + ] + } + }, + "remote_java_tools_windows": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "fe2f88169696d6c6fc6e90ba61bb46be7d0ae3693cbafdf336041bf56679e8d1", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_windows-v13.4.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_windows-v13.4.zip" + ] + } + }, + "remotejdk11_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "43408193ce2fa0862819495b5ae8541085b95660153f2adcf91a52d3a1710e83", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-win_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-win_x64.zip" + ] + } + }, + "remotejdk11_win_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "54174439f2b3fddd11f1048c397fe7bb45d4c9d66d452d6889b013d04d21c4de", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_aarch64.tar.gz" + ] + } + }, + "remotejdk17_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "b9482f2304a1a68a614dfacddcf29569a72f0fac32e6c74f83dc1b9a157b8340", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_x64.tar.gz" + ] + } + }, + "remotejdk11_linux_s390x_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:s390x\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_s390x//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux//:jdk\",\n)\n" + } + }, + "remotejdk11_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "bcaab11cfe586fae7583c6d9d311c64384354fb2638eb9a012eca4c3f1a1d9fd", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_x64.tar.gz" + ] + } + }, + "remotejdk11_win_arm64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "b8a28e6e767d90acf793ea6f5bed0bb595ba0ba5ebdf8b99f395266161e53ec2", + "strip_prefix": "jdk-11.0.13+8", + "urls": [ + "https://mirror.bazel.build/aka.ms/download-jdk/microsoft-jdk-11.0.13.8.1-windows-aarch64.zip" + ] + } + }, + "remotejdk17_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "640453e8afe8ffe0fb4dceb4535fb50db9c283c64665eebb0ba68b19e65f4b1f", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-macosx_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-macosx_x64.tar.gz" + ] + } + }, + "remotejdk21_macos": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "3ad8fe288eb57d975c2786ae453a036aa46e47ab2ac3d81538ebae2a54d3c025", + "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-macosx_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-macosx_x64.tar.gz" + ] + } + }, + "remotejdk21_macos_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_macos//:jdk\",\n)\n" + } + }, + "remotejdk17_macos_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_macos_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk17_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "192f2afca57701de6ec496234f7e45d971bf623ff66b8ee4a5c81582054e5637", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_x64.zip" + ] + } + }, + "remotejdk11_macos_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_ppc64le_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_ppc64le//:jdk\",\n)\n" + } + }, + "remotejdk21_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "5ad730fbee6bb49bfff10bf39e84392e728d89103d3474a7e5def0fd134b300a", + "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-linux_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_x64.tar.gz" + ] + } + }, + "remote_java_tools_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "ba10f09a138cf185d04cbc807d67a3da42ab13d618c5d1ce20d776e199c33a39", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_linux-v13.4.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_linux-v13.4.zip" + ] + } + }, + "remotejdk21_win": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "f7cc15ca17295e69c907402dfe8db240db446e75d3b150da7bf67243cded93de", + "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-win_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-win_x64.zip", + "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-win_x64.zip" + ] + } + }, + "remotejdk21_linux_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 21,\n)\n", + "sha256": "ce7df1af5d44a9f455617c4b8891443fbe3e4b269c777d8b82ed66f77167cfe0", + "strip_prefix": "zulu21.32.17-ca-jdk21.0.2-linux_aarch64", + "urls": [ + "https://cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz", + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu21.32.17-ca-jdk21.0.2-linux_aarch64.tar.gz" + ] + } + }, + "remotejdk11_linux_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_linux_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_s390x": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "a58fc0361966af0a5d5a31a2d8a208e3c9bb0f54f345596fd80b99ea9a39788b", + "strip_prefix": "jdk-11.0.15+10", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz", + "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_s390x_linux_hotspot_11.0.15_10.tar.gz" + ] + } + }, + "remotejdk17_linux_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "6531cef61e416d5a7b691555c8cf2bdff689201b8a001ff45ab6740062b44313", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-linux_aarch64.tar.gz" + ] + } + }, + "remotejdk17_win_arm64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win_arm64//:jdk\",\n)\n" + } + }, + "remotejdk11_linux": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "a34b404f87a08a61148b38e1416d837189e1df7a040d949e743633daf4695a3c", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-linux_x64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-linux_x64.tar.gz" + ] + } + }, + "remotejdk11_macos_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:macos\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_macos//:jdk\",\n)\n" + } + }, + "remotejdk17_linux_ppc64le_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:ppc\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_linux_ppc64le//:jdk\",\n)\n" + } + }, + "remotejdk17_win_arm64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "6802c99eae0d788e21f52d03cab2e2b3bf42bc334ca03cbf19f71eb70ee19f85", + "strip_prefix": "zulu17.44.53-ca-jdk17.0.8.1-win_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip", + "https://cdn.azul.com/zulu/bin/zulu17.44.53-ca-jdk17.0.8.1-win_aarch64.zip" + ] + } + }, + "remote_java_tools_darwin_arm64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "076a7e198ad077f8c7d997986ef5102427fae6bbfce7a7852d2e080ed8767528", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_darwin_arm64-v13.4.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_darwin_arm64-v13.4.zip" + ] + } + }, + "remotejdk17_linux_ppc64le": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "00a4c07603d0218cd678461b5b3b7e25b3253102da4022d31fc35907f21a2efd", + "strip_prefix": "jdk-17.0.8.1+1", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz", + "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_ppc64le_linux_hotspot_17.0.8.1_1.tar.gz" + ] + } + }, + "remotejdk21_linux_aarch64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:linux\", \"@platforms//cpu:aarch64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_linux_aarch64//:jdk\",\n)\n" + } + }, + "remotejdk11_win_arm64_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_11\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"11\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:arm64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk11_win_arm64//:jdk\",\n)\n" + } + }, + "local_jdk": { + "bzlFile": "@@rules_java~//toolchains:local_java_repository.bzl", + "ruleClassName": "_local_java_repository_rule", + "attributes": { + "java_home": "", + "version": "", + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = {RUNTIME_VERSION},\n)\n" + } + }, + "remote_java_tools_darwin_x86_64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "4523aec4d09c587091a2dae6f5c9bc6922c220f3b6030e5aba9c8f015913cc65", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools_darwin_x86_64-v13.4.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools_darwin_x86_64-v13.4.zip" + ] + } + }, + "remote_java_tools": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "sha256": "e025fd260ac39b47c111f5212d64ec0d00d85dec16e49368aae82fc626a940cf", + "urls": [ + "https://mirror.bazel.build/bazel_java_tools/releases/java/v13.4/java_tools-v13.4.zip", + "https://github.com/bazelbuild/java_tools/releases/download/java_v13.4/java_tools-v13.4.zip" + ] + } + }, + "remotejdk17_linux_s390x": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 17,\n)\n", + "sha256": "ffacba69c6843d7ca70d572489d6cc7ab7ae52c60f0852cedf4cf0d248b6fc37", + "strip_prefix": "jdk-17.0.8.1+1", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz", + "https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.8.1%2B1/OpenJDK17U-jdk_s390x_linux_hotspot_17.0.8.1_1.tar.gz" + ] + } + }, + "remotejdk17_win_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_17\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"17\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk17_win//:jdk\",\n)\n" + } + }, + "remotejdk11_linux_ppc64le": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "a8fba686f6eb8ae1d1a9566821dbd5a85a1108b96ad857fdbac5c1e4649fc56f", + "strip_prefix": "jdk-11.0.15+10", + "urls": [ + "https://mirror.bazel.build/github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz", + "https://github.com/adoptium/temurin11-binaries/releases/download/jdk-11.0.15+10/OpenJDK11U-jdk_ppc64le_linux_hotspot_11.0.15_10.tar.gz" + ] + } + }, + "remotejdk11_macos_aarch64": { + "bzlFile": "@@bazel_tools//tools/build_defs/repo:http.bzl", + "ruleClassName": "http_archive", + "attributes": { + "build_file_content": "load(\"@rules_java//java:defs.bzl\", \"java_runtime\")\n\npackage(default_visibility = [\"//visibility:public\"])\n\nexports_files([\"WORKSPACE\", \"BUILD.bazel\"])\n\nfilegroup(\n name = \"jre\",\n srcs = glob(\n [\n \"jre/bin/**\",\n \"jre/lib/**\",\n ],\n allow_empty = True,\n # In some configurations, Java browser plugin is considered harmful and\n # common antivirus software blocks access to npjp2.dll interfering with Bazel,\n # so do not include it in JRE on Windows.\n exclude = [\"jre/bin/plugin2/**\"],\n ),\n)\n\nfilegroup(\n name = \"jdk-bin\",\n srcs = glob(\n [\"bin/**\"],\n # The JDK on Windows sometimes contains a directory called\n # \"%systemroot%\", which is not a valid label.\n exclude = [\"**/*%*/**\"],\n ),\n)\n\n# This folder holds security policies.\nfilegroup(\n name = \"jdk-conf\",\n srcs = glob(\n [\"conf/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-include\",\n srcs = glob(\n [\"include/**\"],\n allow_empty = True,\n ),\n)\n\nfilegroup(\n name = \"jdk-lib\",\n srcs = glob(\n [\"lib/**\", \"release\"],\n allow_empty = True,\n exclude = [\n \"lib/missioncontrol/**\",\n \"lib/visualvm/**\",\n ],\n ),\n)\n\njava_runtime(\n name = \"jdk\",\n srcs = [\n \":jdk-bin\",\n \":jdk-conf\",\n \":jdk-include\",\n \":jdk-lib\",\n \":jre\",\n ],\n # Provide the 'java` binary explicitly so that the correct path is used by\n # Bazel even when the host platform differs from the execution platform.\n # Exactly one of the two globs will be empty depending on the host platform.\n # When --incompatible_disallow_empty_glob is enabled, each individual empty\n # glob will fail without allow_empty = True, even if the overall result is\n # non-empty.\n java = glob([\"bin/java.exe\", \"bin/java\"], allow_empty = True)[0],\n version = 11,\n)\n", + "sha256": "7632bc29f8a4b7d492b93f3bc75a7b61630894db85d136456035ab2a24d38885", + "strip_prefix": "zulu11.66.15-ca-jdk11.0.20-macosx_aarch64", + "urls": [ + "https://mirror.bazel.build/cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz", + "https://cdn.azul.com/zulu/bin/zulu11.66.15-ca-jdk11.0.20-macosx_aarch64.tar.gz" + ] + } + }, + "remotejdk21_win_toolchain_config_repo": { + "bzlFile": "@@rules_java~//toolchains:remote_java_repository.bzl", + "ruleClassName": "_toolchain_config", + "attributes": { + "build_file": "\nconfig_setting(\n name = \"prefix_version_setting\",\n values = {\"java_runtime_version\": \"remotejdk_21\"},\n visibility = [\"//visibility:private\"],\n)\nconfig_setting(\n name = \"version_setting\",\n values = {\"java_runtime_version\": \"21\"},\n visibility = [\"//visibility:private\"],\n)\nalias(\n name = \"version_or_prefix_version_setting\",\n actual = select({\n \":version_setting\": \":version_setting\",\n \"//conditions:default\": \":prefix_version_setting\",\n }),\n visibility = [\"//visibility:private\"],\n)\ntoolchain(\n name = \"toolchain\",\n target_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:runtime_toolchain_type\",\n toolchain = \"@remotejdk21_win//:jdk\",\n)\ntoolchain(\n name = \"bootstrap_runtime_toolchain\",\n # These constraints are not required for correctness, but prevent fetches of remote JDK for\n # different architectures. As every Java compilation toolchain depends on a bootstrap runtime in\n # the same configuration, this constraint will not result in toolchain resolution failures.\n exec_compatible_with = [\"@platforms//os:windows\", \"@platforms//cpu:x86_64\"],\n target_settings = [\":version_or_prefix_version_setting\"],\n toolchain_type = \"@bazel_tools//tools/jdk:bootstrap_runtime_toolchain_type\",\n toolchain = \"@remotejdk21_win//:jdk\",\n)\n" + } + } + }, + "recordedRepoMappingEntries": [ + [ + "rules_java~", + "bazel_tools", + "bazel_tools" + ], + [ + "rules_java~", + "remote_java_tools", + "rules_java~~toolchains~remote_java_tools" + ] + ] + } + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a9a1b7 --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +# 😎 Radiant Library (C++) + +Radiant is a C++ library that is cross platform, kernel safe, and exception free. + +## Guiding Principles + +* Exception free and safe +* Kernel compatible +* Platform portable +* Bring your own allocator + +## Contributing + +Radiant is an open-source project, and we warmly welcome contributions. Please +refer to the [Contributing Guide](CONTRIBUTING.md) for more information. + +## Strategic Doctrine + +Radiant takes security seriously. C++ is a powerful language that can introduce +vulnerabilities if not used correctly. Radiant cannot prevent this, but it can +help minimize the likelihood of mistakes. Wherever possible, Radiant will +encourage safe practices. + +Radiant takes performance seriously. Radiant is designed to be fast and +efficient. Radiant will be regularly profiled and optimized to ensure maximum +performance. + +Radiant is designed for use in kernel or other low level environments where the +C++ Standard Library (STL) may not be suitable. Radiant is not intended to be a +drop in replacement for the STL. Where possible, Radiant adopts naming +conventions similar to those of the STL. Radiant will intentionally deviate from +the STL naming conventions when necessary. + +Radiant is not restricted to kernel or low level environments. Radiant can be +used in any C++ project (C++ 14 and above). Radiant is designed to be portable +across platforms. + +Radiant emphasizes the importance of control over allocations. Any sufficiently +complex system must be capable of reasoning about allocations. Radiant is +designed to provide users with full control over memory allocations. +* Users are required to implement their own allocators. +* Allocators can be scoped globally, per-file, or per-object. +* Implemented allocators must adhere to the Radiant allocator contract. + +Radiant will never throw an exception. Radiant will provide error codes and +error handling mechanisms. Radiant is safe from exceptions, guaranteeing that it +will not leak memory, resources, or be left in an undefined state in the event +of an exception. + +Radiant will break backwards compatibility and application binary interfaces +when necessary and justified. Radiant is not intended for use with foreign +function interfaces. [Semantic Versioning](https://semver.org/) will be used to +indicate breaking changes. + +Radiant acknowledges the fact that C++ is incumbent in many industries. Radiant +is designed to be a modern C++ library that can be used in conjunction with +existing code bases. The success of alternate systems programming languages has +shown that C++ is not the only option. But for many industries and code bases, +C++ is the incumbent and displacing it means an alternative must interoperate +with it seamlessly. While Radiant helps to fill this gap, it does not aim to +displace or discourage the development of alternative libraries or languages. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..ef20119 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policies and Procedures + +This document outlines security procedures and general policies for the Radiant +project. + +## Reporting a Bug + +The Radiant team and community take all security bugs very seriously. We +appreciate efforts for responsible disclosure and will make every effort to +acknowledge your contributions. + +Report security bugs by emailing the lead maintainers at johnny.shaw@live.com. + +The Radiant team will acknowledge your email within 48 hours, and will send a +more detailed response indicating the next steps in handling your report. After +the initial reply to your report, the security team will endeavor to keep you +informed of the progress towards a fix and full announcement, and may ask for +additional information or guidance. + +## Disclosure Policy + +When the Raditn team receives a security bug report, they will assign it to a +primary developer. This person will coordinate the fix and release process, +involving the following steps: + +* Confirm the problem and determine the affected versions. +* Audit code to find any potential similar problems. +* Seek review from respected third-party security experts. +* Prepare fixes for all releases still under maintenance. These fixes will be + released as fast as possible. diff --git a/default_copts.bzl b/default_copts.bzl new file mode 100644 index 0000000..faff6aa --- /dev/null +++ b/default_copts.bzl @@ -0,0 +1,49 @@ +""" Default copts for various compilers""" + +RAD_CPP14 = select({ + "//:msvc": ["/std:c++14"], + "//:gcc": ["-std=c++14"], + "//:clang": ["-std=c++14"], +}) + +RAD_CPP17 = select({ + "//:msvc": ["/std:c++17"], + "//:gcc": ["-std=c++17"], + "//:clang": ["-std=c++17"], +}) + +RAD_CPP20 = select({ + "//:msvc": ["/std:c++20"], + "//:gcc": ["-std=c++20"], + "//:clang": ["-std=c++20"], +}) + +_RAD_GCC_OPTS = [ + "-Wall", + "-Wextra", + "-Wcast-qual", + "-Wconversion", + "-Wconversion-null", + "-Wmissing-declarations", + "-Woverlength-strings", + "-Wpointer-arith", + "-Wundef", + "-Wunused-local-typedefs", + "-Wunused-result", + "-Wvarargs", + "-Wvla", + "-Wwrite-strings", + "-Werror", + "-Wpedantic", +] + +RAD_DEFAULT_COPTS = select({ + "//:msvc": [ + "/W4", + "/WX", + "/DNOMINMAX", + "/Zc:__cplusplus", + ], + "//:gcc": _RAD_GCC_OPTS, + "//:clang": _RAD_GCC_OPTS, +}) diff --git a/radiant/Atomic.h b/radiant/Atomic.h new file mode 100644 index 0000000..a1d8234 --- /dev/null +++ b/radiant/Atomic.h @@ -0,0 +1,600 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/detail/AtomicIntrinsics.h" + +namespace rad +{ + +template +using MemoryOrderTag = detail::atomic::OrderTag; + +RAD_INLINE_VAR constexpr detail::atomic::RelaxedTag MemOrderRelaxed{}; +RAD_INLINE_VAR constexpr detail::atomic::ConsumeTag MemOrderConsume{}; +RAD_INLINE_VAR constexpr detail::atomic::AcquireTag MemOrderAcquire{}; +RAD_INLINE_VAR constexpr detail::atomic::ReleaseTag MemOrderRelease{}; +RAD_INLINE_VAR constexpr detail::atomic::AcqRelTag MemOrderAcqRel{}; +RAD_INLINE_VAR constexpr detail::atomic::SeqCstTag MemOrderSeqCst{}; + +namespace detail +{ +namespace atomic +{ + +template +class AtomicIntegral +{ +public: + + RAD_S_ASSERTMSG(IsIntegral, + "rad::Atomic supports only integral and pointer types"); + + using ValueType = T; + using DifferenceType = ValueType; + + constexpr AtomicIntegral() noexcept = default; + + constexpr AtomicIntegral(T value) noexcept + : m_val(value) + { + } + + RAD_NOT_COPYABLE(AtomicIntegral); + +#ifndef RAD_REQUIRE_EXPLICIT_ATOMIC_ORDERING + + template + void Store(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + detail::atomic::SelectIntrinsic::Store(m_val, val, Order()); + } + + template + T Load(MemoryOrderTag = MemoryOrderTag()) const noexcept + { + return detail::atomic::SelectIntrinsic::Load(m_val, Order()); + } + + template + T Exchange(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::Exchange(m_val, + val, + Order()); + } + + template + bool CompareExchangeWeak(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeWeak( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeWeak( + T& expected, + T desired, + MemoryOrderTag = MemoryOrderTag()) noexcept + { + return CompareExchangeWeak(expected, + desired, + Order(), + Order()); + } + + template + bool CompareExchangeStrong(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeStrong( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeStrong( + T& expected, + T desired, + MemoryOrderTag = MemoryOrderTag()) noexcept + { + return CompareExchangeStrong(expected, desired, Order(), Order()); + } + + template + T FetchAdd(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchAdd(m_val, + val, + Order()); + } + + template + T FetchSub(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchSub(m_val, + val, + Order()); + } + + template + T FetchAnd(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchAnd(m_val, + val, + Order()); + } + + template + T FetchOr(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchOr(m_val, val, Order()); + } + + template + T FetchXor(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchXor(m_val, + val, + Order()); + } + + operator T() const noexcept + { + return Load(); + } + + T operator=(T val) noexcept + { + Store(val); + return val; + } + + T operator++() noexcept + { + return static_cast(FetchAdd(1) + static_cast(1)); + } + + T operator++(int) noexcept + { + return FetchAdd(1); + } + + T operator--() noexcept + { + return static_cast(FetchSub(1) - static_cast(1)); + } + + T operator--(int) noexcept + { + return FetchSub(1); + } + + T operator+=(T val) noexcept + { + return static_cast(FetchAdd(val) + val); + } + + T operator-=(T val) noexcept + { + return static_cast(FetchSub(val) - val); + } + + T operator&=(T val) noexcept + { + return FetchAnd(val) & val; + } + + T operator|=(T val) noexcept + { + return FetchOr(val) | val; + } + + T operator^=(T val) noexcept + { + return FetchXor(val) ^ val; + } +#else + template + void Store(T val, MemoryOrderTag) noexcept + { + detail::atomic::SelectIntrinsic::Store(m_val, val, Order()); + } + + template + T Load(MemoryOrderTag) const noexcept + { + return detail::atomic::SelectIntrinsic::Load(m_val, Order()); + } + + template + T Exchange(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::Exchange(m_val, + val, + Order()); + } + + template + bool CompareExchangeWeak(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeWeak( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeWeak(T& expected, + T desired, + MemoryOrderTag) noexcept + { + return CompareExchangeWeak(expected, + desired, + Order(), + Order()); + } + + template + bool CompareExchangeStrong(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeStrong( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeStrong(T& expected, + T desired, + MemoryOrderTag) noexcept + { + return CompareExchangeStrong(expected, desired, Order(), Order()); + } + + template + T FetchAdd(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchAdd(m_val, + val, + Order()); + } + + template + T FetchSub(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchSub(m_val, + val, + Order()); + } + + template + T FetchAnd(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchAnd(m_val, + val, + Order()); + } + + template + T FetchOr(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchOr(m_val, val, Order()); + } + + template + T FetchXor(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchXor(m_val, + val, + Order()); + } +#endif + +private: + + T m_val{}; +}; + +template +class AtomicPointer +{ +public: + + RAD_S_ASSERTMSG(IsPointer, + "rad::Atomic supports only integral and pointer types"); + + using ValueType = T; + using DifferenceType = ValueType; + + constexpr AtomicPointer() noexcept = default; + + constexpr AtomicPointer(T value) noexcept + : m_val(value) + { + } + + RAD_NOT_COPYABLE(AtomicPointer); + +#ifndef RAD_REQUIRE_EXPLICIT_ATOMIC_ORDERING + + template + void Store(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + detail::atomic::SelectIntrinsic::Store(m_val, val, Order()); + } + + template + T Load(MemoryOrderTag = MemoryOrderTag()) const noexcept + { + return detail::atomic::SelectIntrinsic::Load(m_val, Order()); + } + + template + T Exchange(T val, MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::Exchange(m_val, + val, + Order()); + } + + template + bool CompareExchangeWeak(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeWeak( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeWeak( + T& expected, + T desired, + MemoryOrderTag = MemoryOrderTag()) noexcept + { + return CompareExchangeWeak(expected, + desired, + Order(), + Order()); + } + + template + bool CompareExchangeStrong(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeStrong( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeStrong( + T& expected, + T desired, + MemoryOrderTag = MemoryOrderTag()) noexcept + { + return CompareExchangeStrong(expected, desired, Order(), Order()); + } + + template + T FetchAdd(ptrdiff_t val, + MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchAdd(m_val, + val, + Order()); + } + + template + T FetchSub(ptrdiff_t val, + MemoryOrderTag = MemoryOrderTag()) noexcept + { + return detail::atomic::SelectIntrinsic::FetchSub(m_val, + val, + Order()); + } + + operator T() const noexcept + { + return Load(); + } + + T operator=(T val) noexcept + { + Store(val); + return val; + } + + T operator++() noexcept + { + return static_cast(FetchAdd(1) + static_cast(1)); + } + + T operator++(int) noexcept + { + return FetchAdd(1); + } + + T operator--() noexcept + { + return static_cast(FetchSub(1) - static_cast(1)); + } + + T operator--(int) noexcept + { + return FetchSub(1); + } + + T operator+=(ptrdiff_t val) noexcept + { + return static_cast(FetchAdd(val) + val); + } + + T operator-=(ptrdiff_t val) noexcept + { + return static_cast(FetchSub(val) - val); + } + +#else + + template + void Store(T val, MemoryOrderTag) noexcept + { + detail::atomic::SelectIntrinsic::Store(m_val, val, Order()); + } + + template + T Load(MemoryOrderTag) const noexcept + { + return detail::atomic::SelectIntrinsic::Load(m_val, Order()); + } + + template + T Exchange(T val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::Exchange(m_val, + val, + Order()); + } + + template + bool CompareExchangeWeak(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeWeak( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeWeak(T& expected, + T desired, + MemoryOrderTag) noexcept + { + return CompareExchangeWeak(expected, + desired, + Order(), + Order()); + } + + template + bool CompareExchangeStrong(T& expected, + T desired, + MemoryOrderTag, + MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::CompareExchangeStrong( + m_val, + desired, + expected, + Success(), + Failure()); + } + + template + bool CompareExchangeStrong(T& expected, + T desired, + MemoryOrderTag) noexcept + { + return CompareExchangeStrong(expected, desired, Order(), Order()); + } + + template + T FetchAdd(ptrdiff_t val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchAdd(m_val, + val, + Order()); + } + + template + T FetchSub(ptrdiff_t val, MemoryOrderTag) noexcept + { + return detail::atomic::SelectIntrinsic::FetchSub(m_val, + val, + Order()); + } + +#endif + +private: + + T m_val{}; +}; + +} // namespace atomic +} // namespace detail + +template +class Atomic final : public Cond, + detail::atomic::AtomicIntegral, + detail::atomic::AtomicPointer> +{ + using BaseType = Cond, + detail::atomic::AtomicIntegral, + detail::atomic::AtomicPointer>; + +public: + + RAD_NOT_COPYABLE(Atomic); + + constexpr Atomic() noexcept + : BaseType() + { + } + + using BaseType::BaseType; + using BaseType::operator=; +}; + +} // namespace rad diff --git a/radiant/Byte.h b/radiant/Byte.h new file mode 100644 index 0000000..2841ee4 --- /dev/null +++ b/radiant/Byte.h @@ -0,0 +1,24 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +namespace rad +{ + +enum class Byte : unsigned char +{ +}; + +} // namespace rad diff --git a/radiant/EmptyOptimizedPair.h b/radiant/EmptyOptimizedPair.h new file mode 100644 index 0000000..5f407d3 --- /dev/null +++ b/radiant/EmptyOptimizedPair.h @@ -0,0 +1,213 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Utility.h" + +namespace rad +{ + +/// \brief Pair implementing the "empty base optimization" technique. +/// \details With this technique, it is possible to create zero-sized storage +/// for stateless objects when paired with another type. A common use-case is +/// allocators. Sometimes allocators are stateless and don't require storage, +/// and sometimes they are stateful. With this technique, stateless allocators +/// require no storage. +/// \tparam TEmptyOptimized Type that can optionally be empty (stateless) +/// \tparam T2 Type which will always use storage +template > +class EmptyOptimizedPair final : private TEmptyOptimized +{ +public: + + using FirstType = TEmptyOptimized; + using SecondType = T2; + + constexpr EmptyOptimizedPair() noexcept(IsNoThrowDefaultCtor && + IsNoThrowDefaultCtor) + : FirstType(), + m_second() + { + } + + template + constexpr explicit EmptyOptimizedPair(Args&&... args) noexcept( + IsNoThrowDefaultCtor && IsNoThrowCtor) + : FirstType(), + m_second(Forward(args)...) + { + } + + template + constexpr explicit EmptyOptimizedPair( + FirstType& first, + Args&&... args) noexcept(noexcept(FirstType(first)) && + IsNoThrowCtor) + : FirstType(first), + m_second(Forward(args)...) + { + } + + template + constexpr explicit EmptyOptimizedPair( + const FirstType& first, + Args&&... args) noexcept(noexcept(FirstType(first)) && + IsNoThrowCtor) + : FirstType(first), + m_second(Forward(args)...) + { + } + + template + constexpr explicit EmptyOptimizedPair( + FirstType&& first, + Args&&... args) noexcept(noexcept(FirstType(Move(first))) && + IsNoThrowCtor) + : FirstType(Move(first)), + m_second(Forward(args)...) + { + } + + constexpr explicit EmptyOptimizedPair(EmptyOptimizedPair& r) noexcept( + IsNoThrowCopyCtor && IsNoThrowCopyCtor) + : FirstType(r.First()), + m_second(r.m_second) + { + } + + constexpr explicit EmptyOptimizedPair(EmptyOptimizedPair const& r) noexcept( + IsNoThrowCopyCtor && IsNoThrowCopyCtor) + : FirstType(r.First()), + m_second(r.m_second) + { + } + + constexpr explicit EmptyOptimizedPair(EmptyOptimizedPair&& r) noexcept( + IsNoThrowMoveCtor && IsNoThrowMoveCtor) + : FirstType(Move(r)), + m_second(Move(r.m_second)) + { + } + + constexpr FirstType& First() noexcept + { + return *this; + } + + constexpr const FirstType& First() const noexcept + { + return *this; + } + + constexpr SecondType& Second() noexcept + { + return m_second; + } + + constexpr const SecondType& Second() const noexcept + { + return m_second; + } + +private: + + SecondType m_second; +}; + +template +class EmptyOptimizedPair final +{ +public: + + using FirstType = TEmptyOptimized; + using SecondType = T2; + + constexpr EmptyOptimizedPair() noexcept(IsNoThrowDefaultCtor && + IsNoThrowDefaultCtor) + : m_first(), + m_second() + { + } + + template + constexpr explicit EmptyOptimizedPair( + const TEmptyOptimized& first, + Args&&... args) noexcept(IsNoThrowCtor && + IsNoThrowCtor) + : m_first(first), + m_second(Forward(args)...) + { + } + + template + constexpr explicit EmptyOptimizedPair( + TEmptyOptimized&& first, + Args&&... args) noexcept(IsNoThrowCtor && + IsNoThrowCtor) + : m_first(Forward(first)), + m_second(Forward(args)...) + { + } + + constexpr EmptyOptimizedPair(EmptyOptimizedPair& r) noexcept( + IsNoThrowCopyCtor && IsNoThrowCopyCtor) + : m_first(r.m_first), + m_second(r.m_second) + { + } + + constexpr EmptyOptimizedPair(EmptyOptimizedPair const& r) noexcept( + IsNoThrowCopyCtor && IsNoThrowCopyCtor) + : m_first(r.m_first), + m_second(r.m_second) + { + } + + constexpr EmptyOptimizedPair(EmptyOptimizedPair&& r) noexcept( + IsNoThrowMoveCtor && IsNoThrowMoveCtor) + : m_first(Move(r.m_first)), + m_second(Move(r.m_second)) + { + } + + constexpr FirstType& First() noexcept + { + return m_first; + } + + constexpr const FirstType& First() const noexcept + { + return m_first; + } + + constexpr SecondType& Second() noexcept + { + return m_second; + } + + constexpr const SecondType& Second() const noexcept + { + return m_second; + } + +private: + + FirstType m_first; + SecondType m_second; +}; + +} // namespace rad diff --git a/radiant/Handle.h b/radiant/Handle.h new file mode 100644 index 0000000..d9bb86d --- /dev/null +++ b/radiant/Handle.h @@ -0,0 +1,335 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Utility.h" + +namespace rad +{ + +/// @brief Container to manage lifetime of some resource. +/// @details This contains is semantically similar to a unique pointer however +/// tailored to managing things like file handles or references to other +/// objects. +/// @tparam Policy The policy for the handle must define and implement static +/// types and functionality for how to validate or close the resource. See +/// HandlePolicy<>. +template +class Handle +{ +public: + + using PolicyType = Policy; + using ValueType = typename Policy::ValueType; + static constexpr ValueType InvalidValue = Policy::InvalidValue; + using ThisType = Handle; + + RAD_S_ASSERTMSG(IsTriv, "Handle manages a trivial type."); + RAD_S_ASSERTMSG(noexcept(PolicyType::IsValid(DeclVal())), + "Handle validator may not throw."); + RAD_S_ASSERTMSG(noexcept(PolicyType::Close(DeclVal())), + "Handle closer may not throw."); + + ~Handle() + { + if (Policy::IsValid(static_cast(m_value))) + { + Policy::Close(m_value); + } + } + + /// @brief Default constructs the handle with an invalid value. + constexpr Handle() noexcept + : m_value(InvalidValue) + { + } + + /// @brief Explicit construction of the handle with some value. + /// @param value The handle value to store. + constexpr explicit Handle(const ValueType& value) noexcept + : m_value(value) + { + } + + RAD_NOT_COPYABLE(Handle); + + /// @brief Move constructs this handle from another. + /// @param other Other handle to move into this one. + constexpr Handle(ThisType&& other) noexcept + : m_value(other.m_value) + { + other.m_value = InvalidValue; + } + + /// @brief Move assigns a handle to this. + /// @param other Other handle to move into this one. + ThisType& operator=(ThisType&& other) noexcept + { + if RAD_LIKELY (AddrOf(other) != this) + { + Reset(other.m_value); + other.m_value = InvalidValue; + } + return *this; + } + + /// @brief Checks if contained handle is valid. + /// @return True if the contained handle is valid, false otherwise. + constexpr bool IsValid() const noexcept + { + return Policy::IsValid(static_cast(m_value)); + } + + /// @brief Checks if the contained handle is valid. + constexpr explicit operator bool() const + { + return IsValid(); + } + + /// @brief Resets the contained handle with a different value. + /// @param value The handle value to store. + constexpr void Reset(const ValueType& value = InvalidValue) noexcept + { + auto prev = m_value; + m_value = value; + + if (Policy::IsValid(static_cast(prev))) + { + Policy::Close(prev); + } + } + + /// @brief Retrieves the handle. + constexpr ValueType& Get() noexcept + { + return m_value; + } + + /// @brief Retrieves the handle. + constexpr const ValueType& Get() const noexcept + { + return m_value; + } + + /// @brief Releases ownership of the handle to the caller. + /// @return The previously managed resource from this object. + RAD_NODISCARD constexpr ValueType Release() noexcept + { + auto value = m_value; + m_value = InvalidValue; + return value; + } + + /// @brief Swaps two handle object's managed resource. + /// @param other Other handle object to swap with this. + constexpr void Swap(ThisType& other) noexcept + { + auto value = m_value; + m_value = other.m_value; + other.m_value = value; + } + + /// @brief Helper function for putting something into this object. + /// @details Useful for opening handles using an API where a parameter is an + /// output parameter. + /// @return Pointer to the internal storage of this object. + ValueType* Put() noexcept + { + RAD_ASSERT(m_value == InvalidValue); + Reset(); + return &m_value; + } + + /// @brief Overloaded address of operator to simplify the semantics of + /// putting information into this object. + /// @return Pointer to the internal storage of this object. + ValueType* operator&() noexcept + { + return Put(); + } + +private: + + ValueType m_value; +}; + +template +constexpr typename Policy::ValueType Handle::InvalidValue; + +template +constexpr inline bool operator==(const Handle& l, + const Handle& r) +{ + return l.Get() == r.Get(); +} + +template +constexpr inline bool operator!=(const Handle& l, + const Handle& r) +{ + return !(l == r); +} + +template +constexpr inline bool operator<(const Handle& l, + const Handle& r) +{ + return l.Get() < r.Get(); +} + +template +constexpr inline bool operator>(const Handle& l, + const Handle& r) +{ + return r < l; +} + +template +constexpr inline bool operator<=(const Handle& l, + const Handle& r) +{ + return !(l > r); +} + +template +constexpr inline bool operator>=(const Handle& l, + const Handle& r) +{ + return !(l < r); +} + +template +constexpr inline bool operator==(const Handle& l, + const typename Policy::ValueType& r) +{ + return l.Get() == r; +} + +template +constexpr inline bool operator!=(const Handle& l, + const typename Policy::ValueType& r) +{ + return !(l == r); +} + +template +constexpr inline bool operator<(const Handle& l, + const typename Policy::ValueType& r) +{ + return l.Get() < r; +} + +template +constexpr inline bool operator>(const Handle& l, + const typename Policy::ValueType& r) +{ + return r < l; +} + +template +constexpr inline bool operator<=(const Handle& l, + const typename Policy::ValueType& r) +{ + return !(l > r); +} + +template +constexpr inline bool operator>=(const Handle& l, + const typename Policy::ValueType& r) +{ + return !(l < r); +} + +template +constexpr inline bool operator==(const typename Policy::ValueType& l, + const Handle& r) +{ + return l == r.Get(); +} + +template +constexpr inline bool operator!=(const typename Policy::ValueType& l, + const Handle& r) +{ + return !(l == r); +} + +template +constexpr inline bool operator<(const typename Policy::ValueType& l, + const Handle& r) +{ + return l < r.Get(); +} + +template +constexpr inline bool operator>(const typename Policy::ValueType& l, + const Handle& r) +{ + return r < l; +} + +template +constexpr inline bool operator<=(const typename Policy::ValueType& l, + const Handle& r) +{ + return !(l > r); +} + +template +constexpr inline bool operator>=(const typename Policy::ValueType& l, + const Handle& r) +{ + return !(l < r); +} + +/// @brief Default handle policy for convenience. +/// @details The handle container enables users to define their own policy +/// verbosely. Which is useful in situations where a handle may have more than +/// one invalid states. This default policy enables a short-hand for most use +/// cases through HandleDef<>. +/// @tparam T The type of handle to manage. +/// @tparam Closer Structure which implements how to close the handle. +/// @tparam Invalid The value of the handle which indicates it is invalid. +template (0)> +struct HandlePolicy +{ + using ValueType = T; + static constexpr T InvalidValue = Invalid; + + static constexpr bool IsValid(const T& value) noexcept + { + return value != InvalidValue; + } + + static constexpr void Close(T& value) noexcept + { + RAD_S_ASSERTMSG(noexcept(Closer::Close(value)), + "Handle closer may not throw"); + + Closer::Close(value); + } +}; + +template +constexpr T HandlePolicy::InvalidValue; + +/// @brief Helper defining template for a handle. +/// @tparam T The type of handle to manage. +/// @tparam Closer Structure which implements how to close the handle. +/// @tparam Invalid The value of the handle which indicates it is invalid. +template (0)> +using HandleDef = Handle>; + +} // namespace rad diff --git a/radiant/Integer.h b/radiant/Integer.h new file mode 100644 index 0000000..97c22f1 --- /dev/null +++ b/radiant/Integer.h @@ -0,0 +1,505 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "radiant/TypeTraits.h" +#include "radiant/Res.h" + +namespace rad +{ + +template +struct IntegerTraits; + +template > +struct GenericIntegerTraits; + +template +struct GenericIntegerTraits +{ + static constexpr T MIN = TMin; + static constexpr T MAX = TMax; + + RAD_S_ASSERT(IsSigned == IsSigned); + + static constexpr Res Add(T lhs, T rhs) noexcept + { + W res = static_cast(lhs) + static_cast(rhs); + + if ((res > MAX) || (res < MIN)) + { + return Error::IntegerOverflow; + } + + return static_cast(res); + } + + static constexpr Res Sub(T lhs, T rhs) noexcept + { + W res = static_cast(lhs) - static_cast(rhs); + + if ((res > MAX) || (res < MIN)) + { + return Error::IntegerOverflow; + } + + return static_cast(res); + } + + static constexpr Res Mul(T lhs, T rhs) noexcept + { + W res = static_cast(lhs) * static_cast(rhs); + + if ((res > MAX) || (res < MIN)) + { + return Error::IntegerOverflow; + } + + return static_cast(res); + } + + static constexpr T SaturatingAdd(T lhs, T rhs) noexcept + { + if (rhs > 0) + { + return Add(lhs, rhs).Or(MAX); + } + + return Add(lhs, rhs).Or(MIN); + } + + static constexpr T SaturatingSub(T lhs, T rhs) noexcept + { + if (rhs > 0) + { + return Sub(lhs, rhs).Or(MIN); + } + + return Sub(lhs, rhs).Or(MAX); + } + + static constexpr T SaturatingMul(T lhs, T rhs) noexcept + { + if ((lhs > 0) && (rhs > 0)) + { + return Mul(lhs, rhs).Or(MAX); + } + + if ((lhs < 0) && (rhs < 0)) + { + return Mul(lhs, rhs).Or(MAX); + } + + return Mul(lhs, rhs).Or(MIN); + } +}; + +template +struct GenericIntegerTraits +{ + static constexpr T MIN = TMin; + static constexpr T MAX = TMax; + + RAD_S_ASSERT(IsUnsigned == IsUnsigned); + + static constexpr Res Add(T lhs, T rhs) noexcept + { + T res = lhs + rhs; + + if (res < lhs) + { + return Error::IntegerOverflow; + } + + return res; + } + + static constexpr Res Sub(T lhs, T rhs) noexcept + { + T res = lhs - rhs; + + if (res > lhs) + { + return Error::IntegerOverflow; + } + + return static_cast(res); + } + + static constexpr Res Mul(T lhs, T rhs) noexcept + { + W res = static_cast(lhs) * static_cast(rhs); + + if (res > MAX) + { + return Error::IntegerOverflow; + } + + return static_cast(res); + } + + static constexpr T SaturatingAdd(T lhs, T rhs) noexcept + { + return Add(lhs, rhs).Or(MAX); + } + + static constexpr T SaturatingSub(T lhs, T rhs) noexcept + { + return Sub(lhs, rhs).Or(MIN); + } + + static constexpr T SaturatingMul(T lhs, T rhs) noexcept + { + return Mul(lhs, rhs).Or(MAX); + } +}; + +template +constexpr T GenericIntegerTraits::MIN; +template +constexpr T GenericIntegerTraits::MIN; +template +constexpr T GenericIntegerTraits::MAX; +template +constexpr T GenericIntegerTraits::MAX; + +template <> +struct IntegerTraits + : public GenericIntegerTraits +{ +}; + +template <> +struct IntegerTraits + : public GenericIntegerTraits +{ +}; + +template <> +struct IntegerTraits + : public GenericIntegerTraits +{ +}; + +template <> +struct IntegerTraits + : public GenericIntegerTraits +{ +}; + +template <> +struct IntegerTraits + : public GenericIntegerTraits +{ +}; + +template <> +struct IntegerTraits + : public GenericIntegerTraits +{ +}; + +template +class RAD_NODISCARD Integer +{ +public: + + using ThisType = Integer; + using ValueType = T; + using Traits = IntegerTraits; + + static constexpr T MIN = Traits::MIN; + static constexpr T MAX = Traits::MAX; + + ~Integer() = default; + + constexpr Integer() noexcept + : m_value() + { + } + + constexpr Integer(T value) noexcept + : m_value(value) + { + } + + constexpr Integer(const Integer& other) noexcept + : m_value(other.m_value) + { + } + + constexpr Integer(Integer&& other) noexcept + : m_value(other.m_value) + { + other.m_value = T(); + } + + constexpr Integer& operator=(T value) noexcept + { + m_value = value; + return *this; + } + + constexpr Integer& operator=(const Integer& other) noexcept + { + m_value = other.m_value; + return *this; + } + + constexpr Integer& operator=(Integer&& other) noexcept + { + m_value = other.m_value; + other.m_value = T(); + return *this; + } + + constexpr explicit operator T() const noexcept + { + return m_value; + } + + constexpr Integer Max(T rhs) const noexcept + { + return m_value > rhs ? m_value : rhs; + } + + constexpr Integer Min(T lhs) const noexcept + { + return m_value < lhs ? m_value : lhs; + } + + // checked operations + + constexpr Res Add(T rhs) const noexcept + { + return Traits::Add(m_value, rhs); + } + + constexpr Res Sub(T rhs) const noexcept + { + return Traits::Sub(m_value, rhs); + } + + constexpr Res Mul(T rhs) const noexcept + { + return Traits::Mul(m_value, rhs); + } + + // saturating operations + + RAD_NODISCARD constexpr Integer SaturatingAdd(T rhs) const noexcept + { + return Traits::SaturatingAdd(m_value, rhs); + } + + RAD_NODISCARD constexpr Integer SaturatingSub(T rhs) const noexcept + { + return Traits::SaturatingSub(m_value, rhs); + } + + RAD_NODISCARD constexpr Integer SaturatingMul(T rhs) const noexcept + { + return Traits::SaturatingMul(m_value, rhs); + } + + // unchecked operations + + RAD_NODISCARD constexpr Integer UncheckedAdd(T rhs) const noexcept + { + return m_value + rhs; + } + + RAD_NODISCARD constexpr Integer UncheckedSub(T rhs) const noexcept + { + return m_value - rhs; + } + + RAD_NODISCARD constexpr Integer UncheckedMul(T rhs) const noexcept + { + return m_value * rhs; + } + +private: + + T m_value; +}; + +template +constexpr T Integer::MIN; +template +constexpr T Integer::MAX; + +using i8 = Integer; +using u8 = Integer; +using i16 = Integer; +using u16 = Integer; +using i32 = Integer; +using u32 = Integer; + +template +constexpr inline bool operator==(const Integer& lhs, + const Integer& rhs) noexcept +{ + return static_cast(lhs) == static_cast(rhs); +} + +template +constexpr inline bool operator!=(const Integer& lhs, + const Integer& rhs) noexcept +{ + return static_cast(lhs) != static_cast(rhs); +} + +template +constexpr inline bool operator<(const Integer& lhs, + const Integer& rhs) noexcept +{ + return static_cast(lhs) < static_cast(rhs); +} + +template +constexpr inline bool operator<=(const Integer& lhs, + const Integer& rhs) noexcept +{ + return static_cast(lhs) <= static_cast(rhs); +} + +template +constexpr inline bool operator>(const Integer& lhs, + const Integer& rhs) noexcept +{ + return static_cast(lhs) > static_cast(rhs); +} + +template +constexpr inline bool operator>=(const Integer& lhs, + const Integer& rhs) noexcept +{ + return static_cast(lhs) >= static_cast(rhs); +} + +template +constexpr inline bool operator==(const Integer& lhs, const T& rhs) noexcept +{ + return static_cast(lhs) == rhs; +} + +template +constexpr inline bool operator!=(const Integer& lhs, const T& rhs) noexcept +{ + return static_cast(lhs) != rhs; +} + +template +constexpr inline bool operator<(const Integer& lhs, const T& rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + +template +constexpr inline bool operator<=(const Integer& lhs, const T& rhs) noexcept +{ + return static_cast(lhs) <= rhs; +} + +template +constexpr inline bool operator>(const Integer& lhs, const T& rhs) noexcept +{ + return static_cast(lhs) > rhs; +} + +template +constexpr inline bool operator>=(const Integer& lhs, const T& rhs) noexcept +{ + return static_cast(lhs) >= rhs; +} + +template && IsSigned, int> = 0> +constexpr inline bool operator==(const Integer& lhs, int rhs) noexcept +{ + return static_cast(lhs) == rhs; +} + +template && IsSigned, int> = 0> +constexpr inline bool operator!=(const Integer& lhs, int rhs) noexcept +{ + return static_cast(lhs) != rhs; +} + +template && IsSigned, int> = 0> +constexpr inline bool operator<(const Integer& lhs, int rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + +template && IsSigned, int> = 0> +constexpr inline bool operator<=(const Integer& lhs, int rhs) noexcept +{ + return static_cast(lhs) <= rhs; +} + +template && IsSigned, int> = 0> +constexpr inline bool operator>(const Integer& lhs, int rhs) noexcept +{ + return static_cast(lhs) > rhs; +} + +template && IsSigned, int> = 0> +constexpr inline bool operator>=(const Integer& lhs, int rhs) noexcept +{ + return static_cast(lhs) >= rhs; +} + +template && IsUnsigned, int> = 0> +constexpr inline bool operator==(const Integer& lhs, + unsigned int rhs) noexcept +{ + return static_cast(lhs) == rhs; +} + +template && IsUnsigned, int> = 0> +constexpr inline bool operator!=(const Integer& lhs, + unsigned int rhs) noexcept +{ + return static_cast(lhs) != rhs; +} + +template && IsUnsigned, int> = 0> +constexpr inline bool operator<(const Integer& lhs, + unsigned int rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + +template && IsUnsigned, int> = 0> +constexpr inline bool operator<=(const Integer& lhs, + unsigned int rhs) noexcept +{ + return static_cast(lhs) <= rhs; +} + +template && IsUnsigned, int> = 0> +constexpr inline bool operator>(const Integer& lhs, + unsigned int rhs) noexcept +{ + return static_cast(lhs) > rhs; +} + +template && IsUnsigned, int> = 0> +constexpr inline bool operator>=(const Integer& lhs, + unsigned int rhs) noexcept +{ + return static_cast(lhs) >= rhs; +} + +} // namespace rad diff --git a/radiant/Iterator.h b/radiant/Iterator.h new file mode 100644 index 0000000..24b20c5 --- /dev/null +++ b/radiant/Iterator.h @@ -0,0 +1,398 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Utility.h" + +namespace rad +{ + +template +struct IteratorTraits +{ + using ValueType = typename T::ValueType; + using DifferenceType = typename T::DifferenceType; + using PointerType = typename T::PointerType; + using ReferenceType = typename T::ReferenceType; +}; + +template +struct IteratorTraits +{ + using ValueType = T; + using DifferenceType = ptrdiff_t; + using PointerType = T*; + using ReferenceType = T&; +}; + +template +struct IteratorTraits +{ + using ValueType = T; + using DifferenceType = ptrdiff_t; + using PointerType = const T*; + using ReferenceType = const T&; +}; + +template +class Iterator +{ +public: + + using ThisType = Iterator; + using TraitsType = IteratorTraits; + using ValueType = typename TraitsType::ValueType; + using DifferenceType = typename TraitsType::DifferenceType; + using PointerType = typename TraitsType::PointerType; + using ReferenceType = typename TraitsType::ReferenceType; + + ~Iterator() = default; + + constexpr Iterator() noexcept + : m_current(T()) + { + } + + constexpr Iterator(T current) noexcept + : m_current(current) + { + } + + constexpr Iterator(const ThisType& other) noexcept + : m_current(other.m_current) + { + } + + constexpr ThisType& operator=(const ThisType& other) noexcept + { + m_current = other.m_current; + return *this; + } + + template , int> = 0> + constexpr PointerType operator->() const noexcept + { + T tmp = m_current; + return tmp; + } + + template , int> = 0> + constexpr PointerType operator->() const noexcept + { + T tmp = m_current; + return tmp.operator->(); + } + + constexpr ReferenceType operator*() const noexcept + { + T tmp = m_current; + return *tmp; + } + + constexpr ReferenceType operator[](DifferenceType Diff) const noexcept + { + return m_current[Diff]; + } + + constexpr ThisType& operator++() noexcept + { + ++m_current; + return *this; + } + + constexpr ThisType operator++(int) noexcept + { + return ThisType(m_current++); + } + + constexpr ThisType& operator--() noexcept + { + --m_current; + return *this; + } + + constexpr ThisType operator--(int) noexcept + { + return ThisType(m_current--); + } + + constexpr ThisType& operator+=(DifferenceType diff) noexcept + { + m_current += diff; + return *this; + } + + constexpr ThisType& operator-=(DifferenceType diff) noexcept + { + m_current -= diff; + return *this; + } + + constexpr ThisType operator+(DifferenceType diff) const noexcept + { + return ThisType(m_current + diff); + } + + constexpr ThisType operator-(DifferenceType diff) const noexcept + { + return ThisType(m_current - diff); + } + + const T& Base() const noexcept + { + return m_current; + } + + const T& base() const noexcept + { + return Base(); + } + +protected: + + T m_current; +}; + +template +constexpr inline bool operator==(const Iterator& left, + const Iterator& right) noexcept +{ + return (left.Base() == right.Base()); +} + +template +constexpr inline bool operator!=(const Iterator& left, + const Iterator& right) noexcept +{ + return (left.Base() != right.Base()); +} + +template +constexpr inline bool operator<(const Iterator& left, + const Iterator& right) noexcept +{ + return (left.Base() < right.Base()); +} + +template +constexpr inline bool operator>(const Iterator& left, + const Iterator& right) noexcept +{ + return (right.Base() > left.Base()); +} + +template +constexpr inline bool operator<=(const Iterator& left, + const Iterator& right) noexcept +{ + return (left.Base() <= right.Base()); +} + +template +constexpr inline bool operator>=(const Iterator& left, + const Iterator& right) noexcept +{ + return (left.Base() >= right.Base()); +} + +template +constexpr inline typename Iterator::DifferenceType operator-( + const Iterator& left, const Iterator& right) noexcept +{ + return (left.Base() - right.Base()); +} + +template +constexpr inline typename Iterator::DifferenceType operator+( + const Iterator& left, const Iterator& right) noexcept +{ + return (left.Base() + right.Base()); +} + +template +class ReverseIterator +{ +public: + + using ThisType = ReverseIterator; + using TraitsType = IteratorTraits; + using ValueType = typename TraitsType::ValueType; + using DifferenceType = typename TraitsType::DifferenceType; + using PointerType = typename TraitsType::PointerType; + using ReferenceType = typename TraitsType::ReferenceType; + + ~ReverseIterator() = default; + + constexpr ReverseIterator() noexcept = default; + + constexpr explicit ReverseIterator(T other) noexcept + : m_current(Move(other)) + { + } + + template + constexpr ReverseIterator(const ReverseIterator& other) noexcept + : m_current(other.m_current) + { + } + + template + constexpr ThisType& operator=(const ReverseIterator& other) noexcept + { + m_current = other.m_current; + return *this; + } + + template , int> = 0> + constexpr PointerType operator->() const noexcept + { + T tmp = m_current; + return tmp; + } + + template , int> = 0> + constexpr PointerType operator->() const noexcept + { + T tmp = m_current; + return tmp.operator->(); + } + + constexpr ReferenceType operator*() const noexcept + { + T tmp = m_current; + return *tmp; + } + + constexpr ReferenceType operator[](DifferenceType diff) const noexcept + { + return m_current[static_cast(-diff)]; + } + + constexpr ThisType& operator++() noexcept + { + --m_current; + return *this; + } + + constexpr ThisType operator++(int) noexcept + { + return ThisType(m_current--); + } + + constexpr ThisType& operator--() noexcept + { + ++m_current; + return *this; + } + + constexpr ThisType operator--(int) noexcept + { + return ThisType(m_current++); + } + + constexpr ThisType& operator+=(DifferenceType diff) noexcept + { + m_current -= diff; + return *this; + } + + constexpr ThisType& operator-=(DifferenceType diff) noexcept + { + m_current += diff; + return *this; + } + + constexpr ThisType operator+(DifferenceType diff) const noexcept + { + return ThisType(m_current - diff); + } + + constexpr ThisType operator-(DifferenceType diff) const noexcept + { + return ThisType(m_current + diff); + } + + const T& Base() const noexcept + { + return m_current; + } + + const T& base() const noexcept + { + return Base(); + } + +protected: + + T m_current; +}; + +template +constexpr inline bool operator==(const ReverseIterator& left, + const ReverseIterator& right) noexcept +{ + return (left.Base() == right.Base()); +} + +template +constexpr inline bool operator!=(const ReverseIterator& left, + const ReverseIterator& right) noexcept +{ + return (left.Base() != right.Base()); +} + +template +constexpr inline bool operator<(const ReverseIterator& left, + const ReverseIterator& right) noexcept +{ + return (left.Base() < right.Base()); +} + +template +constexpr inline bool operator>(const ReverseIterator& left, + const ReverseIterator& right) noexcept +{ + return (right.Base() > left.Base()); +} + +template +constexpr inline bool operator<=(const ReverseIterator& left, + const ReverseIterator& right) noexcept +{ + return (left.Base() <= right.Base()); +} + +template +constexpr inline bool operator>=(const ReverseIterator& left, + const ReverseIterator& right) noexcept +{ + return (left.Base() >= right.Base()); +} + +template +constexpr inline typename ReverseIterator::DifferenceType operator-( + const ReverseIterator& left, const ReverseIterator& right) noexcept +{ + return (left.Base() + right.Base()); +} + +template +constexpr inline typename ReverseIterator::DifferenceType operator+( + const ReverseIterator& left, const ReverseIterator& right) noexcept +{ + return (left.Base() - right.Base()); +} + +} // namespace rad diff --git a/radiant/Locks.h b/radiant/Locks.h new file mode 100644 index 0000000..e2dd3b1 --- /dev/null +++ b/radiant/Locks.h @@ -0,0 +1,227 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/TypeTraits.h" + +namespace rad +{ + +// Lock guards implementing management of locking and unlocking of locks. Locks +// are expected to implement the following interface to be compatible with these +// guards: +// +// void Unlock(); +// +// and one or both of: +// +// void LockExclusive(); +// void LockShared(); +// +// Lock guards are not allowed to fail lock acquisition in construction, and so +// by extension, LockExclusive() and LockShared() cannot fail. + +/// @brief Lock guard to acquire a lock exclusively. Guarantees lock is acquired +/// in construction. +template +class RAD_NODISCARD LockExclusive final +{ +public: + + using LockType = TLock; + + RAD_NOT_COPYABLE(LockExclusive); + + /// @brief Acquire lock exclusively during construction + LockExclusive(LockType& lock) noexcept(noexcept(lock.LockExclusive())) + : m_lock(lock) + { + m_lock.LockExclusive(); + } + + ~LockExclusive() + { + m_lock.Unlock(); + } + +private: + + LockType& m_lock; +}; + +/// @brief Lock guard to acquire a lock shared. Guarantees lock is acquired in +/// construction. +template +class RAD_NODISCARD LockShared final +{ +public: + + using LockType = TLock; + + RAD_NOT_COPYABLE(LockShared); + + /// @brief Acquire lock shared during construction + LockShared(LockType& lock) noexcept(noexcept(lock.LockShared())) + : m_lock(lock) + { + m_lock.LockShared(); + } + + ~LockShared() + { + m_lock.Unlock(); + } + +private: + + LockType& m_lock; +}; + +/// @brief Tag-type to indicate Relockable guards to defer locking to after +/// construction with a manual call to Lock. +struct DeferLockingTag +{ +}; + +RAD_INLINE_VAR constexpr DeferLockingTag DeferLocking{}; + +/// @brief Lock guard to acquire a lock exclusively and allow Unlock() and +/// Lock() in an RAII-safe manner mid-use. +/// @details Acquires the given lock exclusively in construction unless the +/// DeferLocking tag is specified. In the DeferLocking case, Lock() must be +/// called to acquire the lock. +template +class RAD_NODISCARD RelockableExclusive final +{ +public: + + using LockType = TLock; + + RAD_NOT_COPYABLE(RelockableExclusive); + + /// @brief Automatically acquire lock during construction + RelockableExclusive(LockType& lock) noexcept(noexcept(lock.LockExclusive())) + : m_lock(lock), + m_acquired(true) + { + m_lock.LockExclusive(); + } + + /// @brief Defer acquiring the lock to a manual call to Lock() + RelockableExclusive(LockType& lock, DeferLockingTag) noexcept + : m_lock(lock), + m_acquired(false) + { + } + + ~RelockableExclusive() + { + if (m_acquired) + { + m_lock.Unlock(); + } + } + + /// @brief Releases the lock. Destruction will not release the lock again. + /// @warning It is not allowed to call Unlock() if the lock is not acquired. + void Unlock() noexcept(noexcept(DeclVal().Unlock())) + { + RAD_ASSERT(m_acquired); + m_acquired = false; + m_lock.Unlock(); + } + + /// @brief Acquires the lock exclusively. The lock will automatically be + /// released upon destruction. + /// @warning It is not allowed to call Lock() if the lock is already + /// acquired. + void Lock() noexcept(noexcept(DeclVal().LockExclusive())) + { + RAD_ASSERT(!m_acquired); + m_lock.LockExclusive(); + m_acquired = true; + } + +private: + + LockType& m_lock; + bool m_acquired; +}; + +/// @brief Lock guard to acquire a lock shared and allow Unlock() and Lock() +/// in an RAII-safe manner mid-use. +/// @details Acquires the given lock shared in construction unless the +/// DeferLocking tag is specified. In the DeferLocking case, Lock() must be +/// called to acquire the lock. +template +class RAD_NODISCARD RelockableShared final +{ +public: + + using LockType = TLock; + + RAD_NOT_COPYABLE(RelockableShared); + + /// @brief Automatically acquire lock during construction + RelockableShared(LockType& lock) noexcept(noexcept(lock.LockShared())) + : m_lock(lock), + m_acquired(true) + { + m_lock.LockShared(); + } + + /// @brief Defer acquiring the lock to a manual call to Lock() + RelockableShared(LockType& lock, DeferLockingTag) noexcept + : m_lock(lock), + m_acquired(false) + { + } + + /// @brief Defer acquiring the lock to a manual call to Lock() + ~RelockableShared() + { + if (m_acquired) + { + m_lock.Unlock(); + } + } + + /// @brief Releases the lock. Destruction will not release the lock again. + /// @warning It is not allowed to call Unlock() if the lock is not acquired. + void Unlock() noexcept(noexcept(DeclVal().Unlock())) + { + RAD_ASSERT(m_acquired); + m_acquired = false; + m_lock.Unlock(); + } + + /// @brief Acquires the lock shared. The lock will automatically be + /// released upon destruction. + /// @warning It is not allowed to call Lock() if the lock is already + /// acquired. + void Lock() noexcept(noexcept(DeclVal().LockShared())) + { + RAD_ASSERT(!m_acquired); + m_lock.LockShared(); + m_acquired = true; + } + +private: + + LockType& m_lock; + bool m_acquired; +}; + +} // namespace rad diff --git a/radiant/Memory.h b/radiant/Memory.h new file mode 100644 index 0000000..56e383d --- /dev/null +++ b/radiant/Memory.h @@ -0,0 +1,126 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/TotallyRad.h" + +// +// Users of Radiant may define their own default allocator. Radiant itself +// defines no default allocator. This is done to allow users of the library to +// enforce verbosity if they wish. +// +#ifdef RAD_DEFAULT_ALLOCATOR +#define _RAD_DEFAULT_ALLOCATOR(x) RAD_DEFAULT_ALLOCATOR +#define _RAD_DEFAULT_ALLOCATOR_EQ(x) = RAD_DEFAULT_ALLOCATOR +#else +#define _RAD_DEFAULT_ALLOCATOR(x) +#define _RAD_DEFAULT_ALLOCATOR_EQ(x) +#endif + +namespace rad +{ + +#if 0 +/// @brief Example Radiant compatible allocator. +/// @details Note that this allocator has no implementation and exists only as +/// an example for allocator implementors. Consider it a concept contract for an +/// allocator compatible with Radiant. +/// @tparam T The type of object to allocate. +template +class Allocator +{ +public: + + /// @brief Trait indicating if freeing memory is required. + /// @details If this is false, users of the allocator need not free memory + /// that was allocated. This trait enables certain run or compile time + /// optimizations. An example of an allocator that might leverage this trait + /// is a stack allocator. When false, an allocator implementor may implement + /// a no-op Free function. + static constexpr bool NeedsFree = true; + + /// @brief Trait indicating if the allocator supports reallocations. + /// @details If this is false, users of the allocator should not call + /// Realloc as the allocator does not support it. In some scenarios it may + /// be possible and more efficient to reallocate memory in place rather than + /// allocating new memory. When false, an allocator implementor may + /// implement a no-op Realloc function. + static constexpr bool HasRealloc = true; + + /// @brief Trait indicating if the allocator supports allocating by bytes. + /// @details If this is true, users of the allocator may call the Bytes + /// suffixed functions to allocate and free memory as bytes rather than as + /// number of T elements. An example is cases where some memory needs + /// allocated before or after T. However it may not be possible for all types + /// of allocators, such as a slab/lookaside allocator. When false, an + /// allocator may implement appropriate no-op functions for the various Bytes + /// suffixes function. + static constexpr bool HasAllocBytes = true; + + using ThisType = Allocator; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + ~Allocator() = default; + + constexpr Allocator() = default; + + constexpr Allocator(const Allocator&) noexcept = default; + + template + constexpr Allocator(const Allocator&) noexcept + { + } + + template + struct Rebind + { + using Other = Allocator; + }; + + /// @brief Frees memory allocated by Alloc. + /// @param ptr Pointer to the memory to free. + void Free(ValueType* ptr) noexcept; + + /// @brief Allocates memory for count number of T. + /// @param count The number of T to allocate memory for. + /// @return Pointer to the allocated memory. + ValueType* Alloc(SizeType count); + + /// @brief Reallocates memory for count number of T. + /// @param ptr Pointer to the memory to reallocate. If nullptr a new memory block is allocated. + /// @param count The number of T to allocate memory for. + /// @return Pointer to the reallocated memory. + ValueType* Realloc(ValueType* ptr, SizeType count); + + /// @brief Frees memory allocated by AllocBytes. + /// @param ptr Pointer to the memory to free. + void FreeBytes(void* ptr) noexcept; + + /// @brief Allocates memory for size number of bytes. + /// @param size The number of bytes to allocate memory for. + /// @return Pointer to the allocated memory. + void* AllocBytes(SizeType size); + + /// @brief Reallocates memory for size number of bytes. + /// @param ptr Pointer to the memory to reallocate. If nullptr a new memory block is allocated. + /// @param size The number of bytes to allocate memory for. + /// @return Pointer to the reallocated memory. + void* ReallocBytes(void* ptr, SizeType size); +}; +#endif + +} // namespace rad diff --git a/radiant/Res.h b/radiant/Res.h new file mode 100644 index 0000000..b4e03bf --- /dev/null +++ b/radiant/Res.h @@ -0,0 +1,54 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Result.h" + +namespace rad +{ + +enum class Error : uint16_t +{ + Unsuccessful = 1, + NoMemory, + LockNotGranted, + IntegerOverflow, + OutOfRange, + InvalidAddress, +}; + +template +using Res = Result; + +struct EmptyOkType +{ +}; + +using Err = Res; + +RAD_INLINE_VAR constexpr EmptyOkType NoError{}; + +RAD_INLINE_VAR constexpr ResultOkTagType ResOkTag{}; +RAD_INLINE_VAR constexpr ResultErrTagType ResErrTag{}; +RAD_INLINE_VAR constexpr ResultEmptyTagType ResVoidTag{}; +RAD_INLINE_VAR constexpr ResultEmptyTagType ResEmptyTag{}; + +template +using ResOk = ResultOk; + +template +using ResErr = ResultErr; + +} // namespace rad diff --git a/radiant/Result.h b/radiant/Result.h new file mode 100644 index 0000000..eb77d29 --- /dev/null +++ b/radiant/Result.h @@ -0,0 +1,1257 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/TypeWrapper.h" + +#include + +namespace rad +{ + +/// @brief States that a result can be in. Indicates what information the result +/// holds. A "valid" result is "Ok", an "errant" result is "Err", and an "Empty" +/// result holds neither "Ok" or "err" state. Generally, results are never in +/// the empty state, users need not check for it. +enum class ResultState : uint8_t +{ + Empty, + Valid, + Errant +}; + +/// @brief Result "Ok" tag type, indicator for emplacement construction. +struct ResultEmptyTagType +{ +}; + +/// @brief Result "Err" tag type, indicator for emplacement construction. +struct ResultOkTagType +{ +}; + +/// @brief Result "Empty" tag type, indicator for explicit default construction. +struct ResultErrTagType +{ +}; + +/// @brief Result "Ok" tag, indicator for emplacement construction. +RAD_INLINE_VAR constexpr ResultOkTagType ResultOkTag{}; + +/// @brief Result "Err" tag type, indicator for emplacement construction. +RAD_INLINE_VAR constexpr ResultErrTagType ResultErrTag{}; + +/// @brief Result "Empty" tag, indicator for explicit default construction. +RAD_INLINE_VAR constexpr ResultEmptyTagType ResultEmptyTag{}; + +namespace detail +{ + +template && IsTrivDtor)> +struct ResultStorage; + +/// @brief Result storage specialization for trivially destructible data. +/// @tparam T "Ok" type. +/// @tparam E "Err" type. +template +struct ResultStorage +{ + using OkWrap = TypeWrapper; + using ErrWrap = TypeWrapper; + using OkType = T; + using ErrType = E; + + ~ResultStorage() noexcept = default; + + constexpr ResultStorage() noexcept + : m_state(ResultState::Empty) +#if !RAD_CPP17 + , + m_default(false) +#endif + { + } + + constexpr ResultStorage(ResultEmptyTagType) noexcept + : ResultStorage() + { + } + + template + constexpr ResultStorage(ResultOkTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + : m_state(ResultState::Valid), + m_ok(Forward(args)...) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + template + constexpr ResultStorage(ResultErrTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + : m_state(ResultState::Errant), + m_err(Forward(args)...) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + constexpr void Construct(ResultEmptyTagType) noexcept + { + m_state = ResultState::Empty; + } + + template + constexpr void Construct(ResultOkTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + RAD_ASSERT(m_state == ResultState::Empty); + + new (&m_ok) OkWrap(Forward(args)...); + m_state = ResultState::Valid; + } + + template + constexpr void Construct(ResultErrTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + { + RAD_S_ASSERT_NOTHROW(noexcept(IsNoThrowCtor)); + RAD_ASSERT(m_state == ResultState::Empty); + + new (&m_err) ErrWrap(Forward(args)...); + m_state = ResultState::Errant; + } + + void Destruct() noexcept + { + m_state = ResultState::Empty; + } + + ResultState m_state; + + union + { + OkWrap m_ok; + ErrWrap m_err; + bool m_default; + }; +}; + +/// @brief Result storage specialization for non-trivially destructible data. +/// @tparam T "Ok" type. +/// @tparam E "Err" type. +template +struct ResultStorage +{ + using OkWrap = TypeWrapper; + using ErrWrap = TypeWrapper; + using OkType = T; + using ErrType = E; + + ~ResultStorage() noexcept + { + RAD_S_ASSERTMSG(IsNoThrowDtor && IsNoThrowDtor, + "Destructors should not throw!"); + + Destruct(); + } + + constexpr ResultStorage() noexcept + : m_state(ResultState::Empty) +#if !RAD_CPP17 + , + m_default(false) +#endif + { + } + + constexpr ResultStorage(ResultEmptyTagType) noexcept + : ResultStorage() + { + } + + template + constexpr ResultStorage(ResultOkTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + : m_state(ResultState::Valid), + m_ok(Forward(args)...) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + template + constexpr ResultStorage(ResultErrTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + : m_state(ResultState::Errant), + m_err(Forward(args)...) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + constexpr void Construct(ResultEmptyTagType) noexcept + { + m_state = ResultState::Empty; + } + + template + constexpr void Construct(ResultOkTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + RAD_ASSERT(m_state == ResultState::Empty); + + new (&m_ok) OkWrap(Forward(args)...); + m_state = ResultState::Valid; + } + + template + constexpr void Construct(ResultErrTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + RAD_ASSERT(m_state == ResultState::Empty); + + new (&m_err) ErrWrap(Forward(args)...); + m_state = ResultState::Errant; + } + + void Destruct() noexcept + { + if (m_state == ResultState::Valid) + { + m_ok.~OkWrap(); + m_state = ResultState::Empty; + } + else if (m_state == ResultState::Errant) + { + m_err.~ErrWrap(); + m_state = ResultState::Empty; + } + else + { + RAD_ASSERT(m_state == ResultState::Empty); + } + } + + ResultState m_state; + + union + { + OkWrap m_ok; + ErrWrap m_err; + bool m_default; + }; +}; + +template +struct ResultTypeWrapper : public TypeWrapper +{ + using TypeWrapper::TypeWrapper; +}; + +} // namespace detail + +/// @brief Container that associates a type with valid "Ok" state. +/// @tparam T Type to associate. +/// @details Generally used for "casting" to an "Ok" result when a result object +/// holds identical "Ok" and "Err" types. May also be used to declare some +/// commonly used "Ok" result. +template +using ResultOk = detail::ResultTypeWrapper; + +/// @brief Container that associates a type with valid "Err" state. +/// @tparam T Type to associate. +/// @details Generally used for "casting" to an "Err" result when a result +/// object holds identical "Ok" and "Err" types. May also be used to declare +/// some commonly used "Err" result. +template +using ResultErr = detail::ResultTypeWrapper; + +/// @brief Result object that stores a valid "Ok" result or an errant "Err". +/// This is useful for communicating either some valid result or an error. +/// @tparam T "Ok" type. +/// @tparam E "Err" type. +/// @details A result may also be "empty". This state exists for the sake of +/// default initialization and semantics/patterns of use. An empty result should +/// never be returned to a caller in practice and should only be in one of two +/// states, either "Ok" or "Err". Therefore a user need not check if a result is +/// "empty". +/// @code{.cpp} +/// #include +/// #include +/// +/// std::string g_HelloWorld; +/// +/// rad::Result GetGreeting() +/// { +/// if (g_HelloWold.empty()) +/// { +/// return -1; +/// } +/// +/// return g_HelloWorld; +/// } +/// +/// int main() +/// { +/// auto hello = GetGreeting(); +/// if (!hello.IsOk()) +/// { +/// return hello.Err(); +/// } +/// +/// std::cout << hello.Ok(); +/// +/// return 0; +/// } +/// @endcode +template +class RAD_NODISCARD Result final : private detail::ResultStorage +{ +public: + + using StorageType = detail::ResultStorage; + using ThisType = Result; + using typename StorageType::OkType; + using typename StorageType::ErrType; + + constexpr Result() noexcept + : StorageType(ResultEmptyTag) + { + } + + constexpr Result(const ResultEmptyTagType&) noexcept + : StorageType(ResultEmptyTag) + { + } + + // Ok ctors + template , int> = 0> + constexpr Result(ResultOkTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + : StorageType(ResultOkTag, Forward(args)...) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + + template < + typename U = OkType, + EnIf && IsCtor, + int> = 0> + constexpr Result(const OkType& value) noexcept( + IsNoThrowCtor) + : StorageType(ResultOkTag, value) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + + template && !IsRelated && + IsCtor, + int> = 0> + constexpr Result(OkType&& value) noexcept( + IsNoThrowCtor) + : StorageType(ResultOkTag, Forward(value)) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + +#if RAD_ENABLE_STD + template + constexpr Result(ResultOkTagType, std::initializer_list init) noexcept( + IsNoThrowCtor>) + : StorageType(ResultOkTag, Forward>(init)) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor>)); + } +#endif + + template + constexpr Result(const ResultOk& value) noexcept( + IsNoThrowCtor) + : StorageType(ResultOkTag, value.Get()) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + + template + constexpr Result(ResultOk&& value) noexcept( + IsNoThrowCtor&&>) + : StorageType(ResultOkTag, Forward>(value)) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor&&>)); + } + + // Err ctors + template , int> = 0> + constexpr Result(ResultErrTagType, TArgs&&... args) noexcept( + IsNoThrowCtor) + : StorageType(ResultErrTag, Forward(args)...) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + + template && + IsCtor, + int> = 0> + constexpr Result(const ErrType& value) noexcept( + IsNoThrowCtor) + : StorageType(ResultErrTag, value) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + + template && !IsRelated && + IsCtor, + int> = 0> + constexpr Result(ErrType&& value) noexcept( + IsNoThrowCtor) + : StorageType(ResultErrTag, Forward(value)) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + +#if RAD_ENABLE_STD + template + constexpr Result(ResultErrTagType, std::initializer_list init) noexcept( + IsNoThrowCtor>) + : StorageType(ResultErrTag, Forward>(init)) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor>)); + } +#endif + + template , int> = 0> + constexpr Result(const ResultErr& value) noexcept( + IsNoThrowCtor) + : StorageType(ResultErrTag, value.Get()) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor)); + } + + template , int> = 0> + constexpr Result(ResultErr&& value) noexcept( + IsNoThrowCtor&&>) + : StorageType(ResultErrTag, Forward>(value)) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor&&>)); + } + + // Copy/Move ctors + constexpr Result(const Result& r) noexcept( + noexcept(DeclVal()->CopyCtor(DeclVal()))) + : StorageType() + { + RAD_S_ASSERT_NOTHROW(noexcept(CopyCtor(r))); + CopyCtor(r); + } + + constexpr Result(Result&& r) noexcept( + noexcept(DeclVal()->MoveCtor(DeclVal()))) + : StorageType() + { + RAD_S_ASSERT_NOTHROW(noexcept(MoveCtor(r))); + MoveCtor(r); + } + + template || IsLRefBindable) && + (IsCtor || IsLRefBindable), + int> = 0> + constexpr Result(Result& r) noexcept( + noexcept(DeclVal()->CopyCtor(r))) + : StorageType() + { + RAD_S_ASSERT_NOTHROW(noexcept(CopyCtor(r))); + CopyCtor(r); + } + + template < + typename O, + typename F, + EnIf<(IsCtor && IsCtor), int> = 0> + constexpr Result(const Result& r) noexcept( + noexcept(DeclVal()->CopyCtor(r))) + : StorageType() + { + RAD_S_ASSERT_NOTHROW(noexcept(CopyCtor(r))); + CopyCtor(r); + } + + template && IsCtor), int> = 0> + constexpr Result(Result&& r) noexcept( + noexcept(DeclVal()->MoveCtor(r))) + : StorageType() + { + RAD_S_ASSERT_NOTHROW(noexcept(MoveCtor(r))); + MoveCtor(r); + } + + // Assignment + Result& Assign(const Result& r) noexcept( + noexcept(DeclVal()->Copy(r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Copy(r))); + Copy(r); + return *this; + } + + Result& Assign(Result&& r) noexcept( + noexcept(DeclVal()->MoveAssign(Forward(r)))) + { + RAD_S_ASSERT_NOTHROW(noexcept(MoveAssign(Forward(r)))); + MoveAssign(Forward(r)); + return *this; + } + + template , int> = 0> + constexpr Result& Assign(const OkType& r) noexcept( + noexcept(DeclVal()->m_ok = r) && // + noexcept(DeclVal()->Construct(ResultOkTag, r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(this->m_ok = r) && + noexcept(Construct(ResultOkTag, r))); + if (IsOk()) + { + this->m_ok = r; + } + else + { + Destruct(); + Construct(ResultOkTag, r); + } + return *this; + } + + template && !IsRelated, int> = 0> + constexpr Result& Assign(OkType&& r) noexcept( + noexcept(DeclVal()->m_ok = Forward(r)) && // + noexcept(DeclVal()->Construct(ResultOkTag, + Forward(r)))) + { + RAD_S_ASSERT_NOTHROW( + noexcept(this->m_ok = Forward(r)) && + noexcept(Construct(ResultOkTag, Forward(r)))); + if (IsOk()) + { + this->m_ok = Forward(r); + } + else + { + Destruct(); + Construct(ResultOkTag, Forward(r)); + } + return *this; + } + + template , int> = 0> + constexpr Result& Assign(const ErrType& r) noexcept( + noexcept(DeclVal()->m_err = r) && // + noexcept(DeclVal()->Construct(ResultErrTag, r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(this->m_err = r) && + noexcept(Construct(ResultErrTag, r))); + if (IsErr()) + { + this->m_err = r; + } + else + { + Destruct(); + Construct(ResultErrTag, r); + } + return *this; + } + + template && !IsRelated, int> = 0> + constexpr Result& Assign(ErrType&& r) noexcept( + noexcept(DeclVal()->m_err = Forward(r)) && // + noexcept(DeclVal()->Construct(ResultErrTag, + Forward(r)))) + { + RAD_S_ASSERT_NOTHROW( + noexcept(this->m_err = Forward(r)) && + noexcept(Construct(ResultErrTag, Forward(r)))); + if (IsErr()) + { + this->m_err = Forward(r); + } + else + { + Destruct(); + Construct(ResultErrTag, Forward(r)); + } + return *this; + } + + template + constexpr Result& Assign(const ResultOk& r) noexcept( + noexcept(DeclVal()->m_ok = r.Get()) && // + noexcept(DeclVal()->Construct(ResultOkTag, r.Get()))) + { + RAD_S_ASSERT_NOTHROW(noexcept(this->m_ok = r.Get()) && + noexcept(Construct(ResultOkTag, r.Get()))); + if (IsOk()) + { + this->m_ok = r.Get(); + } + else + { + Destruct(); + Construct(ResultOkTag, r.Get()); + } + return *this; + } + + template + constexpr Result& Assign(ResultOk&& r) noexcept( + noexcept(DeclVal()->m_ok = Forward>(r).Get()) && // + noexcept(DeclVal()->Construct(ResultOkTag, + Forward>(r).Get()))) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(this->m_ok = Forward>(r) .Get()) && + noexcept(Construct(ResultOkTag, Forward>(r).Get()))); + // clang-format on + if (IsOk()) + { + this->m_ok = Forward>(r).Get(); + } + else + { + Destruct(); + Construct(ResultOkTag, Forward>(r).Get()); + } + return *this; + } + + template + constexpr Result& Assign(const ResultErr& r) noexcept( + noexcept(DeclVal()->m_err = r.Get()) && // + noexcept(DeclVal()->Construct(ResultErrTag, r.Get()))) + { + RAD_S_ASSERT_NOTHROW(noexcept(this->m_err = r.Get()) && + noexcept(Construct(ResultErrTag, r.Get()))); + if (IsErr()) + { + this->m_err = r.Get(); + } + else + { + Destruct(); + Construct(ResultErrTag, r.Get()); + } + return *this; + } + + template + constexpr Result& Assign(ResultErr&& r) noexcept( + noexcept( + DeclVal()->m_err = Forward>(r).Get()) && // + noexcept(DeclVal()->Construct(ResultErrTag, + Forward>(r).Get()))) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(this->m_err = Forward>(r).Get()) && + noexcept(Construct(ResultErrTag, Forward>(r).Get()))); + // clang-format on + if (IsErr()) + { + this->m_err = Forward>(r).Get(); + } + else + { + Destruct(); + Construct(ResultErrTag, Forward>(r).Get()); + } + return *this; + } + + constexpr Result& operator=(const Result& r) noexcept( + noexcept(DeclVal()->Assign(r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(r))); + return Assign(r); + } + + constexpr Result& operator=(Result&& r) noexcept( + noexcept(DeclVal()->Assign(Forward(r)))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(Forward(r)))); + return Assign(Forward(r)); + } + + template , int> = 0> + constexpr Result& operator=(const OkType& r) noexcept( + noexcept(DeclVal()->Assign(r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(r))); + return Assign(r); + } + + template && !IsRelated, int> = 0> + constexpr Result& operator=(OkType&& r) noexcept( + noexcept(DeclVal()->Assign(Forward(r)))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(Forward(r)))); + return Assign(Forward(r)); + } + + template , int> = 0> + constexpr Result& operator=(const ErrType& r) noexcept( + noexcept(DeclVal()->Assign(r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(r))); + return Assign(r); + } + + template && !IsRelated, int> = 0> + constexpr Result& operator=(ErrType&& r) noexcept( + noexcept(DeclVal()->Assign(Forward(r)))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(Forward(r)))); + return Assign(Forward(r)); + } + + template + constexpr Result& operator=(const ResultOk& value) noexcept( + noexcept(DeclVal()->Assign(value))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(value))); + return Assign(value); + } + + template + constexpr Result& operator=(ResultOk&& value) noexcept( + noexcept(DeclVal()->Assign(Forward>(value)))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(Forward>(value)))); + return Assign(Forward>(value)); + } + + template + constexpr Result& operator=(const ResultErr& value) noexcept( + noexcept(DeclVal()->Assign(value))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(value))); + return Assign(value); + } + + template + constexpr Result& operator=(ResultErr&& value) noexcept( + noexcept(DeclVal()->Assign(Forward>(value)))) + { + RAD_S_ASSERT_NOTHROW(noexcept(Assign(Forward>(value)))); + return Assign(Forward>(value)); + } + + constexpr explicit operator bool() const noexcept + { + return IsOk(); + } + + constexpr bool IsOk() const noexcept + { + return State() == ResultState::Valid; + } + + constexpr bool IsErr() const noexcept + { + return State() == ResultState::Errant; + } + + constexpr bool IsEmpty() const noexcept + { + return State() == ResultState::Empty; + } + + constexpr ResultState State() const noexcept + { + return this->m_state; + } + + constexpr OkType& Ok() & noexcept + { + RAD_ASSERT(IsOk()); + return this->m_ok.Get(); + } + + constexpr const OkType& Ok() const& noexcept + { + RAD_ASSERT(IsOk()); + return this->m_ok.Get(); + } + + constexpr OkType&& Ok() && noexcept + { + RAD_ASSERT(IsOk()); + return Move(this->m_ok).Get(); + } + + constexpr RemoveRef* operator->() noexcept + { + RAD_ASSERT(IsOk()); + return &this->m_ok.Get(); + } + + constexpr const RemoveRef* operator->() const noexcept + { + RAD_ASSERT(IsOk()); + return &this->m_ok.Get(); + } + + constexpr OkType& operator*() noexcept + { + RAD_ASSERT(IsOk()); + return this->m_ok.Get(); + } + + constexpr const OkType& operator*() const noexcept + { + RAD_ASSERT(IsOk()); + return this->m_ok.Get(); + } + + constexpr ErrType& Err() & noexcept + { + RAD_ASSERT(IsErr()); + return this->m_err.Get(); + } + + constexpr const ErrType& Err() const& noexcept + { + RAD_ASSERT(IsErr()); + return this->m_err.Get(); + } + + constexpr ErrType&& Err() && noexcept + { + RAD_ASSERT(IsErr()); + return Move(this->m_err).Get(); + } + + template , const U&>, int> = 0> + constexpr Decay Or(const U& value) const + noexcept(IsNoThrowCtor, OkType&> && + IsNoThrowCtor, U&&>) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor, OkType&> && + IsNoThrowCtor, U&&>)); + if (IsOk()) + { + return Ok(); + } + return value; + } + + template , U&&>, int> = 0> + constexpr Decay Or(U&& value) const + noexcept(IsNoThrowCtor, OkType&> && + IsNoThrowCtor, U&&>) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor, OkType&> && + IsNoThrowCtor, U&&>)); + if (IsOk()) + { + return Ok(); + } + return Forward(value); + } + + template + constexpr Result OnOk(U&& value) const noexcept( + IsNoThrowCtor, ResultOkTagType, U&&> && + IsNoThrowCtor, ResultErrTagType, const ErrType&>) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor, ResultOkTagType, U&&> && + IsNoThrowCtor, + ResultErrTagType, + const ErrType&>)); + if (IsErr()) + { + return Result(ResultErrTag, Err()); + } + return Result(ResultOkTag, Forward(value)); + } + + template + constexpr Result OnErr(U&& value) const noexcept( + IsNoThrowCtor, ResultErrTagType, U&&> && + IsNoThrowCtor, ResultOkTagType, const OkType&>) + { + RAD_S_ASSERT_NOTHROW( + (IsNoThrowCtor, ResultErrTagType, U&&> && + IsNoThrowCtor, ResultOkTagType, const OkType&>)); + if (IsOk()) + { + return Result(ResultOkTag, Ok()); + } + return Result(ResultErrTag, Forward(value)); + } + +private: + + using StorageType::Construct; + using StorageType::Destruct; + + template + friend class Result; + + template + constexpr void CopyCtor(R& r) noexcept( + // clang-format off + noexcept(DeclVal()->Construct(ResultOkTag, r.m_ok)) && + noexcept(DeclVal()->Construct(ResultErrTag, r.m_err)) && + noexcept(DeclVal()->Construct(ResultEmptyTag)) + // clang-format on + ) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(Construct(ResultOkTag, r.m_ok)) && + noexcept(Construct(ResultErrTag, r.m_err)) && + noexcept(Construct(ResultEmptyTag))); + // clang-format on + if (r.IsOk()) + { + Construct(ResultOkTag, r.m_ok); + } + else if (r.IsErr()) + { + Construct(ResultErrTag, r.m_err); + } + else + { + Construct(ResultEmptyTag); + } + } + + template + constexpr void CopyCtor(const R& r) noexcept( + // clang-format off + noexcept(DeclVal()->Construct(ResultOkTag, r.m_ok)) && + noexcept(DeclVal()->Construct(ResultErrTag, r.m_err)) && + noexcept(DeclVal()->Construct(ResultEmptyTag)) + // clang-format on + ) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(Construct(ResultOkTag, r.m_ok)) && + noexcept(Construct(ResultErrTag, r.m_err)) && + noexcept(Construct(ResultEmptyTag))); + // clang-format on + if (r.IsOk()) + { + Construct(ResultOkTag, r.m_ok); + } + else if (r.IsErr()) + { + Construct(ResultErrTag, r.m_err); + } + else + { + Construct(ResultEmptyTag); + } + } + + template + constexpr void Copy(const R& r) noexcept( + // clang-format off + noexcept(DeclVal()->CopyCtor(DeclVal())) && + noexcept(DeclVal()->m_ok = r.m_ok) && + noexcept(DeclVal()->m_err = r.m_err) + // clang-format on + ) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(CopyCtor(r)) && + noexcept(this->m_ok = r.m_ok) && + noexcept(this->m_err = r.m_err)); + // clang-format on + if (State() != r.State()) + { + Destruct(); + CopyCtor(r); + } + else if (IsOk()) + { + this->m_ok = r.m_ok; + } + else if (IsErr()) + { + this->m_err = r.m_err; + } + } + + template + constexpr void MoveCtor(R&& r) noexcept( + // clang-format off + noexcept(DeclVal()->Construct(ResultOkTag, Move(r).Ok())) && + noexcept(DeclVal()->Construct(ResultErrTag, Move(r).Err())) && + noexcept(DeclVal()->Construct(ResultEmptyTag)) + // clang-format on + ) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(Construct(ResultOkTag, Move(r).Ok())) && + noexcept(Construct(ResultErrTag, Move(r).Err())) && + noexcept(Construct(ResultEmptyTag))); + // clang-format on + if (r.IsOk()) + { + Construct(ResultOkTag, Move(r).Ok()); + } + else if (r.IsErr()) + { + Construct(ResultErrTag, Move(r).Err()); + } + else + { + Construct(ResultEmptyTag); + } + r.Destruct(); + } + + template + constexpr void MoveAssign(R&& r) noexcept( + // clang-format off + noexcept(DeclVal()->MoveCtor(Move(r))) && + noexcept(DeclVal()->m_ok = Move(r).Ok()) && + noexcept(DeclVal()->m_err = Move(r).Err()) + // clang-format on + ) + { + // clang-format off + RAD_S_ASSERT_NOTHROW( + noexcept(MoveCtor(Move(r))) && + noexcept(this->m_ok = Move(r).Ok()) && + noexcept(this->m_err = Move(r).Err())); + // clang-format on + if (State() != r.State()) + { + Destruct(); + MoveCtor(Move(r)); + return; + } + + if (IsOk()) + { + this->m_ok = Move(r).Ok(); + } + else if (IsErr()) + { + this->m_err = Move(r).Err(); + } + r.Destruct(); + } +}; + +template +constexpr inline bool operator<(const Result& Left, + const Result& Right) +{ + if (Left.IsOk() && Right.IsOk()) + { + return (Left.Ok() < Right.Ok()); + } + if (Left.IsErr() && Right.IsErr()) + { + return (Left.Err() < Right.Err()); + } + return (Left.State() < Right.State()); +} + +template +constexpr inline bool operator==(const Result& Left, + const Result& Right) +{ + if (Left.IsOk() && Right.IsOk()) + { + return (Left.Ok() == Right.Ok()); + } + if (Left.IsErr() && Right.IsErr()) + { + return (Left.Err() == Right.Err()); + } + return (Left.State() == Right.State()); +} + +template +constexpr inline bool operator!=(const Result& Left, + const Result& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator==(const Result& Left, const T2& Right) +{ + if (!Left.IsOk()) + { + return false; + } + return (Left.Ok() == Right); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator==(const T2& Left, const Result& Right) +{ + return (Right == Left); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator!=(const Result& Left, const T2& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator!=(const T2& Left, const Result& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator==(const Result& Left, const E2& Right) +{ + if (!Left.IsErr()) + { + return false; + } + return (Left.Err() == Right); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator==(const E2& Left, const Result& Right) +{ + return (Right == Left); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator!=(const Result& Left, const E2& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf && IsSame, Decay>, bool> +operator!=(const E2& Left, const Result& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf, Decay>, bool> operator==( + const Result& Left, const ResultOk& Right) +{ + if (!Left.IsOk()) + { + return false; + } + return (Left.Ok() == Right.Get()); +} + +template +constexpr inline EnIf, Decay>, bool> operator==( + const ResultOk& Left, const Result& Right) +{ + return (Right == Left); +} + +template +constexpr inline EnIf, Decay>, bool> operator!=( + const Result& Left, const ResultOk& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf, Decay>, bool> operator!=( + const ResultOk& Left, const Result& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf, Decay>, bool> operator==( + const Result& Left, const ResultErr& Right) +{ + if (!Left.IsErr()) + { + return false; + } + return (Left.Err() == Right.Get()); +} + +template +constexpr inline EnIf, Decay>, bool> operator==( + const ResultErr& Left, const Result& Right) +{ + return (Right == Left); +} + +template +constexpr inline EnIf, Decay>, bool> operator!=( + const Result& Left, const ResultErr& Right) +{ + return !(Left == Right); +} + +template +constexpr inline EnIf, Decay>, bool> operator!=( + const ResultErr& Left, const Result& Right) +{ + return !(Left == Right); +} + +} // namespace rad diff --git a/radiant/ScopeExit.h b/radiant/ScopeExit.h new file mode 100644 index 0000000..61717c4 --- /dev/null +++ b/radiant/ScopeExit.h @@ -0,0 +1,128 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Utility.h" + +namespace rad +{ + +/// @brief General purpose scope guard which invokes a callable target when +/// scope is exited as long as it hasn't been released. +template +class ScopeExit +{ +public: + + RAD_NOT_COPYABLE(ScopeExit); + + ~ScopeExit() + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERTMSG(noexcept(m_fn()), "Destructors should not throw!"); + + if (m_call) + { + m_fn(); + } + } + + template + explicit ScopeExit(Fn&& fn) noexcept(IsNoThrowCtor) + : m_fn(Forward(fn)) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + ScopeExit(ScopeExit&& other) noexcept(IsNoThrowMoveCtor) + : m_fn(Move(other.m_fn)) + { + RAD_S_ASSERT_NOTHROW(IsNoThrowMoveCtor); + } + + void Release() noexcept + { + m_call = false; + } + +private: + + bool m_call{ true }; + const T m_fn; +}; + +/// @brief General purpose scope guard which invokes a callable target when +/// scope is exited. +/// @details A scope guard is irrevocable, in contrast to a scope exit which may +/// be revoked by releasing it. +template +class ScopeGuard +{ +public: + + RAD_NOT_COPYABLE(ScopeGuard); + + ~ScopeGuard() + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + RAD_S_ASSERTMSG(noexcept(m_fn()), "Destructors should not throw!"); + + m_fn(); + } + + template + explicit ScopeGuard(Fn&& fn) noexcept(IsNoThrowCtor) + : m_fn(Forward(fn)) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + ScopeGuard(ScopeGuard&& other) noexcept(IsNoThrowMoveCtor) + : m_fn(Move(other.m_fn)) + { + RAD_S_ASSERT_NOTHROW(IsNoThrowMoveCtor); + } + +private: + + const T m_fn; +}; + +/// @brief Makes a ScopeExit object. +/// @tparam Fn Callable type to invoke at scope exit. +/// @param fn Callback target to store to be invoked at scope exit. +/// @return ScopeExit object. +template +ScopeExit MakeScopeExit(Fn&& fn) +{ + return ScopeExit(Forward(fn)); +} + +/// @brief Makes a ScopeGuard object. +/// @tparam Fn Callable type to invoke at scope exit. +/// @param fn Callback target to store to be invoked at scope exit. +/// @return ScopeGuard object. +template +ScopeGuard MakeScopeGuard(Fn&& fn) +{ + return ScopeGuard(Forward(fn)); +} + +} // namespace rad + +#define RAD_SCOPE_GUARD_NAME_STRINGIFY(suffix) __scopeGuard##suffix +#define RAD_SCOPE_GUARD_NAME(suffix) RAD_SCOPE_GUARD_NAME_STRINGIFY(suffix) +#define RAD_SCOPE_GUARD(fn) \ + const auto RAD_SCOPE_GUARD_NAME(__LINE__) = rad::MakeScopeGuard(fn) diff --git a/radiant/SharedPtr.h b/radiant/SharedPtr.h new file mode 100644 index 0000000..f18d168 --- /dev/null +++ b/radiant/SharedPtr.h @@ -0,0 +1,1306 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Memory.h" +#include "radiant/Atomic.h" +#include "radiant/Locks.h" +#include "radiant/EmptyOptimizedPair.h" + +namespace rad +{ + +namespace detail +{ + +/// @brief Internal use only. Reference counting management for smart pointers. +/// @tparam Atomic counter type +template +class _TPtrRefCount final +{ +public: + + _TPtrRefCount() noexcept + : m_strongCount(1), + m_weakCount(1) + { + } + + void Increment() const noexcept + { + m_strongCount.FetchAdd(1, rad::MemOrderRelaxed); + } + + bool Decrement() const noexcept + { + return m_strongCount.FetchSub(1, rad::MemOrderAcqRel) == 1; + } + + void IncrementWeak() const noexcept + { + m_weakCount.FetchAdd(1, rad::MemOrderRelaxed); + } + + bool DecrementWeak() const noexcept + { + return m_weakCount.FetchSub(1, rad::MemOrderAcqRel) == 1; + } + + bool LockWeak() const noexcept + { + uint32_t count = m_strongCount.Load(rad::MemOrderRelaxed); + if (count == 0) + { + return false; + } + + atomic::LockRegion lockRegion; + do + { + if RAD_LIKELY (m_strongCount.CompareExchangeWeak( + count, + count + 1, + rad::MemOrderAcqRel, + rad::MemOrderRelaxed)) + { + return true; + } + } + while (count != 0); + + return false; + } + + uint32_t StrongCount() const + { + return m_strongCount.Load(rad::MemOrderRelaxed); + } + + uint32_t WeakCount() const + { + return m_weakCount.Load(rad::MemOrderRelaxed); + } + +private: + + mutable TAtomic m_strongCount; + mutable TAtomic m_weakCount; +}; + +using _PtrRefCount = _TPtrRefCount>; + +class _PtrBlockBase +{ +public: + + virtual ~_PtrBlockBase() noexcept + { + } + + _PtrBlockBase() noexcept = default; + + RAD_NOT_COPYABLE(_PtrBlockBase); + + virtual void Destroy() const noexcept = 0; + virtual void Delete() const noexcept = 0; + + const _PtrRefCount& RefCount() const noexcept + { + return m_refcount; + } + + void Acquire() const noexcept + { + RefCount().Increment(); + } + + void AcquireWeak() const noexcept + { + RefCount().IncrementWeak(); + } + + bool LockWeak() const noexcept + { + return RefCount().LockWeak(); + } + + void Release() const noexcept + { + if (RefCount().Decrement()) + { + Destroy(); + ReleaseWeak(); + } + } + + void ReleaseWeak() const noexcept + { + if (RefCount().DecrementWeak()) + { + Delete(); + } + } + + uint32_t UseCount() const + { + return RefCount().StrongCount(); + } + + uint32_t WeakCount() const + { + return RefCount().WeakCount(); + } + +private: + + _PtrRefCount m_refcount; +}; + +/// @brief Internal use only. SharedPtr control block +/// @tparam T Value type +/// @tparam TAlloc Allocator type +template +class _PtrBlock final : public _PtrBlockBase +{ +public: + + using AllocatorType = typename TAlloc::template Rebind<_PtrBlock>::Other; + using ValueType = T; + using PairType = EmptyOptimizedPair; + + template + _PtrBlock(const AllocatorType& alloc, TArgs&&... args) noexcept( + noexcept(PairType(alloc, Forward(args)...))) + : _PtrBlockBase(), + m_pair(alloc, Forward(args)...) + { + } + + void Destroy() const noexcept override + { + m_pair.~PairType(); + } + + void Delete() const noexcept override + { + auto _this = const_cast<_PtrBlock*>(this); + _this->Allocator().Free(_this); + } + + AllocatorType& Allocator() noexcept + { + return m_pair.First(); + } + + ValueType& Value() noexcept + { + return m_pair.Second(); + } + + const ValueType& Value() const noexcept + { + return m_pair.Second(); + } + +private: + + mutable PairType m_pair; +}; + +struct _AllocateSharedImpl; + +} // namespace detail + +template +class WeakPtr; + +template +class AtomicSharedPtr; + +template +class AtomicWeakPtr; + +/// @brief Smart pointer implementing shared ownership mechanics. +/// @tparam T Value type to point to +template +class SharedPtr final +{ + +public: + + using ValueType = T; + + ~SharedPtr() + { + Reset(); + } + + /// @brief Construct empty SharedPtr (nullptr) + SharedPtr() noexcept + : m_block(), + m_ptr() + { + } + + /// @brief Construct empty SharedPtr (nullptr) + SharedPtr(rad::nullptr_t) noexcept + : SharedPtr() + { + } + + /// @brief Construct a new SharedPtr taking an additional reference to an + /// existing pointer. + /// @param r Existing pointer + SharedPtr(const SharedPtr& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + RAD_S_ASSERT(noexcept(m_block->Acquire())); + + if (m_block) + { + m_block->Acquire(); + } + } + + /// @brief Construct a new SharedPtr by moving an existing SharedPtr. + /// @param r Existing pointer + SharedPtr(SharedPtr&& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + r.m_block = nullptr; + r.m_ptr = nullptr; + } + + /// @brief Construct a new SharedPtr from a convertible pointer. + /// @param r Existing pointer + template , int> = 0> + SharedPtr(const SharedPtr& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + RAD_S_ASSERT(noexcept(m_block->Acquire())); + + if (m_block) + { + m_block->Acquire(); + } + } + + /// @brief bool operator returning true if the pointer is not nullptr. + operator bool() const noexcept + { + return m_ptr != nullptr; + } + + /// @brief Retrieve a pointer to the stored object + ValueType* Get() const noexcept + { + return m_ptr; + } + + /// @brief Check if the pointer is not nullptr + bool operator!=(rad::nullptr_t) const noexcept + { + return *this; + } + + /// @brief Check if the pointer is nullptr + bool operator==(rad::nullptr_t) const noexcept + { + return !*this; + } + + /// @brief Dereference the pointer + T& operator*() noexcept + { + RAD_ASSERT(m_ptr != nullptr); + return *Get(); + } + + /// @brief Dereference the pointer + const T& operator*() const noexcept + { + RAD_ASSERT(m_ptr != nullptr); + return *Get(); + } + + /// @brief Dereference the pointer + T* operator->() noexcept + { + RAD_ASSERT(m_ptr != nullptr); + return Get(); + } + + /// @brief Dereference the pointer + const T* operator->() const noexcept + { + RAD_ASSERT(m_ptr != nullptr); + return Get(); + } + + /// @brief Take an additional reference to an existing pointer. Drops + /// reference to its current stored pointer if not nullptr. + SharedPtr& operator=(const SharedPtr& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->Release())); + RAD_S_ASSERT(noexcept(m_block->Acquire())); + + if RAD_LIKELY (&r != this) + { + if (m_block) + { + m_block->Release(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + + if (m_block) + { + m_block->Acquire(); + } + } + + return *this; + } + + /// @brief Take ownership of an existing pointer by move semantics. Drops + /// reference to its current stored pointer if not nullptr. + SharedPtr& operator=(SharedPtr&& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->Release())); + + if RAD_LIKELY (&r != this) + { + if (m_block) + { + m_block->Release(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + r.m_block = nullptr; + r.m_ptr = nullptr; + } + + return *this; + } + + /// @brief Take additional reference to an existing, convertible pointer. + /// Drops reference to its current stored pointer if not nullptr. + template , int> = 0> + SharedPtr& operator=(const SharedPtr& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->Release())); + RAD_S_ASSERT(noexcept(m_block->Acquire())); + + if (m_block) + { + m_block->Release(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + + if (m_block) + { + m_block->Acquire(); + } + + return *this; + } + + /// @brief Drop existing reference if a reference is held. + void Reset() noexcept + { + RAD_S_ASSERT(noexcept(m_block->Release())); + + if (m_block) + { + m_block->Release(); + m_block = nullptr; + m_ptr = nullptr; + } + } + + /// @brief Swaps the managed objects. + /// @param o Other shared ptr to swap with this. + void Swap(SharedPtr& o) noexcept + { + if RAD_LIKELY (&o != this) + { + auto block = m_block; + auto ptr = m_ptr; + m_block = o.m_block; + m_ptr = o.m_ptr; + o.m_block = block; + o.m_ptr = ptr; + } + } + + /// @brief Current refcount (this is inherently not thread-safe and only + /// exposed for testing) + uint32_t UseCount() const noexcept + { + return m_block ? m_block->UseCount() : 0; + } + + /// @brief Current WeakPtr refcount (this is inherently not thread-safe and + /// only exposed for testing) + uint32_t WeakCount() const noexcept + { + return m_block ? m_block->WeakCount() : 0; + } + +private: + + SharedPtr(detail::_PtrBlockBase* block, T* ptr) noexcept + : m_block(block), + m_ptr(ptr) + { + } + + template + friend class SharedPtr; + + template + friend class WeakPtr; + + friend class AtomicSharedPtr; + friend class AtomicWeakPtr; + + detail::_PtrBlockBase* m_block; + T* m_ptr; + + friend struct detail::_AllocateSharedImpl; +}; + +template +RAD_NODISCARD bool operator==(rad::nullptr_t, const SharedPtr& sp) noexcept +{ + return sp == nullptr; +} + +template +RAD_NODISCARD bool operator!=(rad::nullptr_t, const SharedPtr& sp) noexcept +{ + return sp != nullptr; +} + +template +RAD_NODISCARD bool operator==(const SharedPtr& l, + const SharedPtr& r) noexcept +{ + return l.Get() == r.Get(); +} + +template +RAD_NODISCARD bool operator!=(const SharedPtr& l, + const SharedPtr& r) noexcept +{ + return l.Get() != r.Get(); +} + +template +RAD_NODISCARD bool operator<(const SharedPtr& l, + const SharedPtr& r) noexcept +{ + return l.Get() < r.Get(); +} + +template +RAD_NODISCARD bool operator<=(const SharedPtr& l, + const SharedPtr& r) noexcept +{ + return l.Get() <= r.Get(); +} + +template +RAD_NODISCARD bool operator>(const SharedPtr& l, + const SharedPtr& r) noexcept +{ + return l.Get() > r.Get(); +} + +template +RAD_NODISCARD bool operator>=(const SharedPtr& l, + const SharedPtr& r) noexcept +{ + return l.Get() >= r.Get(); +} + +/// @brief Smart pointer implementing weak ownership mechanics. +/// @tparam T Value type to point to +template +class WeakPtr final +{ +public: + + using ValueType = T; + using SharedType = SharedPtr; + + ~WeakPtr() + { + if (m_block) + { + m_block->ReleaseWeak(); + } + } + + /// @brief Construct empty WeakPtr + WeakPtr() noexcept + : m_block(), + m_ptr() + { + } + + /// @brief Construct a new WeakPtr taking an additional weak reference to an + /// existing pointer. + /// @param r Existing pointer + WeakPtr(const WeakPtr& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->AcquireWeak(); + } + } + + /// @brief Construct a new WeakPtr taking an additional weak reference to an + /// existing pointer. + /// @param r Existing pointer + WeakPtr(const SharedType& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->AcquireWeak(); + } + } + + /// @brief Construct a new WeakPtr by moving an existing WeakPtr. + /// @param r Existing pointer + WeakPtr(WeakPtr&& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + r.m_block = nullptr; + r.m_ptr = nullptr; + } + + /// @brief Construct a new WeakPtr from a convertible pointer. + /// @param r Existing pointer + template , int> = 0> + WeakPtr(const WeakPtr& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->AcquireWeak(); + } + } + + /// @brief Construct a new WeakPtr from a convertible pointer. + /// @param r Existing pointer + template , int> = 0> + WeakPtr(const SharedPtr& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->AcquireWeak(); + } + } + + /// @brief Construct a new WeakPtr by moving an existing convertible + /// pointer. + /// @param r Existing pointer + template , int> = 0> + WeakPtr(WeakPtr&& r) noexcept + : m_block(r.m_block), + m_ptr(r.m_ptr) + { + r.m_block = nullptr; + r.m_ptr = nullptr; + } + + /// @brief Replaces the managed object with another. + /// @param r Other object to store in this. + /// @return Reference to this. + WeakPtr& operator=(const WeakPtr& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if RAD_LIKELY (&r != this) + { + if (m_block) + { + m_block->ReleaseWeak(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + + if (m_block) + { + m_block->AcquireWeak(); + } + } + + return *this; + } + + /// @brief Replaces the managed object with another. + /// @param r Other object to store in this. + /// @return Reference to this. + template , int> = 0> + WeakPtr& operator=(const WeakPtr& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->ReleaseWeak(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + + if (m_block) + { + m_block->AcquireWeak(); + } + + return *this; + } + + /// @brief Replaces the managed object with another. + /// @param r Other object to store in this. + /// @return Reference to this. + WeakPtr& operator=(const SharedType& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->ReleaseWeak(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + + if (m_block) + { + m_block->AcquireWeak(); + } + + return *this; + } + + /// @brief Replaces the managed object with another. + /// @param r Other object to store in this. + /// @return Reference to this. + template , int> = 0> + WeakPtr& operator=(const SharedPtr& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + RAD_S_ASSERT(noexcept(m_block->AcquireWeak())); + + if (m_block) + { + m_block->ReleaseWeak(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + + if (m_block) + { + m_block->AcquireWeak(); + } + + return *this; + } + + /// @brief Moves another managed object into this. + /// @param r Other object to move into this. + /// @return Reference to this. + WeakPtr& operator=(WeakPtr&& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + + if RAD_LIKELY (&r != this) + { + if (m_block) + { + m_block->ReleaseWeak(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + r.m_block = nullptr; + r.m_ptr = nullptr; + } + + return *this; + } + + /// @brief Moves another managed object into this. + /// @param r Other object to move into this. + /// @return Reference to this. + template , int> = 0> + WeakPtr& operator=(WeakPtr&& r) noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + + if (m_block) + { + m_block->ReleaseWeak(); + } + + m_block = r.m_block; + m_ptr = r.m_ptr; + r.m_block = nullptr; + r.m_ptr = nullptr; + + return *this; + } + + /// @brief Resets the referenced to the managed object. + void Reset() noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + + if (m_block) + { + m_block->ReleaseWeak(); + m_block = nullptr; + m_ptr = nullptr; + } + } + + /// @brief Swaps the managed objects. + /// @param o Other shared ptr to swap with this. + void Swap(WeakPtr& o) noexcept + { + if RAD_LIKELY (&o != this) + { + auto block = m_block; + auto ptr = m_ptr; + m_block = o.m_block; + m_ptr = o.m_ptr; + o.m_block = block; + o.m_ptr = ptr; + } + } + + /// @brief Retrieves the number of shared objects that manage the object. + /// @return Number of shared objects that manage the object. + uint32_t UseCount() const noexcept + { + return m_block ? m_block->UseCount() : 0; + } + + /// @brief Checks whether the referenced object was already deleted. + /// @return True if the object was already deleted, false otherwise. + bool Expired() const noexcept + { + return UseCount() == 0; + } + + /// @brief Creates a SharedPtr that manages the object. + /// @return SharedPtr that manages the object, nullptr if the object is + /// already destructed. + RAD_NODISCARD SharedType Lock() const noexcept + { + RAD_S_ASSERT(noexcept(m_block->ReleaseWeak())); + + if (m_block && m_block->LockWeak()) + { + return SharedType(m_block, m_ptr); + } + + return nullptr; + } + +private: + + WeakPtr(detail::_PtrBlockBase* block, T* ptr) noexcept + : m_block(block), + m_ptr(ptr) + { + } + + template + friend class WeakPtr; + + friend class AtomicWeakPtr; + + detail::_PtrBlockBase* m_block; + T* m_ptr; +}; + +namespace detail +{ +/// @brief Internal use only +struct _AllocateSharedImpl +{ + /// @brief RAII-safety wrapper helper + template + struct _AllocateSharedHelper + { + constexpr _AllocateSharedHelper(TAlloc& ta) noexcept + : alloc(ta), + block(nullptr) + { + } + + ~_AllocateSharedHelper() + { + if (block) + { + alloc.Free(block); + } + } + + TAlloc& alloc; + typename TAlloc::ValueType* block; + }; + + template + static inline SharedPtr AllocateShared(const TAlloc& alloc, + TArgs&&... args) // + noexcept( + noexcept(DeclVal::AllocatorType>() + .Alloc(1)) && + IsNoThrowCtor) + { + using BlockType = _PtrBlock; + typename BlockType::AllocatorType blockAlloc(alloc); + + _AllocateSharedHelper excSafe(blockAlloc); + + excSafe.block = blockAlloc.Alloc(1); + if RAD_LIKELY (excSafe.block != nullptr) + { + new (excSafe.block) BlockType(blockAlloc, Forward(args)...); + auto block = excSafe.block; + excSafe.block = nullptr; + return SharedPtr(block, &block->Value()); + } + return nullptr; + } +}; + +} // namespace detail + +/// @brief Constructs and wraps an object of type T in a SharedPtr with a custom +/// allocator. +/// @tparam T Type of object to construct +/// @tparam TAlloc Type of the custom allocator +/// @param alloc Allocator instance +/// @param args Arguments for T construction +/// @return A SharedPtr +template +SharedPtr AllocateShared(const TAlloc& alloc, TArgs&&... args) // + noexcept(noexcept(detail::_AllocateSharedImpl::AllocateShared( + alloc, Forward(args)...))) +{ + return detail::_AllocateSharedImpl::AllocateShared( + alloc, + Forward(args)...); +} + +#ifdef RAD_DEFAULT_ALLOCATOR +/// @brief Constructs and wraps an object of type T in a SharedPtr with the +/// default allocator. +/// @tparam T Type of object to construct +/// @tparam TAlloc Type of the custom allocator +/// @param args Arguments for T construction +/// @return A SharedPtr> +template +SharedPtr MakeShared(TArgs&&... args) noexcept( + noexcept(AllocateShared(DeclVal(), Forward(args)...))) +{ + TAlloc alloc; + return AllocateShared(alloc, Forward(args)...); +} +#endif + +namespace detail +{ + +template +class _LockablePtr +{ +public: + + // can consider making this 3 on 64-bit systems + static constexpr uintptr_t ExclusiveBit = 2; + static constexpr uintptr_t ExclusiveFlag = 1 << ExclusiveBit; + RAD_S_ASSERTMSG(alignof(T) >= ExclusiveFlag, + "low order bits are needed by LockablePtr"); + static constexpr uintptr_t SharedMax = ExclusiveFlag - 1; + static constexpr uintptr_t LockMask = (ExclusiveFlag | SharedMax); + static constexpr uintptr_t PtrMask = ~LockMask; + + using ThisType = _LockablePtr; + using ValueType = T; + using PointerType = T*; + + ~_LockablePtr() = default; + + constexpr _LockablePtr() noexcept = default; + + constexpr _LockablePtr(PointerType value) noexcept + : m_storage(reinterpret_cast(value)) + { + } + + RAD_NOT_COPYABLE(_LockablePtr); + + RAD_NODISCARD PointerType UnsafeGet() const noexcept + { + return reinterpret_cast(m_storage.Load(MemOrderRelaxed) & + PtrMask); + } + + void UnsafeSet(PointerType value) noexcept + { + auto ptr = m_storage.Load(MemOrderRelaxed); + m_storage.Store(reinterpret_cast(value) | (ptr & LockMask), + MemOrderRelaxed); + } + + void Unlock() noexcept + { + if (m_storage.Load(MemOrderRelaxed) & ExclusiveFlag) + { + m_storage.FetchAnd(~ExclusiveFlag, MemOrderRelease); + } + else + { + m_storage.FetchSub(1, MemOrderRelease); + } + } + + void LockShared() noexcept + { + auto ptr = m_storage.Load(MemOrderAcquire); + + for (;; RAD_YIELD_PROCESSOR()) + { + if ((ptr & LockMask) >= SharedMax) + { + continue; + } + + if RAD_LIKELY (m_storage.CompareExchangeWeak(ptr, + ptr + 1, + MemOrderAcqRel, + MemOrderRelaxed)) + { + break; + } + } + } + + void LockExclusive() noexcept + { + auto ptr = m_storage.Load(MemOrderAcquire); + + for (;; RAD_YIELD_PROCESSOR()) + { + if (ptr & ExclusiveFlag) + { + continue; + } + + if RAD_LIKELY (m_storage.CompareExchangeWeak(ptr, + ptr | ExclusiveFlag, + MemOrderAcqRel, + MemOrderRelaxed)) + { + break; + } + } + + for (; ptr & SharedMax; RAD_YIELD_PROCESSOR()) + { + ptr = m_storage.Load(MemOrderAcquire); + } + } + +private: + + Atomic m_storage{ 0 }; +}; + +} // namespace detail + +/// @brief Object for atomically managing a shared pointer strong reference. +/// @tparam T Type held in a shared pointer. +/// @tparam TAlloc Allocator for the shared pointer. +template +class AtomicSharedPtr final +{ + using BlockType = detail::_PtrBlockBase; + using LockType = detail::_LockablePtr; + +public: + + using ThisType = AtomicSharedPtr; + using ValueType = SharedPtr; + + ~AtomicSharedPtr() + { + auto block = m_block.UnsafeGet(); + if (block) + { + block->Release(); + } + } + + constexpr AtomicSharedPtr() noexcept = default; + + constexpr AtomicSharedPtr(rad::nullptr_t) noexcept + : m_block() + { + } + + /// @brief Constructs from an existing shared pointer. + /// @param value Shared pointer to store. + constexpr AtomicSharedPtr(const ValueType& value) noexcept + : m_block(value.m_block), + m_ptr(value.m_ptr) + { + if (value.m_block) + { + value.m_block->Acquire(); + } + } + + RAD_NOT_COPYABLE(AtomicSharedPtr); + + /// @brief Stores a shared pointer in the atomic storage. + /// @param value Shared pointer to store. + void Store(ValueType value) noexcept + { + detail::atomic::LockRegion lockRegion; + LockExclusive lock(m_block); + auto temp = value.m_block; + value.m_block = m_block.UnsafeGet(); + m_block.UnsafeSet(temp); + m_ptr.Store(value.m_ptr, MemOrderRelaxed); + } + + /// @brief Loads the shared pointer from atomic storage. + /// @return Shared pointer. + RAD_NODISCARD ValueType Load() const noexcept + { + ValueType res; + { + detail::atomic::LockRegion lockRegion; + LockShared lock(m_block); + res.m_block = m_block.UnsafeGet(); + res.m_ptr = m_ptr.Load(MemOrderRelaxed); + if (res.m_block) + { + res.m_block->Acquire(); + } + } + return res; + } + + /// @brief Exchanges the stored shared pointer with another. + /// @param value Shared pointer to store. + /// @return Previously stored shared pointer. + RAD_NODISCARD ValueType Exchange(ValueType value) noexcept + { + ValueType res; + { + detail::atomic::LockRegion lockRegion; + LockExclusive lock(m_block); + res.m_block = m_block.UnsafeGet(); + res.m_ptr = m_ptr.Load(MemOrderRelaxed); + m_block.UnsafeSet(value.m_block); + m_ptr.Store(value.m_ptr, MemOrderRelaxed); + value.m_block = nullptr; + } + return res; + } + + void operator=(ValueType value) noexcept + { + Store(Move(value)); + } + + operator ValueType() const noexcept + { + return Load(); + } + +private: + + mutable LockType m_block; + Atomic m_ptr{ nullptr }; +}; + +/// @brief Object for atomically managing a weak pointer reference. +/// @tparam T Type held in a weak pointer. +/// @tparam TAlloc Allocator for the weak pointer. +template +class AtomicWeakPtr final +{ + using BlockType = detail::_PtrBlockBase; + using LockType = detail::_LockablePtr; + +public: + + using ThisType = AtomicWeakPtr; + using ValueType = WeakPtr; + using SharedType = SharedPtr; + + ~AtomicWeakPtr() + { + auto block = m_block.UnsafeGet(); + if (block) + { + block->ReleaseWeak(); + } + } + + constexpr AtomicWeakPtr() noexcept = default; + + /// @brief Constructs from an existing weak pointer. + /// @param value Weak pointer to store. + constexpr AtomicWeakPtr(const ValueType& value) noexcept + : m_block(value.m_block), + m_ptr(value.m_ptr) + { + if (value.m_block) + { + value.m_block->AcquireWeak(); + } + } + + /// @brief Constructs from an existing shared pointer. + /// @param value Weak pointer to store. + constexpr AtomicWeakPtr(const SharedType& value) noexcept + : m_block(value.m_block), + m_ptr(value.m_ptr) + { + if (value.m_block) + { + value.m_block->AcquireWeak(); + } + } + + RAD_NOT_COPYABLE(AtomicWeakPtr); + + /// @brief Stores a weak pointer in the atomic storage. + /// @param value Weak pointer to store. + void Store(ValueType value) noexcept + { + detail::atomic::LockRegion lockRegion; + LockExclusive lock(m_block); + auto temp = value.m_block; + value.m_block = m_block.UnsafeGet(); + m_block.UnsafeSet(temp); + m_ptr.Store(value.m_ptr, MemOrderRelaxed); + } + + /// @brief Loads the weak pointer from atomic storage. + /// @return Weak pointer. + RAD_NODISCARD ValueType Load() const noexcept + { + ValueType res; + { + detail::atomic::LockRegion lockRegion; + LockShared lock(m_block); + res.m_block = m_block.UnsafeGet(); + res.m_ptr = m_ptr.Load(MemOrderRelaxed); + if (res.m_block) + { + res.m_block->AcquireWeak(); + } + } + return res; + } + + /// @brief Exchanges the stored weak pointer with another. + /// @param value Weak pointer to store. + /// @return Previously stored weak pointer. + RAD_NODISCARD ValueType Exchange(ValueType value) noexcept + { + ValueType res; + { + detail::atomic::LockRegion lockRegion; + LockExclusive lock(m_block); + res.m_block = m_block.UnsafeGet(); + res.m_ptr = m_ptr.Load(MemOrderRelaxed); + m_block.UnsafeSet(value.m_block); + m_ptr.Store(value.m_ptr, MemOrderRelaxed); + value.m_block = nullptr; + } + return res; + } + + void operator=(ValueType value) noexcept + { + Store(Move(value)); + } + + void operator=(SharedType value) noexcept + { + Store(Move(value)); + } + + operator ValueType() const noexcept + { + return Load(); + } + + operator SharedType() const noexcept + { + return Load().Lock(); + } + +private: + + mutable LockType m_block; + Atomic m_ptr{ nullptr }; +}; + +} // namespace rad diff --git a/radiant/Span.h b/radiant/Span.h new file mode 100644 index 0000000..6e10c56 --- /dev/null +++ b/radiant/Span.h @@ -0,0 +1,475 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Iterator.h" +#include "radiant/Byte.h" + +namespace rad +{ + +using SpanSizeType = uint32_t; +RAD_INLINE_VAR constexpr SpanSizeType DynamicExtent = SpanSizeType(-1); + +template +class Span; + +namespace detail +{ + +template +struct SpanIsAllowedExtentConvType + : IntegralConstant +{ +}; + +template +RAD_INLINE_VAR constexpr bool SpanIsAllowedExtentConv = + SpanIsAllowedExtentConvType::value; + +template +RAD_INLINE_VAR constexpr bool SpanIsAllowedElemTypeConv = + IsConv; + +template +using SpanMakeSubspanType = + rad::Span; + +template +class SpanExtentType +{ +public: + + constexpr SpanExtentType() noexcept = default; + + constexpr explicit SpanExtentType(SpanExtentType) noexcept; + + constexpr explicit SpanExtentType(SpanSizeType size) noexcept + { + RAD_UNUSED(size); + RAD_ASSERT(size == N); + } + + constexpr SpanSizeType Size() const noexcept + { + return N; + } +}; + +template <> +class SpanExtentType +{ +public: + + template + constexpr explicit SpanExtentType(SpanExtentType extent) noexcept + : m_size(extent.Size()) + { + } + + constexpr explicit SpanExtentType(SpanSizeType size) noexcept + : m_size(size) + { + } + + constexpr SpanSizeType Size() const noexcept + { + return m_size; + } + +private: + + SpanSizeType m_size; +}; + +template +constexpr SpanExtentType::SpanExtentType( + SpanExtentType ext) noexcept +{ + RAD_UNUSED(ext); + RAD_ASSERT(ext.Size() >= N); +} + +} // namespace detail + +/// @brief Span object that can refer to a contiguous sequence of objects. +/// @details A span can either have a static extent, in which the number of +/// elements in the sequence is known a at compile-time, or a dynamic extent. +/// @tparam T Element type. +/// @tparam N The number of elements in the sequence, or DynamicExtent. +template +class Span +{ +public: + + using ElementType = T; + using ValueType = RemoveCV; + using SizeType = SpanSizeType; + using PointerType = ElementType*; + using ConstPointerType = const ElementType*; + using ReferenceType = ElementType&; + using ConstReferenceType = const ElementType&; + using DifferenceType = ptrdiff_t; + using IteratorType = Iterator; + using ReverseIteratorType = ReverseIterator; + + static constexpr SizeType Extent = N; + + ~Span() = default; + + constexpr Span& operator=(const Span&) noexcept = default; + + template + constexpr Span(U, SizeType) = delete; + + template + constexpr Span(U, U) = delete; + + template , int> = 0> + constexpr Span() noexcept + : m_storage(nullptr, detail::SpanExtentType<0>()) + { + } + + template = 0> + constexpr explicit Span(PointerType data, SizeType count) noexcept + : m_storage(data, count) + { + } + + template , int> = 0> + constexpr explicit Span(RemoveConst* data, SizeType count) noexcept + : m_storage(data, count) + { + } + + template = 0> + constexpr Span(PointerType data, SizeType count) noexcept + : m_storage(data, count) + { + } + + template , int> = 0> + constexpr Span(RemoveConst* data, SizeType count) noexcept + : m_storage(data, count) + { + } + + template = 0> + constexpr explicit Span(PointerType start, PointerType end) noexcept + : m_storage(start, static_cast(end - start)) + { + } + + template , int> = 0> + constexpr explicit Span(RemoveConst* start, RemoveConst* end) noexcept + : m_storage(start, static_cast(end - start)) + { + } + + template = 0> + constexpr Span(PointerType start, PointerType end) noexcept + : m_storage(start, static_cast(end - start)) + { + } + + template , int> = 0> + constexpr Span(RemoveConst* start, RemoveConst* end) noexcept + : m_storage(start, static_cast(end - start)) + { + } + + template , int> = 0> + constexpr Span(ElementType (&arr)[C]) noexcept + : m_storage(KnownNotNull{ arr }, detail::SpanExtentType()) + { + } + + template ), + int> = 0> + constexpr Span(const Span& other) noexcept + : m_storage(other.Data(), detail::SpanExtentType(other.Size())) + { + } + + template ), + int> = 0> + constexpr explicit Span(const Span& other) noexcept + : m_storage(other.Data(), detail::SpanExtentType(other.Size())) + { + } + + constexpr PointerType Data() const noexcept + { + return m_storage.Data(); + } + + constexpr SizeType Size() const noexcept + { + return m_storage.Size(); + } + + constexpr SizeType SizeBytes() const noexcept + { + RAD_ASSERT(Size() < (DynamicExtent / sizeof(ElementType))); + return static_cast(Size() * sizeof(ElementType)); + } + + constexpr bool Empty() const noexcept + { + return (Size() == 0); + } + + constexpr ReferenceType operator[](SizeType index) const noexcept + { + RAD_ASSERT(index < Size()); + + return Data()[index]; + } + + constexpr ReferenceType Front() const noexcept + { + RAD_ASSERT(!Empty()); + + return Data()[0]; + } + + constexpr ReferenceType Back() const noexcept + { + RAD_ASSERT(!Empty()); + + return Data()[Size() - 1]; + } + + constexpr Span AsBytes() const noexcept + { + return Span(reinterpret_cast(Data()), + SizeBytes()); + } + + template , int> = 0> + Span AsBytes() noexcept + { + return Span(reinterpret_cast(Data()), SizeBytes()); + } + + template + constexpr detail::SpanMakeSubspanType Subspan() + const noexcept + { + RAD_ASSERT(Size() >= O && (C == DynamicExtent || C <= (Size() - O))); + using Type = detail::SpanMakeSubspanType; + return Type(Data() + O, C == DynamicExtent ? Size() - O : C); + } + + constexpr Span Subspan( + SizeType offset, SizeType count = DynamicExtent) const noexcept + { + return MakeSubspan(offset, count, SubspanSelector()); + } + + template + constexpr Span First() const noexcept + { + RAD_ASSERT(C <= Size()); + + return Span(Data(), C); + } + + template + constexpr Span Last() const noexcept + { + RAD_ASSERT(C <= Size()); + + return Span(Data() + (Size() - C), C); + } + + constexpr Span First( + SizeType count) const noexcept + { + RAD_ASSERT(count <= Size()); + + return { Data(), count }; + } + + constexpr Span Last( + SizeType count) const noexcept + { + RAD_ASSERT(count <= Size()); + + return MakeSubspan(Size() - count, + DynamicExtent, + SubspanSelector()); + } + + constexpr IteratorType begin() const noexcept + { + return IteratorType(Data()); + } + + constexpr IteratorType end() const noexcept + { + return IteratorType(Data() + Size()); + } + + constexpr ReverseIteratorType rbegin() const noexcept + { + if (Empty()) + { + return ReverseIteratorType(nullptr); + } + return ReverseIteratorType(Data() + (Size() - 1)); + } + + constexpr ReverseIteratorType rend() const noexcept + { + if (Empty()) + { + return ReverseIteratorType(nullptr); + } +#if !RAD_DBG && defined(RAD_GCC_VERSION) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + return ReverseIteratorType(Data() - 1); +#if !RAD_DBG && defined(RAD_GCC_VERSION) +#pragma GCC diagnostic pop +#endif + } + +private: + + struct KnownNotNull + { + PointerType p; + }; + + template + class StorageType : public ExtentType + { + public: + + template + constexpr StorageType(KnownNotNull data, U extent) noexcept + : ExtentType(extent), + m_data(data.p) + { + } + + template + constexpr StorageType(PointerType data, U extent) noexcept + : ExtentType(extent), + m_data(data) + { + RAD_ASSERT(m_data || Size() == 0); + } + + constexpr PointerType Data() const noexcept + { + return m_data; + } + + using ExtentType::Size; + + private: + + PointerType m_data; + }; + + constexpr Span(KnownNotNull data, SpanSizeType count) noexcept + : m_storage(data, count) + { + } + + template + struct SubspanSelector + { + }; + + template + constexpr Span MakeSubspan( + SizeType offset, SizeType count, SubspanSelector) const noexcept + { + const Span temp(*this); + return temp.Subspan(offset, count); + } + + constexpr Span MakeSubspan( + SizeType offset, + SizeType count, + SubspanSelector) const noexcept + { + RAD_ASSERT(Size() >= offset); + + if (count == DynamicExtent) + { + return { KnownNotNull{ Data() + offset }, Size() - offset }; + } + + RAD_ASSERT((Size() - offset) >= count); + + return { KnownNotNull{ Data() + offset }, count }; + } + + StorageType> m_storage; +}; + +template +constexpr Span MakeSpan(T (&arr)[N]) noexcept +{ + return Span(arr); +} + +template +constexpr Span MakeSpan(T* data) noexcept +{ + return Span(data, N); +} + +template +constexpr Span MakeSpan(T* data, SpanSizeType count) noexcept +{ + return Span(data, count); +} + +template +constexpr Span MakeSpan(T* start, T* end) noexcept +{ + return Span(start, end); +} + +template +constexpr bool SpansOverlap(const Span& left, + const Span& right) noexcept +{ + return (((left.begin() >= right.begin()) && (left.begin() < right.end())) || + ((right.begin() >= left.begin()) && (right.begin() < left.end()))); +} + +} // namespace rad diff --git a/radiant/TotallyRad.h b/radiant/TotallyRad.h new file mode 100644 index 0000000..e65cad0 --- /dev/null +++ b/radiant/TotallyRad.h @@ -0,0 +1,259 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#ifdef __cplusplus + +#if __cplusplus >= 202002L // c++20 +#define RAD_CPP 20 +#define RAD_CPP20 1 +#define RAD_CPP17 1 +#define RAD_CPP14 1 +#define RAD_CPP11 1 +#elif __cplusplus >= 201703L // c++17 +#define RAD_CPP 17 +#define RAD_CPP20 0 +#define RAD_CPP17 1 +#define RAD_CPP14 1 +#define RAD_CPP11 1 +#elif __cplusplus >= 201402L // c++14 +#define RAD_CPP 14 +#define RAD_CPP20 0 +#define RAD_CPP17 0 +#define RAD_CPP14 1 +#define RAD_CPP11 1 +#elif __cplusplus >= 201103L // c++11 +#define RAD_CPP 11 +#define RAD_CPP20 0 +#define RAD_CPP17 0 +#define RAD_CPP14 0 +#define RAD_CPP11 1 +#else +#define RAD_CPP 0 +#define RAD_CPP20 0 +#define RAD_CPP17 0 +#define RAD_CPP14 0 +#define RAD_CPP11 0 +#endif + +#if RAD_CPP20 +#define RAD_LIKELY(x) (x) [[likely]] +#define RAD_UNLIKELY(x) (x) [[unlikely]] +#define RAD_EXPLICIT(x) explicit(x) +#else +#define RAD_LIKELY(x) (x) +#define RAD_UNLIKELY(x) (x) +#define RAD_EXPLICIT(x) +#endif + +#if RAD_CPP17 +#define RAD_NODISCARD [[nodiscard]] +#define RAD_FALLTHROUGH [[fallthrough]] +#define RAD_INLINE_VAR inline +#else +#define RAD_NODISCARD +#define RAD_FALLTHROUGH +#define RAD_INLINE_VAR +#endif + +#endif // __cplusplus + +#if defined(__clang__) && __clang__ +#define RAD_CLANG_VERSION \ + (__clang_major__ * 10000 + __clang_minor__ + 100 + __clang_patchlevel__) +#endif + +#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ +#define RAD_GCC_VERSION \ + (__GNUC__ * 10000 + __GNUC__MINOR__ + 100 + __GNUC_PATCHLEVEL__) +#endif + +#if defined(_MSC_VER) && _MSC_VER +#define RAD_MSC_VERSION _MSC_VER +#endif + +#if defined(_MSVC_VER) && _MSVC_VER +#define RAD_MSVC_VERSION _MSVC_VER +#endif + +#if !defined(NDEBUG) || defined(_DEBUG) +#define RAD_DBG 1 +#else +#define RAD_DBG 0 +#endif + +#if defined(_WIN32) || defined(_WIN64) || defined(__CYGWIN__) +#define RAD_WINDOWS 1 +#define RAD_LINUX 0 +#define RAD_MACOS 0 +#endif + +#if defined(unix) || defined(__unix) || defined(__unix__) +#define RAD_WINDOWS 0 +#define RAD_LINUX 1 +#define RAD_MACOS 0 +#endif + +#if defined(__APPLE__) || defined(__MACH__) +#define RAD_WINDOWS 0 +#define RAD_LINUX 0 +#define RAD_MACOS 1 +#endif + +#if defined(__KERNEL_MODE) +#define RAD_KERNEL_MODE 1 +#define RAD_USER_MODE 0 +#else +#define RAD_KERNEL_MODE 0 +#define RAD_USER_MODE 1 +#endif + +static_assert(RAD_KERNEL_MODE || RAD_USER_MODE, "env undefined mode"); +static_assert(!(RAD_KERNEL_MODE && RAD_USER_MODE), "env invalid mode"); +static_assert(RAD_WINDOWS || RAD_LINUX || RAD_MACOS, "env undefined os"); +static_assert(!(RAD_WINDOWS && RAD_LINUX && RAD_MACOS), "env invalid os"); +static_assert(!(RAD_WINDOWS && RAD_LINUX), "env invalid os"); +static_assert(!(RAD_LINUX && RAD_MACOS), "env invalid os"); +static_assert(!(RAD_WINDOWS && RAD_MACOS), "env invalid os"); + +#if defined(_M_AMD64) || defined(__amd64__) +#define RAD_AMD64 1 +#define RAD_I386 0 +#define RAD_ARM64 0 +#define RAD_ARM 0 +#elif defined(_M_IX86) || defined(__i386__) +#define RAD_AMD64 0 +#define RAD_I386 1 +#define RAD_ARM64 0 +#define RAD_ARM 0 +#elif defined(_M_ARM64) || defined(__arm64__) +#define RAD_AMD64 0 +#define RAD_I386 0 +#define RAD_ARM64 1 +#define RAD_ARM 0 +#elif defined(_M_ARM) || defined(__arm__) +#define RAD_AMD64 0 +#define RAD_I386 0 +#define RAD_ARM64 0 +#define RAD_ARM 1 +#else +#error unsupported hardware +#endif + +#include + +#if RAD_WINDOWS +#if RAD_KERNEL_MODE +#include +#else +#define WIN32_LEAN_AND_MEAN +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#endif +#endif + +#if RAD_WINDOWS +#define RAD_UNUSED(x) UNREFERENCED_PARAMETER(x) +#define RAD_YIELD_PROCESSOR() YieldProcessor() +#else +#define RAD_UNUSED(x) ((void)x) +#define RAD_YIELD_PROCESSOR() sched_yield() +#endif + +#define _RAD_CONCAT(x, y) x##y +#define RAD_CONCAT(x, y) _RAD_CONCAT(x, y) + +#if RAD_USER_MODE && RAD_DBG +#include + +namespace rad +{ + +#if RAD_WINDOWS +extern "C" void __cdecl _assert(const char* Assertion, + const char* File, + unsigned Line); +#endif + +inline bool DoAssert(const char* Assertion, const char* File, int Line) +{ +#if RAD_WINDOWS + _assert(Assertion, File, Line); +#else + __assert(Assertion, File, Line); +#endif + return false; +} + +} // namespace rad + +#endif + +#if RAD_DBG + +#if RAD_WINDOWS && RAD_KERNEL_MODE +#define RAD_ASSERT(x) NT_ASSERT(x) +#define RAD_ASSERTMSG(x, msg) NT_ASSERTMSG(msg, x) +#define RAD_VERIFY(x) NT_VERIFY(x) +#define RAD_VERIFYMSG(x, msg) NT_VERIFYMSG(x) +#else +#define RAD_VERIFY(x) ((!!(x)) || (rad::DoAssert(#x, __FILE__, __LINE__))) +#define RAD_VERIFYMSG(x, m) ((!!(x)) || (rad::DoAssert(m, __FILE__, __LINE__))) +#define RAD_ASSERT(x) (void)RAD_VERIFY(x) +#define RAD_ASSERTMSG(x, m) (void)RAD_VERIFY(x, m) +#endif + +#else + +#define RAD_ASSERT(x) ((void)0) +#define RAD_ASSERTMSG(x, m) ((void)0) +#define RAD_VERIFY(x) (x) +#define RAD_VERIFYMSG(x, m) (x) + +#endif + +#define RAD_S_ASSERT(x) static_assert(x, #x) +#define RAD_S_ASSERTMSG(x, m) static_assert(x, m) + +#ifndef RAD_ENABLE_NOTHROW_ASSERTIONS +#define RAD_ENABLE_NOTHROW_ASSERTIONS 1 +#endif + +#if RAD_ENABLE_NOTHROW_ASSERTIONS +#define RAD_S_ASSERT_NOTHROW(x) RAD_S_ASSERT(x) +#define RAD_S_ASSERT_NOTHROWMSG(x, m) RAD_S_ASSERTMSG(x, m) +#else +#define RAD_S_ASSERT_NOTHROW(x) ((void)0) +#define RAD_S_ASSERT_NOTHROWMSG(x, m) ((void)0) +#endif + +#define RAD_NOT_COPYABLE(x) \ + x(x const&) = delete; \ + x& operator=(x const&) = delete + +#ifdef RAD_NO_STD +#define RAD_ENABLE_STD 0 +#define RAD_NO_STD 1 +#else +#define RAD_ENABLE_STD 1 +#define RAD_NO_STD 0 +#endif + +namespace rad +{ +using nullptr_t = decltype(nullptr); +} // namespace rad diff --git a/radiant/TypeTraits.h b/radiant/TypeTraits.h new file mode 100644 index 0000000..add121f --- /dev/null +++ b/radiant/TypeTraits.h @@ -0,0 +1,251 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/TotallyRad.h" + +#include + +namespace rad +{ + +template +using EnIf = typename std::enable_if::type; + +template +using Cond = typename std::conditional::type; + +template +using Decay = typename std::decay::type; + +template +using RemoveRef = typename std::remove_reference::type; + +template +using RemoveCV = typename std::remove_cv::type; + +template +using RemoveConst = typename std::remove_const::type; + +template +RAD_INLINE_VAR constexpr bool IsSigned = std::is_signed::value; + +template +RAD_INLINE_VAR constexpr bool IsUnsigned = !IsSigned; + +template +using MakeUnsigned = typename std::make_unsigned::type; + +template +RAD_INLINE_VAR constexpr bool IsConv = std::is_convertible::value; + +template +RAD_INLINE_VAR constexpr bool IsSame = std::is_same::value; + +template +RAD_INLINE_VAR constexpr bool IsConst = std::is_const::value; + +template +RAD_INLINE_VAR constexpr bool IsEmpty = std::is_empty::value; + +template +RAD_INLINE_VAR constexpr bool IsRef = std::is_reference::value; + +template +RAD_INLINE_VAR constexpr bool IsPtr = std::is_pointer::value; + +template +RAD_INLINE_VAR constexpr bool IsLRef = std::is_lvalue_reference::value; + +template +RAD_INLINE_VAR constexpr bool IsRRef = std::is_rvalue_reference::value; + +namespace detail +{ +template && !IsRRef> +struct _IsLRefBindable +{ + static constexpr bool value = false; +}; + +template +struct _IsLRefBindable +{ +private: + + template + static char test(TC); + template + static uint32_t test(...); + +public: + + static constexpr bool value = + sizeof(test(std::declval())) == sizeof(char); +}; + +} // namespace detail + +template +RAD_INLINE_VAR constexpr bool IsLRefBindable = + detail::_IsLRefBindable::value; + +// std::decay not used here to avoid array-to-pointer/fn-to-pointer conversions +// (based on a work-around by Luc Danton) +template +RAD_INLINE_VAR constexpr bool IsRelated = IsSame< + typename std::remove_cv::type>::type, + typename std::remove_cv::type>::type>; + +namespace detail +{ +template +struct _EnIfUnrelated : std::enable_if +{ +}; + +template +struct _EnIfUnrelated : std::enable_if> +{ +}; +} // namespace detail + +template +using IntegralConstant = std::integral_constant; +using TrueType = IntegralConstant; +using FalseType = IntegralConstant; + +template +RAD_INLINE_VAR constexpr bool IsIntegral = std::is_integral::value; + +template +RAD_INLINE_VAR constexpr bool IsPointer = std::is_pointer::value; + +template +using EnIfUnrelated = typename detail::_EnIfUnrelated::type; + +template +RAD_INLINE_VAR constexpr bool IsCtor = + std::is_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsTriv = std::is_trivial::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivCtor = + std::is_trivially_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowCtor = + std::is_nothrow_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsDefaultCtor = + std::is_default_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivDefaultCtor = + std::is_trivially_default_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowDefaultCtor = + std::is_nothrow_default_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsCopyCtor = std::is_copy_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivCopyCtor = + std::is_trivially_copy_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowCopyCtor = + std::is_nothrow_copy_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsMoveCtor = std::is_move_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivMoveCtor = + std::is_trivially_move_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowMoveCtor = + std::is_nothrow_move_constructible::value; + +template +RAD_INLINE_VAR constexpr bool IsAssign = std::is_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivAssign = + std::is_trivially_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowAssign = + std::is_nothrow_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsCopyAssign = std::is_copy_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivCopyAssign = + std::is_trivially_copy_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowCopyAssign = + std::is_nothrow_copy_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsMoveAssign = std::is_move_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivMoveAssign = + std::is_trivially_move_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowMoveAssign = + std::is_nothrow_move_assignable::value; + +template +RAD_INLINE_VAR constexpr bool IsDtor = std::is_destructible::value; + +template +RAD_INLINE_VAR constexpr bool IsTrivDtor = + std::is_trivially_destructible::value; + +template +RAD_INLINE_VAR constexpr bool IsNoThrowDtor = + std::is_nothrow_destructible::value; + +namespace detail +{ +template +RAD_INLINE_VAR constexpr bool _AlwaysFalse = false; +} + +template +typename std::add_rvalue_reference::type DeclVal() noexcept +{ + RAD_S_ASSERTMSG(detail::_AlwaysFalse, "Calling DeclVal is ill-formed"); +} + +template +RAD_INLINE_VAR constexpr bool HasVirtualDtor = + std::has_virtual_destructor::value; + +template +RAD_INLINE_VAR constexpr bool IsPoly = std::is_polymorphic::value; + +} // namespace rad diff --git a/radiant/TypeWrapper.h b/radiant/TypeWrapper.h new file mode 100644 index 0000000..107a874 --- /dev/null +++ b/radiant/TypeWrapper.h @@ -0,0 +1,327 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Utility.h" + +#if RAD_ENABLE_STD +#include +#endif + +namespace rad +{ + +template > +class TypeWrapper; + +template +class TypeWrapper +{ +public: + + using Type = T; + + template , int> = 0> + constexpr TypeWrapper() noexcept(IsNoThrowDefaultCtor) + : m_value() + { + RAD_S_ASSERT_NOTHROW((IsNoThrowDefaultCtor)); + } + + // wrapped-type move ctor + constexpr explicit TypeWrapper(T&& value) noexcept(IsNoThrowMoveCtor) + : m_value(Forward(value)) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowMoveCtor)); + } + +#if RAD_ENABLE_STD + // wrapped-type initializer_list ctor + template >, int> = 0> + constexpr explicit TypeWrapper(std::initializer_list init) noexcept( + IsNoThrowCtor>) + : m_value(init) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor>)); + } + + template + constexpr TypeWrapper(std::initializer_list>) = delete; +#endif + + // wrapper convertible ctor + template , int> = 0> + constexpr explicit TypeWrapper(const TypeWrapper& container) noexcept( + IsNoThrowCtor) + : m_value(container.Get()) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + // wrapper convertible move ctor + template , int> = 0> + constexpr explicit TypeWrapper(TypeWrapper&& container) noexcept( + IsNoThrowCtor) + : m_value(Forward>(container).Get()) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + // arg forwarding ctor (without wrapper copy/move ctor conflict) + template && + IsCtor>> + constexpr TypeWrapper(TArgs&&... args) noexcept( + IsNoThrowCtor) + : m_value(Forward(args)...) + { + RAD_S_ASSERT_NOTHROW((IsNoThrowCtor)); + } + + // type assign + template + constexpr TypeWrapper& operator=(const U& r) noexcept( + noexcept(DeclVal()->m_value = r)) + { + RAD_S_ASSERT_NOTHROW(noexcept(m_value = r)); + m_value = r; + return *this; + } + + // type move assign + constexpr TypeWrapper& operator=(T&& r) noexcept( + noexcept(DeclVal()->m_value = Forward(r))) + { + RAD_S_ASSERT_NOTHROW(noexcept(m_value = Forward(r))); + m_value = Forward(r); + return *this; + } + + // wrapper copy assign + template + constexpr TypeWrapper& operator=(const TypeWrapper& r) noexcept( + noexcept(DeclVal()->m_value = r.Get())) + { + RAD_S_ASSERT_NOTHROW(noexcept(m_value = r.Get())); + m_value = r.Get(); + return *this; + } + + // wrapper move assign + template + constexpr TypeWrapper& operator=(TypeWrapper&& r) noexcept(noexcept( + DeclVal()->m_value = Forward>(r).Get())) + { + RAD_S_ASSERT_NOTHROW( + noexcept(m_value = Forward>(r).Get())); + m_value = Forward>(r).Get(); + return *this; + } + + constexpr T& Get() & noexcept + { + return m_value; + } + + constexpr const T& Get() const& noexcept + { + return m_value; + } + + constexpr T&& Get() && noexcept + { + return Move(m_value); + } + +private: + + T m_value; +}; + +template +class TypeWrapper +{ +public: + + using Type = T; + + constexpr TypeWrapper() = delete; + + template , int> = 0> + constexpr explicit TypeWrapper(U&& val) noexcept + : m_ref(Forward(val)) + { + } + + template , int> = 0> + constexpr explicit TypeWrapper(TypeWrapper& r) noexcept + : m_ref(r.Get()) + { + } + + template , int> = 0> + constexpr explicit TypeWrapper(const TypeWrapper& r) noexcept + : m_ref(r.Get()) + { + } + + template , int> = 0> + constexpr explicit TypeWrapper(TypeWrapper&& r) noexcept + : m_ref(r.Get()) + { + } + + template , int> = 0> + constexpr TypeWrapper& operator=(U&& val) noexcept + { + new (this) TypeWrapper(Forward(val)); + return *this; + } + + template , int> = 0> + constexpr TypeWrapper& operator=(TypeWrapper& r) noexcept + { + new (this) TypeWrapper(r.Get()); + return *this; + } + + constexpr T& Get() noexcept + { + return m_ref; + } + + constexpr const T& Get() const noexcept + { + return m_ref; + } + +private: + + T m_ref; +}; + +template +inline bool operator==(const TypeWrapper& l, + const TypeWrapper& r) noexcept +{ + return l.Get() == r.Get(); +} + +template +inline bool operator==(const TypeWrapper& l, const U& r) noexcept +{ + return l.Get() == r; +} + +template +inline bool operator==(const U& l, const TypeWrapper& r) noexcept +{ + return r.Get() == l; +} + +template +inline bool operator!=(const TypeWrapper& l, + const TypeWrapper& r) noexcept +{ + return !(l == r); +} + +template +inline bool operator!=(const TypeWrapper& l, const U& r) noexcept +{ + return !(l == r); +} + +template +inline bool operator!=(const U& r, const TypeWrapper& l) noexcept +{ + return !(l == r); +} + +template +inline bool operator<(const TypeWrapper& l, const TypeWrapper& r) noexcept +{ + return l.Get() < r.Get(); +} + +template +inline bool operator<(const TypeWrapper& l, const U& r) noexcept +{ + return l.Get() < r; +} + +template +inline bool operator<(const U& l, const TypeWrapper& r) noexcept +{ + return l < r.Get(); +} + +template +inline bool operator<=(const TypeWrapper& l, + const TypeWrapper& r) noexcept +{ + return l.Get() <= r.Get(); +} + +template +inline bool operator<=(const TypeWrapper& l, const U& r) noexcept +{ + return l.Get() <= r; +} + +template +inline bool operator<=(const U& l, const TypeWrapper& r) noexcept +{ + return l <= r.Get(); +} + +template +inline bool operator>(const TypeWrapper& l, const TypeWrapper& r) noexcept +{ + return l.Get() > r.Get(); +} + +template +inline bool operator>(const TypeWrapper& l, const U& r) noexcept +{ + return l.Get() > r; +} + +template +inline bool operator>(const U& l, const TypeWrapper& r) noexcept +{ + return l > r.Get(); +} + +template +inline bool operator>=(const TypeWrapper& l, + const TypeWrapper& r) noexcept +{ + return l.Get() >= r.Get(); +} + +template +inline bool operator>=(const TypeWrapper& l, const U& r) noexcept +{ + return l.Get() >= r; +} + +template +inline bool operator>=(const U& l, const TypeWrapper& r) noexcept +{ + return l >= r.Get(); +} + +} // namespace rad diff --git a/radiant/Utility.h b/radiant/Utility.h new file mode 100644 index 0000000..115598e --- /dev/null +++ b/radiant/Utility.h @@ -0,0 +1,108 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/TypeTraits.h" + +namespace rad +{ + +template +RAD_NODISCARD constexpr inline T&& Forward(RemoveRef& v) noexcept +{ + return static_cast(v); +} + +template +RAD_NODISCARD constexpr inline T&& Forward(RemoveRef&& v) noexcept +{ + RAD_S_ASSERTMSG(!IsLRef, "cannot forward an rvalue as an lvalue."); + + return static_cast(v); +} + +template +RAD_NODISCARD constexpr inline RemoveRef&& Move(T&& v) noexcept +{ + return static_cast&&>(v); +} + +template +RAD_NODISCARD constexpr inline // + Cond<(IsNoThrowMoveCtor && IsNoThrowCopyCtor), const T&, T&&> + MoveIfNoExcept(T& v) noexcept +{ + return Move(v); +} + +template +RAD_NODISCARD constexpr inline uint32_t Size(T (&)[N]) noexcept +{ + return N; +} + +template +RAD_NODISCARD constexpr inline auto Size(const T& c) noexcept( + noexcept(c.Size())) -> decltype(c.Size()) +{ + RAD_S_ASSERT_NOTHROW(noexcept(c.Size())); + + return c.Size(); +} + +template +RAD_NODISCARD constexpr inline T* Add2Ptr(void* p, uint32_t offset) noexcept +{ + return reinterpret_cast(static_cast(p) + offset); +} + +#if defined(RAD_MSC_VERSION) || defined(RAD_CLANG_VERSION) || \ + defined(RAD_GCC_VERSION) +template +RAD_NODISCARD constexpr inline T* AddrOf(T& v) noexcept +{ + return __builtin_addressof(v); +} +#else +template +RAD_NODISCARD constexpr inline T* AddrOf(T& v) noexcept +{ + return reinterpret_cast( + &const_cast(reinterpret_cast(v))); +} +#endif + +template +constexpr const T* AddrOf(const T&& v) noexcept = delete; + +template +RAD_NODISCARD constexpr inline const T& Min(const T& a, const T& b) // + noexcept(noexcept(a < b)) +{ + RAD_S_ASSERT_NOTHROW(noexcept(a < b)); + + return (a < b) ? a : b; +} + +template +RAD_NODISCARD constexpr inline const T& Max(const T& a, const T& b) // + noexcept(noexcept(a > b)) +{ + RAD_S_ASSERT_NOTHROW(noexcept(a > b)); + + return (a > b) ? a : b; +} + +} // namespace rad diff --git a/radiant/Vector.h b/radiant/Vector.h new file mode 100644 index 0000000..b4542a1 --- /dev/null +++ b/radiant/Vector.h @@ -0,0 +1,601 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Memory.h" +#include "radiant/EmptyOptimizedPair.h" +#include "radiant/detail/VectorOperations.h" + +namespace rad +{ + +/// @brief Stores a contiguous set of elements. +/// @tparam T The type of elements. +/// @tparam TAllocator Allocator type to use. +/// @tparam TInlineCount Optionally specifies a number of elements for inline +/// storage which may be used for small optimizations. +template +class Vector final +{ +private: + + using OperationalType = detail::VectorOperations; + using StorageType = EmptyOptimizedPair; + +public: + + using ThisType = Vector; + using ValueType = T; + using SizeType = uint32_t; + static constexpr uint16_t InlineCount = TInlineCount; + using AllocatorType = TAllocator; + + // + // TODO - rad::Vector does not yet support types that might throw exceptions + // when manipulating it. It is the intention of the radiant authors for the + // library to be exception free. Radiant will never throw an exception + // itself, rather it will communicate errors by returning a Result. However, + // the radiant containers may be used with types that do not adhere to this + // philosophy. For rad::Vector to comply to this, it must cleanly handle + // cases where an exception interjects manipulation of it. If an exception + // happens rad::Vector should be in the state prior to the call to + // manipulate it. In other words, an exception occurring when manipulating + // rad::Vector should not leave it in a partially-manipulated state. This + // does not mean it should swallow the exception internally and return an + // error. The exception should propagate from rad::Vector but rad::Vector + // itself should be in a valid state when this happens. + // + RAD_S_ASSERTMSG((IsNoThrowDtor && IsNoThrowDefaultCtor && + IsNoThrowCopyCtor && IsNoThrowMoveCtor && + IsNoThrowCopyAssign && IsNoThrowMoveAssign), + "rad::Vector does not yet support types that might throw."); + + ~Vector() + { + RAD_S_ASSERTMSG(IsNoThrowDtor && IsNoThrowDtor, + "Destructors should not throw!"); + + Clear(); + Storage().Free(Allocator()); + } + + /// @brief Constructs empty container with default-constructed allocator. + Vector() = default; + + /// @brief Constructs empty container with copy-constructed allocator. + /// @param alloc Allocator to copy. + Vector(const AllocatorType& alloc) + : m_storage(alloc) + { + } + + /// @brief Constructs empty container with move-constructed allocator. + /// @param alloc Allocator to move. + Vector(AllocatorType&& alloc) + : m_storage(Forward(alloc)) + { + } + + ThisType& operator=(const ThisType& other) = delete; + + /// @brief Moves elements in another container into this. + /// @param other Other container to move elements from. + /// @return Reference to this container. + ThisType& operator=(ThisType&& other) noexcept + { + other.Storage().Move(other.Allocator(), Storage()); + return *this; + } + + /// @brief Checks if the container is empty. + /// @return True if the container is empty, false otherwise. + bool Empty() const noexcept + { + return (Storage().m_size == 0); + } + + /// @brief Retrieves the number of elements in the container. + /// @return Number of elements in the container. + SizeType Size() const noexcept + { + return Storage().m_size; + } + + /// @brief Retrieves the current number of elements that could be stored in + /// the container without reallocating storage. + /// @return Number of elements that could be stored in the container without + /// reallocating. + SizeType Capacity() const noexcept + { + return Storage().m_capacity; + } + + /// @brief Retrieves a pointer to the first element in the contiguous set of + /// elements. Pointer value is undefined if the container is empty. + /// @return Pointer to the first element in the contiguious set of elements. + ValueType* Data() noexcept + { + return Storage().Data(); + } + + /// @brief Retrieves a pointer to the first element in the contiguous set of + /// elements. Pointer value is undefined if the container is empty. + /// @return Pointer to the first element in the contiguious set of elements. + const ValueType* Data() const noexcept + { + return Storage().Data(); + } + + /// @brief Returns a reference to the element at a given index. Behavior is + /// undefined if the container is empty or if the index is outside the + /// bounds of the contained elements. + /// @param index Index into the contiguous set to get the element of. + /// @return Reference to the element at the supplied index. + ValueType& At(SizeType index) noexcept + { + RAD_ASSERT(index < Storage().m_size); + + return Storage().Data()[index]; + } + + /// @brief Returns a reference to the element at a given index. Behavior is + /// undefined if the container is empty or if the index is outside the + /// bounds of the contained elements. + /// @param index Index into the contiguous set to get the element of. + /// @return Reference to the element at the supplied index. + ValueType& operator[](SizeType index) noexcept + { + return At(index); + } + + /// @brief Returns a reference to the element at a given index. Behavior is + /// undefined if the container is empty or if the index is outside the + /// bounds of the contained elements. + /// @param index Index into the contiguous set to get the element of. + /// @return Reference to the element at the supplied index. + const ValueType& At(SizeType index) const noexcept + { + RAD_ASSERT(index < Storage().m_size); + + return Storage().Data()[index]; + } + + /// @brief Returns a reference to the element at a given index. Behavior is + /// undefined if the container is empty or if the index is outside the + /// bounds of the contained elements. + /// @param index Index into the contiguous set to get the element of. + /// @return Reference to the element at the supplied index. + const ValueType& operator[](SizeType index) const noexcept + { + return At(index); + } + + /// @brief Returns a reference to the first element in the container. + /// Behavior is undefined if the container is empty. + /// @return Reference to the first element in the container. + ValueType& Front() noexcept + { + return At(0); + } + + /// @brief Returns a reference to the first element in the container. + /// Behavior is undefined if the container is empty. + /// @return Reference to the first element in the container. + const ValueType& Front() const noexcept + { + return At(0); + } + + /// @brief Returns a reference to the last element in the container. + /// Behavior is undefined if the container is empty. + /// @return Reference to the last element in the container. + ValueType& Back() noexcept + { + return At(Storage().m_size - 1); + } + + /// @brief Returns a reference to the last element in the container. + /// Behavior is undefined if the container is empty. + /// @return Reference to the last element in the container. + const ValueType& Back() const noexcept + { + return At(Storage().m_size - 1); + } + + /// @brief Reserves space for a number elements in the container. + /// @param capacity Number of elements to reserve space for. + /// @return Result reference to this container on success or an error. + Res Reserve(SizeType capacity) noexcept + { + return Storage().Reserve(Allocator(), capacity).OnOk(*this); + } + + /// @brief Clears all elements from the container. + /// @return Reference to this container. + ThisType& Clear() noexcept + { + Storage().Clear(); + return *this; + } + + /// @brief Shrinks the allocated storage to fit the current number of + /// elements in the container. + /// @return Result reference to this container on success or an error. + Res ShrinkToFit() noexcept + { + return Storage().ShrinkToFit(Allocator()).OnOk(*this); + } + + /// @brief Swaps the elements in this container with another. + /// @param other Other container to swap elements with. + /// @return Reference to this container. + ThisType& Swap(ThisType& other) noexcept + { + Storage().Swap(other.Storage()); + return *this; + } + + /// @brief Resizes the number of elements in the container. + /// @details If the current size is greater than the requested count, the + /// container is reduced to the first count elements. If the current size is + /// less than count a number of default-constructed elements are appended. + /// @param count Requested size of the container. + /// @return Result reference to this container on success or an error. + Res Resize(SizeType count) noexcept + { + return Storage().Resize(Allocator(), count).OnOk(*this); + } + + /// @brief Resizes the number of elements in the container. + /// @details If the current size is greater than the requested count, the + /// container is reduced to the first count elements. If the current size is + /// less than count a number of copy-constructed elements are appended. + /// @param count Requested size of the container. + /// @param value Value to initialize new elements with. + /// @return Result reference to this container on success or an error. + Res Resize(SizeType count, const ValueType& value) noexcept + { + return Storage().Resize(Allocator(), count, value).OnOk(*this); + } + + /// @brief Replaces the contents of the container. + /// @param count Number of elements to assign to the container. + /// @param value Value to initialize elements with. + /// @return Result reference to this container on success or an error. + Res Assign(SizeType count, const ValueType& value) noexcept + { + return Storage().Assign(Allocator(), count, value).OnOk(*this); + } + +#if RAD_ENABLE_STD + /// @brief Replaces the contents of the container. + /// @param Init List of values to initialize elements with. + /// @return Result reference to this container on success or an error. + Res Assign(std::initializer_list init) noexcept + { + return Storage() + .Assign(Allocator(), + Span(init.begin(), init.end())) + .OnOk(*this); + } +#endif + + /// @brief Replaces the contents of the container. + /// @param span Span of values to initialize elements with. + /// @return Result reference to this container on success or an error. + Res Assign(Span span) noexcept + { + return Storage().Assign(Allocator(), span).OnOk(*this); + } + + /// @brief Appends a new element to the end of the container. + /// @tparam ...TArgs Argument types forward to the constructor of the + /// element. + /// @param ...args Arguments to forward to the constructor of the element. + /// @return Result reference to this container on success or an error. + template + Res EmplaceBack(TArgs&&... args) noexcept + { + return Storage() + .EmplaceBack(Allocator(), Forward(args)...) + .OnOk(*this); + } + + /// @brief Appends a new element to the end of the container. + /// @param value Value to be appended. + /// @return Result reference to this container on success or an error. + Res PushBack(const ValueType& value) noexcept + { + return EmplaceBack(value); + } + + /// @brief Appends a new element to the end of the container. + /// @param value Value to be appended. + /// @return Result reference to this container on success or an error. + Res PushBack(ValueType&& value) noexcept + { + return EmplaceBack(Forward(value)); + } + + /// @brief Removes the last element from the back of the container. + /// @return Reference to this container. + ThisType& PopBack() noexcept + { + Storage().PopBack(); + return *this; + } + + /// @brief Returns the associated allocator. + /// @return The associated allocator. + AllocatorType GetAllocator() const noexcept + { + return AllocatorType(Allocator()); + } + + /// @brief Converts the container to a span of contiguous elements. + /// @param offset Offset into the container to begin the span. + /// @param count Number of elements to span. + /// @return Span of elements in the container. + Span ToSpan(SizeType offset = 0, + SizeType count = DynamicExtent) noexcept + { + if RAD_LIKELY (RAD_VERIFY(offset <= Size())) + { + if (count == DynamicExtent) + { + count = Size() - offset; + } + + if RAD_LIKELY (RAD_VERIFY((count + offset) <= (Size() + 1))) + { + return Span(Data() + offset, count); + } + } + + return Span(); + } + + /// @brief Copies the elements in this container to another. + /// @param to Container to copy elements to. + /// @return Result reference to this container on success or an error. + Res Copy(ThisType& to) noexcept + { + return Storage().Copy(Allocator(), to.Storage()).OnOk(*this); + } + + /// @brief Moves the elements in this container to another. + /// @param to Container to move elements to. + /// @return Result reference to this container on success or an error. + ThisType& Move(ThisType& to) noexcept + { + Storage().Move(Allocator(), to.Storage()); + return *this; + } + + /// @brief Removes an element from back of the container. + /// @return Removed element. + ValueType RemoveBack() noexcept + { + auto value = rad::Move(Back()); + PopBack(); + return rad::Move(value); + } + + /// @brief Seeks an element at a given index within the container. + /// @param index Index to seek. + /// @return Result reference to the element at requested index on success or + /// an error if the index it outside the bounds of the container. + Res Seek(SizeType index) noexcept + { + if (index >= Storage().m_size) + { + return Error::OutOfRange; + } + + return Storage().Data()[index]; + } + + /// @brief Seeks an element at a given index within the container. + /// @param index Index to seek. + /// @return Result reference to the element at requested index on success or + /// an error if the index it outside the bounds of the container. + Res Seek(SizeType index) const noexcept + { + if (index >= Storage().m_size) + { + return Error::OutOfRange; + } + + return Storage().Data()[index]; + } + + /// @brief Seeks the first element. + /// @return Result reference to the element at at the first element on + /// success or an error if the container is empty. + Res SeekFront() noexcept + { + return Seek(0); + } + + /// @brief Seeks the first element. + /// @return Result reference to the element at at the first element on + /// success or an error if the container is empty. + Res SeekFront() const noexcept + { + return Seek(0); + } + + /// @brief Seeks the last element. + /// @return Result reference to the element at at the last element on + /// success or an error if the container is empty. + Res SeekBack() noexcept + { + return Seek(Storage().m_size - 1); + } + + /// @brief Seeks the last element. + /// @return Result reference to the element at at the last element on + /// success or an error if the container is empty. + Res SeekBack() const noexcept + { + return Seek(Storage().m_size - 1); + } + +private: + + AllocatorType& Allocator() noexcept + { + return m_storage.First(); + } + + const AllocatorType& Allocator() const noexcept + { + return m_storage.First(); + } + + OperationalType& Storage() noexcept + { + return m_storage.Second(); + } + + const OperationalType& Storage() const noexcept + { + return m_storage.Second(); + } + + StorageType m_storage; +}; + +template +constexpr uint16_t Vector::InlineCount; + +/// @brief Stores a contiguous set of elements. +/// @tparam T The type of elements. +/// @tparam TInlineCount Specifies a number of elements for inline storage which +/// may be used for small optimizations. +/// @tparam TAllocator Allocator type to use. +template +using InlineVector = Vector; + +template +constexpr inline bool operator==( + const Vector& left, + const Vector& right) +{ + if (left.Size() != right.Size()) + { + return false; + } + + for (uint32_t i = 0; i < left.Size(); i++) + { + if (left[i] != right[i]) + { + return false; + } + } + + return true; +} + +template +constexpr inline bool operator!=( + const Vector& left, + const Vector& right) +{ + return !(left == right); +} + +template +constexpr inline bool operator<( + const Vector& left, + const Vector& right) +{ + auto min = Min(left.Size(), right.Size()); + for (uint32_t i = 0; i < min; i++) + { + if (left[i] < right[i]) + { + return true; + } + + if (left[i] > right[i]) + { + return false; + } + } + + return left.Size() < right.Size(); +} + +template +constexpr inline bool operator>( + const Vector& left, + const Vector& right) +{ + return right < left; +} + +template +constexpr inline bool operator<=( + const Vector& left, + const Vector& right) +{ + return !(right < left); +} + +template +constexpr inline bool operator>=( + const Vector& left, + const Vector& right) +{ + return !(left < right); +} + +} // namespace rad diff --git a/radiant/detail/AtomicIntrinsics.h b/radiant/detail/AtomicIntrinsics.h new file mode 100644 index 0000000..bcfbb62 --- /dev/null +++ b/radiant/detail/AtomicIntrinsics.h @@ -0,0 +1,227 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Utility.h" + +#if RAD_WINDOWS && RAD_KERNEL_MODE +#include +#endif + +namespace rad +{ +enum class MemoryOrder : int +{ + Relaxed, + Consume, + Acquire, + Release, + AcqRel, + SeqCst +}; + +namespace detail +{ +namespace atomic +{ + +class LockRegion +{ +public: + + LockRegion() noexcept + { +#if RAD_WINDOWS && RAD_KERNEL_MODE + region = KeGetCurrentIrql() <= APC_LEVEL; + if (region) + { + KeEnterCriticalRegion(); + } +#endif + } + + ~LockRegion() + { +#if RAD_WINDOWS && RAD_KERNEL_MODE + if (region) + { + KeLeaveCriticalRegion(); + } +#endif + } + +#if RAD_WINDOWS && RAD_KERNEL_MODE +private: + + bool region; +#endif + + RAD_NOT_COPYABLE(LockRegion); +}; + +template +struct OrderTag +{ + using Type = T; +}; + +struct RelaxedTag : public OrderTag +{ + static constexpr MemoryOrder Order = MemoryOrder::Relaxed; +}; + +struct ConsumeTag : public OrderTag +{ + static constexpr MemoryOrder Order = MemoryOrder::Consume; +}; + +struct AcquireTag : public OrderTag +{ + static constexpr MemoryOrder Order = MemoryOrder::Acquire; + +#ifdef RAD_MSC_VERSION + AcquireTag() = default; + + AcquireTag(ConsumeTag) + { + } +#endif +}; + +struct ReleaseTag : public OrderTag +{ + static constexpr MemoryOrder Order = MemoryOrder::Release; +}; + +struct AcqRelTag : public OrderTag +{ + static constexpr MemoryOrder Order = MemoryOrder::AcqRel; +}; + +struct SeqCstTag : public OrderTag +{ + static constexpr MemoryOrder Order = MemoryOrder::SeqCst; + +#ifdef RAD_MSC_VERSION + SeqCstTag() = default; + + SeqCstTag(AcqRelTag) + { + } +#endif +}; + +template +struct OrderToTag +{ +}; + +template <> +struct OrderToTag +{ + using Type = RelaxedTag; +}; + +template <> +struct OrderToTag +{ + using Type = ConsumeTag; +}; + +template <> +struct OrderToTag +{ + using Type = AcquireTag; +}; + +template <> +struct OrderToTag +{ + using Type = ReleaseTag; +}; + +template <> +struct OrderToTag +{ + using Type = AcqRelTag; +}; + +template <> +struct OrderToTag +{ + using Type = SeqCstTag; +}; + +template +constexpr inline void CheckLoadMemoryOrder() +{ + RAD_S_ASSERTMSG((IsSame || // + IsSame || // + IsSame || // + IsSame), // + "Invalid memory order for atomic load operation"); +} + +template +constexpr inline void CheckCasMemoryOrdering() +{ + constexpr int success = static_cast(Success::Order); + constexpr int fail = static_cast(Failure::Order); + RAD_S_ASSERTMSG(success >= fail, + "Invalid memory order for atomic load operation"); +} + +template +constexpr inline void CheckStoreMemoryOrder() +{ + RAD_S_ASSERTMSG((IsSame || // + IsSame || // + IsSame), // + "Invalid memory order for atomic store operation"); +} + +template +volatile R* AddrAs(T& val) noexcept +{ + return reinterpret_cast(AddrOf(val)); +} + +template +const volatile R* AddrAs(const T& val) noexcept +{ + return reinterpret_cast(AddrOf(val)); +} + +template && IsIntegral, int> = 0> +R ValAs(T val) noexcept +{ + return static_cast(val); +} + +template || IsPointer, int> = 0> +R ValAs(T val) noexcept +{ + return reinterpret_cast(val); +} + +#ifdef RAD_MSC_VERSION +#include "radiant/detail/msvc/AtomicIntrinsics.h" +#elif defined(RAD_GCC_VERSION) || defined(RAD_CLANG_VERSION) +#include "radiant/detail/gcc/AtomicIntrinsics.h" +#endif + +} // namespace atomic +} // namespace detail +} // namespace rad diff --git a/radiant/detail/VectorOperations.h b/radiant/detail/VectorOperations.h new file mode 100644 index 0000000..fcc1db5 --- /dev/null +++ b/radiant/detail/VectorOperations.h @@ -0,0 +1,826 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include "radiant/Res.h" +#include "radiant/Utility.h" +#include "radiant/Span.h" + +namespace rad +{ + +namespace detail +{ + +// +// TODO - The noexcept contracts are not complete yet, see the comment near +// the top of the rad::Vector class. Many of the storage and operational +// contracts are just set to noexcept, when we cleanly support objects that +// might throw during manipulation, we need to go back and make the decorations +// correct. The manipulation decorations are largely correct, but missing +// assertions. +// + +template +struct VectorManipulation +{ + template , int> = 0> + inline void Dtor(T* item) noexcept + { + RAD_UNUSED(item); + } + + template , int> = 0> + inline void Dtor(T* item) noexcept + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + + item->~T(); + } + + template , int> = 0> + inline void DtorRange(T* start, T* end) noexcept + { + RAD_UNUSED(start); + RAD_UNUSED(end); + } + + template , int> = 0> + inline void DtorRange(T* start, T* end) noexcept + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + + for (T* p = start; p != end; p++) + { + p->~T(); + } + } + + template , int> = 0> + inline void DefaultCtor(T* dest, uint32_t count) noexcept + { + memset(dest, 0, count * sizeof(T)); + } + + template , int> = 0> + inline void DefaultCtor(T* dest, uint32_t count) noexcept(IsNoThrowCtor) + { + for (uint32_t i = 0; i < count; i++) + { + new (dest + i) T(); + } + } + + template && (sizeof(T) > sizeof(void*)), int> = 0> + inline void CopyCtor(T* dest, const T& value) noexcept + { + memcpy(dest, AddrOf(value), sizeof(T)); + } + + template || (sizeof(T) <= sizeof(void*)), int> = 0> + inline void CopyCtor(T* dest, const T& value) noexcept(IsNoThrowCopyCtor) + { + new (dest) T(value); + } + + inline void CopyCtor(T* dest, uint32_t count, const T& value) noexcept + { + for (uint32_t i = 0; i < count; i++) + { + CopyCtor(dest + i, value); + } + } + + template , int> = 0> + inline void CopyCtorDtorDest(T* dest, + uint32_t count, + const T& value) noexcept + { + for (uint32_t i = 0; i < count; i++) + { + memcpy(dest + i, AddrOf(value), sizeof(T)); + } + } + + template , int> = 0> + inline void CopyCtorDtorDest(T* dest, + uint32_t count, + const T& value) noexcept(IsNoThrowCopyCtor) + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + + for (uint32_t i = 0; i < count; i++) + { + T* d = dest + i; + + d->~T(); + new (d) T(value); + } + } + + template , int> = 0> + inline void CopyCtorDtorDestRange(T* dest, T* src, uint32_t count) noexcept + { + memmove(dest, src, count * sizeof(T)); + } + + template , int> = 0> + inline void CopyCtorDtorDestRange(T* dest, T* src, uint32_t count) noexcept( + IsNoThrowCopyCtor) + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + + for (uint32_t i = 0; i < count; i++) + { + T* d = dest + i; + T* s = src + i; + + d->~T(); + new (d) T(*s); + } + } + + template , int> = 0> + inline void MoveCtorRange(T* dest, T* src, uint32_t count) noexcept + { + memmove(dest, src, count * sizeof(T)); + } + + template , int> = 0> + inline void MoveCtorRange(T* dest, + T* src, + uint32_t count) noexcept(IsNoThrowMoveCtor) + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + + for (uint32_t i = 0; i < count; i++) + { + T* s = src + i; + T* d = dest + i; + + new (d) T(Move(*s)); + } + } + + template , int> = 0> + inline void MoveCtorDtorSrcRange(T* dest, T* src, uint32_t count) noexcept + { + // + // GCC optimizations gets confused here when inlining a vector with + // "inline" storage from ShrinkToFit when m_size <= InlineCount. + // +#if !RAD_DBG && defined(RAD_GCC_VERSION) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif + memmove(dest, src, count * sizeof(T)); +#if !RAD_DBG && defined(RAD_GCC_VERSION) +#pragma GCC diagnostic pop +#endif + } + + template , int> = 0> + inline void MoveCtorDtorSrcRange(T* dest, T* src, uint32_t count) noexcept( + IsNoThrowMoveCtor) + { + RAD_S_ASSERTMSG(IsNoThrowDtor, "Destructors should not throw!"); + + for (uint32_t i = 0; i < count; i++) + { + T* s = src + i; + T* d = dest + i; + + new (d) T(Move(*s)); + s->~T(); + } + } + + template , int> = 0> + inline void MoveAssignRange(T* dest, T* src, uint32_t count) noexcept + { + memmove(dest, src, count * sizeof(T)); + } + + template , int> = 0> + inline void MoveAssignRange(T* dest, + T* src, + uint32_t count) noexcept(IsNoThrowMoveAssign) + { + for (uint32_t i = 0; i < count; i++) + { + T* s = src + i; + T* d = dest + i; + + *d = Move(*s); + } + } +}; + +template +struct VectorAlloc +{ + ~VectorAlloc() + { + if (buffer) + { + allocator.Free(buffer); + } + } + + VectorAlloc(TAllocator& alloc) noexcept + : allocator(alloc), + buffer(nullptr) + { + } + + T* Release() noexcept + { + T* tmp = buffer; + buffer = nullptr; + return tmp; + } + + TAllocator& allocator; + T* buffer; +}; + +template 0)> +struct VectorStorage; + +template +struct VectorStorage +{ + using ThisType = VectorStorage; + using ValueType = T; + using SizeType = uint32_t; + static constexpr uint16_t InlineCount = TInlineCount; + using ManipType = VectorManipulation; + + ~VectorStorage() = default; + + VectorStorage() noexcept + : m_data(nullptr), + m_size(0), + m_capacity(0) + { + } + + bool IsInline() const noexcept + { + return false; + } + + ValueType* Data() noexcept + { + return m_data; + } + + const ValueType* Data() const noexcept + { + return m_data; + } + + template + void Free(TAllocator& alloc) noexcept + { + if (m_data) + { + alloc.Free(m_data); + } + } + + template + Err ShrinkToFit(TAllocator& alloc) noexcept + { + if (m_size == m_capacity) + { + return NoError; + } + + if (m_size == 0) + { + Free(alloc); + m_data = nullptr; + m_capacity = 0; + + return NoError; + } + + VectorAlloc vec(alloc); + vec.buffer = vec.allocator.Alloc(m_size); + if (!vec.buffer) + { + return Error::NoMemory; + } + + ManipType().MoveCtorDtorSrcRange(vec.buffer, m_data, m_size); + + Free(alloc); + + m_data = vec.Release(); + m_capacity = m_size; + + return NoError; + } + + void Swap(ThisType& other) noexcept + { + if (this == &other) + { + return; + } + + auto data = m_data; + m_data = other.m_data; + other.m_data = data; + + auto size = m_size; + m_size = other.m_size; + other.m_size = size; + + size = m_capacity; + m_capacity = other.m_capacity; + other.m_capacity = size; + } + + ValueType* m_data; + SizeType m_size; + SizeType m_capacity; +}; + +template +struct VectorStorage +{ + using ThisType = VectorStorage; + using ValueType = T; + using SizeType = uint32_t; + static constexpr uint16_t InlineCount = TInlineCount; + using ManipType = VectorManipulation; + + ~VectorStorage() + { + } + + VectorStorage() noexcept + : m_data(nullptr), + m_size(0), + m_capacity(InlineCount) + { + } + + bool IsInline() const noexcept + { + return (m_capacity <= InlineCount); + } + + ValueType* Data() noexcept + { + return IsInline() ? m_inline : m_data; + } + + const ValueType* Data() const noexcept + { + return IsInline() ? m_inline : m_data; + } + + template + void Free(TAllocator& alloc) noexcept + { + if (!IsInline() && m_data) + { + alloc.Free(m_data); + } + } + + template + Err ShrinkToFit(TAllocator& alloc) noexcept + { + if (IsInline()) + { + return NoError; + } + + if (m_size == m_capacity) + { + return NoError; + } + + if (m_size == 0) + { + Free(alloc); + m_data = nullptr; + m_capacity = InlineCount; + return NoError; + } + + if (m_size <= InlineCount) + { + // + // Shrinking the vector will fit inline, make it so. + // + auto data = m_data; + ManipType().MoveCtorDtorSrcRange(m_inline, data, m_size); + if (data) + { + alloc.Free(data); + } + m_capacity = InlineCount; + } + else + { + VectorAlloc vec(alloc); + vec.buffer = vec.allocator.Alloc(m_size); + if (!vec.buffer) + { + return Error::NoMemory; + } + + ManipType().MoveCtorDtorSrcRange(vec.buffer, m_data, m_size); + + Free(alloc); + + m_data = vec.Release(); + m_capacity = m_size; + } + + return NoError; + } + + void Swap(ThisType& other) noexcept + { + if (this == &other) + { + return; + } + + if (!IsInline() && !other.IsInline()) + { + // + // Neither are inline, just swap the pointers. + // + auto temp = m_data; + m_data = other.m_data; + other.m_data = temp; + } + else if (IsInline() && other.IsInline()) + { + // + // Both are inline, move the data between the inline buffers + // by using some stack. + // + uint8_t storage[sizeof(m_inline)]; + auto buffer = reinterpret_cast(storage); + ManipType().MoveCtorRange(buffer, m_inline, m_size); + ManipType().MoveAssignRange(m_inline, other.m_inline, other.m_size); + ManipType().MoveCtorDtorSrcRange(other.m_inline, buffer, m_size); + } + else + { + // + // One is inline and the other isn't. We guarantee there is enough + // space in the inline data to store the other's information. + // 1. Move the inline data. + // 2. Store the allocated buffer in the object we moved data from. + // 3. Point the data at the inline data. + // + + if (IsInline()) + { + // + // This is inline, move this data to the other's inline. + // + auto data = other.m_data; + ManipType().MoveCtorDtorSrcRange(other.m_inline, + m_inline, + m_size); + m_data = data; + } + else + { + // + // Other is inline, move the other's data to this inline. + // + auto data = m_data; + ManipType().MoveCtorDtorSrcRange(m_inline, + other.m_inline, + other.m_size); + other.m_data = data; + } + } + + auto size = m_size; + m_size = other.m_size; + other.m_size = size; + + size = m_capacity; + m_capacity = other.m_capacity; + other.m_capacity = size; + } + + union + { + ValueType* m_data; + ValueType m_inline[InlineCount]; + }; + + SizeType m_size; + SizeType m_capacity; +}; + +template +struct VectorOperations : public VectorStorage +{ + using ThisType = VectorOperations; + using ValueType = T; + using SizeType = uint32_t; + static constexpr uint16_t InlineCount = TInlineCount; + using BaseType = VectorStorage; + using ManipType = VectorManipulation; + using ConstIteratorType = Iterator; + + SizeType GrowthFor(SizeType size) noexcept + { + if (m_capacity > UINT32_MAX - m_capacity / 2) + { + return UINT32_MAX; + } + + SizeType growth = m_capacity + m_capacity / 2; + if (growth < size) + { + return size; + } + + return growth; + } + + void Clear() noexcept + { + ManipType().DtorRange(Data(), Data() + m_size); + m_size = 0; + } + + template + Err Reserve(TAllocator& alloc, SizeType capacity) noexcept + { + if (capacity <= m_capacity) + { + return NoError; + } + + VectorAlloc vec(alloc); + vec.buffer = vec.allocator.Alloc(capacity); + if (!vec.buffer) + { + return Error::NoMemory; + } + + ManipType().MoveCtorDtorSrcRange(vec.buffer, Data(), m_size); + + Free(alloc); + + m_data = vec.Release(); + m_capacity = capacity; + + return NoError; + } + + template + void Move(TAllocator& alloc, ThisType& to) noexcept + { + if RAD_UNLIKELY (this == &to) + { + return; + } + + Swap(to); + Clear(); + RAD_VERIFY(ShrinkToFit(alloc).IsOk()); + } + + template + Err Copy(TAllocator& alloc, ThisType& to) noexcept + { + if RAD_UNLIKELY (this == &to) + { + return NoError; + } + + Err res = to.Reserve(alloc, m_size); + if (!res.IsOk()) + { + return res.Err(); + } + + ManipType().CopyCtorDtorDestRange(to.Data(), Data(), m_size); + + if (to.m_size > m_size) + { + ManipType().DtorRange(to.Data() + m_size, to.Data() + to.m_size); + } + + to.m_size = m_size; + + return NoError; + } + + void PopBack() noexcept + { + if RAD_LIKELY (m_size > 0) + { + --m_size; + ManipType().Dtor(Data() + m_size); + } + } + + template + Err Resize(TAllocator& alloc, SizeType count) noexcept + { + if RAD_UNLIKELY (count == m_size) + { + return NoError; + } + + if (count < m_size) + { + ManipType().DtorRange(Data() + count, Data() + m_size); + } + else + { + if (count > m_capacity) + { + Err res = Reserve(alloc, GrowthFor(count)); + if (!res.IsOk()) + { + return res.Err(); + } + } + + ManipType().DefaultCtor(Data() + m_size, count - m_size); + } + + m_size = count; + + return NoError; + } + + template + Err Resize(TAllocator& alloc, + SizeType count, + const ValueType& value) noexcept + { + if RAD_UNLIKELY (count == m_size) + { + return NoError; + } + + if (count < m_size) + { + ManipType().DtorRange(Data() + count, Data() + m_size); + } + else + { + if (count > m_capacity) + { + Err res = Reserve(alloc, GrowthFor(count)); + if (!res.IsOk()) + { + return res.Err(); + } + } + + ManipType().CopyCtor(Data() + m_size, count - m_size, value); + } + + m_size = count; + + return NoError; + } + + template + Err Assign(TAllocator& alloc, + SizeType count, + const ValueType& value) noexcept + { + if (count <= m_size) + { + ManipType().CopyCtorDtorDest(Data(), count, value); + ManipType().DtorRange(Data() + count, Data() + m_size); + } + else if (count <= m_capacity) + { + ManipType().CopyCtorDtorDest(Data(), m_size, value); + ManipType().CopyCtor(Data() + m_size, count - m_size, value); + } + else + { + VectorAlloc vec(alloc); + vec.buffer = vec.allocator.Alloc(count); + if (!vec.buffer) + { + return Error::NoMemory; + } + + ManipType().CopyCtor(vec.buffer, count, value); + ManipType().DtorRange(Data(), Data() + m_size); + + m_data = vec.Release(); + m_capacity = count; + } + + m_size = count; + + return NoError; + } + + template + Err Assign(TAllocator& Allocator, Span span) noexcept + { + if (SpansOverlap(span, Span(Data(), m_size))) + { + return Error::InvalidAddress; + } + + Err res = Reserve(Allocator, span.Size()); + if (!res.IsOk()) + { + return res.Err(); + } + + ValueType* data = Data(); + ValueType* end = Data() + m_size; + for (const auto& entry : span) + { + if (data < end) + { + ManipType().Dtor(data); + } + + ManipType().CopyCtor(data, entry); + + data++; + } + + ManipType().DtorRange(data, end); + + m_size = span.Size(); + + return NoError; + } + + template + Err EmplaceBack(TAllocator& alloc, TArgs&&... args) noexcept + { + if RAD_LIKELY (RAD_VERIFY(m_size < UINT32_MAX)) + { + if (m_size + 1 > m_capacity) + { + Err res = Reserve(alloc, GrowthFor(m_size + 1)); + if (!res.IsOk()) + { + return res; + } + } + + // + // See comments near top of of rad::Vector class. + // + RAD_S_ASSERTMSG( + noexcept(ValueType(Forward(args)...)), + "rad::Vector does not yet support types that might throw."); + + new (AddrOf(Data()[m_size++])) ValueType(Forward(args)...); + + return NoError; + } + + return Error::IntegerOverflow; + } + + using BaseType::Data; + using BaseType::Free; + using BaseType::Swap; + using BaseType::ShrinkToFit; + + using BaseType::m_data; + using BaseType::m_size; + using BaseType::m_capacity; +}; + +} // namespace detail + +} // namespace rad diff --git a/radiant/detail/gcc/AtomicIntrinsics.h b/radiant/detail/gcc/AtomicIntrinsics.h new file mode 100644 index 0000000..58cc221 --- /dev/null +++ b/radiant/detail/gcc/AtomicIntrinsics.h @@ -0,0 +1,191 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +template +struct SelectIntrinsic +{ + RAD_S_ASSERTMSG( + (IsIntegral && sizeof(T) == Size && + (Size == 1 || Size == 2 || Size == 4 || Size == 8)) || + (IsPointer && Size == sizeof(void*)), + "rad::detail::atomic::SelectIntrinsic not supported for this type"); + + template + static inline T Load(const volatile T& storage, OrderTag) noexcept + { + CheckLoadMemoryOrder(); + constexpr int order = static_cast(TOrder::Order); + return __atomic_load_n(&storage, order); + } + + template + static inline void Store(volatile T& storage, + T val, + OrderTag) noexcept + { + CheckStoreMemoryOrder(); + constexpr int order = static_cast(TOrder::Order); + __atomic_store_n(&storage, val, order); + } + + template + static inline T Exchange(volatile T& storage, + T val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_exchange_n(&storage, val, order); + } + + static inline bool CompareExchangeWeak(volatile T& storage, + T val, + T& expected, + ReleaseTag, + ReleaseTag) noexcept + { + ReleaseTag success; + RelaxedTag fail; + return CompareExchangeWeak(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeWeak( + volatile T& storage, T val, T& expected, AcqRelTag, AcqRelTag) noexcept + { + AcqRelTag success; + AcquireTag fail; + return CompareExchangeWeak(storage, val, expected, success, fail); + } + + template + static inline bool CompareExchangeWeak(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + CheckLoadMemoryOrder(); + CheckCasMemoryOrdering(); + constexpr int success = static_cast(Ts::Order); + constexpr int fail = static_cast(Tf::Order); + return __atomic_compare_exchange_n(&storage, + &expected, + val, + true, + success, + fail); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag, + ReleaseTag) noexcept + { + ReleaseTag success; + RelaxedTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong( + volatile T& storage, T val, T& expected, AcqRelTag, AcqRelTag) noexcept + { + AcqRelTag success; + AcquireTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + template + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + CheckLoadMemoryOrder(); + CheckCasMemoryOrdering(); + constexpr int success = static_cast(Ts::Order); + constexpr int fail = static_cast(Tf::Order); + return __atomic_compare_exchange_n(&storage, + &expected, + val, + false, + success, + fail); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + T val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_add(&storage, val, order); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_add(&storage, val, order); + } + + template , int> = 0> + static inline T FetchSub(volatile T& storage, + T val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_sub(&storage, val, order); + } + + template , int> = 0> + static inline T FetchSub(volatile T& storage, + ptrdiff_t val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_sub(&storage, val, order); + } + + template + static inline T FetchAnd(volatile T& storage, + T val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_and(&storage, val, order); + } + + template + static inline T FetchOr(volatile T& storage, + T val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_or(&storage, val, order); + } + + template + static inline T FetchXor(volatile T& storage, + T val, + OrderTag) noexcept + { + constexpr int order = static_cast(TOrder::Order); + return __atomic_fetch_xor(&storage, val, order); + } +}; diff --git a/radiant/detail/msvc/AtomicIntrinsics.h b/radiant/detail/msvc/AtomicIntrinsics.h new file mode 100644 index 0000000..75d79a1 --- /dev/null +++ b/radiant/detail/msvc/AtomicIntrinsics.h @@ -0,0 +1,1271 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) +#define _RAD_MEM_BARRIER __dmb(0xb); // _ARM_BARRIER_ISH / _ARM64_BARRIER_ISH +#define _RAD_INTRIN_RELAXED(Intrin) RAD_CONCAT(Intrin, _nf) +#define _RAD_INTRIN_ACQUIRE(Intrin) RAD_CONCAT(Intrin, _acq) +#define _RAD_INTRIN_RELEASE(Intrin) RAD_CONCAT(Intrin, _rel) +#elif defined(_M_IX86) || defined(_M_AMD64) +#define _RAD_MEM_BARRIER \ + _Pragma("warning(push)") _Pragma("warning(disable : 4996)") \ + _ReadWriteBarrier() _Pragma("warning(pop)") +#define _RAD_INTRIN_RELAXED(Intrin) Intrin +#define _RAD_INTRIN_ACQUIRE(Intrin) Intrin +#define _RAD_INTRIN_RELEASE(Intrin) Intrin +#endif + +// clang-format off +RAD_INLINE_VAR constexpr MemoryOrder _combinedOrders[6][6] = { + {MemoryOrder::Relaxed, MemoryOrder::Consume, MemoryOrder::Acquire, MemoryOrder::Release, MemoryOrder::AcqRel, MemoryOrder::SeqCst}, + {MemoryOrder::Consume, MemoryOrder::Consume, MemoryOrder::Acquire, MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::SeqCst}, + {MemoryOrder::Acquire, MemoryOrder::Acquire, MemoryOrder::Acquire, MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::SeqCst}, + {MemoryOrder::Release, MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::Release, MemoryOrder::AcqRel, MemoryOrder::SeqCst}, + {MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::AcqRel, MemoryOrder::SeqCst}, + {MemoryOrder::SeqCst, MemoryOrder::SeqCst, MemoryOrder::SeqCst, MemoryOrder::SeqCst, MemoryOrder::SeqCst, MemoryOrder::SeqCst}}; + +// clang-format on + +constexpr inline MemoryOrder CombineMemoryOrders(MemoryOrder success, + MemoryOrder fail) noexcept +{ + return _combinedOrders[static_cast(success)][static_cast(fail)]; +} + +template +constexpr inline T Negate(const T val) noexcept +{ + return static_cast(0U - static_cast>(val)); +} + +template +struct SelectIntrinsic +{ + RAD_S_ASSERTMSG( + Size == 0, + "rad::detail::atomic::SelectIntrinsic not supported for this type"); +}; + +template +struct SelectIntrinsic +{ + RAD_S_ASSERT(sizeof(T) == 1); + using Type = char; + + static inline T Load(const volatile T& storage, RelaxedTag) noexcept + { + return static_cast(__iso_volatile_load8(AddrAs(storage))); + } + + template + static inline T Load(const volatile T& storage, OrderTag) noexcept + { + CheckLoadMemoryOrder(); + T ret = static_cast(__iso_volatile_load8(AddrAs(storage))); + _RAD_MEM_BARRIER; + return ret; + } + + static inline void Store(volatile T& storage, T val, RelaxedTag) noexcept + { + __iso_volatile_store8(AddrAs(storage), ValAs(val)); + } + + template + static inline void Store(volatile T& storage, + T val, + OrderTag) noexcept + { + CheckStoreMemoryOrder(); + _RAD_MEM_BARRIER; + __iso_volatile_store8(AddrAs(storage), ValAs(val)); + } + + static inline T Exchange(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedExchange8)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedExchange8)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedExchange8)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedExchange8(AddrAs(storage), ValAs(val))); + } + + static inline bool CasRet(T& expected, Type comparand, Type old) noexcept + { + if (old != comparand) + { + expected = static_cast(old); + return false; + } + return true; + } + + template + static inline bool CompareExchangeWeak(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + // MSVC does not provide a weak CAS intrinsic for any platform + Ts success; + Tf fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag, + ReleaseTag) noexcept + { + ReleaseTag success; + RelaxedTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong( + volatile T& storage, T val, T& expected, AcqRelTag, AcqRelTag) noexcept + { + AcqRelTag success; + AcquireTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + template + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + CheckLoadMemoryOrder(); + CheckCasMemoryOrdering(); + typename OrderToTag::Type o; + return CompareExchangeStrong(storage, val, expected, o); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + RelaxedTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELAXED(_InterlockedCompareExchange8)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + AcquireTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_ACQUIRE(_InterlockedCompareExchange8)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELEASE(_InterlockedCompareExchange8)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + SeqCstTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _InterlockedCompareExchange8(AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline T FetchAdd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedExchangeAdd8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAdd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedExchangeAdd8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAdd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedExchangeAdd8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAdd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedExchangeAdd8(AddrAs(storage), ValAs(val))); + } + + template + static inline T FetchSub(volatile T& storage, + T val, + OrderTag) noexcept + { + typename OrderTag::Type order; + return FetchAdd(storage, Negate(val), order); + } + + static inline T FetchAnd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedAnd8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedAnd8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedAnd8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedAnd8(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedOr8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedOr8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedOr8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedOr8(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedXor8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedXor8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedXor8)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedXor8(AddrAs(storage), ValAs(val))); + } +}; + +template +struct SelectIntrinsic +{ + RAD_S_ASSERT(sizeof(T) == 2); + using Type = short; + + static inline T Load(const volatile T& storage, RelaxedTag) noexcept + { + return static_cast(__iso_volatile_load16(AddrAs(storage))); + } + + template + static inline T Load(const volatile T& storage, OrderTag) noexcept + { + CheckLoadMemoryOrder(); + T ret = static_cast(__iso_volatile_load16(AddrAs(storage))); + _RAD_MEM_BARRIER; + return ret; + } + + static inline void Store(volatile T& storage, T val, RelaxedTag) noexcept + { + __iso_volatile_store16(AddrAs(storage), ValAs(val)); + } + + template + static inline void Store(volatile T& storage, + T val, + OrderTag) noexcept + { + CheckStoreMemoryOrder(); + _RAD_MEM_BARRIER; + __iso_volatile_store16(AddrAs(storage), ValAs(val)); + } + + static inline T Exchange(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedExchange16)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedExchange16)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedExchange16)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedExchange16(AddrAs(storage), ValAs(val))); + } + + static inline bool CasRet(T& expected, Type comparand, Type old) noexcept + { + if (old != comparand) + { + expected = static_cast(old); + return false; + } + return true; + } + + template + static inline bool CompareExchangeWeak(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + // MSVC does not provide a weak CAS intrinsic for any platform + Ts success; + Tf fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag, + ReleaseTag) noexcept + { + ReleaseTag success; + RelaxedTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong( + volatile T& storage, T val, T& expected, AcqRelTag, AcqRelTag) noexcept + { + AcqRelTag success; + AcquireTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + template + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + CheckLoadMemoryOrder(); + CheckCasMemoryOrdering(); + typename OrderToTag::Type o; + return CompareExchangeStrong(storage, val, expected, o); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + RelaxedTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELAXED(_InterlockedCompareExchange16)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + AcquireTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_ACQUIRE(_InterlockedCompareExchange16)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELEASE(_InterlockedCompareExchange16)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + SeqCstTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _InterlockedCompareExchange16(AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline T FetchAdd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED(_InterlockedExchangeAdd16)( + AddrAs(storage), + ValAs(val))); + } + + static inline T FetchAdd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE(_InterlockedExchangeAdd16)( + AddrAs(storage), + ValAs(val))); + } + + static inline T FetchAdd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE(_InterlockedExchangeAdd16)( + AddrAs(storage), + ValAs(val))); + } + + static inline T FetchAdd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedExchangeAdd16(AddrAs(storage), ValAs(val))); + } + + template + static inline T FetchSub(volatile T& storage, + T val, + OrderTag) noexcept + { + typename OrderTag::Type order; + return FetchAdd(storage, Negate(val), order); + } + + static inline T FetchAnd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedAnd16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedAnd16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedAnd16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedAnd16(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedOr16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedOr16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedOr16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedOr16(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedXor16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedXor16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedXor16)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedXor16(AddrAs(storage), ValAs(val))); + } +}; + +template +struct SelectIntrinsic +{ + RAD_S_ASSERT(sizeof(T) == 4); + using Type = long; + + static inline T Load(const volatile T& storage, RelaxedTag) noexcept + { + return ValAs(__iso_volatile_load32(AddrAs(storage))); + } + + template + static inline T Load(const volatile T& storage, OrderTag) noexcept + { + CheckLoadMemoryOrder(); + T ret = ValAs(__iso_volatile_load32(AddrAs(storage))); + _RAD_MEM_BARRIER; + return ret; + } + + static inline void Store(volatile T& storage, T val, RelaxedTag) noexcept + { + __iso_volatile_store32(AddrAs(storage), ValAs(val)); + } + + template + static inline void Store(volatile T& storage, + T val, + OrderTag) noexcept + { + CheckStoreMemoryOrder(); + _RAD_MEM_BARRIER; + __iso_volatile_store32(AddrAs(storage), ValAs(val)); + } + + static inline T Exchange(volatile T& storage, T val, RelaxedTag) noexcept + { + return ValAs(_RAD_INTRIN_RELAXED( + _InterlockedExchange)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, AcquireTag) noexcept + { + return ValAs(_RAD_INTRIN_ACQUIRE( + _InterlockedExchange)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, ReleaseTag) noexcept + { + return ValAs(_RAD_INTRIN_RELEASE( + _InterlockedExchange)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, SeqCstTag) noexcept + { + return ValAs( + _InterlockedExchange(AddrAs(storage), ValAs(val))); + } + + static inline bool CasRet(T& expected, Type comparand, Type old) noexcept + { + if (old != comparand) + { + expected = ValAs(old); + return false; + } + return true; + } + + template + static inline bool CompareExchangeWeak(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + // MSVC does not provide a weak CAS intrinsic for any platform + Ts success; + Tf fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag, + ReleaseTag) noexcept + { + ReleaseTag success; + RelaxedTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong( + volatile T& storage, T val, T& expected, AcqRelTag, AcqRelTag) noexcept + { + AcqRelTag success; + AcquireTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + template + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + CheckLoadMemoryOrder(); + CheckCasMemoryOrdering(); + typename OrderToTag::Type o; + return CompareExchangeStrong(storage, val, expected, o); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + RelaxedTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELAXED(_InterlockedCompareExchange)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + AcquireTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_ACQUIRE(_InterlockedCompareExchange)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELEASE(_InterlockedCompareExchange)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + SeqCstTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _InterlockedCompareExchange(AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedExchangeAdd)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + RelaxedTag) noexcept + { + return reinterpret_cast(_RAD_INTRIN_RELAXED( + _InterlockedExchangeAdd)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedExchangeAdd)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + AcquireTag) noexcept + { + return reinterpret_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedExchangeAdd)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedExchangeAdd)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + ReleaseTag) noexcept + { + return reinterpret_cast(_RAD_INTRIN_RELEASE( + _InterlockedExchangeAdd)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedExchangeAdd(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + SeqCstTag) noexcept + { + return reinterpret_cast( + _InterlockedExchangeAdd(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchSub(volatile T& storage, + T val, + OrderTag) noexcept + { + typename OrderTag::Type order; + return FetchAdd(storage, Negate(val), order); + } + + template , int> = 0> + static inline T FetchSub(volatile T& storage, + ptrdiff_t val, + OrderTag) noexcept + { + typename OrderTag::Type order; + return FetchAdd(storage, Negate(val), order); + } + + static inline T FetchAnd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedAnd)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedAnd)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedAnd)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedAnd(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedOr)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedOr)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedOr)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedOr(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + _InterlockedXor)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + _InterlockedXor)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + _InterlockedXor)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + _InterlockedXor(AddrAs(storage), ValAs(val))); + } +}; + +template +struct SelectIntrinsic +{ + RAD_S_ASSERT(sizeof(T) == 8); + using Type = __int64; + + // Load and Store operations for 64-bit integrals on x86 (32-bit) will + // produce fild and flstp instructions for atomicity when compiling with + // /kernel. This is due to /kernel forcing IA86 and not allowing override + // with SSE support. This, unfortunately, can cause alignment issues at + // DISPATCH_LEVEL and bug check. + // + // The workaround for this is to use slower atomic exchange and cas. + // This will unfortunately result in all loads/stores on x86 enforcing + // sequential ordering with the associated performance impact. + + static inline T Load(const volatile T& storage, RelaxedTag) noexcept + { +#if RAD_I386 && RAD_KERNEL_MODE + return InterlockedCompareExchange64((volatile LONG64*)&storage, 0, 0); +#else + return ValAs(__iso_volatile_load64(AddrAs(storage))); +#endif + } + + template + static inline T Load(const volatile T& storage, OrderTag) noexcept + { + CheckLoadMemoryOrder(); +#if RAD_I386 && RAD_KERNEL_MODE + return InterlockedCompareExchange64((volatile LONG64*)&storage, 0, 0); +#else + T ret = ValAs(__iso_volatile_load64(AddrAs(storage))); + _RAD_MEM_BARRIER; + return ret; +#endif + } + + static inline void Store(volatile T& storage, T val, RelaxedTag) noexcept + { +#if RAD_I386 && RAD_KERNEL_MODE + InterlockedExchange64(AddrAs(storage), val); +#else + __iso_volatile_store64(AddrAs(storage), ValAs(val)); +#endif + } + + template + static inline void Store(volatile T& storage, + T val, + OrderTag) noexcept + { + CheckStoreMemoryOrder(); +#if RAD_I386 && RAD_KERNEL_MODE + InterlockedExchange64(AddrAs(storage), val); +#else + _RAD_MEM_BARRIER; + __iso_volatile_store64(AddrAs(storage), ValAs(val)); +#endif + } + + static inline T Exchange(volatile T& storage, T val, RelaxedTag) noexcept + { + return ValAs(_RAD_INTRIN_RELAXED( + InterlockedExchange64)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, AcquireTag) noexcept + { + return ValAs(_RAD_INTRIN_ACQUIRE( + InterlockedExchange64)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, ReleaseTag) noexcept + { + return ValAs(_RAD_INTRIN_RELEASE( + InterlockedExchange64)(AddrAs(storage), ValAs(val))); + } + + static inline T Exchange(volatile T& storage, T val, SeqCstTag) noexcept + { + return ValAs( + InterlockedExchange64(AddrAs(storage), ValAs(val))); + } + + static inline bool CasRet(T& expected, Type comparand, Type old) noexcept + { + if (old != comparand) + { + expected = ValAs(old); + return false; + } + return true; + } + + template + static inline bool CompareExchangeWeak(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + // MSVC does not provide a weak CAS intrinsic for any platform + Ts success; + Tf fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag, + ReleaseTag) noexcept + { + ReleaseTag success; + RelaxedTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + static inline bool CompareExchangeStrong( + volatile T& storage, T val, T& expected, AcqRelTag, AcqRelTag) noexcept + { + AcqRelTag success; + AcquireTag fail; + return CompareExchangeStrong(storage, val, expected, success, fail); + } + + template + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + OrderTag, + OrderTag) noexcept + { + CheckLoadMemoryOrder(); + CheckCasMemoryOrdering(); + typename OrderToTag::Type o; + return CompareExchangeStrong(storage, val, expected, o); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + RelaxedTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELAXED(_InterlockedCompareExchange64)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + AcquireTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_ACQUIRE(_InterlockedCompareExchange64)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + ReleaseTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _RAD_INTRIN_RELEASE(_InterlockedCompareExchange64)( + AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + static inline bool CompareExchangeStrong(volatile T& storage, + T val, + T& expected, + SeqCstTag) noexcept + { + return CasRet(expected, + ValAs(expected), + _InterlockedCompareExchange64(AddrAs(storage), + ValAs(val), + ValAs(expected))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + InterlockedExchangeAdd64)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + RelaxedTag) noexcept + { + return reinterpret_cast(_RAD_INTRIN_RELAXED( + InterlockedExchangeAdd64)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + InterlockedExchangeAdd64)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + AcquireTag) noexcept + { + return reinterpret_cast(_RAD_INTRIN_ACQUIRE( + InterlockedExchangeAdd64)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + InterlockedExchangeAdd64)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + ReleaseTag) noexcept + { + return reinterpret_cast(_RAD_INTRIN_RELEASE( + InterlockedExchangeAdd64)(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + InterlockedExchangeAdd64(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchAdd(volatile T& storage, + ptrdiff_t val, + SeqCstTag) noexcept + { + return reinterpret_cast( + InterlockedExchangeAdd64(AddrAs(storage), ValAs(val))); + } + + template , int> = 0> + static inline T FetchSub(volatile T& storage, + T val, + OrderTag) noexcept + { + typename OrderTag::Type order; + return FetchAdd(storage, Negate(val), order); + } + + template , int> = 0> + static inline T FetchSub(volatile T& storage, + ptrdiff_t val, + OrderTag) noexcept + { + typename OrderTag::Type order; + return FetchAdd(storage, Negate(val), order); + } + + static inline T FetchAnd(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + InterlockedAnd64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + InterlockedAnd64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + InterlockedAnd64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchAnd(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + InterlockedAnd64(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + InterlockedOr64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + InterlockedOr64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + InterlockedOr64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchOr(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + InterlockedOr64(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, RelaxedTag) noexcept + { + return static_cast(_RAD_INTRIN_RELAXED( + InterlockedXor64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, AcquireTag) noexcept + { + return static_cast(_RAD_INTRIN_ACQUIRE( + InterlockedXor64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, ReleaseTag) noexcept + { + return static_cast(_RAD_INTRIN_RELEASE( + InterlockedXor64)(AddrAs(storage), ValAs(val))); + } + + static inline T FetchXor(volatile T& storage, T val, SeqCstTag) noexcept + { + return static_cast( + InterlockedXor64(AddrAs(storage), ValAs(val))); + } +}; diff --git a/test/BUILD.bazel b/test/BUILD.bazel new file mode 100644 index 0000000..30d924e --- /dev/null +++ b/test/BUILD.bazel @@ -0,0 +1,42 @@ +load("//:default_copts.bzl", "RAD_CPP14", "RAD_CPP17", "RAD_CPP20", "RAD_DEFAULT_COPTS") + +filegroup( + name = "test_srcs", + srcs = glob([ + "*.cpp", + "*.h", + ]), +) + +TEST_SIZE = "small" + +TEST_SRCS = [":test_srcs"] + +TEST_DEPS = [ + "@//:radiant", + "@googletest//:gtest_main", +] + +cc_test( + name = "all_test14", + size = TEST_SIZE, + srcs = TEST_SRCS, + copts = RAD_CPP14 + RAD_DEFAULT_COPTS, + deps = TEST_DEPS, +) + +cc_test( + name = "all_test17", + size = TEST_SIZE, + srcs = TEST_SRCS, + copts = RAD_CPP17 + RAD_DEFAULT_COPTS, + deps = TEST_DEPS, +) + +cc_test( + name = "all_test20", + size = TEST_SIZE, + srcs = TEST_SRCS, + copts = RAD_CPP20 + RAD_DEFAULT_COPTS, + deps = TEST_DEPS, +) diff --git a/test/TestAlloc.cpp b/test/TestAlloc.cpp new file mode 100644 index 0000000..c19d7e5 --- /dev/null +++ b/test/TestAlloc.cpp @@ -0,0 +1,66 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "test/TestAlloc.h" + +#include + +namespace radtest +{ + +uint32_t StaticCountingAllocatorImpl::freeCount = 0; +uint32_t StaticCountingAllocatorImpl::allocCount = 0; +uint32_t StaticCountingAllocatorImpl::reallocCount = 0; +uint32_t StaticCountingAllocatorImpl::freeBytesCount = 0; +uint32_t StaticCountingAllocatorImpl::allocBytesCount = 0; +uint32_t StaticCountingAllocatorImpl::reallocBytesCount = 0; + +void StaticCountingAllocatorImpl::Free(void* ptr) noexcept +{ + ++freeCount; + free(ptr); +} + +void* StaticCountingAllocatorImpl::Alloc(uint32_t size) noexcept +{ + ++allocCount; + return malloc(size); +} + +void* StaticCountingAllocatorImpl::Realloc(void* ptr, uint32_t size) noexcept +{ + ++reallocCount; + return realloc(ptr, size); +} + +void StaticCountingAllocatorImpl::FreeBytes(void* ptr) noexcept +{ + ++freeBytesCount; + free(ptr); +} + +void* StaticCountingAllocatorImpl::AllocBytes(uint32_t size) noexcept +{ + ++allocBytesCount; + return malloc(size); +} + +void* StaticCountingAllocatorImpl::ReallocBytes(void* ptr, + uint32_t size) noexcept +{ + ++reallocBytesCount; + return realloc(ptr, size); +} + +} // namespace radtest diff --git a/test/TestAlloc.h b/test/TestAlloc.h new file mode 100644 index 0000000..0542ad8 --- /dev/null +++ b/test/TestAlloc.h @@ -0,0 +1,570 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include +#include +#include + +namespace radtest +{ + +template +class Allocator +{ +public: + + static constexpr bool NeedsFree = true; + static constexpr bool HasRealloc = true; + static constexpr bool HasAllocBytes = true; + + using ThisType = Allocator; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + ~Allocator() = default; + + constexpr Allocator() = default; + + constexpr Allocator(const Allocator&) noexcept = default; + + template + constexpr Allocator(const Allocator&) noexcept + { + } + + template + struct Rebind + { + using Other = Allocator; + }; + + void Free(ValueType* ptr) noexcept + { + free(ptr); + } + + ValueType* Alloc(SizeType count) noexcept + { + return (ValueType*)malloc(count * sizeof(T)); + } + + ValueType* Realloc(ValueType* ptr, SizeType count) noexcept + { + return (ValueType*)realloc(ptr, count * sizeof(T)); + } + + void FreeBytes(void* ptr) noexcept + { + free(ptr); + } + + void* AllocBytes(SizeType size) noexcept + { + return malloc(size); + } + + void* ReallocBytes(void* ptr, SizeType size) noexcept + { + return realloc(ptr, size); + } +}; + +template +class StatefulAllocator +{ +public: + + static constexpr bool NeedsFree = true; + static constexpr bool HasRealloc = true; + static constexpr bool HasAllocBytes = true; + + using ThisType = StatefulAllocator; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + ~StatefulAllocator() = default; + + StatefulAllocator() + : value(0) + { + } + + StatefulAllocator(const StatefulAllocator&) noexcept = default; + + template + StatefulAllocator(const StatefulAllocator& other) + : value(other.value) + { + } + + template + struct Rebind + { + using Other = StatefulAllocator; + }; + + void Free(ValueType* ptr) noexcept + { + free(ptr); + } + + ValueType* Alloc(SizeType count) noexcept + { + return (ValueType*)malloc(count * sizeof(T)); + } + + ValueType* Realloc(ValueType* ptr, SizeType count) noexcept + { + return (ValueType*)realloc(ptr, count * sizeof(T)); + } + + void FreeBytes(void* ptr) noexcept + { + free(ptr); + } + + void* AllocBytes(SizeType size) noexcept + { + return malloc(size); + } + + uint32_t value; +}; + +template +class FailingAllocator +{ +public: + + static constexpr bool NeedsFree = true; + static constexpr bool HasRealloc = true; + static constexpr bool HasAllocBytes = true; + + using ThisType = FailingAllocator; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + ~FailingAllocator() = default; + + constexpr FailingAllocator() = default; + + constexpr FailingAllocator(const FailingAllocator&) noexcept = default; + + template + constexpr FailingAllocator(const FailingAllocator&) noexcept + { + } + + template + struct Rebind + { + using Other = FailingAllocator; + }; + + void Free(ValueType*) noexcept + { + } + + ValueType* Alloc(SizeType) noexcept + { + return nullptr; + } + + ValueType* Realloc(ValueType*, SizeType) noexcept + { + return nullptr; + } + + void FreeBytes(void*) noexcept + { + } + + void* AllocBytes(SizeType) noexcept + { + return nullptr; + } + + void* ReallocBytes(void*, SizeType) noexcept + { + return nullptr; + } +}; + +template +class CountingAllocator +{ +public: + + static constexpr bool NeedsFree = true; + static constexpr bool HasRealloc = true; + static constexpr bool HasAllocBytes = true; + + using ThisType = CountingAllocator; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + ~CountingAllocator() = default; + + constexpr CountingAllocator() = default; + + constexpr CountingAllocator(const CountingAllocator&) noexcept = default; + + template + constexpr CountingAllocator(const CountingAllocator&) noexcept + { + } + + template + struct Rebind + { + using Other = CountingAllocator; + }; + + void Free(ValueType* ptr) noexcept + { + ++freeCount; + free(ptr); + } + + ValueType* Alloc(SizeType count) noexcept + { + ++allocCount; + return (ValueType*)malloc(count * sizeof(T)); + } + + ValueType* Realloc(ValueType* ptr, SizeType count) noexcept + { + ++reallocCount; + return (ValueType*)realloc(ptr, count * sizeof(T)); + } + + void FreeBytes(void* ptr) noexcept + { + ++freeBytesCount; + free(ptr); + } + + void* AllocBytes(SizeType size) noexcept + { + ++allocBytesCount; + return malloc(size); + } + + void* ReallocBytes(void* ptr, SizeType size) noexcept + { + ++reallocBytesCount; + return realloc(ptr, size); + } + + uint32_t freeCount; + uint32_t allocCount; + uint32_t reallocCount; + uint32_t freeBytesCount; + uint32_t allocBytesCount; + uint32_t reallocBytesCount; +}; + +struct StaticCountingAllocatorImpl +{ + static void Free(void* ptr) noexcept; + static void* Alloc(uint32_t size) noexcept; + static void* Realloc(void* ptr, uint32_t size) noexcept; + static void FreeBytes(void* ptr) noexcept; + static void* AllocBytes(uint32_t size) noexcept; + static void* ReallocBytes(void* ptr, uint32_t size) noexcept; + + static uint32_t freeCount; + static uint32_t allocCount; + static uint32_t reallocCount; + static uint32_t freeBytesCount; + static uint32_t allocBytesCount; + static uint32_t reallocBytesCount; +}; + +template +class StaticCountingAllocator +{ +public: + + static constexpr bool NeedsFree = true; + static constexpr bool HasRealloc = true; + static constexpr bool HasAllocBytes = true; + + using ThisType = StaticCountingAllocator; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + using Impl = StaticCountingAllocatorImpl; + + ~StaticCountingAllocator() = default; + + StaticCountingAllocator() + : state(0) + { + } + + StaticCountingAllocator(const StaticCountingAllocator&) noexcept = default; + + template + StaticCountingAllocator(const StaticCountingAllocator& other) + : state(other.state) + { + } + + template + struct Rebind + { + using Other = StaticCountingAllocator; + }; + + void Free(ValueType* ptr) noexcept + { + Impl::Free(ptr); + } + + ValueType* Alloc(SizeType count) noexcept + { + return (ValueType*)Impl::Alloc((sizeof(T) * count)); + } + + ValueType* Realloc(ValueType* ptr, SizeType count) noexcept + { + return (ValueType*)Impl::Realloc(ptr, (sizeof(T) * count)); + } + + void FreeBytes(void* ptr) noexcept + { + Impl::Free(ptr); + } + + void* AllocBytes(SizeType size) noexcept + { + return (ValueType*)Impl::Alloc(size); + } + + void* ReallocBytes(void* ptr, SizeType size) noexcept + { + return Impl::Realloc(ptr, size); + } + + uint32_t FreeCount() const noexcept + { + return Impl::freeCount; + } + + uint32_t AllocCount() const noexcept + { + return Impl::allocCount; + } + + uint32_t ReallocCount() const noexcept + { + return Impl::reallocCount; + } + + uint32_t FreeBytesCount() const noexcept + { + return Impl::freeBytesCount; + } + + uint32_t AllocBytesCount() const noexcept + { + return Impl::allocBytesCount; + } + + uint32_t ReallocBytesCount() const noexcept + { + return Impl::reallocBytesCount; + } + + void ResetCounts() noexcept + { + Impl::freeCount = 0; + Impl::allocCount = 0; + Impl::reallocCount = 0; + Impl::freeBytesCount = 0; + Impl::allocBytesCount = 0; + Impl::reallocBytesCount = 0; + } + + bool VerifyCounts() const noexcept + { + return Impl::allocCount == Impl::freeCount; + } + + bool VerifyCounts(uint32_t expectedAllocs, + uint32_t expectedFrees) const noexcept + { + return Impl::allocCount == expectedAllocs && + Impl::freeCount == expectedFrees; + } + + uint32_t state; // make sure stateful +}; + +struct HeapAllocator +{ + void Free(void* ptr) + { + freeCount++; + free(ptr); + } + + void* Alloc(uint32_t count) + { + if (forceAllocFails > 0) + { + forceAllocFails--; + return nullptr; + } + + allocCount++; + return malloc(count); + } + + void* Realloc(void* ptr, uint32_t count) + { + if (forceReallocFails > 0) + { + forceReallocFails--; + return nullptr; + } + + reallocCount++; + return realloc(ptr, count); + } + + void FreeBytes(void* ptr) + { + freeBytesCount++; + free(ptr); + } + + void* AllocBytes(uint32_t count) + { + if (forceAllocBytesFails > 0) + { + forceAllocBytesFails--; + return nullptr; + } + + allocBytesCount++; + return malloc(count); + } + + void* ReallocBytes(void* ptr, uint32_t count) + { + if (forceReallocBytesFails > 0) + { + forceReallocBytesFails--; + return nullptr; + } + + reallocBytesCount++; + return realloc(ptr, count); + } + + int32_t freeCount{ 0 }; + + int32_t forceAllocFails{ 0 }; + int32_t allocCount{ 0 }; + + uint32_t forceReallocFails{ 0 }; + uint32_t reallocCount{ 0 }; + + uint32_t freeBytesCount{ 0 }; + + uint32_t forceAllocBytesFails{ 0 }; + uint32_t allocBytesCount{ 0 }; + + uint32_t forceReallocBytesFails{ 0 }; + uint32_t reallocBytesCount{ 0 }; +}; + +template +class AllocWrapper +{ +public: + + static constexpr bool NeedsFree = true; + static constexpr bool HasRealloc = true; + static constexpr bool HasAllocBytes = true; + + using ThisType = AllocWrapper; + using ValueType = T; + using SizeType = uint32_t; + using DifferenceType = ptrdiff_t; + + ~AllocWrapper() = default; + + constexpr AllocWrapper(TBase& alloc) + : base(&alloc) + { + } + + constexpr AllocWrapper(const AllocWrapper&) noexcept = default; + + template + constexpr AllocWrapper(const AllocWrapper& other) + : base(other.base) + { + } + + template + struct Rebind + { + using Other = AllocWrapper; + }; + + void Free(ValueType* ptr) noexcept + { + base->Free(ptr); + } + + ValueType* Alloc(SizeType count) noexcept + { + return (ValueType*)base->Alloc(count * sizeof(T)); + } + + ValueType* Realloc(ValueType* ptr, SizeType count) noexcept + { + return (ValueType*)base->Realloc(ptr, count * sizeof(T)); + } + + void FreeBytes(void* ptr) noexcept + { + base->FreeBytes(ptr); + } + + void* AllocBytes(SizeType size) noexcept + { + return base->AllocBytes(size); + } + + void* ReallocBytes(void* ptr, SizeType size) noexcept + { + return base->ReallocBytes(ptr, size); + } + + TBase* base; +}; + +} // namespace radtest diff --git a/test/TestMove.cpp b/test/TestMove.cpp new file mode 100644 index 0000000..f4e4e51 --- /dev/null +++ b/test/TestMove.cpp @@ -0,0 +1,58 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "test/TestMove.h" + +namespace radtest +{ + +static uint32_t g_MoveCount = 0; + +MoveTester::MoveTester(const MoveTester& other) + : value(other.value) +{ +} + +MoveTester::MoveTester(MoveTester&& other) + : value(other.value) +{ + other.value = 0xDEADBEEF; + g_MoveCount++; +} + +MoveTester& MoveTester::operator=(const MoveTester& other) +{ + value = other.value; + return *this; +} + +MoveTester& MoveTester::operator=(MoveTester&& other) +{ + value = other.value; + other.value = 0xDEADBEEF; + g_MoveCount++; + return *this; +} + +uint32_t MoveTester::MoveCount() +{ + return g_MoveCount; +} + +void MoveTester::ResetCounts() +{ + g_MoveCount = 0; +} + +}; // namespace radtest diff --git a/test/TestMove.h b/test/TestMove.h new file mode 100644 index 0000000..7ddb843 --- /dev/null +++ b/test/TestMove.h @@ -0,0 +1,37 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#pragma once + +#include + +namespace radtest +{ + +struct MoveTester +{ + MoveTester() = default; + MoveTester(const MoveTester& other); + MoveTester(MoveTester&& other); + + MoveTester& operator=(const MoveTester& other); + MoveTester& operator=(MoveTester&& other); + + static uint32_t MoveCount(); + static void ResetCounts(); + + uint32_t value; +}; + +} // namespace radtest diff --git a/test/TestThrow.h b/test/TestThrow.h new file mode 100644 index 0000000..a565656 --- /dev/null +++ b/test/TestThrow.h @@ -0,0 +1,519 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "radiant/Utility.h" + +#include +#include + +namespace radtest +{ + +/// @brief Will throw if constructed with a value of 1 +struct ThrowingObject +{ + ~ThrowingObject() + { + m_value = 0; + } + + ThrowingObject() + : m_value(0) + { + } + + ThrowingObject(int Value) + : m_value(Value) + { + if (m_value == 1) + { + throw std::exception(); + } + } + + ThrowingObject(std::initializer_list Init) + : m_value(*Init.begin()) + { + } + + ThrowingObject(const ThrowingObject& Other) + : m_value(Other.m_value) + { + } + + ThrowingObject(ThrowingObject&& Other) + : m_value(rad::Move(Other.m_value)) + { + Other.m_value = 0; + } + + ThrowingObject& operator=(const ThrowingObject& Other) + { + m_value = Other.m_value; + return *this; + } + + ThrowingObject& operator=(ThrowingObject&& Other) + { + m_value = rad::Move(Other.m_value); + Other.m_value = 0; + return *this; + } + + operator int() const + { + return m_value; + } + + int m_value = 0; +}; + +struct DerivedThrowingObject : public ThrowingObject +{ + using ThrowingObject::ThrowingObject; + using ThrowingObject::operator=; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~ThrowingObject())); +RAD_S_ASSERT(!noexcept(ThrowingObject())); +RAD_S_ASSERT(!noexcept(ThrowingObject(1))); +RAD_S_ASSERT(!noexcept(ThrowingObject(ThrowingObject()))); +RAD_S_ASSERT(!noexcept(ThrowingObject(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(ThrowingObject()))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct ThrowingObjectTwo +{ + ~ThrowingObjectTwo() + { + m_value = 0; + } + + ThrowingObjectTwo() + : m_value(0) + { + } + + ThrowingObjectTwo(int Value) + : m_value(Value) + { + } + + ThrowingObjectTwo(std::initializer_list Init) + : m_value(*Init.begin()) + { + } + + ThrowingObjectTwo(const ThrowingObjectTwo& Other) + : m_value(Other.m_value) + { + } + + ThrowingObjectTwo(ThrowingObjectTwo&& Other) + : m_value(rad::Move(Other.m_value)) + { + } + + ThrowingObjectTwo& operator=(const ThrowingObjectTwo& Other) + { + m_value = Other.m_value; + return *this; + } + + ThrowingObjectTwo& operator=(ThrowingObjectTwo&& Other) + { + m_value = rad::Move(Other.m_value); + return *this; + } + + long long m_value = 0; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~ThrowingObjectTwo())); +RAD_S_ASSERT(!noexcept(ThrowingObjectTwo())); +RAD_S_ASSERT(!noexcept(ThrowingObjectTwo(1))); +RAD_S_ASSERT(!noexcept(ThrowingObjectTwo(ThrowingObjectTwo()))); +RAD_S_ASSERT(!noexcept(ThrowingObjectTwo(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(ThrowingObjectTwo()))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct NonThrowingObject +{ + ~NonThrowingObject() noexcept + { + m_value = 0; + } + + NonThrowingObject() noexcept + : m_value(0) + { + } + + NonThrowingObject(int Value) noexcept + : m_value(Value) + { + } + + NonThrowingObject(std::initializer_list Init) noexcept + : m_value(*Init.begin()) + { + } + + NonThrowingObject(const NonThrowingObject& Other) noexcept + : m_value(Other.m_value) + { + } + + NonThrowingObject(NonThrowingObject&& Other) noexcept + : m_value(rad::Move(Other.m_value)) + { + Other.m_value = 0; + } + + NonThrowingObject& operator=(const NonThrowingObject& Other) noexcept + { + m_value = Other.m_value; + return *this; + } + + NonThrowingObject& operator=(NonThrowingObject&& Other) noexcept + { + m_value = rad::Move(Other.m_value); + Other.m_value = 0; + return *this; + } + + operator int() const noexcept + { + return m_value; + } + + int m_value = 0; +}; + +struct DerivedNonThrowingObject : public NonThrowingObject +{ + using NonThrowingObject::NonThrowingObject; + using NonThrowingObject::operator=; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~NonThrowingObject())); +RAD_S_ASSERT(noexcept(NonThrowingObject())); +RAD_S_ASSERT(noexcept(NonThrowingObject(1))); +RAD_S_ASSERT(noexcept(NonThrowingObject(NonThrowingObject()))); +RAD_S_ASSERT(noexcept(NonThrowingObject(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(NonThrowingObject()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct NonThrowingObjectTwo +{ + ~NonThrowingObjectTwo() noexcept + { + m_value = 0; + } + + NonThrowingObjectTwo() noexcept + : m_value(0) + { + } + + NonThrowingObjectTwo(int Value) noexcept + : m_value(Value) + { + } + + NonThrowingObjectTwo(std::initializer_list Init) noexcept + : m_value(*Init.begin()) + { + } + + NonThrowingObjectTwo(const NonThrowingObjectTwo& Other) noexcept + : m_value(Other.m_value) + { + } + + NonThrowingObjectTwo(NonThrowingObjectTwo&& Other) noexcept + : m_value(rad::Move(Other.m_value)) + { + } + + NonThrowingObjectTwo& operator=(const NonThrowingObjectTwo& Other) noexcept + { + m_value = Other.m_value; + return *this; + } + + NonThrowingObjectTwo& operator=(NonThrowingObjectTwo&& Other) noexcept + { + m_value = rad::Move(Other.m_value); + return *this; + } + + int m_value = 0; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~NonThrowingObjectTwo())); +RAD_S_ASSERT(noexcept(NonThrowingObjectTwo())); +RAD_S_ASSERT(noexcept(NonThrowingObjectTwo(1))); +RAD_S_ASSERT(noexcept(NonThrowingObjectTwo(NonThrowingObjectTwo()))); +RAD_S_ASSERT(noexcept(NonThrowingObjectTwo(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(NonThrowingObjectTwo()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct ThrowingObjectTrivDtor +{ + ~ThrowingObjectTrivDtor() = default; + + ThrowingObjectTrivDtor() + : m_value(0) + { + } + + ThrowingObjectTrivDtor(int Value) + : m_value(Value) + { + } + + ThrowingObjectTrivDtor(std::initializer_list Init) + : m_value(*Init.begin()) + { + } + + ThrowingObjectTrivDtor(const ThrowingObjectTrivDtor& Other) + : m_value(Other.m_value) + { + } + + ThrowingObjectTrivDtor(ThrowingObjectTrivDtor&& Other) + : m_value(rad::Move(Other.m_value)) + { + } + + ThrowingObjectTrivDtor& operator=(const ThrowingObjectTrivDtor& Other) + { + m_value = Other.m_value; + return *this; + } + + ThrowingObjectTrivDtor& operator=(ThrowingObjectTrivDtor&& Other) + { + m_value = rad::Move(Other.m_value); + return *this; + } + + int m_value = 0; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~ThrowingObjectTrivDtor())); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtor())); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtor(1))); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtor(ThrowingObjectTrivDtor()))); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtor(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(ThrowingObjectTrivDtor()))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct ThrowingObjectTrivDtorTwo +{ + ~ThrowingObjectTrivDtorTwo() = default; + + ThrowingObjectTrivDtorTwo() + : m_value(0) + { + } + + ThrowingObjectTrivDtorTwo(int Value) + : m_value(Value) + { + } + + ThrowingObjectTrivDtorTwo(std::initializer_list Init) + : m_value(*Init.begin()) + { + } + + ThrowingObjectTrivDtorTwo(const ThrowingObjectTrivDtorTwo& Other) + : m_value(Other.m_value) + { + } + + ThrowingObjectTrivDtorTwo(ThrowingObjectTrivDtorTwo&& Other) + : m_value(rad::Move(Other.m_value)) + { + } + + ThrowingObjectTrivDtorTwo& operator=(const ThrowingObjectTrivDtorTwo& Other) + { + m_value = Other.m_value; + return *this; + } + + ThrowingObjectTrivDtorTwo& operator=(ThrowingObjectTrivDtorTwo&& Other) + { + m_value = rad::Move(Other.m_value); + return *this; + } + + int m_value = 0; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~ThrowingObjectTrivDtorTwo())); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtorTwo())); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtorTwo(1))); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtorTwo(ThrowingObjectTrivDtorTwo()))); +RAD_S_ASSERT(!noexcept(ThrowingObjectTrivDtorTwo(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(ThrowingObjectTrivDtorTwo()))); +RAD_S_ASSERT(!noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct NonThrowingObjectTrivDtor +{ + ~NonThrowingObjectTrivDtor() = default; + + NonThrowingObjectTrivDtor() noexcept + : m_value(0) + { + } + + NonThrowingObjectTrivDtor(int Value) noexcept + : m_value(Value) + { + } + + NonThrowingObjectTrivDtor(std::initializer_list Init) noexcept + : m_value(*Init.begin()) + { + } + + NonThrowingObjectTrivDtor(const NonThrowingObjectTrivDtor& Other) noexcept + : m_value(Other.m_value) + { + } + + NonThrowingObjectTrivDtor(NonThrowingObjectTrivDtor&& Other) noexcept + : m_value(rad::Move(Other.m_value)) + { + } + + NonThrowingObjectTrivDtor& operator=( + const NonThrowingObjectTrivDtor& Other) noexcept + { + m_value = Other.m_value; + return *this; + } + + NonThrowingObjectTrivDtor& operator=( + NonThrowingObjectTrivDtor&& Other) noexcept + { + m_value = rad::Move(Other.m_value); + return *this; + } + + int m_value = 0; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~NonThrowingObjectTrivDtor())); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtor())); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtor(1))); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtor(NonThrowingObjectTrivDtor()))); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtor(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(NonThrowingObjectTrivDtor()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +struct NonThrowingObjectTrivDtorTwo +{ + ~NonThrowingObjectTrivDtorTwo() = default; + + NonThrowingObjectTrivDtorTwo() noexcept + : m_value(0) + { + } + + NonThrowingObjectTrivDtorTwo(int Value) noexcept + : m_value(Value) + { + } + + NonThrowingObjectTrivDtorTwo(std::initializer_list Init) noexcept + : m_value(*Init.begin()) + { + } + + NonThrowingObjectTrivDtorTwo( + const NonThrowingObjectTrivDtorTwo& Other) noexcept + : m_value(Other.m_value) + { + } + + NonThrowingObjectTrivDtorTwo(NonThrowingObjectTrivDtorTwo&& Other) noexcept + : m_value(rad::Move(Other.m_value)) + { + } + + NonThrowingObjectTrivDtorTwo& operator=( + const NonThrowingObjectTrivDtorTwo& Other) noexcept + { + m_value = Other.m_value; + return *this; + } + + NonThrowingObjectTrivDtorTwo& operator=( + NonThrowingObjectTrivDtorTwo&& Other) noexcept + { + m_value = rad::Move(Other.m_value); + return *this; + } + + int m_value = 0; +}; + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::DeclVal().~NonThrowingObjectTrivDtorTwo())); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtorTwo())); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtorTwo(1))); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtorTwo(NonThrowingObjectTrivDtorTwo()))); +RAD_S_ASSERT(noexcept(NonThrowingObjectTrivDtorTwo(rad::Move(rad::DeclVal())))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(NonThrowingObjectTrivDtorTwo()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::Move(rad::DeclVal())))); + +// clang-format on + +} // namespace radtest diff --git a/test/test_Atomic.cpp b/test/test_Atomic.cpp new file mode 100644 index 0000000..5581e79 --- /dev/null +++ b/test/test_Atomic.cpp @@ -0,0 +1,3288 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "radiant/Atomic.h" +#include "radiant/Utility.h" + +#if defined(RAD_GCC_VERSION) || defined(RAD_CLANG_VERSION) +RAD_S_ASSERT((static_cast(rad::MemoryOrder::Relaxed) == __ATOMIC_RELAXED)); +RAD_S_ASSERT((static_cast(rad::MemoryOrder::Consume) == __ATOMIC_CONSUME)); +RAD_S_ASSERT((static_cast(rad::MemoryOrder::Acquire) == __ATOMIC_ACQUIRE)); +RAD_S_ASSERT((static_cast(rad::MemoryOrder::Release) == __ATOMIC_RELEASE)); +RAD_S_ASSERT((static_cast(rad::MemoryOrder::AcqRel) == __ATOMIC_ACQ_REL)); +RAD_S_ASSERT((static_cast(rad::MemoryOrder::SeqCst) == __ATOMIC_SEQ_CST)); +#endif + +namespace atom = rad::detail::atomic; +using Order = rad::MemoryOrder; +template +using Tag = typename atom::OrderToTag::Type; +template +using SI = atom::SelectIntrinsic; + +using Relaxed = atom::RelaxedTag; +using Consume = atom::ConsumeTag; +using Acquire = atom::AcquireTag; +using Release = atom::ReleaseTag; +using AcqRel = atom::AcqRelTag; +using SeqCst = atom::SeqCstTag; + +static constexpr rad::Atomic constexprGlobalAtomic(0x11223344); + +TEST(AtomicTests, AtomicDefaultCtor) +{ + rad::Atomic val; + EXPECT_EQ(val.Load(rad::MemOrderRelaxed), 0); +} + +TEST(AtomicTests, AtomicCtor) +{ + rad::Atomic val(0x11223344); + EXPECT_EQ(val.Load(), 0x11223344); + EXPECT_EQ(constexprGlobalAtomic, val); +} + +TEST(AtomicTests, Atomic8StoreLoadSeqCst) +{ + rad::Atomic val; + val.Store(2, rad::MemOrderSeqCst); + EXPECT_EQ(val.Load(rad::MemOrderSeqCst), 2); + + val.Store(4); + EXPECT_EQ(val.Load(), 4); +} + +TEST(AtomicTests, Atomic16StoreLoadSeqCst) +{ + rad::Atomic val; + val.Store(0x0fb0, rad::MemOrderSeqCst); + EXPECT_EQ(val.Load(rad::MemOrderSeqCst), 0x0fb0); + + val.Store(0x1122); + EXPECT_EQ(val.Load(), 0x1122); +} + +TEST(AtomicTests, Atomic32StoreLoadSeqCst) +{ + rad::Atomic val; + val.Store(0x0fb0a0c0, rad::MemOrderSeqCst); + EXPECT_EQ(val.Load(rad::MemOrderSeqCst), 0x0fb0a0c0); + + val.Store(0x11223344); + EXPECT_EQ(val.Load(), 0x11223344); +} + +TEST(AtomicTests, Atomic64StoreLoadSeqCst) +{ + rad::Atomic val; + val.Store(0x0fb0a0c00a0b0c0d, rad::MemOrderSeqCst); + EXPECT_EQ(val.Load(rad::MemOrderSeqCst), 0x0fb0a0c00a0b0c0d); + + val.Store(0x1122334455667788); + EXPECT_EQ(val.Load(), 0x1122334455667788); +} + +TEST(AtomicTests, AtomicPtrStoreLoadSeqCst) +{ + rad::Atomic val; + char* ptr = rad::Add2Ptr(nullptr, 0x1234); + val.Store(ptr, rad::MemOrderSeqCst); + EXPECT_EQ(val.Load(rad::MemOrderSeqCst), ptr); + + val.Store(ptr); + EXPECT_EQ(val.Load(), ptr); +} + +TEST(AtomicTests, Atomic8Exchange) +{ + rad::Atomic val(2); + EXPECT_EQ(val.Exchange(8, rad::MemOrderSeqCst), 2); + EXPECT_EQ(val, 8); + + EXPECT_EQ(val.Exchange(9, rad::MemOrderRelaxed), 8); + EXPECT_EQ(val, 9); +} + +TEST(AtomicTests, Atomic16Exchange) +{ + rad::Atomic val(0x1122); + EXPECT_EQ(val.Exchange(0x3344, rad::MemOrderSeqCst), 0x1122); + EXPECT_EQ(val, 0x3344); +} + +TEST(AtomicTests, Atomic32Exchange) +{ + rad::Atomic val(0x11223344); + EXPECT_EQ(val.Exchange(0x33445566, rad::MemOrderSeqCst), 0x11223344u); + EXPECT_EQ(val, 0x33445566u); +} + +TEST(AtomicTests, Atomic64Exchange) +{ + rad::Atomic val(0x1122334455667788); + EXPECT_EQ(val.Exchange(0xaabbccddeeff0011, rad::MemOrderSeqCst), + 0x1122334455667788ull); + EXPECT_EQ(val, 0xaabbccddeeff0011ull); +} + +TEST(AtomicTests, AtomicPtrExchange) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x11223344); + char* ptr2 = rad::Add2Ptr(nullptr, 0xaabbccdd); + rad::Atomic val(ptr); + EXPECT_EQ(val.Exchange(ptr2, rad::MemOrderSeqCst), ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8CasWeak) +{ + rad::Atomic val(2); + int8_t expected = 3; + bool ret = val.CompareExchangeWeak(expected, 4, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 2); + EXPECT_EQ(val, 2); + + ret = val.CompareExchangeWeak(expected, + 4, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 2); + EXPECT_EQ(val, 4); +} + +TEST(AtomicTests, Atomic8CasStrong) +{ + rad::Atomic val(2); + int8_t expected = 3; + bool ret = val.CompareExchangeStrong(expected, 4, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 2); + EXPECT_EQ(val, 2); + + ret = val.CompareExchangeStrong(expected, + 4, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 2); + EXPECT_EQ(val, 4); +} + +TEST(AtomicTests, Atomic16CasWeak) +{ + rad::Atomic val(0x1122); + int16_t expected = 3; + bool ret = val.CompareExchangeWeak(expected, 0x3344, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x1122); + EXPECT_EQ(val, 0x1122); + + ret = val.CompareExchangeWeak(expected, + 0x3344, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x1122); + EXPECT_EQ(val, 0x3344); +} + +TEST(AtomicTests, Atomic16CasStrong) +{ + rad::Atomic val(0x1122); + int16_t expected = 3; + bool ret = + val.CompareExchangeStrong(expected, 0x3344, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x1122); + EXPECT_EQ(val, 0x1122); + + ret = val.CompareExchangeStrong(expected, + 0x3344, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x1122); + EXPECT_EQ(val, 0x3344); +} + +TEST(AtomicTests, Atomic32CasWeak) +{ + rad::Atomic val(0x11223344); + int32_t expected = 3; + bool ret = + val.CompareExchangeWeak(expected, 0x33445566, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x11223344); + EXPECT_EQ(val, 0x11223344); + + ret = val.CompareExchangeWeak(expected, + 0x33445566, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x11223344); + EXPECT_EQ(val, 0x33445566); +} + +TEST(AtomicTests, Atomic32CasStrong) +{ + rad::Atomic val(0x11223344); + int32_t expected = 3; + bool ret = + val.CompareExchangeStrong(expected, 0x33445566, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x11223344); + EXPECT_EQ(val, 0x11223344); + + ret = val.CompareExchangeStrong(expected, + 0x33445566, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x11223344); + EXPECT_EQ(val, 0x33445566); +} + +TEST(AtomicTests, Atomic64CasWeak) +{ + rad::Atomic val(0x1122334455667788); + int64_t expected = 3; + bool ret = val.CompareExchangeWeak(expected, + 0x33445566778899aa, + rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x1122334455667788); + EXPECT_EQ(val, 0x1122334455667788); + + ret = val.CompareExchangeWeak(expected, + 0x33445566778899aa, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x1122334455667788); + EXPECT_EQ(val, 0x33445566778899aa); +} + +TEST(AtomicTests, Atomic64CasStrong) +{ + rad::Atomic val(0x1122334455667788); + int64_t expected = 3; + bool ret = val.CompareExchangeStrong(expected, + 0x33445566778899aa, + rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x1122334455667788); + EXPECT_EQ(val, 0x1122334455667788); + + ret = val.CompareExchangeStrong(expected, + 0x33445566778899aa, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x1122334455667788); + EXPECT_EQ(val, 0x33445566778899aa); +} + +TEST(AtomicTests, AtomicPtrCasWeak) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x11223344); + char* ptr2 = rad::Add2Ptr(nullptr, 0xaabbccdd); + rad::Atomic val(ptr); + char* expected = nullptr; + bool ret = val.CompareExchangeWeak(expected, ptr2, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, ptr); + EXPECT_EQ(val, ptr); + + ret = val.CompareExchangeWeak(expected, + ptr2, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, AtomicPtrCasStrong) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x11223344); + char* ptr2 = rad::Add2Ptr(nullptr, 0xaabbccdd); + rad::Atomic val(ptr); + char* expected = nullptr; + bool ret = val.CompareExchangeStrong(expected, ptr2, rad::MemOrderRelease); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, ptr); + EXPECT_EQ(val, ptr); + + ret = val.CompareExchangeStrong(expected, + ptr2, + rad::MemOrderAcquire, + rad::MemOrderRelaxed); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8FetchAdd) +{ + rad::Atomic val(2); + EXPECT_EQ(val.FetchAdd(4, rad::MemOrderSeqCst), 2); + EXPECT_EQ(val, 6); + + EXPECT_EQ(val.FetchAdd(2), 6); + EXPECT_EQ(val, 8); +} + +TEST(AtomicTests, Atomic16FetchAdd) +{ + rad::Atomic val(0x101); + EXPECT_EQ(val.FetchAdd(0x101, rad::MemOrderSeqCst), 0x101); + EXPECT_EQ(val, 0x202); +} + +TEST(AtomicTests, Atomic32FetchAdd) +{ + rad::Atomic val(0x1010101); + EXPECT_EQ(val.FetchAdd(0x1010101), 0x1010101); + EXPECT_EQ(val, 0x2020202); +} + +TEST(AtomicTests, Atomic64FetchAdd) +{ + rad::Atomic val(0x101010101010101); + EXPECT_EQ(val.FetchAdd(0x101010101010101), 0x101010101010101); + EXPECT_EQ(val, 0x202020202020202); +} + +TEST(AtomicTests, AtomicPtrFetchAdd) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x10101010); + char* ptr2 = rad::Add2Ptr(nullptr, 0x20202020); + rad::Atomic val(ptr); + EXPECT_EQ(val.FetchAdd(0x10101010), ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8FetchSub) +{ + rad::Atomic val(6); + EXPECT_EQ(val.FetchSub(4, rad::MemOrderSeqCst), 6); + EXPECT_EQ(val, 2); +} + +TEST(AtomicTests, Atomic16FetchSub) +{ + rad::Atomic val(0x202); + EXPECT_EQ(val.FetchSub(0x101), 0x202); + EXPECT_EQ(val, 0x101); +} + +TEST(AtomicTests, Atomic32FetchSub) +{ + rad::Atomic val(0x2020202); + EXPECT_EQ(val.FetchSub(0x1010101), 0x2020202); + EXPECT_EQ(val, 0x1010101); +} + +TEST(AtomicTests, Atomic64FetchSub) +{ + rad::Atomic val(0x202020202020202); + EXPECT_EQ(val.FetchSub(0x101010101010101), 0x202020202020202); + EXPECT_EQ(val, 0x101010101010101); +} + +TEST(AtomicTests, AtomicPtrFetchSub) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x20202020); + char* ptr2 = rad::Add2Ptr(nullptr, 0x10101010); + rad::Atomic val(ptr); + EXPECT_EQ(val.FetchSub(0x10101010), ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8FetchAnd) +{ + rad::Atomic val(6); + EXPECT_EQ(val.FetchAnd(3, rad::MemOrderSeqCst), 6); + EXPECT_EQ(val, 2); +} + +TEST(AtomicTests, Atomic16FetchAnd) +{ + rad::Atomic val(0x606); + EXPECT_EQ(val.FetchAnd(0x303), 0x606); + EXPECT_EQ(val, 0x202); +} + +TEST(AtomicTests, Atomic32FetchAnd) +{ + rad::Atomic val(0x6060606); + EXPECT_EQ(val.FetchAnd(0x3030303), 0x6060606); + EXPECT_EQ(val, 0x2020202); +} + +TEST(AtomicTests, Atomic64FetchAnd) +{ + rad::Atomic val(0x606060606060606); + EXPECT_EQ(val.FetchAnd(0x303030303030303), 0x606060606060606); + EXPECT_EQ(val, 0x202020202020202); +} + +TEST(AtomicTests, Atomic8FetchOr) +{ + rad::Atomic val(6); + EXPECT_EQ(val.FetchOr(3, rad::MemOrderSeqCst), 6); + EXPECT_EQ(val, 7); +} + +TEST(AtomicTests, Atomic16FetchOr) +{ + rad::Atomic val(0x606); + EXPECT_EQ(val.FetchOr(0x303), 0x606); + EXPECT_EQ(val, 0x707); +} + +TEST(AtomicTests, Atomic32FetchOr) +{ + rad::Atomic val(0x6060606); + EXPECT_EQ(val.FetchOr(0x3030303), 0x6060606); + EXPECT_EQ(val, 0x7070707); +} + +TEST(AtomicTests, Atomic64FetchOr) +{ + rad::Atomic val(0x606060606060606); + EXPECT_EQ(val.FetchOr(0x303030303030303), 0x606060606060606); + EXPECT_EQ(val, 0x707070707070707); +} + +TEST(AtomicTests, Atomic8FetchXor) +{ + rad::Atomic val(6); + EXPECT_EQ(val.FetchXor(3, rad::MemOrderSeqCst), 6); + EXPECT_EQ(val, 5); +} + +TEST(AtomicTests, Atomic16FetchXor) +{ + rad::Atomic val(0x606); + EXPECT_EQ(val.FetchXor(0x303), 0x606); + EXPECT_EQ(val, 0x505); +} + +TEST(AtomicTests, Atomic32FetchXor) +{ + rad::Atomic val(0x6060606); + EXPECT_EQ(val.FetchXor(0x3030303), 0x6060606); + EXPECT_EQ(val, 0x5050505); +} + +TEST(AtomicTests, Atomic64FetchXor) +{ + rad::Atomic val(0x606060606060606); + EXPECT_EQ(val.FetchXor(0x303030303030303), 0x606060606060606); + EXPECT_EQ(val, 0x505050505050505); +} + +TEST(AtomicTests, Atomic8Assign) +{ + rad::Atomic val; + EXPECT_EQ(val = 6, 6); + EXPECT_EQ(val, 6); +} + +TEST(AtomicTests, Atomic16Assign) +{ + rad::Atomic val; + EXPECT_EQ(val = 0x1122, 0x1122); + EXPECT_EQ(val, 0x1122); +} + +TEST(AtomicTests, Atomic32Assign) +{ + rad::Atomic val; + EXPECT_EQ(val = 0x11223344, 0x11223344); + EXPECT_EQ(val, 0x11223344); +} + +TEST(AtomicTests, Atomic64Assign) +{ + rad::Atomic val; + EXPECT_EQ(val = 0x1122334455667788, 0x1122334455667788); + EXPECT_EQ(val, 0x1122334455667788); +} + +TEST(AtomicTests, AtomicPtrAssign) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x11223344); + rad::Atomic val; + EXPECT_EQ(val = ptr, ptr); + EXPECT_EQ(val, ptr); +} + +TEST(AtomicTests, Atomic8PreInc) +{ + rad::Atomic val(1); + EXPECT_EQ(++val, 2); + EXPECT_EQ(val, 2); +} + +TEST(AtomicTests, Atomic16PreInc) +{ + rad::Atomic val(0x1fff); + EXPECT_EQ(++val, 0x2000); + EXPECT_EQ(val, 0x2000); +} + +TEST(AtomicTests, Atomic32PreInc) +{ + rad::Atomic val(0x1fffffff); + EXPECT_EQ(++val, 0x20000000); + EXPECT_EQ(val, 0x20000000); +} + +TEST(AtomicTests, Atomic64PreInc) +{ + rad::Atomic val(0x1fffffffffffffff); + EXPECT_EQ(++val, 0x2000000000000000); + EXPECT_EQ(val, 0x2000000000000000); +} + +TEST(AtomicTests, AtomicPtrPreInc) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1fffffff); + char* ptr2 = rad::Add2Ptr(nullptr, 0x20000000); + rad::Atomic val(ptr); + EXPECT_EQ(++val, ptr2); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8PostInc) +{ + rad::Atomic val(1); + EXPECT_EQ(val++, 1); + EXPECT_EQ(val, 2); +} + +TEST(AtomicTests, Atomic16PostInc) +{ + rad::Atomic val(0x1fff); + EXPECT_EQ(val++, 0x1fff); + EXPECT_EQ(val, 0x2000); +} + +TEST(AtomicTests, Atomic32PostInc) +{ + rad::Atomic val(0x1fffffff); + EXPECT_EQ(val++, 0x1fffffff); + EXPECT_EQ(val, 0x20000000); +} + +TEST(AtomicTests, Atomic64PostInc) +{ + rad::Atomic val(0x1fffffffffffffff); + EXPECT_EQ(val++, 0x1fffffffffffffff); + EXPECT_EQ(val, 0x2000000000000000); +} + +TEST(AtomicTests, AtomicPtrPostInc) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1fffffff); + char* ptr2 = rad::Add2Ptr(nullptr, 0x20000000); + rad::Atomic val(ptr); + EXPECT_EQ(val++, ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8PreDec) +{ + rad::Atomic val(2); + EXPECT_EQ(--val, 1); + EXPECT_EQ(val, 1); +} + +TEST(AtomicTests, Atomic16PreDec) +{ + rad::Atomic val(0x2000); + EXPECT_EQ(--val, 0x1fff); + EXPECT_EQ(val, 0x1fff); +} + +TEST(AtomicTests, Atomic32PreDec) +{ + rad::Atomic val(0x20000000); + EXPECT_EQ(--val, 0x1fffffff); + EXPECT_EQ(val, 0x1fffffff); +} + +TEST(AtomicTests, Atomic64PreDec) +{ + rad::Atomic val(0x2000000000000000); + EXPECT_EQ(--val, 0x1fffffffffffffff); + EXPECT_EQ(val, 0x1fffffffffffffff); +} + +TEST(AtomicTests, AtomicPtrPreDec) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x20000000); + char* ptr2 = rad::Add2Ptr(nullptr, 0x1fffffff); + rad::Atomic val(ptr); + EXPECT_EQ(--val, ptr2); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8PostDec) +{ + rad::Atomic val(2); + EXPECT_EQ(val--, 2); + EXPECT_EQ(val, 1); +} + +TEST(AtomicTests, Atomic16PostDec) +{ + rad::Atomic val(0x2000); + EXPECT_EQ(val--, 0x2000); + EXPECT_EQ(val, 0x1fff); +} + +TEST(AtomicTests, Atomic32PostDec) +{ + rad::Atomic val(0x20000000); + EXPECT_EQ(val--, 0x20000000); + EXPECT_EQ(val, 0x1fffffff); +} + +TEST(AtomicTests, Atomic64PostDec) +{ + rad::Atomic val(0x2000000000000000); + EXPECT_EQ(val--, 0x2000000000000000); + EXPECT_EQ(val, 0x1fffffffffffffff); +} + +TEST(AtomicTests, AtomicPtrPostDec) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x20000000); + char* ptr2 = rad::Add2Ptr(nullptr, 0x1fffffff); + rad::Atomic val(ptr); + EXPECT_EQ(val--, ptr); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8AddAssign) +{ + rad::Atomic val(2); + EXPECT_EQ(val += 4, 6); + EXPECT_EQ(val, 6); +} + +TEST(AtomicTests, Atomic16AddAssign) +{ + rad::Atomic val(0x1fff); + EXPECT_EQ(val += 4, 0x2003); + EXPECT_EQ(val, 0x2003); +} + +TEST(AtomicTests, Atomic32AddAssign) +{ + rad::Atomic val(0x1fffffff); + EXPECT_EQ(val += 4, 0x20000003); + EXPECT_EQ(val, 0x20000003); +} + +TEST(AtomicTests, Atomic64AddAssign) +{ + rad::Atomic val(0x1fffffffffffffff); + EXPECT_EQ(val += 4, 0x2000000000000003); + EXPECT_EQ(val, 0x2000000000000003); +} + +TEST(AtomicTests, AtomicPtrAddAssign) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1fffffff); + char* ptr2 = rad::Add2Ptr(nullptr, 0x20000003); + rad::Atomic val(ptr); + EXPECT_EQ(val += 4, ptr2); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8SubAssign) +{ + rad::Atomic val(6); + EXPECT_EQ(val -= 4, 2); + EXPECT_EQ(val, 2); +} + +TEST(AtomicTests, Atomic16SubAssign) +{ + rad::Atomic val(0x2003); + EXPECT_EQ(val -= 4, 0x1fff); + EXPECT_EQ(val, 0x1fff); +} + +TEST(AtomicTests, Atomic32SubAssign) +{ + rad::Atomic val(0x20000003); + EXPECT_EQ(val -= 4, 0x1fffffff); + EXPECT_EQ(val, 0x1fffffff); +} + +TEST(AtomicTests, Atomic64SubAssign) +{ + rad::Atomic val(0x2000000000000003); + EXPECT_EQ(val -= 4, 0x1fffffffffffffff); + EXPECT_EQ(val, 0x1fffffffffffffff); +} + +TEST(AtomicTests, AtomicPtrSubAssign) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x20000003); + char* ptr2 = rad::Add2Ptr(nullptr, 0x1fffffff); + rad::Atomic val(ptr); + EXPECT_EQ(val -= 4, ptr2); + EXPECT_EQ(val, ptr2); +} + +TEST(AtomicTests, Atomic8AndAssign) +{ + rad::Atomic val(6); + EXPECT_EQ(val &= 3, 2); + EXPECT_EQ(val, 2); +} + +TEST(AtomicTests, Atomic16AndAssign) +{ + rad::Atomic val(0x606); + EXPECT_EQ(val &= 0x303, 0x202); + EXPECT_EQ(val, 0x202); +} + +TEST(AtomicTests, Atomic32AndAssign) +{ + rad::Atomic val(0x6060606); + EXPECT_EQ(val &= 0x3030303, 0x2020202); + EXPECT_EQ(val, 0x2020202); +} + +TEST(AtomicTests, Atomic64AndAssign) +{ + rad::Atomic val(0x606060606060606); + EXPECT_EQ(val &= 0x303030303030303, 0x202020202020202); + EXPECT_EQ(val, 0x202020202020202); +} + +TEST(AtomicTests, Atomic8OrAssign) +{ + rad::Atomic val(6); + EXPECT_EQ(val |= 3, 7); + EXPECT_EQ(val, 7); +} + +TEST(AtomicTests, Atomic16OrAssign) +{ + rad::Atomic val(0x606); + EXPECT_EQ(val |= 0x303, 0x707); + EXPECT_EQ(val, 0x707); +} + +TEST(AtomicTests, Atomic32OrAssign) +{ + rad::Atomic val(0x6060606); + EXPECT_EQ(val |= 0x3030303, 0x7070707); + EXPECT_EQ(val, 0x7070707); +} + +TEST(AtomicTests, Atomic64OrAssign) +{ + rad::Atomic val(0x606060606060606); + EXPECT_EQ(val |= 0x303030303030303, 0x707070707070707); + EXPECT_EQ(val, 0x707070707070707); +} + +TEST(AtomicTests, Atomic8XorAssign) +{ + rad::Atomic val(6); + EXPECT_EQ(val ^= 3, 5); + EXPECT_EQ(val, 5); +} + +TEST(AtomicTests, Atomic16XorAssign) +{ + rad::Atomic val(0x606); + EXPECT_EQ(val ^= 0x303, 0x505); + EXPECT_EQ(val, 0x505); +} + +TEST(AtomicTests, Atomic32XorAssign) +{ + rad::Atomic val(0x6060606); + EXPECT_EQ(val ^= 0x3030303, 0x5050505); + EXPECT_EQ(val, 0x5050505); +} + +TEST(AtomicTests, Atomic64XorAssign) +{ + rad::Atomic val(0x606060606060606); + EXPECT_EQ(val ^= 0x303030303030303, 0x505050505050505); + EXPECT_EQ(val, 0x505050505050505); +} + +TEST(AtomicTests, IntrinSelect8LoadSeqCst) +{ + int8_t value = 0x0f; + EXPECT_EQ(value, SI::Load(value, SeqCst())); +} + +TEST(AtomicTests, IntrinSelect8LoadRelaxed) +{ + int8_t value = 0x0f; + EXPECT_EQ(value, SI::Load(value, Relaxed())); +} + +TEST(AtomicTests, IntrinSelect16LoadSeqCst) +{ + int16_t value = 0x0fb0; + EXPECT_EQ(value, SI::Load(value, SeqCst())); +} + +TEST(AtomicTests, IntrinSelect16LoadRelaxed) +{ + int16_t value = 0x0fb0; + EXPECT_EQ(value, SI::Load(value, Relaxed())); +} + +TEST(AtomicTests, IntrinSelect32LoadSeqCst) +{ + uint32_t value = 0xf0b0f0c0; + EXPECT_EQ(value, SI::Load(value, SeqCst())); +} + +TEST(AtomicTests, IntrinSelect32LoadRelaxed) +{ + uint32_t value = 0xf0b0f0c0; + EXPECT_EQ(value, SI::Load(value, Relaxed())); +} + +TEST(AtomicTests, IntrinSelect64LoadSeqCst) +{ + uint64_t value = 0xf0b0f0f0c0f0f0f0; + EXPECT_EQ(value, SI::Load(value, SeqCst())); +} + +TEST(AtomicTests, IntrinSelect64LoadRelaxed) +{ + uint64_t value = 0xf0b0f0f0c0f0f0f0; + EXPECT_EQ(value, SI::Load(value, Relaxed())); +} + +TEST(AtomicTests, IntrinSelect8StoreSeqCst) +{ + int8_t value = 0x0f; + SI::Store(value, 8, SeqCst()); + EXPECT_EQ(value, 8); +} + +TEST(AtomicTests, IntrinSelect8StoreRelaxed) +{ + int8_t value = 0x0f; + SI::Store(value, 8, Relaxed()); + EXPECT_EQ(value, 8); +} + +TEST(AtomicTests, IntrinSelect8StoreRelease) +{ + int8_t value = 0x0f; + SI::Store(value, 8, Release()); + EXPECT_EQ(value, 8); +} + +TEST(AtomicTests, IntrinSelect16StoreSeqCst) +{ + int16_t value = 0x0ff0; + SI::Store(value, 0x0cb0, SeqCst()); + EXPECT_EQ(value, 0x0cb0); +} + +TEST(AtomicTests, IntrinSelect16StoreRelaxed) +{ + int16_t value = 0x0ff0; + SI::Store(value, 0x0cb0, Relaxed()); + EXPECT_EQ(value, 0x0cb0); +} + +TEST(AtomicTests, IntrinSelect16StoreRelease) +{ + int16_t value = 0x0ff0; + SI::Store(value, 0x0cb0, Release()); + EXPECT_EQ(value, 0x0cb0); +} + +TEST(AtomicTests, IntrinSelect32StoreSeqCst) +{ + int32_t value = 0x0ff0f0b0; + SI::Store(value, 0x0cb0e0b0, SeqCst()); + EXPECT_EQ(value, 0x0cb0e0b0); +} + +TEST(AtomicTests, IntrinSelect32StoreRelaxed) +{ + int32_t value = 0x0ff0f0b0; + SI::Store(value, 0x0cb0e0b0, Relaxed()); + EXPECT_EQ(value, 0x0cb0e0b0); +} + +TEST(AtomicTests, IntrinSelect32StoreRelease) +{ + int32_t value = 0x0ff0f0b0; + SI::Store(value, 0x0cb0e0b0, Release()); + EXPECT_EQ(value, 0x0cb0e0b0); +} + +TEST(AtomicTests, IntrinSelect64StoreSeqCst) +{ + int64_t value = 0x0ff0f0b0c0b0c0b0; + SI::Store(value, 0x0cb0f0f00a0b0c0d, SeqCst()); + EXPECT_EQ(value, 0x0cb0f0f00a0b0c0d); +} + +TEST(AtomicTests, IntrinSelect64StoreRelaxed) +{ + int64_t value = 0x0ff0f0b0c0b0c0b0; + SI::Store(value, 0x0cb0f0f00a0b0c0d, Relaxed()); + EXPECT_EQ(value, 0x0cb0f0f00a0b0c0d); +} + +TEST(AtomicTests, IntrinSelect64StoreRelease) +{ + int64_t value = 0x0ff0f0b0c0b0c0b0; + SI::Store(value, 0x0cb0f0f00a0b0c0d, Release()); + EXPECT_EQ(value, 0x0cb0f0f00a0b0c0d); +} + +TEST(AtomicTests, IntrinSelect8ExchangeRelaxed) +{ + int8_t value = 8; + int8_t old = SI::Exchange(value, 0x0f, Relaxed()); + EXPECT_EQ(old, 8); + EXPECT_EQ(value, 0x0f); +} + +TEST(AtomicTests, IntrinSelect8ExchangeConsume) +{ + int8_t value = 8; + int8_t old = SI::Exchange(value, 0x0f, Consume()); + EXPECT_EQ(old, 8); + EXPECT_EQ(value, 0x0f); +} + +TEST(AtomicTests, IntrinSelect8ExchangeAcquire) +{ + int8_t value = 8; + int8_t old = SI::Exchange(value, 0x0f, Acquire()); + EXPECT_EQ(old, 8); + EXPECT_EQ(value, 0x0f); +} + +TEST(AtomicTests, IntrinSelect8ExchangeRelease) +{ + int8_t value = 8; + int8_t old = SI::Exchange(value, 0x0f, Release()); + EXPECT_EQ(old, 8); + EXPECT_EQ(value, 0x0f); +} + +TEST(AtomicTests, IntrinSelect8ExchangeAcqRel) +{ + int8_t value = 8; + int8_t old = SI::Exchange(value, 0x0f, AcqRel()); + EXPECT_EQ(old, 8); + EXPECT_EQ(value, 0x0f); +} + +TEST(AtomicTests, IntrinSelect8ExchangeSeqCst) +{ + int8_t value = 8; + int8_t old = SI::Exchange(value, 0x0f, SeqCst()); + EXPECT_EQ(old, 8); + EXPECT_EQ(value, 0x0f); +} + +TEST(AtomicTests, IntrinSelect16ExchangeRelaxed) +{ + int16_t value = 16; + SI::Exchange(value, 0x0ff0, Relaxed()); + EXPECT_EQ(value, 0x0ff0); +} + +TEST(AtomicTests, IntrinSelect16ExchangeConsume) +{ + int16_t value = 16; + SI::Exchange(value, 0x0ff0, Consume()); + EXPECT_EQ(value, 0x0ff0); +} + +TEST(AtomicTests, IntrinSelect16ExchangeAcquire) +{ + int16_t value = 16; + SI::Exchange(value, 0x0ff0, Acquire()); + EXPECT_EQ(value, 0x0ff0); +} + +TEST(AtomicTests, IntrinSelect16ExchangeRelease) +{ + int16_t value = 16; + SI::Exchange(value, 0x0ff0, Release()); + EXPECT_EQ(value, 0x0ff0); +} + +TEST(AtomicTests, IntrinSelect16ExchangeAcqRel) +{ + int16_t value = 16; + SI::Exchange(value, 0x0ff0, AcqRel()); + EXPECT_EQ(value, 0x0ff0); +} + +TEST(AtomicTests, IntrinSelect16ExchangeSeqCst) +{ + int16_t value = 16; + SI::Exchange(value, 0x0ff0, SeqCst()); + EXPECT_EQ(value, 0x0ff0); +} + +TEST(AtomicTests, IntrinSelect32ExchangeRelaxed) +{ + int32_t value = 32; + SI::Exchange(value, 0x0ff0e0d0, Relaxed()); + EXPECT_EQ(value, 0x0ff0e0d0); +} + +TEST(AtomicTests, IntrinSelect32ExchangeConsume) +{ + int32_t value = 32; + SI::Exchange(value, 0x0ff0e0d0, Consume()); + EXPECT_EQ(value, 0x0ff0e0d0); +} + +TEST(AtomicTests, IntrinSelect32ExchangeAcquire) +{ + int32_t value = 32; + SI::Exchange(value, 0x0ff0e0d0, Acquire()); + EXPECT_EQ(value, 0x0ff0e0d0); +} + +TEST(AtomicTests, IntrinSelect32ExchangeRelease) +{ + int32_t value = 32; + SI::Exchange(value, 0x0ff0e0d0, Release()); + EXPECT_EQ(value, 0x0ff0e0d0); +} + +TEST(AtomicTests, IntrinSelect32ExchangeAcqRel) +{ + int32_t value = 32; + SI::Exchange(value, 0x0ff0e0d0, AcqRel()); + EXPECT_EQ(value, 0x0ff0e0d0); +} + +TEST(AtomicTests, IntrinSelect32ExchangeSeqCst) +{ + int32_t value = 32; + SI::Exchange(value, 0x0ff0e0d0, SeqCst()); + EXPECT_EQ(value, 0x0ff0e0d0); +} + +TEST(AtomicTests, IntrinSelect64ExchangeRelaxed) +{ + int64_t value = 64; + SI::Exchange(value, 0x0ff0e0d011223344, Relaxed()); + EXPECT_EQ(value, 0x0ff0e0d011223344); +} + +TEST(AtomicTests, IntrinSelect64ExchangeConsume) +{ + int64_t value = 64; + SI::Exchange(value, 0x0ff0e0d011223344, Consume()); + EXPECT_EQ(value, 0x0ff0e0d011223344); +} + +TEST(AtomicTests, IntrinSelect64ExchangeAcquire) +{ + int64_t value = 64; + SI::Exchange(value, 0x0ff0e0d011223344, Acquire()); + EXPECT_EQ(value, 0x0ff0e0d011223344); +} + +TEST(AtomicTests, IntrinSelect64ExchangeRelease) +{ + int64_t value = 64; + SI::Exchange(value, 0x0ff0e0d011223344, Release()); + EXPECT_EQ(value, 0x0ff0e0d011223344); +} + +TEST(AtomicTests, IntrinSelect64ExchangeAcqRel) +{ + int64_t value = 64; + SI::Exchange(value, 0x0ff0e0d011223344, AcqRel()); + EXPECT_EQ(value, 0x0ff0e0d011223344); +} + +TEST(AtomicTests, IntrinSelect64ExchangeSeqCst) +{ + int64_t value = 64; + SI::Exchange(value, 0x0ff0e0d011223344, SeqCst()); + EXPECT_EQ(value, 0x0ff0e0d011223344); +} + +TEST(AtomicTests, IntrinSelect8CasWeakRelaxed) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeWeak(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasWeakConsume) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeWeak(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasWeakAcquire) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeWeak(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasWeakRelease) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeWeak(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasWeakAcqRel) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeWeak(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasWeakSeqCst) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeWeak(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect16CasWeakRelaxed) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasWeakConsume) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasWeakAcquire) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasWeakRelease) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasWeakAcqRel) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasWeakSeqCst) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect32CasWeakRelaxed) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f4, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasWeakConsume) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f4, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasWeakAcquire) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f4, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasWeakRelease) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f4, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasWeakAcqRel) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f4, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasWeakSeqCst) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f4, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect64CasWeakRelaxed) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f411223344, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasWeakConsume) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f411223344, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasWeakAcquire) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f411223344, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasWeakRelease) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f411223344, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasWeakAcqRel) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f411223344, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasWeakSeqCst) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeWeak(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeWeak(value, + 0x1ff2f3f411223344, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect8CasStrongRelaxed) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeStrong(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasStrongConsume) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeStrong(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasStrongAcquire) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeStrong(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasStrongRelease) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeStrong(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasStrongAcqRel) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeStrong(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect8CasStrongSeqCst) +{ + uint8_t value = 8; + uint8_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 8); + + ret = SI::CompareExchangeStrong(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 8); + EXPECT_EQ(value, 9); +} + +TEST(AtomicTests, IntrinSelect16CasStrongRelaxed) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasStrongConsume) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasStrongAcquire) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasStrongRelease) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasStrongAcqRel) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect16CasStrongSeqCst) +{ + int16_t value = 0x0ff0; + int16_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x0ff0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0); + EXPECT_EQ(value, 0x1ff2); +} + +TEST(AtomicTests, IntrinSelect32CasStrongRelaxed) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f4, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasStrongConsume) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f4, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasStrongAcquire) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f4, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasStrongRelease) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f4, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasStrongAcqRel) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f4, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect32CasStrongSeqCst) +{ + int32_t value = 0x0ff0e0d0; + int32_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x0ff0e0d0); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f4, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d0); + EXPECT_EQ(value, 0x1ff2f3f4); +} + +TEST(AtomicTests, IntrinSelect64CasStrongRelaxed) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f411223344, + expected, + Relaxed(), + Relaxed()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasStrongConsume) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f411223344, + expected, + Consume(), + Consume()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasStrongAcquire) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f411223344, + expected, + Acquire(), + Acquire()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasStrongRelease) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + Release(), + Release()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f411223344, + expected, + Release(), + Release()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasStrongAcqRel) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f411223344, + expected, + AcqRel(), + AcqRel()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect64CasStrongSeqCst) +{ + int64_t value = 0x0ff0e0d011223344; + int64_t expected = 7; + bool ret = SI::CompareExchangeStrong(value, + 9, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, false); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x0ff0e0d011223344); + + ret = SI::CompareExchangeStrong(value, + 0x1ff2f3f411223344, + expected, + SeqCst(), + SeqCst()); + EXPECT_EQ(ret, true); + EXPECT_EQ(expected, 0x0ff0e0d011223344); + EXPECT_EQ(value, 0x1ff2f3f411223344); +} + +TEST(AtomicTests, IntrinSelect8FetchAddRelaxed) +{ + uint8_t value = 8; + uint8_t result = SI::FetchAdd(value, 4, Relaxed()); + EXPECT_EQ(value, 12); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchAddConsume) +{ + uint8_t value = 8; + uint8_t result = SI::FetchAdd(value, 4, Consume()); + EXPECT_EQ(value, 12); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchAddAcquire) +{ + uint8_t value = 8; + uint8_t result = SI::FetchAdd(value, 4, Acquire()); + EXPECT_EQ(value, 12); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchAddRelease) +{ + uint8_t value = 8; + uint8_t result = SI::FetchAdd(value, 4, Release()); + EXPECT_EQ(value, 12); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchAddAcqRel) +{ + uint8_t value = 8; + uint8_t result = SI::FetchAdd(value, 4, AcqRel()); + EXPECT_EQ(value, 12); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchAddSeqCst) +{ + uint8_t value = 8; + uint8_t result = SI::FetchAdd(value, 4, SeqCst()); + EXPECT_EQ(value, 12); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect16FetchAddRelaxed) +{ + uint16_t value = 0x1ff; + uint16_t result = SI::FetchAdd(value, 4, Relaxed()); + EXPECT_EQ(value, 0x203); + EXPECT_EQ(result, 0x1ff); +} + +TEST(AtomicTests, IntrinSelect16FetchAddConsume) +{ + uint16_t value = 0x1ff; + uint16_t result = SI::FetchAdd(value, 4, Consume()); + EXPECT_EQ(value, 0x203); + EXPECT_EQ(result, 0x1ff); +} + +TEST(AtomicTests, IntrinSelect16FetchAddAcquire) +{ + uint16_t value = 0x1ff; + uint16_t result = SI::FetchAdd(value, 4, Acquire()); + EXPECT_EQ(value, 0x203); + EXPECT_EQ(result, 0x1ff); +} + +TEST(AtomicTests, IntrinSelect16FetchAddRelease) +{ + uint16_t value = 0x1ff; + uint16_t result = SI::FetchAdd(value, 4, Release()); + EXPECT_EQ(value, 0x203); + EXPECT_EQ(result, 0x1ff); +} + +TEST(AtomicTests, IntrinSelect16FetchAddAcqRel) +{ + uint16_t value = 0x1ff; + uint16_t result = SI::FetchAdd(value, 4, AcqRel()); + EXPECT_EQ(value, 0x203); + EXPECT_EQ(result, 0x1ff); +} + +TEST(AtomicTests, IntrinSelect16FetchAddSeqCst) +{ + uint16_t value = 0x1ff; + uint16_t result = SI::FetchAdd(value, 4, SeqCst()); + EXPECT_EQ(value, 0x203); + EXPECT_EQ(result, 0x1ff); +} + +TEST(AtomicTests, IntrinSelect32FetchAddRelaxed) +{ + uint32_t value = 0x1ffffff; + uint32_t result = SI::FetchAdd(value, 4, Relaxed()); + EXPECT_EQ(value, 0x2000003u); + EXPECT_EQ(result, 0x1ffffffu); +} + +TEST(AtomicTests, IntrinSelect32FetchAddConsume) +{ + uint32_t value = 0x1ffffff; + uint32_t result = SI::FetchAdd(value, 4, Consume()); + EXPECT_EQ(value, 0x2000003u); + EXPECT_EQ(result, 0x1ffffffu); +} + +TEST(AtomicTests, IntrinSelect32FetchAddAcquire) +{ + uint32_t value = 0x1ffffff; + uint32_t result = SI::FetchAdd(value, 4, Acquire()); + EXPECT_EQ(value, 0x2000003u); + EXPECT_EQ(result, 0x1ffffffu); +} + +TEST(AtomicTests, IntrinSelect32FetchAddRelease) +{ + uint32_t value = 0x1ffffff; + uint32_t result = SI::FetchAdd(value, 4, Release()); + EXPECT_EQ(value, 0x2000003u); + EXPECT_EQ(result, 0x1ffffffu); +} + +TEST(AtomicTests, IntrinSelect32FetchAddAcqRel) +{ + uint32_t value = 0x1ffffff; + uint32_t result = SI::FetchAdd(value, 4, AcqRel()); + EXPECT_EQ(value, 0x2000003u); + EXPECT_EQ(result, 0x1ffffffu); +} + +TEST(AtomicTests, IntrinSelect32FetchAddSeqCst) +{ + uint32_t value = 0x1ffffff; + uint32_t result = SI::FetchAdd(value, 4, SeqCst()); + EXPECT_EQ(value, 0x2000003u); + EXPECT_EQ(result, 0x1ffffffu); +} + +TEST(AtomicTests, IntrinSelect64FetchAddRelaxed) +{ + uint64_t value = 0x1ffffffffffffff; + uint64_t result = SI::FetchAdd(value, 4, Relaxed()); + EXPECT_EQ(value, 0x200000000000003ull); + EXPECT_EQ(result, 0x1ffffffffffffffull); +} + +TEST(AtomicTests, IntrinSelect64FetchAddConsume) +{ + uint64_t value = 0x1ffffffffffffff; + uint64_t result = SI::FetchAdd(value, 4, Consume()); + EXPECT_EQ(value, 0x200000000000003ull); + EXPECT_EQ(result, 0x1ffffffffffffffull); +} + +TEST(AtomicTests, IntrinSelect64FetchAddAcquire) +{ + uint64_t value = 0x1ffffffffffffff; + uint64_t result = SI::FetchAdd(value, 4, Acquire()); + EXPECT_EQ(value, 0x200000000000003ull); + EXPECT_EQ(result, 0x1ffffffffffffffull); +} + +TEST(AtomicTests, IntrinSelect64FetchAddRelease) +{ + uint64_t value = 0x1ffffffffffffff; + uint64_t result = SI::FetchAdd(value, 4, Release()); + EXPECT_EQ(value, 0x200000000000003ull); + EXPECT_EQ(result, 0x1ffffffffffffffull); +} + +TEST(AtomicTests, IntrinSelect64FetchAddAcqRel) +{ + uint64_t value = 0x1ffffffffffffff; + uint64_t result = SI::FetchAdd(value, 4, AcqRel()); + EXPECT_EQ(value, 0x200000000000003ull); + EXPECT_EQ(result, 0x1ffffffffffffffull); +} + +TEST(AtomicTests, IntrinSelect64FetchAddSeqCst) +{ + uint64_t value = 0x1ffffffffffffff; + uint64_t result = SI::FetchAdd(value, 4, SeqCst()); + EXPECT_EQ(value, 0x200000000000003ull); + EXPECT_EQ(result, 0x1ffffffffffffffull); +} + +TEST(AtomicTests, IntrinSelectPtrFetchAddRelaxed) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1ffffff); + char* orgptr = ptr; + char* ptr2 = rad::Add2Ptr(nullptr, 0x2000003); + char* result = SI::FetchAdd(ptr, 4, Relaxed()); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(result, orgptr); +} + +TEST(AtomicTests, IntrinSelectPtrFetchAddConsume) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1ffffff); + char* orgptr = ptr; + char* ptr2 = rad::Add2Ptr(nullptr, 0x2000003); + char* result = SI::FetchAdd(ptr, 4, Consume()); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(result, orgptr); +} + +TEST(AtomicTests, IntrinSelectPtrFetchAddAcquire) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1ffffff); + char* orgptr = ptr; + char* ptr2 = rad::Add2Ptr(nullptr, 0x2000003); + char* result = SI::FetchAdd(ptr, 4, Acquire()); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(result, orgptr); +} + +TEST(AtomicTests, IntrinSelectPtrFetchAddRelease) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1ffffff); + char* orgptr = ptr; + char* ptr2 = rad::Add2Ptr(nullptr, 0x2000003); + char* result = SI::FetchAdd(ptr, 4, Release()); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(result, orgptr); +} + +TEST(AtomicTests, IntrinSelectPtrFetchAddAcqRel) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1ffffff); + char* orgptr = ptr; + char* ptr2 = rad::Add2Ptr(nullptr, 0x2000003); + char* result = SI::FetchAdd(ptr, 4, AcqRel()); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(result, orgptr); +} + +TEST(AtomicTests, IntrinSelectPtrFetchAddSeqCst) +{ + char* ptr = rad::Add2Ptr(nullptr, 0x1ffffff); + char* orgptr = ptr; + char* ptr2 = rad::Add2Ptr(nullptr, 0x2000003); + char* result = SI::FetchAdd(ptr, 4, SeqCst()); + EXPECT_EQ(ptr, ptr2); + EXPECT_EQ(result, orgptr); +} + +TEST(AtomicTests, IntrinSelect8FetchSubRelaxed) +{ + uint8_t value = 8; + uint8_t result = SI::FetchSub(value, 4, Relaxed()); + EXPECT_EQ(value, 4); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchSubConsume) +{ + uint8_t value = 8; + uint8_t result = SI::FetchSub(value, 4, Consume()); + EXPECT_EQ(value, 4); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchSubAcquire) +{ + uint8_t value = 8; + uint8_t result = SI::FetchSub(value, 4, Acquire()); + EXPECT_EQ(value, 4); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchSubRelease) +{ + uint8_t value = 8; + uint8_t result = SI::FetchSub(value, 4, Release()); + EXPECT_EQ(value, 4); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchSubAcqRel) +{ + uint8_t value = 8; + uint8_t result = SI::FetchSub(value, 4, AcqRel()); + EXPECT_EQ(value, 4); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect8FetchSubSeqCst) +{ + uint8_t value = 8; + uint8_t result = SI::FetchSub(value, 4, SeqCst()); + EXPECT_EQ(value, 4); + EXPECT_EQ(result, 8); +} + +TEST(AtomicTests, IntrinSelect16FetchSubRelaxed) +{ + uint16_t value = 0x203; + uint16_t result = SI::FetchSub(value, 4, Relaxed()); + EXPECT_EQ(value, 0x1ff); + EXPECT_EQ(result, 0x203); +} + +TEST(AtomicTests, IntrinSelect16FetchSubConsume) +{ + uint16_t value = 0x203; + uint16_t result = SI::FetchSub(value, 4, Consume()); + EXPECT_EQ(value, 0x1ff); + EXPECT_EQ(result, 0x203); +} + +TEST(AtomicTests, IntrinSelect16FetchSubAcquire) +{ + uint16_t value = 0x203; + uint16_t result = SI::FetchSub(value, 4, Acquire()); + EXPECT_EQ(value, 0x1ff); + EXPECT_EQ(result, 0x203); +} + +TEST(AtomicTests, IntrinSelect16FetchSubRelease) +{ + uint16_t value = 0x203; + uint16_t result = SI::FetchSub(value, 4, Release()); + EXPECT_EQ(value, 0x1ff); + EXPECT_EQ(result, 0x203); +} + +TEST(AtomicTests, IntrinSelect16FetchSubAcqRel) +{ + uint16_t value = 0x203; + uint16_t result = SI::FetchSub(value, 4, AcqRel()); + EXPECT_EQ(value, 0x1ff); + EXPECT_EQ(result, 0x203); +} + +TEST(AtomicTests, IntrinSelect16FetchSubSeqCst) +{ + uint16_t value = 0x203; + uint16_t result = SI::FetchSub(value, 4, SeqCst()); + EXPECT_EQ(value, 0x1ff); + EXPECT_EQ(result, 0x203); +} + +TEST(AtomicTests, IntrinSelect32FetchSubRelaxed) +{ + uint32_t value = 0x2000003; + uint32_t result = SI::FetchSub(value, 4, Relaxed()); + EXPECT_EQ(value, 0x1ffffffu); + EXPECT_EQ(result, 0x2000003u); +} + +TEST(AtomicTests, IntrinSelect32FetchSubConsume) +{ + uint32_t value = 0x2000003; + uint32_t result = SI::FetchSub(value, 4, Consume()); + EXPECT_EQ(value, 0x1ffffffu); + EXPECT_EQ(result, 0x2000003u); +} + +TEST(AtomicTests, IntrinSelect32FetchSubAcquire) +{ + uint32_t value = 0x2000003; + uint32_t result = SI::FetchSub(value, 4, Acquire()); + EXPECT_EQ(value, 0x1ffffffu); + EXPECT_EQ(result, 0x2000003u); +} + +TEST(AtomicTests, IntrinSelect32FetchSubRelease) +{ + uint32_t value = 0x2000003; + uint32_t result = SI::FetchSub(value, 4, Release()); + EXPECT_EQ(value, 0x1ffffffu); + EXPECT_EQ(result, 0x2000003u); +} + +TEST(AtomicTests, IntrinSelect32FetchSubAcqRel) +{ + uint32_t value = 0x2000003; + uint32_t result = SI::FetchSub(value, 4, AcqRel()); + EXPECT_EQ(value, 0x1ffffffu); + EXPECT_EQ(result, 0x2000003u); +} + +TEST(AtomicTests, IntrinSelect32FetchSubSeqCst) +{ + uint32_t value = 0x2000003; + uint32_t result = SI::FetchSub(value, 4, SeqCst()); + EXPECT_EQ(value, 0x1ffffffu); + EXPECT_EQ(result, 0x2000003u); +} + +TEST(AtomicTests, IntrinSelect64FetchSubRelaxed) +{ + uint64_t value = 0x200000000000003; + uint64_t result = SI::FetchSub(value, 4, Relaxed()); + EXPECT_EQ(value, 0x1ffffffffffffffull); + EXPECT_EQ(result, 0x200000000000003ull); +} + +TEST(AtomicTests, IntrinSelect64FetchSubConsume) +{ + uint64_t value = 0x200000000000003; + uint64_t result = SI::FetchSub(value, 4, Consume()); + EXPECT_EQ(value, 0x1ffffffffffffffull); + EXPECT_EQ(result, 0x200000000000003ull); +} + +TEST(AtomicTests, IntrinSelect64FetchSubAcquire) +{ + uint64_t value = 0x200000000000003; + uint64_t result = SI::FetchSub(value, 4, Acquire()); + EXPECT_EQ(value, 0x1ffffffffffffffull); + EXPECT_EQ(result, 0x200000000000003ull); +} + +TEST(AtomicTests, IntrinSelect64FetchSubRelease) +{ + uint64_t value = 0x200000000000003; + uint64_t result = SI::FetchSub(value, 4, Release()); + EXPECT_EQ(value, 0x1ffffffffffffffull); + EXPECT_EQ(result, 0x200000000000003ull); +} + +TEST(AtomicTests, IntrinSelect64FetchSubAcqRel) +{ + uint64_t value = 0x200000000000003; + uint64_t result = SI::FetchSub(value, 4, AcqRel()); + EXPECT_EQ(value, 0x1ffffffffffffffull); + EXPECT_EQ(result, 0x200000000000003ull); +} + +TEST(AtomicTests, IntrinSelect64FetchSubSeqCst) +{ + uint64_t value = 0x200000000000003; + uint64_t result = SI::FetchSub(value, 4, SeqCst()); + EXPECT_EQ(value, 0x1ffffffffffffffull); + EXPECT_EQ(result, 0x200000000000003ull); +} + +TEST(AtomicTests, IntrinSelect8FetchAndRelaxed) +{ + uint8_t value = 6; + uint8_t result = SI::FetchAnd(value, 3, Relaxed()); + EXPECT_EQ(value, 2); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchAndConsume) +{ + uint8_t value = 6; + uint8_t result = SI::FetchAnd(value, 3, Consume()); + EXPECT_EQ(value, 2); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchAndAcquire) +{ + uint8_t value = 6; + uint8_t result = SI::FetchAnd(value, 3, Acquire()); + EXPECT_EQ(value, 2); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchAndRelease) +{ + uint8_t value = 6; + uint8_t result = SI::FetchAnd(value, 3, Release()); + EXPECT_EQ(value, 2); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchAndAcqRel) +{ + uint8_t value = 6; + uint8_t result = SI::FetchAnd(value, 3, AcqRel()); + EXPECT_EQ(value, 2); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchAndSeqCst) +{ + uint8_t value = 6; + uint8_t result = SI::FetchAnd(value, 3, SeqCst()); + EXPECT_EQ(value, 2); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect16FetchAndRelaxed) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchAnd(value, 0x303, Relaxed()); + EXPECT_EQ(value, 0x202); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchAndConsume) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchAnd(value, 0x303, Consume()); + EXPECT_EQ(value, 0x202); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchAndAcquire) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchAnd(value, 0x303, Acquire()); + EXPECT_EQ(value, 0x202); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchAndRelease) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchAnd(value, 0x303, Release()); + EXPECT_EQ(value, 0x202); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchAndAcqRel) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchAnd(value, 0x303, AcqRel()); + EXPECT_EQ(value, 0x202); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchAndSeqCst) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchAnd(value, 0x303, SeqCst()); + EXPECT_EQ(value, 0x202); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect32FetchAndRelaxed) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchAnd(value, 0x3030303, Relaxed()); + EXPECT_EQ(value, 0x2020202u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchAndConsume) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchAnd(value, 0x3030303, Consume()); + EXPECT_EQ(value, 0x2020202u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchAndAcquire) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchAnd(value, 0x3030303, Acquire()); + EXPECT_EQ(value, 0x2020202u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchAndRelease) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchAnd(value, 0x3030303, Release()); + EXPECT_EQ(value, 0x2020202u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchAndAcqRel) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchAnd(value, 0x3030303, AcqRel()); + EXPECT_EQ(value, 0x2020202u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchAndSeqCst) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchAnd(value, 0x3030303, SeqCst()); + EXPECT_EQ(value, 0x2020202u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect64FetchAndRelaxed) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchAnd(value, 0x303030303030303, Relaxed()); + EXPECT_EQ(value, 0x202020202020202ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchAndConsume) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchAnd(value, 0x303030303030303, Consume()); + EXPECT_EQ(value, 0x202020202020202ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchAndAcquire) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchAnd(value, 0x303030303030303, Acquire()); + EXPECT_EQ(value, 0x202020202020202ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchAndRelease) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchAnd(value, 0x303030303030303, Release()); + EXPECT_EQ(value, 0x202020202020202ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchAndAcqRel) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchAnd(value, 0x303030303030303, AcqRel()); + EXPECT_EQ(value, 0x202020202020202ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchAndSeqCst) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchAnd(value, 0x303030303030303, SeqCst()); + EXPECT_EQ(value, 0x202020202020202ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect8FetchOrRelaxed) +{ + uint8_t value = 6; + uint8_t result = SI::FetchOr(value, 3, Relaxed()); + EXPECT_EQ(value, 7); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchOrConsume) +{ + uint8_t value = 6; + uint8_t result = SI::FetchOr(value, 3, Consume()); + EXPECT_EQ(value, 7); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchOrAcquire) +{ + uint8_t value = 6; + uint8_t result = SI::FetchOr(value, 3, Acquire()); + EXPECT_EQ(value, 7); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchOrRelease) +{ + uint8_t value = 6; + uint8_t result = SI::FetchOr(value, 3, Release()); + EXPECT_EQ(value, 7); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchOrAcqRel) +{ + uint8_t value = 6; + uint8_t result = SI::FetchOr(value, 3, AcqRel()); + EXPECT_EQ(value, 7); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchOrSeqCst) +{ + uint8_t value = 6; + uint8_t result = SI::FetchOr(value, 3, SeqCst()); + EXPECT_EQ(value, 7); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect16FetchOrRelaxed) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchOr(value, 0x303, Relaxed()); + EXPECT_EQ(value, 0x707); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchOrConsume) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchOr(value, 0x303, Consume()); + EXPECT_EQ(value, 0x707); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchOrAcquire) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchOr(value, 0x303, Acquire()); + EXPECT_EQ(value, 0x707); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchOrRelease) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchOr(value, 0x303, Release()); + EXPECT_EQ(value, 0x707); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchOrAcqRel) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchOr(value, 0x303, AcqRel()); + EXPECT_EQ(value, 0x707); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchOrSeqCst) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchOr(value, 0x303, SeqCst()); + EXPECT_EQ(value, 0x707); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect32FetchOrRelaxed) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchOr(value, 0x3030303, Relaxed()); + EXPECT_EQ(value, 0x7070707u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchOrConsume) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchOr(value, 0x3030303, Consume()); + EXPECT_EQ(value, 0x7070707u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchOrAcquire) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchOr(value, 0x3030303, Acquire()); + EXPECT_EQ(value, 0x7070707u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchOrRelease) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchOr(value, 0x3030303, Release()); + EXPECT_EQ(value, 0x7070707u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchOrAcqRel) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchOr(value, 0x3030303, AcqRel()); + EXPECT_EQ(value, 0x7070707u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchOrSeqCst) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchOr(value, 0x3030303, SeqCst()); + EXPECT_EQ(value, 0x7070707u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect64FetchOrRelaxed) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchOr(value, 0x303030303030303, Relaxed()); + EXPECT_EQ(value, 0x707070707070707ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchOrConsume) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchOr(value, 0x303030303030303, Consume()); + EXPECT_EQ(value, 0x707070707070707ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchOrAcquire) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchOr(value, 0x303030303030303, Acquire()); + EXPECT_EQ(value, 0x707070707070707ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchOrRelease) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchOr(value, 0x303030303030303, Release()); + EXPECT_EQ(value, 0x707070707070707ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchOrAcqRel) +{ + uint64_t value = 0x606060606060606; + uint64_t result = SI::FetchOr(value, 0x303030303030303, AcqRel()); + EXPECT_EQ(value, 0x707070707070707ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchOrSeqCst) +{ + uint64_t value = 0x606060606060606; + uint64_t result = SI::FetchOr(value, 0x303030303030303, SeqCst()); + EXPECT_EQ(value, 0x707070707070707ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect8FetchXorRelaxed) +{ + uint8_t value = 6; + uint8_t result = SI::FetchXor(value, 3, Relaxed()); + EXPECT_EQ(value, 5); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchXorConsume) +{ + uint8_t value = 6; + uint8_t result = SI::FetchXor(value, 3, Consume()); + EXPECT_EQ(value, 5); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchXorAcquire) +{ + uint8_t value = 6; + uint8_t result = SI::FetchXor(value, 3, Acquire()); + EXPECT_EQ(value, 5); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchXorRelease) +{ + uint8_t value = 6; + uint8_t result = SI::FetchXor(value, 3, Release()); + EXPECT_EQ(value, 5); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchXorAcqRel) +{ + uint8_t value = 6; + uint8_t result = SI::FetchXor(value, 3, AcqRel()); + EXPECT_EQ(value, 5); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect8FetchXorSeqCst) +{ + uint8_t value = 6; + uint8_t result = SI::FetchXor(value, 3, SeqCst()); + EXPECT_EQ(value, 5); + EXPECT_EQ(result, 6); +} + +TEST(AtomicTests, IntrinSelect16FetchXorRelaxed) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchXor(value, 0x303, Relaxed()); + EXPECT_EQ(value, 0x505); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchXorConsume) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchXor(value, 0x303, Consume()); + EXPECT_EQ(value, 0x505); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchXorAcquire) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchXor(value, 0x303, Acquire()); + EXPECT_EQ(value, 0x505); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchXorRelease) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchXor(value, 0x303, Release()); + EXPECT_EQ(value, 0x505); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchXorAcqRel) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchXor(value, 0x303, AcqRel()); + EXPECT_EQ(value, 0x505); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect16FetchXorSeqCst) +{ + uint16_t value = 0x606; + uint16_t result = SI::FetchXor(value, 0x303, SeqCst()); + EXPECT_EQ(value, 0x505); + EXPECT_EQ(result, 0x606); +} + +TEST(AtomicTests, IntrinSelect32FetchXorRelaxed) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchXor(value, 0x3030303, Relaxed()); + EXPECT_EQ(value, 0x5050505u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchXorConsume) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchXor(value, 0x3030303, Consume()); + EXPECT_EQ(value, 0x5050505u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchXorAcquire) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchXor(value, 0x3030303, Acquire()); + EXPECT_EQ(value, 0x5050505u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchXorRelease) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchXor(value, 0x3030303, Release()); + EXPECT_EQ(value, 0x5050505u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchXorAcqRel) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchXor(value, 0x3030303, AcqRel()); + EXPECT_EQ(value, 0x5050505u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect32FetchXorSeqCst) +{ + uint32_t value = 0x6060606; + uint32_t result = SI::FetchXor(value, 0x3030303, SeqCst()); + EXPECT_EQ(value, 0x5050505u); + EXPECT_EQ(result, 0x6060606u); +} + +TEST(AtomicTests, IntrinSelect64FetchXorRelaxed) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchXor(value, 0x303030303030303, Relaxed()); + EXPECT_EQ(value, 0x505050505050505ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchXorConsume) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchXor(value, 0x303030303030303, Consume()); + EXPECT_EQ(value, 0x505050505050505ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchXorAcquire) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchXor(value, 0x303030303030303, Acquire()); + EXPECT_EQ(value, 0x505050505050505ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchXorRelease) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchXor(value, 0x303030303030303, Release()); + EXPECT_EQ(value, 0x505050505050505ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchXorAcqRel) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchXor(value, 0x303030303030303, AcqRel()); + EXPECT_EQ(value, 0x505050505050505ull); + EXPECT_EQ(result, 0x606060606060606ull); +} + +TEST(AtomicTests, IntrinSelect64FetchXorSeqCst) +{ + uint64_t value = 0x606060606060606; + uint64_t result = + SI::FetchXor(value, 0x303030303030303, SeqCst()); + EXPECT_EQ(value, 0x505050505050505ull); + EXPECT_EQ(result, 0x606060606060606ull); +} diff --git a/test/test_EmptyOptimizedPair.cpp b/test/test_EmptyOptimizedPair.cpp new file mode 100644 index 0000000..d8c399a --- /dev/null +++ b/test/test_EmptyOptimizedPair.cpp @@ -0,0 +1,266 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "test/TestAlloc.h" + +#include "radiant/EmptyOptimizedPair.h" + +#include + +struct Empty +{ + Empty() = default; + + Empty(Empty&) noexcept + { + ++counter; + } + + Empty(Empty const&) noexcept + { + counter += 2; + } + + Empty(Empty&&) noexcept + { + counter += 3; + } + + uint32_t One() const noexcept + { + return 1; + } + + static int counter; +}; + +int Empty::counter = 0; + +struct Stateful +{ + uint32_t value; +}; + +struct ThrowingEmpty +{ + ThrowingEmpty() + { + } + + ThrowingEmpty(ThrowingEmpty const&) + { + } + + ThrowingEmpty(ThrowingEmpty&&) + { + } +}; + +struct ThrowingStateful +{ + explicit ThrowingStateful(uint32_t v) + : value(v) + { + } + + ThrowingStateful(ThrowingStateful const&) + { + } + + ThrowingStateful(ThrowingStateful&&) + { + } + + uint32_t value; +}; + +// empty ctors +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair())); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair())); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair())); +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair, bool>( + rad::DeclVal&>(), true))); +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair, bool>( + rad::DeclVal&>(), true))); +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair, bool>( + rad::DeclVal&&>(), true))); + +// empty copy ctors +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&>()))); + +// empty move ctors +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&&>()))); + +// non-empty ctors +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair())); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + ThrowingStateful(0), true))); +RAD_S_ASSERT(!noexcept( + rad::EmptyOptimizedPair(Stateful(), 0))); +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair( + rad::DeclVal(), true))); +RAD_S_ASSERT(noexcept( + rad::EmptyOptimizedPair(rad::DeclVal(), true))); + +// non-empty copy ctors +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&>()))); + +// non-empty move ctors +RAD_S_ASSERT(noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(rad::EmptyOptimizedPair( + rad::DeclVal&&>()))); + +TEST(TestEmptyOptimizedPair, EmptyBaseDefaultValues) +{ + rad::EmptyOptimizedPair pair; + EXPECT_EQ(sizeof(pair), sizeof(uint32_t)); + EXPECT_EQ(pair.First().One(), 1u); + EXPECT_EQ(pair.Second(), 0u); +} + +TEST(TestEmptyOptimizedPair, EmptyBaseInitializedSecond) +{ + rad::EmptyOptimizedPair pair(1u); + EXPECT_EQ(sizeof(pair), sizeof(uint32_t)); + EXPECT_EQ(pair.Second(), 1u); + + const decltype(pair)& ref = pair; + EXPECT_EQ(ref.First().One(), 1u); + EXPECT_EQ(ref.Second(), 1u); +} + +TEST(TestEmptyOptimizedPair, EmptyBaseInitializedBoth) +{ + Empty empty; + + rad::EmptyOptimizedPair pair1(empty, 1u); + EXPECT_EQ(Empty::counter, 1); + EXPECT_EQ(pair1.Second(), 1u); + + const Empty constEmpty; + rad::EmptyOptimizedPair pair2(constEmpty, 1u); + EXPECT_EQ(Empty::counter, 3); + EXPECT_EQ(pair2.Second(), 1u); + + rad::EmptyOptimizedPair pair3(rad::Move(empty), 1u); + EXPECT_EQ(Empty::counter, 6); + EXPECT_EQ(pair3.Second(), 1u); +} + +TEST(TestEmtpyOptimizedPair, EmptyCopy) +{ + rad::EmptyOptimizedPair pair1(1u); + rad::EmptyOptimizedPair pair2(pair1); + + EXPECT_EQ(pair1.Second(), pair2.Second()); + + const rad::EmptyOptimizedPair pair3(2u); + rad::EmptyOptimizedPair pair4(pair3); + + EXPECT_EQ(pair3.Second(), pair4.Second()); +} + +TEST(TestEmtpyOptimizedPair, EmptyMove) +{ + rad::EmptyOptimizedPair> pair1; + pair1.Second().push_back(1); + + rad::EmptyOptimizedPair> pair2(rad::Move(pair1)); + EXPECT_TRUE(pair1.Second().empty()); + EXPECT_EQ(pair2.Second().size(), 1u); +} + +TEST(TestEmptyOptimizedPair, StatefulBaseDefaultValues) +{ + rad::EmptyOptimizedPair pair; + EXPECT_GT(sizeof(pair), sizeof(uint32_t)); + EXPECT_EQ(pair.First().value, 0u); + EXPECT_EQ(pair.Second(), 0u); +} + +TEST(TestEmptyOptimizedPair, StatefulBaseInitialized) +{ + rad::EmptyOptimizedPair pair(Stateful{ 1u }, 2u); + EXPECT_GT(sizeof(pair), sizeof(uint32_t)); + EXPECT_EQ(pair.First().value, 1u); + EXPECT_EQ(pair.Second(), 2u); + + const decltype(pair)& ref = pair; + EXPECT_EQ(ref.First().value, 1u); + EXPECT_EQ(ref.Second(), 2u); + + Stateful s{ 2u }; + rad::EmptyOptimizedPair pair2(s, 3u); + EXPECT_GT(sizeof(pair2), sizeof(uint32_t)); + EXPECT_EQ(pair2.First().value, 2u); + EXPECT_EQ(pair2.Second(), 3u); + + rad::EmptyOptimizedPair pair3(rad::Move(s), 3u); + EXPECT_GT(sizeof(pair3), sizeof(uint32_t)); + EXPECT_EQ(pair3.First().value, 2u); + EXPECT_EQ(pair3.Second(), 3u); +} + +TEST(TestEmtpyOptimizedPair, StatefulCopy) +{ + rad::EmptyOptimizedPair pair1(Stateful{ 1u }, 2u); + rad::EmptyOptimizedPair pair2(pair1); + + EXPECT_EQ(pair1.First().value, pair2.First().value); + EXPECT_EQ(pair1.Second(), pair2.Second()); + + const rad::EmptyOptimizedPair pair3(Stateful{ 2u }, 3u); + rad::EmptyOptimizedPair pair4(pair3); + + EXPECT_EQ(pair3.First().value, pair4.First().value); + EXPECT_EQ(pair3.Second(), pair4.Second()); +} + +TEST(TestEmtpyOptimizedPair, StatefulMove) +{ + rad::EmptyOptimizedPair, std::vector> pair1; + pair1.First().push_back(1); + pair1.Second().push_back(2); + + rad::EmptyOptimizedPair, std::vector> pair2( + rad::Move(pair1)); + + EXPECT_TRUE(pair1.First().empty()); + EXPECT_EQ(pair2.First().size(), 1u); + EXPECT_EQ(pair2.First().front(), 1); + + EXPECT_TRUE(pair1.Second().empty()); + EXPECT_EQ(pair2.Second().size(), 1u); + EXPECT_EQ(pair2.Second().front(), 2); +} diff --git a/test/test_Handle.cpp b/test/test_Handle.cpp new file mode 100644 index 0000000..db62397 --- /dev/null +++ b/test/test_Handle.cpp @@ -0,0 +1,280 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "radiant/Handle.h" + +static int g_IsValidCalls = 0; +static int g_CloseCalls = 0; +static int g_CloserCalls = 0; + +struct HandleCloser +{ + static void Close(int value) noexcept + { + RAD_UNUSED(value); + g_CloserCalls++; + } +}; + +using TestHandle = rad::HandleDef; + +struct MockHandlePolicy +{ + using ValueType = int; + static constexpr int InvalidValue = 0xdead; + + static bool IsValid(int value) noexcept + { + g_IsValidCalls++; + return value != InvalidValue; + } + + static void Close(int value) noexcept + { + RAD_UNUSED(value); + g_CloseCalls++; + } +}; + +constexpr int MockHandlePolicy::InvalidValue; + +using MockHandle = rad::Handle; + +namespace +{ + +void MockOpener(int* handle) +{ + *handle = 123; +} + +} // namespace + +class HandleTests : public ::testing::Test +{ +public: + + void SetUp() override + { + g_IsValidCalls = 0; + g_CloseCalls = 0; + g_CloserCalls = 0; + } + + void TearDown() override + { + } +}; + +TEST_F(HandleTests, DefaultConstruct) +{ + { + MockHandle h; + + EXPECT_EQ(h.Get(), MockHandlePolicy::InvalidValue); + } + + EXPECT_EQ(g_IsValidCalls, 1); + EXPECT_EQ(g_CloseCalls, 0); +} + +TEST_F(HandleTests, Construct) +{ + { + MockHandle h(456); + + EXPECT_EQ(h.Get(), 456); + } + + EXPECT_EQ(g_IsValidCalls, 1); + EXPECT_EQ(g_CloseCalls, 1); +} + +TEST_F(HandleTests, MoveConstruct) +{ + { + MockHandle h(456); + MockHandle o(Move(h)); + + EXPECT_EQ(g_IsValidCalls, 0); + EXPECT_EQ(g_CloseCalls, 0); + + EXPECT_EQ(h.Get(), MockHandlePolicy::InvalidValue); + EXPECT_EQ(o.Get(), 456); + } + + EXPECT_EQ(g_IsValidCalls, 2); + EXPECT_EQ(g_CloseCalls, 1); +} + +TEST_F(HandleTests, MoveAssign) +{ + { + MockHandle h(456); + MockHandle o; + + EXPECT_EQ(g_IsValidCalls, 0); + EXPECT_EQ(g_CloseCalls, 0); + + o = Move(h); + + EXPECT_EQ(g_IsValidCalls, 1); + EXPECT_EQ(g_CloseCalls, 0); + + EXPECT_EQ(h.Get(), MockHandlePolicy::InvalidValue); + EXPECT_EQ(o.Get(), 456); + } + + EXPECT_EQ(g_IsValidCalls, 3); + EXPECT_EQ(g_CloseCalls, 1); +} + +TEST_F(HandleTests, IsValid) +{ + MockHandle h; + + EXPECT_FALSE(h.IsValid()); + + h = MockHandle(123); + + EXPECT_TRUE(h.IsValid()); +} + +TEST_F(HandleTests, Reset) +{ + MockHandle h; + + EXPECT_FALSE(h.IsValid()); + + EXPECT_EQ(g_CloseCalls, 0); + + h.Reset(123); + + EXPECT_EQ(g_CloseCalls, 0); + + EXPECT_EQ(h.Get(), 123); + + EXPECT_TRUE(h.IsValid()); + + EXPECT_EQ(g_CloseCalls, 0); + + h.Reset(); + + EXPECT_EQ(g_CloseCalls, 1); +} + +TEST_F(HandleTests, Release) +{ + MockHandle h; + + EXPECT_FALSE(h.IsValid()); + EXPECT_FALSE(h); + + EXPECT_EQ(h.Release(), MockHandlePolicy::InvalidValue); + + h.Reset(123); + + EXPECT_TRUE(h.IsValid()); + + EXPECT_EQ(h.Release(), 123); + + EXPECT_FALSE(h.IsValid()); +} + +TEST_F(HandleTests, Swap) +{ + MockHandle h(123); + MockHandle o; + + EXPECT_EQ(h.Get(), 123); + EXPECT_EQ(o.Get(), MockHandlePolicy::InvalidValue); + + EXPECT_EQ(g_CloseCalls, 0); + + h.Swap(o); + + EXPECT_EQ(g_CloseCalls, 0); + + EXPECT_EQ(h.Get(), MockHandlePolicy::InvalidValue); + EXPECT_EQ(o.Get(), 123); +} + +TEST_F(HandleTests, Put) +{ + MockHandle h; + + EXPECT_EQ(g_CloseCalls, 0); + + MockOpener(h.Put()); + + EXPECT_EQ(g_CloseCalls, 0); + + EXPECT_EQ(h.Get(), 123); + + h.Reset(); + + EXPECT_EQ(g_CloseCalls, 1); + + MockOpener(&h); + + EXPECT_EQ(g_CloseCalls, 1); + + EXPECT_EQ(h.Get(), 123); +} + +TEST_F(HandleTests, Compare) +{ + MockHandle h(1); + MockHandle o(2); + + EXPECT_FALSE(h == o); + EXPECT_TRUE(h != o); + EXPECT_TRUE(h < o); + EXPECT_TRUE(h <= o); + EXPECT_FALSE(h > o); + EXPECT_FALSE(h >= o); + + EXPECT_FALSE(h == MockHandlePolicy::InvalidValue); + EXPECT_TRUE(h != MockHandlePolicy::InvalidValue); + EXPECT_TRUE(h < MockHandlePolicy::InvalidValue); + EXPECT_TRUE(h <= MockHandlePolicy::InvalidValue); + EXPECT_FALSE(h > MockHandlePolicy::InvalidValue); + EXPECT_FALSE(h >= MockHandlePolicy::InvalidValue); + + EXPECT_FALSE(MockHandlePolicy::InvalidValue == h); + EXPECT_TRUE(MockHandlePolicy::InvalidValue != h); + EXPECT_FALSE(MockHandlePolicy::InvalidValue < h); + EXPECT_FALSE(MockHandlePolicy::InvalidValue <= h); + EXPECT_TRUE(MockHandlePolicy::InvalidValue > h); + EXPECT_TRUE(MockHandlePolicy::InvalidValue >= h); +} + +TEST_F(HandleTests, TestDef) +{ + TestHandle h(123); + + EXPECT_TRUE(h); + + EXPECT_EQ(g_IsValidCalls, 0); + EXPECT_EQ(g_CloserCalls, 0); + + h.Reset(); + + EXPECT_FALSE(h); + + EXPECT_EQ(g_IsValidCalls, 0); + EXPECT_EQ(g_CloserCalls, 1); +} diff --git a/test/test_Integer.cpp b/test/test_Integer.cpp new file mode 100644 index 0000000..c802959 --- /dev/null +++ b/test/test_Integer.cpp @@ -0,0 +1,1177 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" +#include "radiant/Integer.h" + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::i8())); +RAD_S_ASSERT(noexcept(rad::i8(1))); +RAD_S_ASSERT(noexcept(rad::i8(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::i8(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator int8_t())); +RAD_S_ASSERT(noexcept(rad::DeclVal().Max(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Min(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Add(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Sub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Mul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingMul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedMul(1))); + +RAD_S_ASSERT(noexcept(rad::u8())); +RAD_S_ASSERT(noexcept(rad::u8(1))); +RAD_S_ASSERT(noexcept(rad::u8(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::u8(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator uint8_t())); +RAD_S_ASSERT(noexcept(rad::DeclVal().Max(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Min(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Add(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Sub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Mul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingMul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedMul(1))); + +RAD_S_ASSERT(noexcept(rad::i16())); +RAD_S_ASSERT(noexcept(rad::i16(1))); +RAD_S_ASSERT(noexcept(rad::i16(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::i16(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator int16_t())); +RAD_S_ASSERT(noexcept(rad::DeclVal().Max(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Min(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Add(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Sub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Mul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingMul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedMul(1))); + +RAD_S_ASSERT(noexcept(rad::u16())); +RAD_S_ASSERT(noexcept(rad::u16(1))); +RAD_S_ASSERT(noexcept(rad::u16(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::u16(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator uint16_t())); +RAD_S_ASSERT(noexcept(rad::DeclVal().Max(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Min(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Add(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Sub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Mul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingMul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedMul(1))); + +RAD_S_ASSERT(noexcept(rad::i32())); +RAD_S_ASSERT(noexcept(rad::i32(1))); +RAD_S_ASSERT(noexcept(rad::i32(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::i32(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator int32_t())); +RAD_S_ASSERT(noexcept(rad::DeclVal().Max(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Min(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Add(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Sub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Mul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingMul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedMul(1))); + +RAD_S_ASSERT(noexcept(rad::u32())); +RAD_S_ASSERT(noexcept(rad::u32(1))); +RAD_S_ASSERT(noexcept(rad::u32(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::u32(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator=(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal().operator uint32_t())); +RAD_S_ASSERT(noexcept(rad::DeclVal().Max(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Min(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Add(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Sub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().Mul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().SaturatingMul(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedAdd(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedSub(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal().UncheckedMul(1))); + +// clang-format on + +RAD_S_ASSERT(rad::i8::MIN == INT8_MIN); +RAD_S_ASSERT(rad::i8::MAX == INT8_MAX); +RAD_S_ASSERT(rad::u8::MIN == 0); +RAD_S_ASSERT(rad::u8::MAX == UINT8_MAX); +RAD_S_ASSERT(rad::i16::MIN == INT16_MIN); +RAD_S_ASSERT(rad::i16::MAX == INT16_MAX); +RAD_S_ASSERT(rad::u16::MIN == 0); +RAD_S_ASSERT(rad::u16::MAX == UINT16_MAX); +RAD_S_ASSERT(rad::i32::MIN == INT32_MIN); +RAD_S_ASSERT(rad::i32::MAX == INT32_MAX); +RAD_S_ASSERT(rad::u32::MIN == 0); +RAD_S_ASSERT(rad::u32::MAX == UINT32_MAX); + +RAD_S_ASSERT(rad::i8() == int8_t()); +RAD_S_ASSERT(rad::u8() == uint8_t()); +RAD_S_ASSERT(rad::i16() == int16_t()); +RAD_S_ASSERT(rad::u16() == uint16_t()); +RAD_S_ASSERT(rad::i32() == int32_t()); +RAD_S_ASSERT(rad::u32() == uint32_t()); + +RAD_S_ASSERT(rad::i8(1) == 1); +RAD_S_ASSERT(rad::u8(1) == 1); +RAD_S_ASSERT(rad::i16(1) == 1); +RAD_S_ASSERT(rad::u16(1) == 1); +RAD_S_ASSERT(rad::i32(1) == 1); +RAD_S_ASSERT(rad::u32(1) == 1u); + +RAD_S_ASSERT(rad::i8(rad::i8(1)) == 1); +RAD_S_ASSERT(rad::u8(rad::u8(1)) == 1); +RAD_S_ASSERT(rad::i16(rad::i16(1)) == 1); +RAD_S_ASSERT(rad::u16(rad::u16(1)) == 1); +RAD_S_ASSERT(rad::i32(rad::i32(1)) == 1); +RAD_S_ASSERT(rad::u32(rad::u32(1)) == 1u); + +// +// Construct +// + +TEST(IntegerTests, i8_Construct) +{ + rad::i8 a; + EXPECT_EQ(a, int8_t()); + + rad::i8 b(1); + EXPECT_EQ(b, 1); + + rad::i8 c(b); + EXPECT_EQ(c, 1); + + rad::i8 d(rad::Move(c)); + EXPECT_EQ(d, 1); +} + +TEST(IntegerTests, u8_Construct) +{ + rad::u8 a; + EXPECT_EQ(a, uint8_t()); + + rad::u8 b(1); + EXPECT_EQ(b, 1); + + rad::u8 c(b); + EXPECT_EQ(c, 1); + + rad::u8 d(rad::Move(c)); + EXPECT_EQ(d, 1); +} + +TEST(IntegerTests, i16_Construct) +{ + rad::i16 a; + EXPECT_EQ(a, int16_t()); + + rad::i16 b(1); + EXPECT_EQ(b, 1); + + rad::i16 c(b); + EXPECT_EQ(c, 1); + + rad::i16 d(rad::Move(c)); + EXPECT_EQ(d, 1); +} + +TEST(IntegerTests, u16_Construct) +{ + rad::u16 a; + EXPECT_EQ(a, uint16_t()); + + rad::u16 b(1); + EXPECT_EQ(b, 1); + + rad::u16 c(b); + EXPECT_EQ(c, 1); + + rad::u16 d(rad::Move(c)); + EXPECT_EQ(d, 1); +} + +TEST(IntegerTests, i32_Construct) +{ + rad::i32 a; + EXPECT_EQ(a, int32_t()); + + rad::i32 b(1); + EXPECT_EQ(b, 1); + + rad::i32 c(b); + EXPECT_EQ(c, 1); + + rad::i32 d(rad::Move(c)); + EXPECT_EQ(d, 1); +} + +TEST(IntegerTests, u32_Construct) +{ + rad::u32 a; + EXPECT_EQ(a, uint32_t()); + + rad::u32 b(1); + EXPECT_EQ(b, 1u); + + rad::u32 c(b); + EXPECT_EQ(c, 1u); + + rad::u32 d(rad::Move(c)); + EXPECT_EQ(d, 1u); +} + +// +// Assign +// + +TEST(IntegerTests, i8_Assign) +{ + rad::i8 a; + a = 1; + EXPECT_EQ(a, 1); + + rad::i8 b; + b = a; + EXPECT_EQ(b, 1); + + rad::i8 c; + c = rad::i8(1); + EXPECT_EQ(c, 1); +} + +TEST(IntegerTests, u8_Assign) +{ + rad::u8 a; + a = 1; + EXPECT_EQ(a, 1); + + rad::u8 b; + b = a; + EXPECT_EQ(b, 1); + + rad::u8 c; + c = rad::u8(1); + EXPECT_EQ(c, 1); +} + +TEST(IntegerTests, i16_Assign) +{ + rad::i16 a; + a = 1; + EXPECT_EQ(a, 1); + + rad::i16 b; + b = a; + EXPECT_EQ(b, 1); + + rad::i16 c; + c = rad::i16(1); + EXPECT_EQ(c, 1); +} + +TEST(IntegerTests, u16_Assign) +{ + rad::u16 a; + a = 1; + EXPECT_EQ(a, 1); + + rad::u16 b; + b = a; + EXPECT_EQ(b, 1); + + rad::u16 c; + c = rad::u16(1); + EXPECT_EQ(c, 1); +} + +TEST(IntegerTests, i32_Assign) +{ + rad::i32 a; + a = 1; + EXPECT_EQ(a, 1); + + rad::i32 b; + b = a; + EXPECT_EQ(b, 1); + + rad::i32 c; + c = rad::i32(1); + EXPECT_EQ(c, 1); +} + +TEST(IntegerTests, u32_Assign) +{ + rad::u32 a; + a = 1; + EXPECT_EQ(a, 1u); + + rad::u32 b; + b = a; + EXPECT_EQ(b, 1u); + + rad::u32 c; + c = rad::u32(1); + EXPECT_EQ(c, 1u); +} + +// +// Min/Max +// + +TEST(IntegerTests, i8_MinMax) +{ + EXPECT_EQ(rad::i8(1).Max(2), 2); + EXPECT_EQ(rad::i8(2).Max(1), 2); + EXPECT_EQ(rad::i8(1).Min(2), 1); + EXPECT_EQ(rad::i8(2).Min(1), 1); + + EXPECT_EQ(rad::i8(-1).Max(-2), -1); + EXPECT_EQ(rad::i8(-2).Max(-1), -1); + EXPECT_EQ(rad::i8(-1).Min(-2), -2); + EXPECT_EQ(rad::i8(-2).Min(-1), -2); +} + +TEST(IntegerTests, u8_MinMax) +{ + EXPECT_EQ(rad::u8(1).Max(2), 2); + EXPECT_EQ(rad::u8(2).Max(1), 2); + EXPECT_EQ(rad::u8(1).Min(2), 1); + EXPECT_EQ(rad::u8(2).Min(1), 1); +} + +TEST(IntegerTests, i16_MinMax) +{ + EXPECT_EQ(rad::i16(1).Max(2), 2); + EXPECT_EQ(rad::i16(2).Max(1), 2); + EXPECT_EQ(rad::i16(1).Min(2), 1); + EXPECT_EQ(rad::i16(2).Min(1), 1); + + EXPECT_EQ(rad::i16(-1).Max(-2), -1); + EXPECT_EQ(rad::i16(-2).Max(-1), -1); + EXPECT_EQ(rad::i16(-1).Min(-2), -2); + EXPECT_EQ(rad::i16(-2).Min(-1), -2); +} + +TEST(IntegerTests, u16_MinMax) +{ + EXPECT_EQ(rad::u16(1).Max(2), 2); + EXPECT_EQ(rad::u16(2).Max(1), 2); + EXPECT_EQ(rad::u16(1).Min(2), 1); + EXPECT_EQ(rad::u16(2).Min(1), 1); +} + +TEST(IntegerTests, i32_MinMax) +{ + EXPECT_EQ(rad::i32(1).Max(2), 2); + EXPECT_EQ(rad::i32(2).Max(1), 2); + EXPECT_EQ(rad::i32(1).Min(2), 1); + EXPECT_EQ(rad::i32(2).Min(1), 1); + + EXPECT_EQ(rad::i32(-1).Max(-2), -1); + EXPECT_EQ(rad::i32(-2).Max(-1), -1); + EXPECT_EQ(rad::i32(-1).Min(-2), -2); + EXPECT_EQ(rad::i32(-2).Min(-1), -2); +} + +TEST(IntegerTests, u32_MinMax) +{ + EXPECT_EQ(rad::u32(1).Max(2), 2u); + EXPECT_EQ(rad::u32(2).Max(1), 2u); + EXPECT_EQ(rad::u32(1).Min(2), 1u); + EXPECT_EQ(rad::u32(2).Min(1), 1u); +} + +// +// Add +// + +TEST(IntegerTests, i8_Add) +{ + rad::Res a = rad::i8(); + + a = a.Ok().Add(1); + EXPECT_EQ(a, rad::i8(1)); + + a = a.Ok().Add(rad::i8::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i8(); + + a = a.Ok().Add(-1); + EXPECT_EQ(a, rad::i8(-1)); + + a = a.Ok().Add(rad::i8::MIN); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u8_Add) +{ + rad::Res a = rad::u8(); + + a = a.Ok().Add(1); + EXPECT_EQ(a, rad::u8(1)); + + a = a.Ok().Add(rad::u8::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, i16_Add) +{ + rad::Res a = rad::i16(); + + a = a.Ok().Add(1); + EXPECT_EQ(a, rad::i16(1)); + + a = a.Ok().Add(rad::i16::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i16(); + + a = a.Ok().Add(-1); + EXPECT_EQ(a, rad::i16(-1)); + + a = a.Ok().Add(rad::i16::MIN); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u16_Add) +{ + rad::Res a = rad::u16(); + + a = a.Ok().Add(1); + EXPECT_EQ(a, rad::u16(1)); + + a = a.Ok().Add(rad::u16::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, i32_Add) +{ + rad::Res a = rad::i32(); + + a = a.Ok().Add(1); + EXPECT_EQ(a, rad::i32(1)); + + a = a.Ok().Add(rad::i32::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i32(); + + a = a.Ok().Add(-1); + EXPECT_EQ(a, rad::i32(-1)); + + a = a.Ok().Add(rad::i32::MIN); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u32_Add) +{ + rad::Res a = rad::u32(); + + a = a.Ok().Add(1); + EXPECT_EQ(a, rad::u32(1)); + + a = a.Ok().Add(rad::u32::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +// +// Sub +// + +TEST(IntegerTests, i8_Sub) +{ + rad::Res a = rad::i8(); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::i8(-1)); + + a = a.Ok().Sub(1).Ok().Sub(rad::i8::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i8(); + + a = a.Ok().Sub(-1); + EXPECT_EQ(a, rad::i8(1)); + + a = a.Ok().Sub(rad::i8::MIN); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u8_Sub) +{ + rad::Res a = rad::u8(); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::u8(1); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::u8(0)); +} + +TEST(IntegerTests, i16_Sub) +{ + rad::Res a = rad::i16(); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::i16(-1)); + + a = a.Ok().Sub(1).Ok().Sub(rad::i16::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i16(); + + a = a.Ok().Sub(-1); + EXPECT_EQ(a, rad::i16(1)); + + a = a.Ok().Sub(rad::i16::MIN); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u16_Sub) +{ + rad::Res a = rad::u16(); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::u16(1); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::u16(0)); +} + +TEST(IntegerTests, i32_Sub) +{ + rad::Res a = rad::i32(); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::i32(-1)); + + a = a.Ok().Sub(1).Ok().Sub(rad::i32::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i32(); + + a = a.Ok().Sub(-1); + EXPECT_EQ(a, rad::i32(1)); + + a = a.Ok().Sub(rad::i32::MIN); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u32_Sub) +{ + rad::Res a = rad::u32(); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::u32(1); + + a = a.Ok().Sub(1); + EXPECT_EQ(a, rad::u32(0)); +} + +// +// Mul +// + +TEST(IntegerTests, i8_Mul) +{ + rad::Res a = rad::i8(10); + + a = a.Ok().Mul(10); + EXPECT_EQ(a, rad::i8(100)); + + a = a.Ok().Mul(-1); + EXPECT_EQ(a, rad::i8(-100)); + + a = a.Ok().Mul(rad::i8::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i8(10); + + a = a.Ok().Mul(rad::i8::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u8_Mul) +{ + rad::Res a = rad::u8(10); + + a = a.Ok().Mul(10); + EXPECT_EQ(a, rad::u8(100)); + + a = a.Ok().Mul(rad::u8::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, i16_Mul) +{ + rad::Res a = rad::i16(10); + + a = a.Ok().Mul(10); + EXPECT_EQ(a, rad::i16(100)); + + a = a.Ok().Mul(-1); + EXPECT_EQ(a, rad::i16(-100)); + + a = a.Ok().Mul(rad::i16::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i16(10); + + a = a.Ok().Mul(rad::i16::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u16_Mul) +{ + rad::Res a = rad::u16(10); + + a = a.Ok().Mul(10); + EXPECT_EQ(a, rad::u16(100)); + + a = a.Ok().Mul(rad::u16::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, i32_Mul) +{ + rad::Res a = rad::i32(10); + + a = a.Ok().Mul(10); + EXPECT_EQ(a, rad::i32(100)); + + a = a.Ok().Mul(-1); + EXPECT_EQ(a, rad::i32(-100)); + + a = a.Ok().Mul(rad::i32::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); + + a = rad::i32(10); + + a = a.Ok().Mul(rad::i32::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +TEST(IntegerTests, u32_Mul) +{ + rad::Res a = rad::u32(10); + + a = a.Ok().Mul(10); + EXPECT_EQ(a, rad::u32(100)); + + a = a.Ok().Mul(rad::u32::MAX); + EXPECT_EQ(a, rad::Error::IntegerOverflow); +} + +// +// SaturatingAdd +// + +TEST(IntegerTests, i8_SaturatingAdd) +{ + rad::i8 a = 10; + + a = a.SaturatingAdd(10); + EXPECT_EQ(a, 20); + + a = a.SaturatingAdd(rad::i8::MAX); + EXPECT_EQ(a, rad::i8::MAX); + + a = a.SaturatingAdd(rad::i8::MIN); + EXPECT_EQ(a, -1); + + a = a.SaturatingAdd(rad::i8::MIN); + EXPECT_EQ(a, rad::i8::MIN); +} + +TEST(IntegerTests, u8_SaturatingAdd) +{ + rad::u8 a = 10; + + a = a.SaturatingAdd(10); + EXPECT_EQ(a, 20); + + a = a.SaturatingAdd(rad::u8::MAX); + EXPECT_EQ(a, rad::u8::MAX); +} + +TEST(IntegerTests, i16_SaturatingAdd) +{ + rad::i16 a = 10; + + a = a.SaturatingAdd(10); + EXPECT_EQ(a, 20); + + a = a.SaturatingAdd(rad::i16::MAX); + EXPECT_EQ(a, rad::i16::MAX); + + a = a.SaturatingAdd(rad::i16::MIN); + EXPECT_EQ(a, -1); + + a = a.SaturatingAdd(rad::i16::MIN); + EXPECT_EQ(a, rad::i16::MIN); +} + +TEST(IntegerTests, u16_SaturatingAdd) +{ + rad::u16 a = 10; + + a = a.SaturatingAdd(10); + EXPECT_EQ(a, 20); + + a = a.SaturatingAdd(rad::u16::MAX); + EXPECT_EQ(a, rad::u16::MAX); +} + +TEST(IntegerTests, i32_SaturatingAdd) +{ + rad::i32 a = 10; + + a = a.SaturatingAdd(10); + EXPECT_EQ(a, 20); + + a = a.SaturatingAdd(rad::i32::MAX); + EXPECT_EQ(a, rad::i32::MAX); + + a = a.SaturatingAdd(rad::i32::MIN); + EXPECT_EQ(a, -1); + + a = a.SaturatingAdd(rad::i32::MIN); + EXPECT_EQ(a, rad::i32::MIN); +} + +TEST(IntegerTests, u32_SaturatingAdd) +{ + rad::u32 a = 10; + + a = a.SaturatingAdd(10); + EXPECT_EQ(a, 20u); + + a = a.SaturatingAdd(rad::u32::MAX); + EXPECT_EQ(a, rad::u32::MAX); +} + +// +// SaturatingSub +// + +TEST(IntegerTests, i8_SaturatingSub) +{ + rad::i8 a = 0; + + a = a.SaturatingSub(10); + EXPECT_EQ(a, -10); + + a = a.SaturatingSub(rad::i8::MAX); + EXPECT_EQ(a, rad::i8::MIN); + + a = a.SaturatingSub(-1); + EXPECT_EQ(a, rad::i8::MIN + 1); + + a = a.SaturatingSub(rad::i8::MIN); + EXPECT_EQ(a, 1); + + a = a.SaturatingSub(rad::i8::MIN); + EXPECT_EQ(a, rad::i8::MAX); +} + +TEST(IntegerTests, u8_SaturatingSub) +{ + rad::u8 a = 10; + + a = a.SaturatingSub(5); + EXPECT_EQ(a, 5); + + a = a.SaturatingSub(10); + EXPECT_EQ(a, 0); +} + +TEST(IntegerTests, i16_SaturatingSub) +{ + rad::i16 a = 0; + + a = a.SaturatingSub(10); + EXPECT_EQ(a, -10); + + a = a.SaturatingSub(rad::i16::MAX); + EXPECT_EQ(a, rad::i16::MIN); + + a = a.SaturatingSub(-1); + EXPECT_EQ(a, rad::i16::MIN + 1); + + a = a.SaturatingSub(rad::i16::MIN); + EXPECT_EQ(a, 1); + + a = a.SaturatingSub(rad::i16::MIN); + EXPECT_EQ(a, rad::i16::MAX); +} + +TEST(IntegerTests, u16_SaturatingSub) +{ + rad::u16 a = 10; + + a = a.SaturatingSub(5); + EXPECT_EQ(a, 5); + + a = a.SaturatingSub(10); + EXPECT_EQ(a, 0); +} + +TEST(IntegerTests, i32_SaturatingSub) +{ + rad::i32 a = 0; + + a = a.SaturatingSub(10); + EXPECT_EQ(a, -10); + + a = a.SaturatingSub(rad::i32::MAX); + EXPECT_EQ(a, rad::i32::MIN); + + a = a.SaturatingSub(-1); + EXPECT_EQ(a, rad::i32::MIN + 1); + + a = a.SaturatingSub(rad::i32::MIN); + EXPECT_EQ(a, 1); + + a = a.SaturatingSub(rad::i32::MIN); + EXPECT_EQ(a, rad::i32::MAX); +} + +TEST(IntegerTests, u32_SaturatingSub) +{ + rad::u32 a = 10; + + a = a.SaturatingSub(5); + EXPECT_EQ(a, 5u); + + a = a.SaturatingSub(10); + EXPECT_EQ(a, 0u); +} + +// +// SaturatingMul +// + +TEST(IntegerTests, i8_SaturatingMul) +{ + rad::i8 a = 10; + + a = a.SaturatingMul(10); + EXPECT_EQ(a, 100); + + a = a.SaturatingMul(-1); + EXPECT_EQ(a, -100); + + a = a.SaturatingMul(rad::i8::MAX); + EXPECT_EQ(a, rad::i8::MIN); + + a = a.SaturatingMul(rad::i8::MIN); + EXPECT_EQ(a, rad::i8::MAX); + + a = a.SaturatingMul(rad::i8::MAX); + EXPECT_EQ(a, rad::i8::MAX); +} + +TEST(IntegerTests, u8_SaturatingMul) +{ + rad::u8 a = 10; + + a = a.SaturatingMul(10); + EXPECT_EQ(a, 100); + + a = a.SaturatingMul(rad::u8::MAX); + EXPECT_EQ(a, rad::u8::MAX); +} + +TEST(IntegerTests, i16_SaturatingMul) +{ + rad::i16 a = 10; + + a = a.SaturatingMul(10); + EXPECT_EQ(a, 100); + + a = a.SaturatingMul(-1); + EXPECT_EQ(a, -100); + + a = a.SaturatingMul(rad::i16::MAX); + EXPECT_EQ(a, rad::i16::MIN); + + a = a.SaturatingMul(rad::i16::MIN); + EXPECT_EQ(a, rad::i16::MAX); + + a = a.SaturatingMul(rad::i16::MAX); + EXPECT_EQ(a, rad::i16::MAX); +} + +TEST(IntegerTests, u16_SaturatingMul) +{ + rad::u16 a = 10; + + a = a.SaturatingMul(10); + EXPECT_EQ(a, 100); + + a = a.SaturatingMul(rad::u16::MAX); + EXPECT_EQ(a, rad::u16::MAX); +} + +TEST(IntegerTests, i32_SaturatingMul) +{ + rad::i32 a = 10; + + a = a.SaturatingMul(10); + EXPECT_EQ(a, 100); + + a = a.SaturatingMul(-1); + EXPECT_EQ(a, -100); + + a = a.SaturatingMul(rad::i32::MAX); + EXPECT_EQ(a, rad::i32::MIN); + + a = a.SaturatingMul(rad::i32::MIN); + EXPECT_EQ(a, rad::i32::MAX); + + a = a.SaturatingMul(rad::i32::MAX); + EXPECT_EQ(a, rad::i32::MAX); +} + +TEST(IntegerTests, u32_SaturatingMul) +{ + rad::u32 a = 10; + + a = a.SaturatingMul(10); + EXPECT_EQ(a, 100u); + + a = a.SaturatingMul(rad::u32::MAX); + EXPECT_EQ(a, rad::u32::MAX); +} + +// +// Unchecked +// + +TEST(IntegerTests, UncheckedAdd) +{ + EXPECT_EQ(rad::i8(rad::i8::MAX).UncheckedAdd(1), rad::i8::MIN); + EXPECT_EQ(rad::u8(rad::u8::MAX).UncheckedAdd(1), rad::u8::MIN); + EXPECT_EQ(rad::i16(rad::i16::MAX).UncheckedAdd(1), rad::i16::MIN); + EXPECT_EQ(rad::u16(rad::u16::MAX).UncheckedAdd(1), rad::u16::MIN); +} + +TEST(IntegerTests, UncheckedSub) +{ + EXPECT_EQ(rad::i8(rad::i8::MIN).UncheckedSub(1), rad::i8::MAX); + EXPECT_EQ(rad::u8(rad::u8::MIN).UncheckedSub(1), rad::u8::MAX); + EXPECT_EQ(rad::i16(rad::i16::MIN).UncheckedSub(1), rad::i16::MAX); + EXPECT_EQ(rad::u16(rad::u16::MIN).UncheckedSub(1), rad::u16::MAX); +} + +TEST(IntegerTests, UncheckedMul) +{ + EXPECT_EQ(rad::i8(rad::i8::MAX).UncheckedMul(2), -2); + EXPECT_EQ(rad::u8(rad::u8::MAX).UncheckedMul(2), 254); + EXPECT_EQ(rad::i16(rad::i16::MAX).UncheckedMul(2), -2); + EXPECT_EQ(rad::u16(rad::u16::MAX).UncheckedMul(2), 65534); + EXPECT_EQ(rad::i32(rad::i32::MAX).UncheckedMul(2), -2); + EXPECT_EQ(rad::u32(rad::u32::MAX).UncheckedMul(2), 4294967294u); +} + +// +// Compare operators +// + +TEST(IntegerTests, i8_Compare) +{ + EXPECT_TRUE(rad::i8(1) == rad::i8(1)); + EXPECT_TRUE(rad::i8(1) != rad::i8(2)); + EXPECT_TRUE(rad::i8(1) < rad::i8(2)); + EXPECT_TRUE(rad::i8(1) <= rad::i8(2)); + EXPECT_TRUE(rad::i8(2) > rad::i8(1)); + EXPECT_TRUE(rad::i8(2) >= rad::i8(1)); + + EXPECT_TRUE(rad::i8(1) == int8_t(1)); + EXPECT_TRUE(rad::i8(1) != int8_t(2)); + EXPECT_TRUE(rad::i8(1) < int8_t(2)); + EXPECT_TRUE(rad::i8(1) <= int8_t(2)); + EXPECT_TRUE(rad::i8(2) > int8_t(1)); + EXPECT_TRUE(rad::i8(2) >= int8_t(1)); + + EXPECT_TRUE(rad::i8(1) == static_cast(1)); + EXPECT_TRUE(rad::i8(1) != static_cast(2)); + EXPECT_TRUE(rad::i8(1) < static_cast(2)); + EXPECT_TRUE(rad::i8(1) <= static_cast(2)); + EXPECT_TRUE(rad::i8(2) > static_cast(1)); + EXPECT_TRUE(rad::i8(2) >= static_cast(1)); +} + +TEST(IntegerTests, u8_Compare) +{ + EXPECT_TRUE(rad::u8(1) == rad::u8(1)); + EXPECT_TRUE(rad::u8(1) != rad::u8(2)); + EXPECT_TRUE(rad::u8(1) < rad::u8(2)); + EXPECT_TRUE(rad::u8(1) <= rad::u8(2)); + EXPECT_TRUE(rad::u8(2) > rad::u8(1)); + EXPECT_TRUE(rad::u8(2) >= rad::u8(1)); + + EXPECT_TRUE(rad::u8(1) == uint8_t(1)); + EXPECT_TRUE(rad::u8(1) != uint8_t(2)); + EXPECT_TRUE(rad::u8(1) < uint8_t(2)); + EXPECT_TRUE(rad::u8(1) <= uint8_t(2)); + EXPECT_TRUE(rad::u8(2) > uint8_t(1)); + EXPECT_TRUE(rad::u8(2) >= uint8_t(1)); + + EXPECT_TRUE(rad::u8(1) == static_cast(1)); + EXPECT_TRUE(rad::u8(1) != static_cast(2)); + EXPECT_TRUE(rad::u8(1) < static_cast(2)); + EXPECT_TRUE(rad::u8(1) <= static_cast(2)); + EXPECT_TRUE(rad::u8(2) > static_cast(1)); + EXPECT_TRUE(rad::u8(2) >= static_cast(1)); +} + +TEST(IntegerTests, i16_Compare) +{ + EXPECT_TRUE(rad::i16(1) == rad::i16(1)); + EXPECT_TRUE(rad::i16(1) != rad::i16(2)); + EXPECT_TRUE(rad::i16(1) < rad::i16(2)); + EXPECT_TRUE(rad::i16(1) <= rad::i16(2)); + EXPECT_TRUE(rad::i16(2) > rad::i16(1)); + EXPECT_TRUE(rad::i16(2) >= rad::i16(1)); + + EXPECT_TRUE(rad::i16(1) == int16_t(1)); + EXPECT_TRUE(rad::i16(1) != int16_t(2)); + EXPECT_TRUE(rad::i16(1) < int16_t(2)); + EXPECT_TRUE(rad::i16(1) <= int16_t(2)); + EXPECT_TRUE(rad::i16(2) > int16_t(1)); + EXPECT_TRUE(rad::i16(2) >= int16_t(1)); + + EXPECT_TRUE(rad::i16(1) == static_cast(1)); + EXPECT_TRUE(rad::i16(1) != static_cast(2)); + EXPECT_TRUE(rad::i16(1) < static_cast(2)); + EXPECT_TRUE(rad::i16(1) <= static_cast(2)); + EXPECT_TRUE(rad::i16(2) > static_cast(1)); + EXPECT_TRUE(rad::i16(2) >= static_cast(1)); +} + +TEST(IntegerTests, u16_Compare) +{ + EXPECT_TRUE(rad::u16(1) == rad::u16(1)); + EXPECT_TRUE(rad::u16(1) != rad::u16(2)); + EXPECT_TRUE(rad::u16(1) < rad::u16(2)); + EXPECT_TRUE(rad::u16(1) <= rad::u16(2)); + EXPECT_TRUE(rad::u16(2) > rad::u16(1)); + EXPECT_TRUE(rad::u16(2) >= rad::u16(1)); + + EXPECT_TRUE(rad::u16(1) == uint16_t(1)); + EXPECT_TRUE(rad::u16(1) != uint16_t(2)); + EXPECT_TRUE(rad::u16(1) < uint16_t(2)); + EXPECT_TRUE(rad::u16(1) <= uint16_t(2)); + EXPECT_TRUE(rad::u16(2) > uint16_t(1)); + EXPECT_TRUE(rad::u16(2) >= uint16_t(1)); + + EXPECT_TRUE(rad::u16(1) == static_cast(1)); + EXPECT_TRUE(rad::u16(1) != static_cast(2)); + EXPECT_TRUE(rad::u16(1) < static_cast(2)); + EXPECT_TRUE(rad::u16(1) <= static_cast(2)); + EXPECT_TRUE(rad::u16(2) > static_cast(1)); + EXPECT_TRUE(rad::u16(2) >= static_cast(1)); +} + +TEST(IntegerTests, i32_Compare) +{ + EXPECT_TRUE(rad::i32(1) == rad::i32(1)); + EXPECT_TRUE(rad::i32(1) != rad::i32(2)); + EXPECT_TRUE(rad::i32(1) < rad::i32(2)); + EXPECT_TRUE(rad::i32(1) <= rad::i32(2)); + EXPECT_TRUE(rad::i32(2) > rad::i32(1)); + EXPECT_TRUE(rad::i32(2) >= rad::i32(1)); + + EXPECT_TRUE(rad::i32(1) == int32_t(1)); + EXPECT_TRUE(rad::i32(1) != int32_t(2)); + EXPECT_TRUE(rad::i32(1) < int32_t(2)); + EXPECT_TRUE(rad::i32(1) <= int32_t(2)); + EXPECT_TRUE(rad::i32(2) > int32_t(1)); + EXPECT_TRUE(rad::i32(2) >= int32_t(1)); + + EXPECT_TRUE(rad::i32(1) == static_cast(1)); + EXPECT_TRUE(rad::i32(1) != static_cast(2)); + EXPECT_TRUE(rad::i32(1) < static_cast(2)); + EXPECT_TRUE(rad::i32(1) <= static_cast(2)); + EXPECT_TRUE(rad::i32(2) > static_cast(1)); + EXPECT_TRUE(rad::i32(2) >= static_cast(1)); +} + +TEST(IntegerTests, u32_Compare) +{ + EXPECT_TRUE(rad::u32(1) == rad::u32(1)); + EXPECT_TRUE(rad::u32(1) != rad::u32(2)); + EXPECT_TRUE(rad::u32(1) < rad::u32(2)); + EXPECT_TRUE(rad::u32(1) <= rad::u32(2)); + EXPECT_TRUE(rad::u32(2) > rad::u32(1)); + EXPECT_TRUE(rad::u32(2) >= rad::u32(1)); + + EXPECT_TRUE(rad::u32(1) == uint32_t(1)); + EXPECT_TRUE(rad::u32(1) != uint32_t(2)); + EXPECT_TRUE(rad::u32(1) < uint32_t(2)); + EXPECT_TRUE(rad::u32(1) <= uint32_t(2)); + EXPECT_TRUE(rad::u32(2) > uint32_t(1)); + EXPECT_TRUE(rad::u32(2) >= uint32_t(1)); + + EXPECT_TRUE(rad::u32(1) == static_cast(1)); + EXPECT_TRUE(rad::u32(1) != static_cast(2)); + EXPECT_TRUE(rad::u32(1) < static_cast(2)); + EXPECT_TRUE(rad::u32(1) <= static_cast(2)); + EXPECT_TRUE(rad::u32(2) > static_cast(1)); + EXPECT_TRUE(rad::u32(2) >= static_cast(1)); +} diff --git a/test/test_Iterator.cpp b/test/test_Iterator.cpp new file mode 100644 index 0000000..516e433 --- /dev/null +++ b/test/test_Iterator.cpp @@ -0,0 +1,187 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "radiant/Iterator.h" + +struct Data +{ + int value; + bool boolean; +}; + +const Data g_data[] = { + { 0, true }, + { 1, false }, + { 2, true }, + { 3, false }, + { 4, true }, + { 5, false }, + { 6, true }, + { 7, false }, + { 8, true }, + { 9, false }, + { 10, true }, +}; +using It = rad::Iterator; +using RIt = rad::ReverseIterator; + +TEST(IteratorTest, DefaultConstruct) +{ + It it; + EXPECT_EQ(it.operator->(), nullptr); + + RIt rit; + EXPECT_EQ(rit.operator->(), nullptr); +} + +TEST(InteratorTest, ValueConstruct) +{ + It it(g_data); + EXPECT_EQ(it.operator->(), g_data); + + RIt rit(g_data); + EXPECT_EQ(rit.operator->(), g_data); +} + +TEST(InteratorTest, CopyConstruct) +{ + It it(g_data); + It itOther(it); + EXPECT_EQ(it.Base(), itOther.Base()); + + RIt rit(g_data); + RIt ritOther(rit); + EXPECT_EQ(rit.Base(), ritOther.Base()); +} + +TEST(InteratorTest, DereferenceOperator) +{ + It it(g_data); + EXPECT_EQ(&it.operator*(), g_data); + + RIt rit(g_data); + EXPECT_EQ(&rit.operator*(), g_data); +} + +TEST(InteratorTest, SubscriptOperator) +{ + It it(g_data); + EXPECT_EQ(&it.operator[](0), g_data); + EXPECT_EQ(&it.operator[](1), g_data + 1); + + RIt rit(g_data + 1); + EXPECT_EQ(&rit.operator[](0), g_data + 1); + EXPECT_EQ(&rit.operator[](1), g_data); +} + +TEST(InteratorTest, Increment) +{ + It it(g_data); + it++; + EXPECT_EQ(it.operator->(), g_data + 1); + + RIt rit(g_data + 1); + rit++; + EXPECT_EQ(rit.operator->(), g_data); +} + +TEST(InteratorTest, PreIncrement) +{ + It it(g_data); + ++it; + EXPECT_EQ(it.operator->(), g_data + 1); + + RIt rit(g_data + 1); + ++rit; + EXPECT_EQ(rit.operator->(), g_data); +} + +TEST(InteratorTest, Decrement) +{ + It it(g_data + 1); + it--; + EXPECT_EQ(it.operator->(), g_data); + + RIt rit(g_data); + rit--; + EXPECT_EQ(rit.operator->(), g_data + 1); +} + +TEST(InteratorTest, PreDecrement) +{ + It it(g_data + 1); + --it; + EXPECT_EQ(it.operator->(), g_data); + + RIt rit(g_data); + --rit; + EXPECT_EQ(rit.operator->(), g_data + 1); +} + +TEST(InteratorTest, CompoundIncrement) +{ + It it(g_data); + it += 1; + EXPECT_EQ(it.operator->(), g_data + 1); + + RIt rit(g_data + 1); + rit += 1; + EXPECT_EQ(rit.operator->(), g_data); +} + +TEST(InteratorTest, CompoundDecrement) +{ + It it(g_data + 1); + it -= 1; + EXPECT_EQ(it.operator->(), g_data); + + RIt rit(g_data); + rit -= 1; + EXPECT_EQ(rit.operator->(), g_data + 1); +} + +TEST(InteratorTest, AdditionOperator) +{ + It it(g_data); + it = it + 1; + EXPECT_EQ(it.operator->(), g_data + 1); + + RIt rit(g_data + 1); + rit = rit + 1; + EXPECT_EQ(rit.operator->(), g_data); +} + +TEST(InteratorTest, SubtractionOperator) +{ + It it(g_data + 1); + it = it - 1; + EXPECT_EQ(it.operator->(), g_data); + + RIt rit(g_data); + rit = rit - 1; + EXPECT_EQ(rit.operator->(), g_data + 1); +} + +TEST(InteratorTest, Base) +{ + It it(g_data); + EXPECT_EQ(it.Base(), g_data); + EXPECT_EQ(it.base(), g_data); + + RIt rit(g_data); + EXPECT_EQ(rit.Base(), it); + EXPECT_EQ(rit.base(), it); +} diff --git a/test/test_Locks.cpp b/test/test_Locks.cpp new file mode 100644 index 0000000..7d2fecc --- /dev/null +++ b/test/test_Locks.cpp @@ -0,0 +1,199 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "radiant/Locks.h" + +struct TestLock +{ + void LockExclusive() noexcept + { + exclusive = true; + ++count; + } + + void LockShared() noexcept + { + exclusive = false; + ++count; + } + + void Unlock() noexcept + { + exclusive = false; + --count; + } + + bool exclusive{}; + int count{}; +}; + +static_assert(noexcept(TestLock().LockExclusive()), ""); +static_assert(noexcept(TestLock().LockShared()), ""); +static_assert(noexcept(TestLock().Unlock()), ""); + +#if RAD_CPP17 +#define DEDUCED(GuardType) GuardType +#else +#define DEDUCED(GuardType) GuardType +#endif + +TEST(LocksTest, LockExclusive) +{ + TestLock lock; + + { + DEDUCED(rad::LockExclusive) guard(lock); + EXPECT_TRUE(lock.exclusive); + EXPECT_EQ(lock.count, 1); + } + + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, LockShared) +{ + TestLock lock; + + { + DEDUCED(rad::LockShared) guard(lock); + EXPECT_FALSE(lock.exclusive); + EXPECT_EQ(lock.count, 1); + } + + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableExclusiveCtor) +{ + TestLock lock; + + DEDUCED(rad::RelockableExclusive) guard(lock); + EXPECT_TRUE(lock.exclusive); + EXPECT_EQ(lock.count, 1); +} + +TEST(LocksTest, RelockableExclusiveDeferredCtor) +{ + TestLock lock; + + DEDUCED(rad::RelockableExclusive) guard(lock, rad::DeferLocking); + EXPECT_FALSE(lock.exclusive); + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableExclusiveLockedDtor) +{ + TestLock lock; + + { + DEDUCED(rad::RelockableExclusive) guard(lock); + EXPECT_EQ(lock.count, 1); + } + + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableExclusiveUnlockedDtor) +{ + TestLock lock; + + { + DEDUCED(rad::RelockableExclusive) guard(lock, rad::DeferLocking); + EXPECT_EQ(lock.count, 0); + } + + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableExclusiveLock) +{ + TestLock lock; + + DEDUCED(rad::RelockableExclusive) guard(lock, rad::DeferLocking); + guard.Lock(); + EXPECT_TRUE(lock.exclusive); + EXPECT_EQ(lock.count, 1); +} + +TEST(LocksTest, RelockableExclusiveUnlock) +{ + TestLock lock; + + DEDUCED(rad::RelockableExclusive) guard(lock); + guard.Unlock(); + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableSharedCtor) +{ + TestLock lock; + + DEDUCED(rad::RelockableShared) guard(lock); + EXPECT_FALSE(lock.exclusive); + EXPECT_EQ(lock.count, 1); +} + +TEST(LocksTest, RelockableSharedDeferredCtor) +{ + TestLock lock; + + DEDUCED(rad::RelockableShared) guard(lock, rad::DeferLocking); + EXPECT_FALSE(lock.exclusive); + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableSharedLockedDtor) +{ + TestLock lock; + + { + DEDUCED(rad::RelockableShared) guard(lock); + EXPECT_EQ(lock.count, 1); + } + + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableSharedUnlockedDtor) +{ + TestLock lock; + + { + DEDUCED(rad::RelockableShared) guard(lock, rad::DeferLocking); + EXPECT_EQ(lock.count, 0); + } + + EXPECT_EQ(lock.count, 0); +} + +TEST(LocksTest, RelockableSharedLock) +{ + TestLock lock; + + DEDUCED(rad::RelockableShared) guard(lock, rad::DeferLocking); + guard.Lock(); + EXPECT_FALSE(lock.exclusive); + EXPECT_EQ(lock.count, 1); +} + +TEST(LocksTest, RelockableSharedUnlock) +{ + TestLock lock; + + DEDUCED(rad::RelockableShared) guard(lock); + guard.Unlock(); + EXPECT_EQ(lock.count, 0); +} diff --git a/test/test_Result.cpp b/test/test_Result.cpp new file mode 100644 index 0000000..54be86d --- /dev/null +++ b/test/test_Result.cpp @@ -0,0 +1,3189 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +// +// Disable nothrow assertions for unit testing +// +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 + +#include "gtest/gtest.h" + +#include "test/TestThrow.h" +#include "test/TestMove.h" + +#include "radiant/Result.h" +#include "radiant/Utility.h" + +#include +#include + +namespace testnoexcept +{ + +// +// ResultContainer<[throwing]> +// +using ThrowingResultContainer = rad::TypeWrapper; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~TypeWrapper())); +RAD_S_ASSERT(!noexcept(ThrowingResultContainer())); +RAD_S_ASSERT(!noexcept(ThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowingResultContainer(1))); +#if RAD_ENABLE_STD +RAD_S_ASSERT(!noexcept(ThrowingResultContainer({ 1 }))); +#endif +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Get())); + +// clang-format on + +// +// ResultContainer<[non-throwing]> +// + +using NonThrowingResultContainer = rad::TypeWrapper; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~TypeWrapper())); +RAD_S_ASSERT(noexcept(NonThrowingResultContainer())); +RAD_S_ASSERT(noexcept(NonThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NonThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NonThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NonThrowingResultContainer(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NonThrowingResultContainer(1))); +#if RAD_ENABLE_STD +RAD_S_ASSERT(noexcept(NonThrowingResultContainer({ 1 }))); +#endif +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Get())); + +// clang-format on + +// +// ResultStorage<[throwing], [throwing], true> +// + +using ThrowThrowResultStorageTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(ThrowThrowResultStorageTriv())); +RAD_S_ASSERT(noexcept(ThrowThrowResultStorageTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorage<[non-throwing], [non-throwing], true> +// + +using NoThrowNoThrowResultStorageTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv())); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorage<[throwing], [non-throwing], true> +// + +using ThrowNoThrowResultStorageTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv())); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorage<[non-throwing], [throwing], true> +// + +using NoThrowThrowResultStorageTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv())); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorage<[throwing], [throwing], false> +// + +using ThrowThrowResultStorageNonTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(ThrowThrowResultStorageNonTriv())); +RAD_S_ASSERT(noexcept(ThrowThrowResultStorageNonTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorage<[non-throwing], [non-throwing], false> +// + +using NoThrowNoThrowResultStorageNonTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv())); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorageNon<[throwing], [non-throwing], false> +// + +using ThrowNoThrowResultStorageNonTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv())); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// ResultStorage<[non-throwing], [throwing], false> +// + +using NoThrowThrowResultStorageNonTriv = + rad::detail::ResultStorage; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~ResultStorage())); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv())); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultEmptyTag))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultOkTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResultStorageNonTriv(rad::ResultErrTag, ::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Destruct())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, 1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Construct(rad::ResultOkTag, std::initializer_list()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, 1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Construct(rad::ResultErrTag, std::initializer_list()))); + +// clang-format on + +// +// Result<[throwing], [throwing]> +// + +using ThrowThrowResult = + rad::Result; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~Result())); +RAD_S_ASSERT(noexcept(ThrowThrowResult())); +RAD_S_ASSERT(noexcept(ThrowThrowResult(rad::ResultEmptyTag))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(rad::ResultOkTag, 1))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(rad::ResultErrTag, 1))); +#if RAD_ENABLE_STD +RAD_S_ASSERT(!noexcept(ThrowThrowResult(rad::ResultOkTag, { 1 }))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(rad::ResultErrTag, { 1 }))); +#endif +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator bool())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsOk())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsErr())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().State())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Ok())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Err())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Or(1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnErr(1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnOk(1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator->())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator*())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); + +// +// Result<[non-throwing], [throwing]> +// + +using NoThrowThrowResult = + rad::Result; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~Result())); +RAD_S_ASSERT(noexcept(NoThrowThrowResult())); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(rad::ResultEmptyTag))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(NoThrowThrowResult(rad::ResultOkTag, 1))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(rad::ResultErrTag, 1))); +#if RAD_ENABLE_STD +RAD_S_ASSERT(noexcept(NoThrowThrowResult(rad::ResultOkTag, { 1 }))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(rad::ResultErrTag, { 1 }))); +#endif +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(NoThrowThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator bool())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsOk())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsErr())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().State())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Ok())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Err())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Or(1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnErr(1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnOk(1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator->())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator*())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); + +// clang-format on + +// +// Result<[throwing], [non-throwing]> +// + +using ThrowNoThrowResult = + rad::Result; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~Result())); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult())); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(rad::ResultEmptyTag))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(rad::ResultOkTag, 1))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(rad::ResultErrTag, 1))); +#if RAD_ENABLE_STD +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(rad::ResultOkTag, { 1 }))); +RAD_S_ASSERT(noexcept(ThrowNoThrowResult(rad::ResultErrTag, { 1 }))); +#endif +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(ThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator bool())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsOk())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsErr())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().State())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Ok())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Err())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Or(1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnErr(1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnOk(1))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator->())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator*())); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); + +// +// Result<[non-throwing], [non-throwing]> +// + +using NoThrowNoThrowResult = + rad::Result; + +// clang-format off + +RAD_S_ASSERT(noexcept(::rad::DeclVal().~Result())); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult())); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(rad::ResultEmptyTag))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(rad::ResultOkTag, 1))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(rad::ResultErrTag, 1))); +#if RAD_ENABLE_STD +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(rad::ResultOkTag, { 1 }))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(rad::ResultErrTag, { 1 }))); +#endif +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(NoThrowNoThrowResult(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator bool())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsOk())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().IsErr())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().State())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Ok())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Err())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Or(1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnErr(1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnOk(1))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnErr(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().OnOk(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator->())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator*())); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().Assign(::rad::DeclVal&&>()))); +RAD_S_ASSERT(noexcept(::rad::DeclVal().operator=(::rad::DeclVal&&>()))); + +} // namespace testnoexcept + +typedef long MYSTATUS; +#define MYSTATUS_UNSUCCESSFUL ((MYSTATUS)0xC0000001L) +#define MYSTATUS_INVALID_ADDRESS ((MYSTATUS)0xC0000141L) +template +using ResultStatus = rad::Result; + +struct EmptyOk +{ +}; + +class TestObject +{ +public: + + ResultStatus DoIt(bool ActuallyDoIt) + { + if (ActuallyDoIt) + { + m_didIt = true; + return *this; + } + + m_didIt = false; + return MYSTATUS_UNSUCCESSFUL; + } + + ResultStatus DoEmpty(bool Error) + { + if (Error) + { + return MYSTATUS_UNSUCCESSFUL; + } + + return EmptyOk(); + } + + ResultStatus NestedEmpty(bool Error, bool ActuallyDoIt) + { + auto res = DoEmpty(Error); + if (!res.IsOk()) + { + return res.Err(); + } + + return DoIt(ActuallyDoIt); + } + + ResultStatus OnErr(bool ActuallyDoIt) + { + return DoIt(ActuallyDoIt).OnErr(MYSTATUS_INVALID_ADDRESS); + } + + ResultStatus OnOk(bool Error) + { + return DoEmpty(Error).OnOk(*this); + } + + bool m_didIt = false; +}; + +RAD_S_ASSERT(std::is_trivially_destructible>::value); +RAD_S_ASSERT(!std::is_trivially_destructible>::value); +RAD_S_ASSERT(std::is_trivially_destructible>::value); +RAD_S_ASSERT((!std::is_trivially_destructible< + rad::Result>::value)); +RAD_S_ASSERT(( + !std::is_trivially_destructible>::value)); + +#if RAD_CPP17 +RAD_S_ASSERT( + std::is_trivially_destructible>::value); +RAD_S_ASSERT((std::is_trivially_destructible< + rad::Result>::value)); +RAD_S_ASSERT((!std::is_trivially_destructible< + rad::Result>::value)); +RAD_S_ASSERT((!std::is_trivially_destructible< + rad::Result>::value)); +RAD_S_ASSERT((std::is_trivially_destructible< + rad::Result>::value)); +#endif + +static constexpr ResultStatus k_ResultNum = 0xc001; +static constexpr ResultStatus k_ResultNumErr = MYSTATUS_UNSUCCESSFUL; +static constexpr ResultStatus k_Error = MYSTATUS_UNSUCCESSFUL; + +#if RAD_CPP17 +static constexpr ResultStatus k_ResultStr{ "Yeet!" }; +static constexpr ResultStatus k_ResultStrErr = + MYSTATUS_UNSUCCESSFUL; +static constexpr rad::Result + k_ResultStrStr = rad::ResultOk("Ok!"); +static constexpr rad::Result + k_ResultStrStrErr = rad::ResultErr("Err!"); +static constexpr rad::Result k_ErrStr{ "Err!" }; +#endif + +TEST(ResultTests, TestObjectDidIt) +{ + TestObject object; + + auto res = object.DoIt(true); + EXPECT_TRUE(res.IsOk()); + EXPECT_EQ(&res.Ok(), &object); + EXPECT_TRUE(object.m_didIt); + + res = res->DoIt(false); + EXPECT_TRUE(res.IsErr()); + EXPECT_EQ(res.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(object.m_didIt); + + object.m_didIt = false; + + EXPECT_TRUE(object.DoEmpty(true).IsErr()); + EXPECT_TRUE(object.DoEmpty(false).IsOk()); + EXPECT_EQ(object.DoEmpty(true).Err(), MYSTATUS_UNSUCCESSFUL); + + res = object.NestedEmpty(true, false); + EXPECT_FALSE(object.m_didIt); + EXPECT_TRUE(res.IsErr()); + + res = object.NestedEmpty(true, true); + EXPECT_FALSE(object.m_didIt); + EXPECT_TRUE(res.IsErr()); + + res = object.NestedEmpty(false, false); + EXPECT_FALSE(object.m_didIt); + EXPECT_TRUE(res.IsErr()); + + res = object.NestedEmpty(false, true); + EXPECT_TRUE(object.m_didIt); + EXPECT_TRUE(res.IsOk()); + + object.m_didIt = false; + + res = object.OnErr(false); + EXPECT_FALSE(object.m_didIt); + EXPECT_TRUE(res.IsErr()); + EXPECT_EQ(res.Err(), MYSTATUS_INVALID_ADDRESS); + + res = object.OnErr(true); + EXPECT_TRUE(object.m_didIt); + EXPECT_TRUE(res.IsOk()); + EXPECT_EQ(&res.Ok(), &object); + + res = object.OnOk(true); + EXPECT_TRUE(res.IsErr()); + EXPECT_EQ(res.Err(), MYSTATUS_UNSUCCESSFUL); + + res = object.OnOk(false); + EXPECT_TRUE(res.IsOk()); + EXPECT_EQ(&res.Ok(), &object); +} + +TEST(ResultTests, ConstexprErrStatus) +{ + EXPECT_EQ(k_Error, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstexprOkNumeric) +{ + EXPECT_FALSE(k_ResultNum.IsEmpty()); + EXPECT_FALSE(k_ResultNum.IsErr()); + EXPECT_TRUE(k_ResultNum.IsOk()); + EXPECT_EQ(k_ResultNum, 0xc001); + auto resNum = k_ResultNum; + EXPECT_EQ(resNum, k_ResultNum); +} + +TEST(ResultTests, ConstexprErrNumeric) +{ + EXPECT_FALSE(k_ResultNumErr.IsEmpty()); + EXPECT_TRUE(k_ResultNumErr.IsErr()); + EXPECT_FALSE(k_ResultNumErr.IsOk()); + EXPECT_EQ(k_ResultNumErr, MYSTATUS_UNSUCCESSFUL); + auto resNum = k_ResultNumErr; + EXPECT_EQ(resNum, k_ResultNumErr); + EXPECT_NE(k_ResultNum, k_ResultNumErr); +} + +#if RAD_CPP17 + +TEST(ResultTests, ConstexprErrString) +{ + EXPECT_EQ(k_ErrStr, std::string_view("Err!")); +} + +TEST(ResultTests, ConstexprOkString) +{ + EXPECT_FALSE(k_ResultStr.IsEmpty()); + EXPECT_FALSE(k_ResultStr.IsErr()); + EXPECT_TRUE(k_ResultStr.IsOk()); + EXPECT_EQ(k_ResultStr, std::string_view("Yeet!")); + auto resStr = k_ResultStr; + EXPECT_EQ(resStr, k_ResultStr); + ResultStatus strAllocStr = resStr; + EXPECT_EQ(strAllocStr, resStr); +} + +TEST(ResultTests, ConstexprOkStringErrStatus) +{ + EXPECT_FALSE(k_ResultStrErr.IsEmpty()); + EXPECT_TRUE(k_ResultStrErr.IsErr()); + EXPECT_FALSE(k_ResultStrErr.IsOk()); + EXPECT_EQ(k_ResultStrErr, MYSTATUS_UNSUCCESSFUL); + auto resStr = k_ResultStrErr; + EXPECT_EQ(resStr, k_ResultStrErr); + EXPECT_NE(k_ResultStr, k_ResultStrErr); +} + +TEST(ResultTests, ConstexprOkStringErr) +{ + EXPECT_FALSE(k_ResultStrStr.IsEmpty()); + EXPECT_FALSE(k_ResultStrStr.IsErr()); + EXPECT_TRUE(k_ResultStrStr.IsOk()); + EXPECT_EQ(k_ResultStrStr, rad::ResultOk("Ok!")); + auto resStrStr = k_ResultStrStr; + EXPECT_EQ(resStrStr, k_ResultStrStr); + ResultStatus strAllocStrStr = + rad::ResultOk(resStrStr.Ok()); + EXPECT_EQ(strAllocStrStr.Ok(), resStrStr.Ok()); +} + +TEST(ResultTests, ConstexprOkStringErrString) +{ + EXPECT_FALSE(k_ResultStrStrErr.IsEmpty()); + EXPECT_TRUE(k_ResultStrStrErr.IsErr()); + EXPECT_FALSE(k_ResultStrStrErr.IsOk()); + EXPECT_EQ(k_ResultStrStrErr, rad::ResultErr("Err!")); + auto resStrStr = k_ResultStrStrErr; + EXPECT_EQ(resStrStr, k_ResultStrStrErr); + EXPECT_NE(k_ResultStrStr, k_ResultStrStrErr); + ResultStatus strAllocStrStrErr = + rad::ResultOk(resStrStr.Err()); + EXPECT_EQ(strAllocStrStrErr.Ok(), resStrStr.Err()); +} + +#endif + +static const ResultStatus g_ResultEmpty{}; +static const ResultStatus g_ResultNum{ 0xc001 }; +static const ResultStatus g_ResultNumErr{ MYSTATUS_UNSUCCESSFUL }; +static const ResultStatus g_ResultStr{ "Yeet!" }; +static const ResultStatus g_ResultStrErr{ MYSTATUS_UNSUCCESSFUL }; +static const rad::Result g_ResultStrStr{ + rad::ResultOk("Ok!") +}; +static const rad::Result g_ResultStrStrErr{ + rad::ResultErr("Err!") +}; + +TEST(ResultTests, GlobalEmpty) +{ + EXPECT_TRUE(g_ResultEmpty.IsEmpty()); + EXPECT_FALSE(g_ResultEmpty.IsErr()); + EXPECT_FALSE(g_ResultEmpty.IsOk()); + auto resEmpty = g_ResultEmpty; + EXPECT_EQ(resEmpty, g_ResultEmpty); +} + +TEST(ResultTests, GlobalOkNumeric) +{ + EXPECT_FALSE(g_ResultNum.IsEmpty()); + EXPECT_FALSE(g_ResultNum.IsErr()); + EXPECT_TRUE(g_ResultNum.IsOk()); + EXPECT_EQ(g_ResultNum, 0xc001); + auto resNum = g_ResultNum; + EXPECT_EQ(resNum, g_ResultNum); + EXPECT_NE(resNum, g_ResultEmpty); + resNum = g_ResultEmpty; + EXPECT_TRUE(resNum.IsEmpty()); +} + +TEST(ResultTests, GlobalErrNumeric) +{ + EXPECT_FALSE(g_ResultNumErr.IsEmpty()); + EXPECT_TRUE(g_ResultNumErr.IsErr()); + EXPECT_FALSE(g_ResultNumErr.IsOk()); + EXPECT_EQ(g_ResultNumErr, MYSTATUS_UNSUCCESSFUL); + auto resNum = g_ResultNumErr; + EXPECT_EQ(resNum, g_ResultNumErr); + EXPECT_NE(resNum, g_ResultEmpty); + EXPECT_NE(g_ResultNum, g_ResultNumErr); +} + +TEST(ResultTests, GlobalOkString) +{ + EXPECT_FALSE(g_ResultStr.IsEmpty()); + EXPECT_FALSE(g_ResultStr.IsErr()); + EXPECT_TRUE(g_ResultStr.IsOk()); + EXPECT_EQ(g_ResultStr, std::string("Yeet!")); + auto resStr = g_ResultStr; + EXPECT_EQ(resStr, g_ResultStr); + ResultStatus strAllocStr = resStr; + EXPECT_EQ(strAllocStr, resStr); + ResultStatus empty; + EXPECT_TRUE(empty.IsEmpty()); + ResultStatus second = empty; + EXPECT_TRUE(second.IsEmpty()); +} + +TEST(ResultTests, GlobalErrString) +{ + EXPECT_FALSE(g_ResultStrErr.IsEmpty()); + EXPECT_TRUE(g_ResultStrErr.IsErr()); + EXPECT_FALSE(g_ResultStrErr.IsOk()); + EXPECT_EQ(g_ResultStrErr, MYSTATUS_UNSUCCESSFUL); + auto resStr = g_ResultStrErr; + EXPECT_EQ(resStr, g_ResultStrErr); + EXPECT_NE(g_ResultStr, g_ResultStrErr); +} + +TEST(ResultTests, GlobalErrStringOkString) +{ + EXPECT_FALSE(g_ResultStrStr.IsEmpty()); + EXPECT_FALSE(g_ResultStrStr.IsErr()); + EXPECT_TRUE(g_ResultStrStr.IsOk()); + EXPECT_EQ(g_ResultStrStr, rad::ResultOk("Ok!")); + auto resStrStr = g_ResultStrStr; + EXPECT_EQ(resStrStr, g_ResultStrStr); + ResultStatus strAllocStrStr = + rad::ResultOk(resStrStr.Ok()); + EXPECT_EQ(strAllocStrStr.Ok(), resStrStr.Ok()); +} + +TEST(ResultTests, GlobalErrStringErrString) +{ + EXPECT_FALSE(g_ResultStrStrErr.IsEmpty()); + EXPECT_TRUE(g_ResultStrStrErr.IsErr()); + EXPECT_FALSE(g_ResultStrStrErr.IsOk()); + EXPECT_EQ(g_ResultStrStrErr, rad::ResultErr("Err!")); + auto resStrStr = g_ResultStrStrErr; + ResultStatus strAllocStrStr = + rad::ResultOk(resStrStr.Err()); + EXPECT_EQ(resStrStr, g_ResultStrStrErr); + EXPECT_NE(g_ResultStrStr, g_ResultStrStrErr); + ResultStatus strAllocStrStrNonErr = + rad::ResultOk(resStrStr.Err()); + EXPECT_EQ(strAllocStrStrNonErr.Ok(), resStrStr.Err()); + EXPECT_EQ(strAllocStrStr, strAllocStrStrNonErr); +} + +TEST(ResultTests, AssignCopyEmptyOk) +{ + ResultStatus value(rad::ResultEmptyTag); + ResultStatus other = 1; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(other).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignCopyEmptyErr) +{ + ResultStatus value; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(other).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyEmptyEmpty) +{ + ResultStatus value; + ResultStatus other; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(other).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignCopyOkOk) +{ + ResultStatus value = 1337; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(other).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOkErr) +{ + ResultStatus value = 1337; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(other).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOkEmpty) +{ + ResultStatus value = 1337; + ResultStatus other; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(other).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignCopyErrOk) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(other).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignCopyErrErr) +{ + ResultStatus value = MYSTATUS_INVALID_ADDRESS; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(other).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyErrEmpty) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(other).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignCopyOpEmptyOk) +{ + ResultStatus value; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpEmptyErr) +{ + ResultStatus value; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpEmptyEmpty) +{ + ResultStatus value; + ResultStatus other; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignCopyOpOkOk) +{ + ResultStatus value = 1337; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpOkErr) +{ + ResultStatus value = 1337; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpOkEmpty) +{ + ResultStatus value = 1337; + ResultStatus other; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignCopyOpErrOk) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpErrErr) +{ + ResultStatus value = MYSTATUS_INVALID_ADDRESS; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpErrEmpty) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignConstCopyEmptyOk) +{ + ResultStatus value; + const ResultStatus other = 1; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(other).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignConstCopyEmptyErr) +{ + ResultStatus value; + const ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(other).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignConstCopyEmptyEmpty) +{ + ResultStatus value; + const ResultStatus other; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(other).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignConstCopyOkOk) +{ + ResultStatus value = 1337; + const ResultStatus other = 1; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(other).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignConstCopyOkErr) +{ + ResultStatus value = 1337; + const ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(other).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignConstCopyOkEmpty) +{ + ResultStatus value = 1337; + const ResultStatus other; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(other).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignConstCopyErrOk) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const ResultStatus other = 1; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(other).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignConstCopyErrErr) +{ + ResultStatus value = MYSTATUS_INVALID_ADDRESS; + const ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(other).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignConstCopyErrEmpty) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const ResultStatus other; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(other).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignConstCopyOpEmptyOk) +{ + ResultStatus value; + const ResultStatus other = 1; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignConstCopyOpEmptyErr) +{ + ResultStatus value; + const ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignConstCopyOpEmptyEmpty) +{ + ResultStatus value; + const ResultStatus other; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignConstCopyOpOkOk) +{ + ResultStatus value = 1337; + const ResultStatus other = 1; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignConstCopyOpOkErr) +{ + ResultStatus value = 1337; + const ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignConstCopyOpOkEmpty) +{ + ResultStatus value = 1337; + const ResultStatus other; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignConstCopyOpErrOk) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const ResultStatus other = 1; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_EQ(other.Ok(), 1); +} + +TEST(ResultTests, AssignConstCopyOpErrErr) +{ + ResultStatus value = MYSTATUS_INVALID_ADDRESS; + const ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(other.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignConstCopyOpErrEmpty) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const ResultStatus other; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveEmptyOk) +{ + ResultStatus value; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveEmptyErr) +{ + ResultStatus value; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveEmptyEmpty) +{ + ResultStatus value; + ResultStatus other; + + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOkOk) +{ + ResultStatus value = 1337; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOkErr) +{ + ResultStatus value = 1337; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOkEmpty) +{ + ResultStatus value = 1337; + ResultStatus other; + + EXPECT_TRUE(value.IsOk()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveErrOk) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveErrErr) +{ + ResultStatus value = MYSTATUS_INVALID_ADDRESS; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveErrEmpty) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other; + + EXPECT_TRUE(value.IsErr()); + EXPECT_TRUE(value.Assign(rad::Move(other)).IsEmpty()); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpEmptyOk) +{ + ResultStatus value; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsEmpty()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpEmptyErr) +{ + ResultStatus value; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpEmptyEmpty) +{ + ResultStatus value; + ResultStatus other; + + EXPECT_TRUE(value.IsEmpty()); + value = rad::Move(other); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpOkOk) +{ + ResultStatus value = 1337; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpOkErr) +{ + ResultStatus value = 1337; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpOkEmpty) +{ + ResultStatus value = 1337; + ResultStatus other; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpErrOk) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other = 1; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpErrErr) +{ + ResultStatus value = MYSTATUS_INVALID_ADDRESS; + ResultStatus other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignMoveOpErrEmpty) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + ResultStatus other; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsEmpty()); + EXPECT_TRUE(other.IsEmpty()); +} + +TEST(ResultTests, AssignCopyResOkErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsErr()); + value.Assign(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyResOkOk) +{ + ResultStatus value = 0; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsOk()); + value.Assign(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpResOkErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpResOkOk) +{ + ResultStatus value = 0; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignMoveResOkErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsErr()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignMoveResOkOk) +{ + ResultStatus value = 0; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsOk()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignMoveOpResOkErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignMoveOpResOkOk) +{ + ResultStatus value = 0; + rad::ResultOk other = 1; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyResErrErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsErr()); + value.Assign(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignCopyResErrOk) +{ + ResultStatus value = 0; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsOk()); + value.Assign(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignCopyOpResErrErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignCopyOpResErrOk) +{ + ResultStatus value = 0; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignMoveResErrErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsErr()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignMoveResErrOk) +{ + ResultStatus value = 0; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsOk()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignMoveOpResErrErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignMoveOpResErrOk) +{ + ResultStatus value = 0; + rad::ResultErr other = MYSTATUS_INVALID_ADDRESS; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_INVALID_ADDRESS); +} + +TEST(ResultTests, AssignCopyValueEmpty) +{ + ResultStatus value; + + EXPECT_TRUE(value.IsEmpty()); + value.Assign(1); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyValueOk) +{ + ResultStatus value = 0; + + EXPECT_TRUE(value.IsOk()); + value.Assign(1); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value.Assign(1); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyConstValueEmpty) +{ + ResultStatus value; + const int other = 1; + + EXPECT_TRUE(value.IsEmpty()); + value.Assign(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyConstValueOk) +{ + ResultStatus value = 0; + const int other = 1; + + EXPECT_TRUE(value.IsOk()); + value.Assign(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyConstValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const int other = 1; + + EXPECT_TRUE(value.IsErr()); + value.Assign(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignMoveValueEmpty) +{ + ResultStatus value; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsEmpty()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveValueOk) +{ + ResultStatus value("derp"); + std::string other = "yeet"; + + EXPECT_TRUE(value.IsOk()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsErr()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignCopyOpValueEmpty) +{ + ResultStatus value; + + EXPECT_TRUE(value.IsEmpty()); + value = 1; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpValueOk) +{ + ResultStatus value = 0; + + EXPECT_TRUE(value.IsOk()); + value = 1; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value = 1; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpConstValueEmpty) +{ + ResultStatus value; + const int other = 1; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpConstValueOk) +{ + ResultStatus value = 0; + const int other = 1; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignCopyOpConstValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const int other = 1; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, AssignMoveOpValueEmpty) +{ + ResultStatus value; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsEmpty()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveOpValueOk) +{ + ResultStatus value("derp"); + std::string other = "yeet"; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveOpValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignCopyErrValueEmpty) +{ + ResultStatus value; + + EXPECT_TRUE(value.IsEmpty()); + value.Assign(MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyErrValueOk) +{ + ResultStatus value = 0; + + EXPECT_TRUE(value.IsOk()); + value.Assign(MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyErrValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value.Assign(MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyConsErrValueEmpty) +{ + ResultStatus value; + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + value.Assign(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyConstErrValueOk) +{ + ResultStatus value = 0; + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + value.Assign(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyConstErrValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value.Assign(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignMoveErrValueEmpty) +{ + rad::Result value; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsEmpty()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveErrValueOk) +{ + rad::Result value = 1; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsOk()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveErrValueErr) +{ + rad::Result value("derp"); + std::string other = "yeet"; + + EXPECT_TRUE(value.IsErr()); + value.Assign(rad::Move(other)); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignCopyOpErrValueEmpty) +{ + ResultStatus value; + + EXPECT_TRUE(value.IsEmpty()); + value = MYSTATUS_UNSUCCESSFUL; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpErrValueOk) +{ + ResultStatus value = 0; + + EXPECT_TRUE(value.IsOk()); + value = MYSTATUS_UNSUCCESSFUL; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpErrValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value = MYSTATUS_UNSUCCESSFUL; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpConstErrValueEmpty) +{ + ResultStatus value; + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsEmpty()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpConstErrValueOk) +{ + ResultStatus value = 0; + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsOk()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignCopyOpConstErrValueErr) +{ + ResultStatus value = MYSTATUS_UNSUCCESSFUL; + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + + EXPECT_TRUE(value.IsErr()); + value = other; + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignMoveOpErrValueEmpty) +{ + rad::Result value; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsEmpty()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveOpErrValueOk) +{ + rad::Result value = 1; + std::string other = "yeet"; + + EXPECT_TRUE(value.IsOk()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, AssignMoveOpErrValueErr) +{ + rad::Result value("derp"); + std::string other = "yeet"; + + EXPECT_TRUE(value.IsErr()); + value = rad::Move(other); + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), "yeet"); + EXPECT_TRUE(other.empty()); +} + +TEST(ResultTests, ConstructCopyOk) +{ + int other = 1; + ResultStatus value(other); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, ConstructConstCopyOk) +{ + const int other = 1; + ResultStatus value(other); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, ConstructCopyErr) +{ + MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + ResultStatus value(other); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstructConstCopyErr) +{ + const MYSTATUS other = MYSTATUS_UNSUCCESSFUL; + ResultStatus value(other); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstructCopyResOk) +{ + auto other = rad::ResultOk(1); + ResultStatus value(other); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, ConstructConstCopyResOk) +{ + const auto other = rad::ResultOk(1); + ResultStatus value(other); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok(), 1); +} + +TEST(ResultTests, ConstructCopyResErr) +{ + auto other = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + ResultStatus value(other); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstructConstCopyResErr) +{ + const auto other = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + ResultStatus value(other); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstructEmplaceOk) +{ + rad::Result, std::pair> value( + rad::ResultOkTag, 1, 2); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().first, 1); + EXPECT_EQ(value.Ok().second, 2); +} + +TEST(ResultTests, ConstructEmplaceErr) +{ + rad::Result, std::pair> value( + rad::ResultErrTag, MYSTATUS_UNSUCCESSFUL, MYSTATUS_INVALID_ADDRESS); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().first, MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(value.Err().second, MYSTATUS_INVALID_ADDRESS); +} + +#if RAD_ENABLE_STD + +TEST(ResultTests, ConstructListOk) +{ + rad::Result, std::vector> value(rad::ResultOkTag, + { 1, 2 }); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().front(), 1); + EXPECT_EQ(value.Ok().back(), 2); +} + +TEST(ResultTests, ConstructListErr) +{ + rad::Result, std::vector> value( + rad::ResultErrTag, { MYSTATUS_UNSUCCESSFUL, MYSTATUS_INVALID_ADDRESS }); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().front(), MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(value.Err().back(), MYSTATUS_INVALID_ADDRESS); +} + +#endif // RAD_ENABLE_STD + +TEST(ResultTests, CopyAssignErr) +{ + auto status = MYSTATUS_UNSUCCESSFUL; + ResultStatus copy = status; + EXPECT_FALSE(copy.IsOk()); + EXPECT_EQ(copy, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, CopyConstructErr) +{ + auto status = MYSTATUS_UNSUCCESSFUL; + ResultStatus copy2(status); + EXPECT_FALSE(copy2.IsOk()); + EXPECT_EQ(copy2, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, CopyAssignInitErr) +{ + ResultStatus temp = MYSTATUS_UNSUCCESSFUL; + EXPECT_FALSE(temp.IsOk()); + EXPECT_EQ(temp, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, CopyConstructRValErr) +{ + ResultStatus temp2(MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(temp2.IsOk()); + EXPECT_EQ(temp2, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, CopyConstructErrToErr) +{ + ResultStatus temp = MYSTATUS_UNSUCCESSFUL; + EXPECT_FALSE(temp.IsOk()); + EXPECT_EQ(temp, MYSTATUS_UNSUCCESSFUL); + + ResultStatus temp2(MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(temp2.IsOk()); + EXPECT_EQ(temp2, MYSTATUS_UNSUCCESSFUL); + + ResultStatus other(temp); + EXPECT_FALSE(other.IsOk()); + EXPECT_EQ(other, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstructNonTrivialCopy) +{ + std::vector vec({ 1, 2, 3 }); + ResultStatus> value(vec); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().size(), 3u); + EXPECT_EQ(value.Ok().front(), 1); + EXPECT_EQ(value.Ok().back(), 3); + EXPECT_EQ(vec, value.Ok()); +} + +TEST(ResultTests, ConstructNonTrivialConstCopy) +{ + const std::vector vec({ 1, 2, 3 }); + ResultStatus> value(vec); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().size(), 3u); + EXPECT_EQ(value.Ok().front(), 1); + EXPECT_EQ(value.Ok().back(), 3); + EXPECT_EQ(vec, value.Ok()); +} + +TEST(ResultTests, ConstructNonTrivialCopyRes) +{ + rad::ResultOk> vec = std::vector({ 1, 2, 3 }); + ResultStatus> value(vec); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().size(), 3u); + EXPECT_EQ(value.Ok().front(), 1); + EXPECT_EQ(value.Ok().back(), 3); + EXPECT_EQ(vec.Get(), value.Ok()); +} + +TEST(ResultTests, ConstructNonTrivialConstCopyRes) +{ + const rad::ResultOk> vec = std::vector({ 1, 2, 3 }); + ResultStatus> value(vec); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().size(), 3u); + EXPECT_EQ(value.Ok().front(), 1); + EXPECT_EQ(value.Ok().back(), 3); + EXPECT_EQ(vec.Get(), value.Ok()); +} + +TEST(ResultTests, ConstructNonTrivialCopyErr) +{ + std::vector vec({ 1, 2, 3 }); + rad::Result> value(vec); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().size(), 3u); + EXPECT_EQ(value.Err().front(), 1); + EXPECT_EQ(value.Err().back(), 3); + EXPECT_EQ(vec, value.Err()); +} + +TEST(ResultTests, ConstructNonTrivialConstCopyErr) +{ + const std::vector vec({ 1, 2, 3 }); + rad::Result> value(vec); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().size(), 3u); + EXPECT_EQ(value.Err().front(), 1); + EXPECT_EQ(value.Err().back(), 3); + EXPECT_EQ(vec, value.Err()); +} + +TEST(ResultTests, ConstructNonTrivialCopyErrRes) +{ + rad::ResultErr> vec = std::vector({ 1, 2, 3 }); + rad::Result> value(vec); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().size(), 3u); + EXPECT_EQ(value.Err().front(), 1); + EXPECT_EQ(value.Err().back(), 3); + EXPECT_EQ(vec.Get(), value.Err()); +} + +TEST(ResultTests, ConstructNonTrivialConstCopyErrRes) +{ + const rad::ResultErr> vec = std::vector({ 1, 2, 3 }); + rad::Result> value(vec); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().size(), 3u); + EXPECT_EQ(value.Err().front(), 1); + EXPECT_EQ(value.Err().back(), 3); + EXPECT_EQ(vec.Get(), value.Err()); +} + +TEST(ResultTests, EmplaceNonTrivialOk) +{ + rad::Result, int> value(rad::ResultOkTag, 7u, 1337); + + EXPECT_TRUE(value.IsOk()); + EXPECT_EQ(value.Ok().size(), 7u); + EXPECT_EQ(value.Ok().front(), 1337); + EXPECT_EQ(value.Ok().back(), 1337); +} + +TEST(ResultTests, EmplaceNonTrivialErr) +{ + rad::Result> value(rad::ResultErrTag, 7u, 1337); + + EXPECT_TRUE(value.IsErr()); + EXPECT_EQ(value.Err().size(), 7u); + EXPECT_EQ(value.Err().front(), 1337); + EXPECT_EQ(value.Err().back(), 1337); +} + +TEST(ResultTests, MoveConstructErr) +{ + ResultStatus moved(ResultStatus(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(moved.IsOk()); + EXPECT_EQ(moved, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, MoveAssignRValErr) +{ + ResultStatus moved2 = ResultStatus(MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(moved2.IsOk()); + EXPECT_EQ(moved2, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, AssignRValOkOk) +{ + auto value = 0xc001; + ResultStatus copy = rad::ResultOk(value); + EXPECT_TRUE(copy.IsOk()); + EXPECT_EQ(copy, 0xc001); +} + +TEST(ResultTests, AssignRValOk) +{ + ResultStatus temp = 0xc001; + EXPECT_TRUE(temp.IsOk()); + EXPECT_EQ(temp, 0xc001); +} + +TEST(ResultTests, CopyConstructOk) +{ + ResultStatus temp = 0xc001; + EXPECT_TRUE(temp.IsOk()); + EXPECT_EQ(temp, 0xc001); + + ResultStatus other(temp); + EXPECT_TRUE(other.IsOk()); + EXPECT_EQ(other, 0xc001); +} + +TEST(ResultTests, MoveConstructOk) +{ + ResultStatus moved(ResultStatus(rad::ResultOk(0xc001))); + EXPECT_TRUE(moved.IsOk()); + EXPECT_EQ(moved, 0xc001); +} + +TEST(ResultTests, AssignRefOk) +{ + int value = 0xc001; + + ResultStatus test = value; + EXPECT_TRUE(test.IsOk()); + EXPECT_EQ(&test.Ok(), &value); + EXPECT_EQ(test, value); +} + +TEST(ResultTests, ConstructRefOkRefOk) +{ + int value = 0xc001; + ResultStatus test = value; + + ResultStatus other(test); + EXPECT_TRUE(other.IsOk()); + EXPECT_EQ(&other.Ok(), &value); + EXPECT_EQ(other, value); +} + +TEST(ResultTests, ConstructRefError) +{ + ResultStatus test(rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(test.IsOk()); + EXPECT_EQ(test, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, ConstructRefErrorRefError) +{ + ResultStatus test(rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(test.IsOk()); + EXPECT_EQ(test, MYSTATUS_UNSUCCESSFUL); + + ResultStatus other(test); + EXPECT_FALSE(other.IsOk()); + EXPECT_EQ(other, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, MoveRefErrRVal) +{ + ResultStatus moved( + ResultStatus(rad::ResultErr(MYSTATUS_UNSUCCESSFUL))); + EXPECT_FALSE(moved.IsOk()); + EXPECT_EQ(moved, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, CopyAssignOkContainer) +{ + auto value = std::vector({ 1, 2, 3 }); + + ResultStatus> copy = + rad::ResultOk>(value); + EXPECT_TRUE(copy.IsOk()); + EXPECT_EQ(copy, value); +} + +#if RAD_ENABLE_STD + +TEST(ResultTests, EmplaceConstructOkContainer) +{ + auto value = std::vector({ 1, 2, 3 }); + ResultStatus> temp(rad::ResultOkTag, { 1, 2, 3 }); + EXPECT_TRUE(temp.IsOk()); + EXPECT_EQ(temp, value); +} + +TEST(ResultTests, ConstructFromRValueEmplacedOkContainer) +{ + auto value = std::vector({ 1, 2, 3 }); + ResultStatus> moved( + ResultStatus>(rad::ResultOkTag, { 1, 2, 3 })); + EXPECT_TRUE(moved.IsOk()); + EXPECT_EQ(moved, value); +} + +#endif // RAD_ENABLE_STD + +TEST(ResultTests, RefOkContainer) +{ + auto value = std::vector({ 1, 2, 3 }); + ResultStatus&> ref = + rad::ResultOk&>(value); + EXPECT_TRUE(ref.IsOk()); + EXPECT_EQ(&ref.Ok(), &value); + EXPECT_EQ(ref, value); +} + +TEST(ResultTests, RefOkContainerMultiple) +{ + auto value = std::vector({ 1, 2, 3 }); + ResultStatus&> ref = + rad::ResultOk&>(value); + EXPECT_TRUE(ref.IsOk()); + EXPECT_EQ(&ref.Ok(), &value); + EXPECT_EQ(ref, value); + + ResultStatus&> ref2 = + rad::ResultOk&>(value); + EXPECT_TRUE(ref2.IsOk()); + EXPECT_EQ(&ref2.Ok(), &value); + EXPECT_EQ(ref2, value); + + ResultStatus&> ref3(ref); + EXPECT_TRUE(ref3.IsOk()); + EXPECT_EQ(&ref3.Ok(), &value); + EXPECT_EQ(ref3, value); +} + +TEST(ResultTests, ConstructErrContainer) +{ + ResultStatus> test( + rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(test.IsOk()); + EXPECT_EQ(test.Err(), MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, EmplaceOkString) +{ + ResultStatus test(rad::ResultOkTag, "Hello"); + EXPECT_TRUE(test.IsOk()); + EXPECT_EQ(test, rad::ResultOk("Hello")); +} + +TEST(ResultTests, RefOkString) +{ + ResultStatus test(rad::ResultOkTag, "Hello"); + ResultStatus ref(test); + EXPECT_TRUE(ref.IsOk()); + EXPECT_EQ(&ref.Ok(), &test.Ok()); + EXPECT_EQ(ref, test); +} + +TEST(ResultTests, RefOkStringMultiple) +{ + ResultStatus test(rad::ResultOkTag, "Hello"); + ResultStatus ref(test); + EXPECT_TRUE(ref.IsOk()); + EXPECT_EQ(&ref.Ok(), &test.Ok()); + EXPECT_EQ(ref, test); + + ResultStatus ref2(test); + EXPECT_TRUE(ref2.IsOk()); + EXPECT_EQ(&ref2.Ok(), &test.Ok()); + EXPECT_EQ(ref2, test); + + ResultStatus ref3(ref); + EXPECT_TRUE(ref3.IsOk()); + EXPECT_EQ(&ref3.Ok(), &test.Ok()); + EXPECT_EQ(ref3, test); +} + +TEST(ResultTests, ConstructEmptyResultThenAssign) +{ + ResultStatus test; + EXPECT_TRUE(test.IsEmpty()); + EXPECT_FALSE(test.IsErr()); + EXPECT_FALSE(test.IsOk()); + + test = rad::Add2Ptr(nullptr, 0x123); + EXPECT_TRUE(test.IsOk()); + EXPECT_EQ(test, rad::Add2Ptr(nullptr, 0x123)); +} + +TEST(ResultTests, ConstructErrPointer) +{ + ResultStatus test2(rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(test2.IsOk()); + EXPECT_EQ(test2, MYSTATUS_UNSUCCESSFUL); +} + +TEST(ResultTests, SameErrAndOkTypeConstruct) +{ + rad::Result err = + rad::ResultErr("Errant"); + rad::Result ok = + rad::ResultOk("Valid"); + EXPECT_EQ(err, rad::ResultErr("Errant")); + EXPECT_EQ(ok, rad::ResultOk("Valid")); + EXPECT_NE(err, rad::ResultOk("Errant")); + EXPECT_NE(ok, rad::ResultErr("Valid")); + EXPECT_TRUE(err.IsErr()); + EXPECT_FALSE(err.IsOk()); + EXPECT_FALSE(err.IsEmpty()); + EXPECT_FALSE(ok.IsErr()); + EXPECT_TRUE(ok.IsOk()); + EXPECT_FALSE(ok.IsEmpty()); +} + +struct ConvA +{ + int value; +}; + +struct ConvB +{ + explicit ConvB(const ConvA& a) : value(a.value) {} + explicit ConvB(ConvA&& a) : value(rad::Forward(a.value)) {} + int value; +}; + +TEST(ResultTests, ConstructConvResult) +{ + ConvA a; + a.value = 123; + + rad::Result value(a); + + rad::Result one(static_cast(value)); + EXPECT_EQ(one.Ok().value, 123); + + rad::Result two(rad::Move(value)); + EXPECT_EQ(two.Ok().value, 123); +} + +TEST(ResultTests, ConstructMoveResult) +{ + radtest::MoveTester::ResetCounts(); + radtest::MoveTester v; + v.value = 123; + rad::Result one(v); + rad::Result two(rad::Move(one)); + EXPECT_EQ(radtest::MoveTester::MoveCount(), 1u); +} + +TEST(ResultTests, AssignMoveResult) +{ + radtest::MoveTester::ResetCounts(); + radtest::MoveTester v; + v.value = 123; + rad::Result one(v); + rad::Result two(v); + two = rad::Move(one); + EXPECT_EQ(radtest::MoveTester::MoveCount(), 1u); +} + +TEST(ResultTests, AccessOkByDeref) +{ + ResultStatus test(rad::ResultOk(123)); + EXPECT_EQ(*test, 123); +} + +TEST(ResultTests, AccessOkRefByDeref) +{ + ResultStatus test(rad::ResultOk(123)); + EXPECT_EQ(*test, 123); + + ResultStatus ref(test); + EXPECT_EQ(&(*ref), &(*test)); + EXPECT_EQ(*ref, *test); + + *ref = 456; + EXPECT_EQ(*ref, 456); + EXPECT_EQ(*test, 456); +} + +TEST(ResultTests, AccessOkRefByDerefMultiple) +{ + ResultStatus test(rad::ResultOk(123)); + EXPECT_EQ(*test, 123); + + ResultStatus ref(test); + EXPECT_EQ(&(*ref), &(*test)); + EXPECT_EQ(*ref, *test); + + ResultStatus ref2(test); + EXPECT_EQ(&(*ref2), &(*test)); + EXPECT_EQ(*ref2, *test); + + ResultStatus ref3(ref); + EXPECT_EQ(&(*ref3), &(*test)); + EXPECT_EQ(*ref3, *test); + + ResultStatus ref4(ref3); + EXPECT_EQ(&(*ref4), &(*test)); + EXPECT_EQ(*ref4, *test); +} + +TEST(ResultTests, AccessOkContainerByPointerOperator) +{ + auto value = std::vector({ 1, 2, 3 }); + + ResultStatus> test = + rad::ResultOk>(value); + EXPECT_EQ(*test, value); + EXPECT_EQ(test->front(), value.front()); + + const auto& test2 = test; + EXPECT_EQ(test2->front(), value.front()); +} + +TEST(ResultTests, AccessOkContainerByPointerOperatorThroughRef) +{ + auto value = std::vector({ 1, 2, 3 }); + + ResultStatus> test = + rad::ResultOk>(value); + EXPECT_EQ(*test, value); + EXPECT_EQ(test->front(), value.front()); + + ResultStatus&> ref(test); + EXPECT_EQ(&(*ref), &(*test)); + EXPECT_EQ(*ref, *test); + EXPECT_EQ(ref->front(), test->front()); +} + +TEST(ResultTests, AccessOkContainerByPointerOperatorThroughRefMultiple) +{ + auto value = std::vector({ 1, 2, 3 }); + + ResultStatus> test = + rad::ResultOk>(value); + EXPECT_EQ(*test, value); + EXPECT_EQ(test->front(), value.front()); + + ResultStatus&> ref(test); + EXPECT_EQ(&(*ref), &(*test)); + EXPECT_EQ(*ref, *test); + EXPECT_EQ(ref->front(), test->front()); + + ResultStatus&> ref2(test); + EXPECT_EQ(&(*ref2), &(*test)); + EXPECT_EQ(*ref2, *test); + EXPECT_EQ(ref2->front(), test->front()); + + ResultStatus&> ref3(ref); + EXPECT_EQ(&(*ref3), &(*test)); + EXPECT_EQ(*ref3, *test); + EXPECT_EQ(ref3->front(), test->front()); + + ResultStatus&> ref4(ref3); + EXPECT_EQ(&(*ref4), &(*test)); + EXPECT_EQ(*ref4, *test); + EXPECT_EQ(ref4->front(), test->front()); +} + +TEST(ResultTests, AccessOkPointerThroughDeref) +{ + auto value = rad::Add2Ptr(nullptr, 123); + ResultStatus test = rad::ResultOk(value); + EXPECT_EQ(*test, value); +} + +TEST(ResultTests, AccessOkContainerPointerThroughDerefAndPointerOperator) +{ + auto value = new std::vector({ 1, 2, 3 }); + ResultStatus*> test = + rad::ResultOk*>(value); + EXPECT_EQ(*test, value); + EXPECT_EQ(**test, *value); + EXPECT_EQ((*test)->front(), value->front()); + delete value; +} + +TEST(ResultTests, AccessConstOkDeref) +{ + const ResultStatus value = 1; + EXPECT_EQ(*value, 1); +} + +TEST(ResultTests, ResCopyAssign) +{ + const rad::ResultOk> value({ 1, 2, 3 }); + rad::ResultOk> other; + other = value; + EXPECT_EQ(value.Get().size(), 3u); + EXPECT_EQ(value.Get().front(), 1); + EXPECT_EQ(value.Get().back(), 3); + EXPECT_EQ(other.Get().size(), 3u); + EXPECT_EQ(other.Get().front(), 1); + EXPECT_EQ(other.Get().back(), 3); +} + +TEST(ResultTests, ResMoveAssign) +{ + rad::ResultOk> value({ 1, 2, 3 }); + rad::ResultOk> other; + other = rad::Move(value); + EXPECT_EQ(value.Get().size(), 0u); + EXPECT_TRUE(value.Get().empty()); + EXPECT_EQ(other.Get().size(), 3u); + EXPECT_EQ(other.Get().front(), 1); + EXPECT_EQ(other.Get().back(), 3); +} + +TEST(ResultTests, CoverNonTrivalEmptyDestruct) +{ + ResultStatus> value; + EXPECT_TRUE(value.IsEmpty()); +} + +TEST(ResultTests, SortableMap) +{ + std::map, int> map; + for (int i = 1; i <= 10; i++) + { + map[i] = i; + map[MYSTATUS_INVALID_ADDRESS] = i; + } + + EXPECT_EQ(map.size(), 11u); + EXPECT_EQ(map[MYSTATUS_INVALID_ADDRESS], 10); + + for (int i = 1; i <= 10; i++) + { + EXPECT_EQ(map[i], i); + } + + for (int i = 1; i <= 10; i++) + { + map[rad::ResultErr(i)] = i; + } + + EXPECT_EQ(map.size(), 21u); + + for (int i = 1; i <= 10; i++) + { + EXPECT_EQ(map[i], i); + } + + for (int i = 1; i <= 10; i++) + { + EXPECT_EQ(map[rad::ResultErr(i)], i); + } + + auto empty = ResultStatus(); + map[empty] = 999; + EXPECT_EQ(map[empty], 999); + map[empty] = 1000; + EXPECT_EQ(map[empty], 1000); + + int iok = 0; + int ierr = 0; + bool foundEmpty = false; + bool foundErr = false; + for (const auto& entry : map) + { + if (entry.first.IsEmpty()) + { + EXPECT_EQ(entry.second, 1000); + foundEmpty = true; + } + else if (entry.first.IsErr()) + { + if (entry.first == MYSTATUS_INVALID_ADDRESS) + { + EXPECT_EQ(entry.second, 10); + foundErr = true; + } + else + { + ierr++; + EXPECT_EQ(entry.first, rad::ResultErr(ierr)); + EXPECT_EQ(entry.second, ierr); + } + } + else if (entry.first.IsOk()) + { + iok++; + EXPECT_EQ(entry.first, iok); + EXPECT_EQ(entry.second, iok); + } + } + + EXPECT_EQ(foundEmpty, true); + EXPECT_EQ(foundErr, true); + EXPECT_EQ(iok, 10); + EXPECT_EQ(ierr, 10); +} + +TEST(ResultTests, SortableSet) +{ + std::set> set; + + set.emplace(rad::ResultOk(4)); + set.emplace(rad::ResultErr(1)); + set.emplace(rad::ResultOk(2)); + set.emplace(rad::ResultErr(5)); + set.emplace(rad::ResultOk(5)); + set.emplace(ResultStatus()); + set.emplace(rad::ResultOk(1)); + set.emplace(rad::ResultErr(3)); + + EXPECT_EQ(set.size(), 8u); + auto it = set.begin(); + EXPECT_EQ(it->IsEmpty(), true); + EXPECT_EQ(it->IsOk(), false); + EXPECT_EQ(it->IsErr(), false); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), true); + EXPECT_EQ(it->IsErr(), false); + EXPECT_EQ(it->Ok(), 1); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), true); + EXPECT_EQ(it->IsErr(), false); + EXPECT_EQ(it->Ok(), 2); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), true); + EXPECT_EQ(it->IsErr(), false); + EXPECT_EQ(it->Ok(), 4); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), true); + EXPECT_EQ(it->IsErr(), false); + EXPECT_EQ(it->Ok(), 5); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), false); + EXPECT_EQ(it->IsErr(), true); + EXPECT_EQ(it->Err(), 1); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), false); + EXPECT_EQ(it->IsErr(), true); + EXPECT_EQ(it->Err(), 3); + ++it; + EXPECT_EQ(it->IsEmpty(), false); + EXPECT_EQ(it->IsOk(), false); + EXPECT_EQ(it->IsErr(), true); + EXPECT_EQ(it->Err(), 5); +} + +TEST(ResultTests, Or) +{ + ResultStatus res = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(res.Or(123), 123); + res = 456; + EXPECT_EQ(res.Or(123), 456); + int value = 789; + EXPECT_EQ(res.Or(static_cast(value)), 456); + res = MYSTATUS_UNSUCCESSFUL; + EXPECT_EQ(res.Or(static_cast(value)), 789); +} + +TEST(ResultTests, OnErr) +{ + ResultStatus res = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(res.OnErr(12u).Err(), 12u); + res = 456; + EXPECT_EQ(res.OnErr(12u).Ok(), 456); +} + +TEST(ResultTests, ConstOnErr) +{ + const ResultStatus resErr = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(resErr.OnErr(12u).Err(), 12u); + const ResultStatus resOk = 456; + EXPECT_EQ(resOk.OnErr(12u).Ok(), 456); +} + +TEST(ResultTests, OnOk) +{ + ResultStatus res = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(res.OnOk(12u).Err(), MYSTATUS_UNSUCCESSFUL); + res = 456; + EXPECT_EQ(res.OnOk(12u).Ok(), 12u); +} + +TEST(ResultTests, ConstOnOk) +{ + const ResultStatus resErr = rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(resErr.OnOk(12u).Err(), MYSTATUS_UNSUCCESSFUL); + const ResultStatus resOk = 456; + EXPECT_EQ(resOk.OnOk(12u).Ok(), 12u); +} + +TEST(ResultTests, ConstOr) +{ + const ResultStatus resErr = + rad::ResultErr(MYSTATUS_UNSUCCESSFUL); + EXPECT_EQ(resErr.Or(123), 123); + const ResultStatus resOk = 456; + EXPECT_EQ(resOk.Or(123), 456); +} + +TEST(ResultTests, ExplicitBool) +{ + ResultStatus value; + + EXPECT_FALSE(value); + + if (value) + { + EXPECT_TRUE(false); + } + else + { + EXPECT_TRUE(true); + } + + value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_FALSE(value); + + if (value) + { + EXPECT_TRUE(false); + } + else + { + EXPECT_TRUE(true); + } + + value = 123; + + EXPECT_TRUE(value); + + if (value) + { + EXPECT_TRUE(true); + } + else + { + EXPECT_FALSE(true); + } +} + +TEST(ResultTests, CompareOkOk) +{ + ResultStatus left = 1; + ResultStatus right = 1; + + EXPECT_TRUE(left == right); + EXPECT_TRUE(right == left); + EXPECT_FALSE(left != right); + EXPECT_FALSE(right != left); + + EXPECT_FALSE(left < right); + EXPECT_FALSE(right < left); +} + +TEST(ResultTests, CompareOkErr) +{ + ResultStatus left = 1; + ResultStatus right = MYSTATUS_UNSUCCESSFUL; + + EXPECT_FALSE(left == right); + EXPECT_FALSE(right == left); + EXPECT_TRUE(left != right); + EXPECT_TRUE(right != left); + + EXPECT_TRUE(left < right); + EXPECT_FALSE(right < left); +} + +TEST(ResultTests, CompareOkEmpty) +{ + ResultStatus left = 1; + ResultStatus right; + + EXPECT_FALSE(left == right); + EXPECT_FALSE(right == left); + EXPECT_TRUE(left != right); + EXPECT_TRUE(right != left); + + EXPECT_FALSE(left < right); + EXPECT_TRUE(right < left); +} + +TEST(ResultTests, CompareValue) +{ + ResultStatus value; + + EXPECT_FALSE(value == 1); + EXPECT_FALSE(1 == value); + EXPECT_TRUE(value != 1); + EXPECT_TRUE(1 != value); + EXPECT_FALSE(value == MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(MYSTATUS_UNSUCCESSFUL == value); + EXPECT_TRUE(value != MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(MYSTATUS_UNSUCCESSFUL != value); + + value = 1; + + EXPECT_TRUE(value == 1); + EXPECT_TRUE(1 == value); + EXPECT_FALSE(value != 1); + EXPECT_FALSE(1 != value); + EXPECT_FALSE(value == MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(MYSTATUS_UNSUCCESSFUL == value); + EXPECT_TRUE(value != MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(MYSTATUS_UNSUCCESSFUL != value); + + value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_FALSE(value == 1); + EXPECT_FALSE(1 == value); + EXPECT_TRUE(value != 1); + EXPECT_TRUE(1 != value); + EXPECT_TRUE(value == MYSTATUS_UNSUCCESSFUL); + EXPECT_TRUE(MYSTATUS_UNSUCCESSFUL == value); + EXPECT_FALSE(value != MYSTATUS_UNSUCCESSFUL); + EXPECT_FALSE(MYSTATUS_UNSUCCESSFUL != value); +} + +TEST(ResultTests, CompareRes) +{ + ResultStatus value; + + EXPECT_FALSE(value == rad::ResultOk(1)); + EXPECT_FALSE(rad::ResultOk(1) == value); + EXPECT_TRUE(value != rad::ResultOk(1)); + EXPECT_TRUE(rad::ResultOk(1) != value); + EXPECT_FALSE(value == rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(rad::ResultErr(MYSTATUS_UNSUCCESSFUL) == value); + EXPECT_TRUE(value != rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_TRUE(rad::ResultErr(MYSTATUS_UNSUCCESSFUL) != value); + + value = 1; + + EXPECT_TRUE(value == rad::ResultOk(1)); + EXPECT_TRUE(rad::ResultOk(1) == value); + EXPECT_FALSE(value != rad::ResultOk(1)); + EXPECT_FALSE(rad::ResultOk(1) != value); + EXPECT_FALSE(value == rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(rad::ResultErr(MYSTATUS_UNSUCCESSFUL) == value); + EXPECT_TRUE(value != rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_TRUE(rad::ResultErr(MYSTATUS_UNSUCCESSFUL) != value); + + value = MYSTATUS_UNSUCCESSFUL; + + EXPECT_FALSE(value == rad::ResultOk(1)); + EXPECT_FALSE(rad::ResultOk(1) == value); + EXPECT_TRUE(value != rad::ResultOk(1)); + EXPECT_TRUE(rad::ResultOk(1) != value); + EXPECT_TRUE(value == rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_TRUE(rad::ResultErr(MYSTATUS_UNSUCCESSFUL) == value); + EXPECT_FALSE(value != rad::ResultErr(MYSTATUS_UNSUCCESSFUL)); + EXPECT_FALSE(rad::ResultErr(MYSTATUS_UNSUCCESSFUL) != value); +} diff --git a/test/test_ScopeExit.cpp b/test/test_ScopeExit.cpp new file mode 100644 index 0000000..51c6d09 --- /dev/null +++ b/test/test_ScopeExit.cpp @@ -0,0 +1,80 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "radiant/ScopeExit.h" + +namespace +{ + +int g_count = 0; + +void ScopeExitTarget(int count) noexcept +{ + g_count += count; +} + +} // namespace + +TEST(ScopeExitTests, ExitInvoked) +{ + int value = 0; + + { + auto scoped = rad::MakeScopeExit([&value]() noexcept { value++; }); + } + + EXPECT_EQ(value, 1); +} + +TEST(ScopeExitTests, ExitReleased) +{ + int value = 0; + + { + auto scoped = rad::MakeScopeExit([&value]() noexcept { value++; }); + scoped.Release(); + } + + EXPECT_EQ(value, 0); +} + +TEST(ScopeExitTests, GuardInvoked) +{ + int value = 0; + + { + auto scoped = rad::MakeScopeGuard([&value]() noexcept { value++; }); + } + + EXPECT_EQ(value, 1); +} + +TEST(ScopeExitTests, GuardInvokedMacro) +{ + int value = 0; + g_count = 0; + int local = 3; + + { + RAD_SCOPE_GUARD([&]() noexcept { value++; }); + RAD_SCOPE_GUARD([&]() noexcept { value++; }); + RAD_SCOPE_GUARD([&]() noexcept { ScopeExitTarget(1); }); + RAD_SCOPE_GUARD([&]() noexcept { ScopeExitTarget(local); }); + } + + EXPECT_EQ(value, 2); + EXPECT_EQ(g_count, 4); +} diff --git a/test/test_SharedPtr.cpp b/test/test_SharedPtr.cpp new file mode 100644 index 0000000..260efeb --- /dev/null +++ b/test/test_SharedPtr.cpp @@ -0,0 +1,1045 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "test/TestThrow.h" +#include "test/TestAlloc.h" +#define RAD_DEFAULT_ALLOCATOR radtest::Allocator + +#include "radiant/SharedPtr.h" + +namespace sptestobjs +{ +// clang-format off +using NoThrowAllocSp = rad::SharedPtr; +using NoThrowObjBlock = rad::detail::_PtrBlock>; +using ThrowObjBlock = rad::detail::_PtrBlock>; +using NoThrowPair = NoThrowObjBlock::PairType; +using ThrowPair = ThrowObjBlock::PairType; + +using NoThrowObjSp = NoThrowAllocSp; +using ThrowObjSp = rad::SharedPtr; +// clang-format on + +// _PtrBlock::PairType construction noexcept +RAD_S_ASSERT(noexcept(NoThrowPair(rad::DeclVal(), 1))); +RAD_S_ASSERT(!noexcept(ThrowPair(rad::DeclVal()))); + +// _PtrBlock construction noexcept (EopPair constructor passthrough) +RAD_S_ASSERT(noexcept(NoThrowObjBlock(NoThrowObjBlock::AllocatorType()))); +RAD_S_ASSERT(!noexcept(ThrowObjBlock(ThrowObjBlock::AllocatorType()))); + +// allocate shared non-throwing allocator required +RAD_S_ASSERT(noexcept(rad::AllocateShared(radtest::Allocator(), 0))); + +// allocate shared throwing constructor +RAD_S_ASSERT(!noexcept(rad::AllocateShared( + radtest::Allocator()))); + +// make shared noexcept +RAD_S_ASSERT(noexcept(rad::MakeShared(0))); +RAD_S_ASSERT(!noexcept(rad::MakeShared())); + +class Base +{ +public: + + virtual ~Base() + { + } + + size_t val; +}; + +struct Extra +{ + int extra; +}; + +class Derived : public Base, + public Extra + +{ +public: + + virtual ~Derived() + { + } +}; +} // namespace sptestobjs + +struct DestructCounter +{ + ~DestructCounter() + { + ++counter; + } + + static int counter; +}; + +int DestructCounter::counter = 0; + +namespace +{ +sptestobjs::NoThrowObjBlock* AllocTestBlock(int val) +{ + typename sptestobjs::NoThrowObjBlock::AllocatorType alloc; + auto block = alloc.Alloc(1); + new (block) sptestobjs::NoThrowObjBlock(alloc, val); + return block; +} + +void FreeTestBlock(sptestobjs::NoThrowObjBlock* block) +{ + block->Allocator().Free(block); +} +} // namespace + +TEST(TestSharedPtr, RefCountCtor) +{ + rad::detail::_PtrRefCount rc; + EXPECT_EQ(rc.StrongCount(), 1u); + EXPECT_EQ(rc.WeakCount(), 1u); +} + +TEST(TestSharedPtr, RefCountIncrement) +{ + rad::detail::_PtrRefCount rc; + rc.Increment(); + EXPECT_EQ(rc.StrongCount(), 2u); + EXPECT_EQ(rc.WeakCount(), 1u); +} + +TEST(TestSharedPtr, RefCountIncrementWeak) +{ + rad::detail::_PtrRefCount rc; + rc.IncrementWeak(); + EXPECT_EQ(rc.StrongCount(), 1u); + EXPECT_EQ(rc.WeakCount(), 2u); +} + +TEST(TestSharedPtr, RefCountDrecment) +{ + rad::detail::_PtrRefCount rc; + rc.Increment(); + EXPECT_FALSE(rc.Decrement()); + EXPECT_EQ(rc.StrongCount(), 1u); + EXPECT_EQ(rc.WeakCount(), 1u); + + EXPECT_TRUE(rc.Decrement()); + EXPECT_EQ(rc.StrongCount(), 0u); + EXPECT_EQ(rc.WeakCount(), 1u); +} + +TEST(TestSharedPtr, RefCountDecrementWeak) +{ + rad::detail::_PtrRefCount rc; + rc.IncrementWeak(); + EXPECT_FALSE(rc.DecrementWeak()); + EXPECT_EQ(rc.StrongCount(), 1u); + EXPECT_EQ(rc.WeakCount(), 1u); + + EXPECT_TRUE(rc.DecrementWeak()); + EXPECT_EQ(rc.StrongCount(), 1u); + EXPECT_EQ(rc.WeakCount(), 0u); +} + +TEST(TestSharedPtr, RefCountLockWeak) +{ + rad::detail::_PtrRefCount rc; + EXPECT_TRUE(rc.LockWeak()); + EXPECT_EQ(rc.StrongCount(), 2u); + EXPECT_EQ(rc.WeakCount(), 1u); +} + +class MockAtomic +{ +public: + + MockAtomic(uint32_t) + { + } + + uint32_t Load(rad::detail::atomic::RelaxedTag) + { + static uint32_t ret = 0; + return ret > 1 ? 1 : ret++; + } + + bool CompareExchangeWeak(uint32_t& expected, + uint32_t, + rad::detail::atomic::AcqRelTag, + rad::detail::atomic::RelaxedTag) + { + expected = 0; + return false; + } +}; + +TEST(TestSharedPtr, RefCountLockWeakFailExchange) +{ + rad::detail::_TPtrRefCount rc; + + EXPECT_FALSE(rc.LockWeak()); + EXPECT_FALSE(rc.LockWeak()); + EXPECT_EQ(rc.StrongCount(), 1u); + EXPECT_EQ(rc.WeakCount(), 1u); +} + +TEST(TestSharedPtr, PtrBlockCtor) +{ + using PtrBlock = rad::detail::_PtrBlock>; + PtrBlock::AllocatorType alloc; + PtrBlock block(alloc, 2); + EXPECT_EQ(block.Value(), 2); + + using StatefulPtrBlock = + rad::detail::_PtrBlock>; + StatefulPtrBlock::AllocatorType statefulAlloc; + StatefulPtrBlock statefulBlock(statefulAlloc, 4); + RAD_S_ASSERT(sizeof(statefulBlock) > + sizeof(decltype(statefulBlock)::PairType::SecondType)); +} + +TEST(TestSharedPtr, PtrBlockAcquire) +{ + auto block = AllocTestBlock(2); + ASSERT_NE(block, nullptr); + + block->Acquire(); + ASSERT_EQ(block->UseCount(), 2u); + ASSERT_EQ(block->WeakCount(), 1u); + + FreeTestBlock(block); +} + +TEST(TestSharedPtr, PtrBlockAcquireWeak) +{ + auto block = AllocTestBlock(2); + ASSERT_NE(block, nullptr); + + block->AcquireWeak(); + ASSERT_EQ(block->UseCount(), 1u); + ASSERT_EQ(block->WeakCount(), 2u); + + FreeTestBlock(block); +} + +TEST(TestSharedPtr, LockWeak) +{ + auto block = AllocTestBlock(2); + ASSERT_NE(block, nullptr); + + block->LockWeak(); + ASSERT_EQ(block->UseCount(), 2u); + ASSERT_EQ(block->WeakCount(), 1u); + + FreeTestBlock(block); +} + +TEST(TestSharedPtr, ReleaseDestruct) +{ + using PtrBlock = + rad::detail::_PtrBlock>; + PtrBlock::AllocatorType alloc; + PtrBlock block(alloc); + EXPECT_EQ(DestructCounter::counter, 0); + + block.AcquireWeak(); + block.Release(); + EXPECT_EQ(block.UseCount(), 0u); + EXPECT_EQ(DestructCounter::counter, 1); +} + +TEST(TestSharedPtr, ReleaseFree) +{ + using PtrBlock = + rad::detail::_PtrBlock>; + PtrBlock::AllocatorType alloc; + alloc.ResetCounts(); + + PtrBlock* block = alloc.Alloc(1); + new (block) PtrBlock(alloc); + + block->Release(); + EXPECT_EQ(alloc.AllocCount(), 1u); + EXPECT_TRUE(alloc.VerifyCounts()); +} + +TEST(TestSharedPtr, Value) +{ + auto block = AllocTestBlock(2); + ASSERT_NE(block, nullptr); + + ASSERT_EQ(block->Value(), 2); + + const sptestobjs::NoThrowObjBlock* constBlock = block; + ASSERT_EQ(constBlock->Value(), 2); + + FreeTestBlock(block); +} + +TEST(TestSharedPtr, NullCtor) +{ + rad::SharedPtr ptr; + EXPECT_EQ(ptr, false); + + rad::SharedPtr ptr2(nullptr); + EXPECT_EQ(ptr2, false); +} + +TEST(TestSharedPtr, AllocateShared) +{ + radtest::Allocator alloc; + auto ptr = rad::AllocateShared(alloc, 2); + EXPECT_TRUE(ptr); + EXPECT_EQ(*ptr, 2); +} + +TEST(TestSharedPtr, AllocateSharedFail) +{ + radtest::FailingAllocator alloc; + auto ptr = rad::AllocateShared(alloc, 2); + EXPECT_FALSE(ptr); +} + +TEST(TestSharedPtr, AllocateSharedThrows) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + EXPECT_THROW(rad::AllocateShared(alloc, 1), + std::exception); + EXPECT_EQ(alloc.AllocCount(), 1u); + EXPECT_TRUE(alloc.VerifyCounts()); +} + +TEST(TestSharedPtr, MakeShared) +{ + auto ptr = rad::MakeShared(2); + EXPECT_TRUE(ptr); + EXPECT_EQ(*ptr, 2); +} + +TEST(TestSharedPtr, CopyCtor) +{ + auto ptr = rad::MakeShared(2); + + decltype(ptr) ptr2(ptr); + EXPECT_EQ(ptr.UseCount(), 2u); + EXPECT_EQ(ptr2.UseCount(), 2u); + EXPECT_EQ(*ptr, *ptr2); +} + +TEST(TestSharedPtr, MoveCtor) +{ + auto ptr = rad::MakeShared(2); + + decltype(ptr) ptr2(Move(ptr)); + EXPECT_FALSE(ptr); + EXPECT_TRUE(ptr2); + EXPECT_EQ(ptr2.UseCount(), 1u); + EXPECT_EQ(*ptr2, 2); +} + +TEST(TestSharedPtr, EqualNull) +{ + rad::SharedPtr ptr; + EXPECT_FALSE(ptr); + EXPECT_EQ(ptr, nullptr); + + EXPECT_EQ(nullptr, ptr); +} + +TEST(TestSharedPtr, NotEqualNull) +{ + auto ptr = rad::MakeShared(2); + EXPECT_TRUE(ptr); + EXPECT_NE(ptr, nullptr); + + EXPECT_NE(nullptr, ptr); +} + +TEST(TestSharedPtr, Equal) +{ + auto left = rad::MakeShared(); + auto right = rad::MakeShared(); + + EXPECT_FALSE(left == right); + left = right; + EXPECT_TRUE(right == left); +} + +TEST(TestSharedPtr, NotEqual) +{ + auto left = rad::MakeShared(); + auto right = rad::MakeShared(); + + EXPECT_TRUE(left != right); + left = right; + EXPECT_FALSE(right != left); +} + +TEST(TestSharedPtr, LessThan) +{ + auto a = rad::MakeShared(); + auto b = rad::MakeShared(); + auto& left = a.Get() < b.Get() ? a : b; + auto& right = a.Get() < b.Get() ? b : a; + + EXPECT_TRUE(left < right); + EXPECT_FALSE(right < left); +} + +TEST(TestSharedPtr, LessThanOrEqual) +{ + auto a = rad::MakeShared(); + auto b = rad::MakeShared(); + auto& left = a.Get() < b.Get() ? a : b; + auto& right = a.Get() < b.Get() ? b : a; + + EXPECT_TRUE(left <= right); + EXPECT_FALSE(right <= left); + left = right; + EXPECT_TRUE(left <= right); + EXPECT_TRUE(right <= left); +} + +TEST(TestSharedPtr, GreaterThan) +{ + auto a = rad::MakeShared(); + auto b = rad::MakeShared(); + auto& left = a.Get() > b.Get() ? a : b; + auto& right = a.Get() > b.Get() ? b : a; + + EXPECT_TRUE(left > right); + EXPECT_FALSE(right > left); +} + +TEST(TestSharedPtr, GreaterThanOrEqual) +{ + auto a = rad::MakeShared(); + auto b = rad::MakeShared(); + auto& left = a.Get() > b.Get() ? a : b; + auto& right = a.Get() > b.Get() ? b : a; + + EXPECT_TRUE(left >= right); + EXPECT_FALSE(right >= left); + left = right; + EXPECT_TRUE(left >= right); + EXPECT_TRUE(right >= left); +} + +TEST(TestSharedPtr, Deref) +{ + auto ptr = rad::MakeShared(2); + EXPECT_NE(ptr.Get(), nullptr); + EXPECT_EQ(*ptr.Get(), 2); + + EXPECT_EQ(*ptr, 2); + EXPECT_EQ(*ptr.operator->(), 2); + + const rad::SharedPtr constPtr(ptr); + EXPECT_EQ(*constPtr, 2); + EXPECT_EQ(*constPtr.operator->(), 2); +} + +TEST(TestSharedPtr, Reset) +{ + auto ptr = rad::MakeShared(2); + + EXPECT_NE(ptr, nullptr); + + ptr.Reset(); + + EXPECT_EQ(ptr, nullptr); +} + +TEST(TestSharedPtr, Swap) +{ + auto ptr = rad::MakeShared(2); + + ptr.Swap(ptr); + + EXPECT_EQ(*ptr, 2); + + auto other = rad::MakeShared(3); + + ptr.Swap(other); + + EXPECT_EQ(*other, 2); + EXPECT_EQ(*ptr, 3); +} + +TEST(TestSharedPtr, SelfCopyAssign) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + auto* somePtr = &ptr; // clang workaround -Wself-assign-overloaded + ptr = *somePtr; + EXPECT_TRUE(ptr); + EXPECT_EQ(ptr.UseCount(), 1u); + + EXPECT_EQ(alloc.AllocCount(), 1u); +} + +TEST(TestSharedPtr, CopyAssignNoReset) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + decltype(ptr) ptr2; + + ptr2 = ptr; + EXPECT_TRUE(ptr2); + EXPECT_EQ(ptr.UseCount(), 2u); + EXPECT_EQ(ptr.WeakCount(), 1u); + + EXPECT_EQ(alloc.AllocCount(), 1u); +} + +TEST(TestSharedPtr, CopyAssignReset) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + auto ptr2 = rad::AllocateShared(alloc, 3); + + ptr2 = ptr; + EXPECT_TRUE(ptr2); + EXPECT_EQ(*ptr2, 2); + EXPECT_EQ(*ptr2, *ptr); + + EXPECT_TRUE(alloc.VerifyCounts(2, 1)); +} + +TEST(TestSharedPtr, CopyAssignNull) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + ptr = nullptr; + EXPECT_FALSE(ptr); + + EXPECT_EQ(alloc.FreeCount(), 1u); +} + +TEST(TestSharedPtr, SelfMoveAssign) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + auto* somePtr = &ptr; // clang workaround -Wself-move + ptr = Move(*somePtr); + EXPECT_TRUE(ptr); + EXPECT_EQ(ptr.UseCount(), 1u); + + EXPECT_TRUE(alloc.VerifyCounts(1, 0)); +} + +TEST(TestSharedPtr, MoveAssignNoReset) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + decltype(ptr) ptr2; + + ptr2 = Move(ptr); + EXPECT_TRUE(ptr2); + EXPECT_FALSE(ptr); + EXPECT_EQ(ptr2.UseCount(), 1u); + EXPECT_EQ(ptr2.WeakCount(), 1u); + + EXPECT_TRUE(alloc.VerifyCounts(1, 0)); +} + +TEST(TestSharedPtr, MoveAssignReset) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + auto ptr = rad::AllocateShared(alloc, 2); + auto ptr2 = rad::AllocateShared(alloc, 3); + + ptr2 = Move(ptr); + EXPECT_TRUE(ptr2); + EXPECT_FALSE(ptr); + EXPECT_EQ(*ptr2, 2); + EXPECT_EQ(ptr2.UseCount(), 1u); + EXPECT_EQ(ptr2.WeakCount(), 1u); + + EXPECT_TRUE(alloc.VerifyCounts(2, 1)); +} + +TEST(TestSharedPtr, PolymorphicCtor) +{ + sptestobjs::Derived d; + sptestobjs::Base* b = &d; + sptestobjs::Extra* e = &d; + EXPECT_EQ(static_cast(&d), static_cast(b)); + EXPECT_NE(static_cast(b), static_cast(e)); + + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + { + auto ptr = rad::AllocateShared(alloc); + ptr->val = 1; + ptr->extra = 2; + + rad::SharedPtr bptr(ptr); + rad::SharedPtr eptr(ptr); + + EXPECT_EQ(ptr.UseCount(), 3u); + EXPECT_EQ(bptr->val, 1u); + EXPECT_EQ(eptr->extra, 2); + } + + EXPECT_EQ(alloc.AllocCount(), 1u); + EXPECT_TRUE(alloc.VerifyCounts()); +} + +TEST(TestSharedPtr, PolymorphicAssign) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + { + rad::SharedPtr eptr; + { + auto ptr = rad::AllocateShared(alloc); + ptr->extra = 2; + + eptr = ptr; + + EXPECT_EQ(ptr.UseCount(), 2u); + EXPECT_EQ(eptr->extra, 2); + + eptr = ptr; + + EXPECT_EQ(ptr.UseCount(), 2u); + EXPECT_EQ(eptr->extra, 2); + } + } + + EXPECT_EQ(alloc.AllocCount(), 1u); + EXPECT_TRUE(alloc.VerifyCounts()); +} + +TEST(TestWeakPtr, ConstructEmpy) +{ + rad::WeakPtr weak; + + auto ptr = weak.Lock(); + + EXPECT_EQ(ptr, nullptr); +} + +TEST(TestWeakPtr, Construct) +{ + auto shared = rad::MakeShared(1); + rad::WeakPtr w1(shared); + + EXPECT_EQ(shared.WeakCount(), 2u); + + rad::WeakPtr w2(w1); + + EXPECT_EQ(shared.WeakCount(), 3u); +} + +TEST(TestWeakPtr, ConstructMove) +{ + auto shared = rad::MakeShared(1); + auto o = shared; + + rad::WeakPtr w1(Move(o)); + + EXPECT_EQ(shared.WeakCount(), 2u); + + rad::WeakPtr w2(Move(w1)); + + EXPECT_EQ(shared.WeakCount(), 2u); +} + +TEST(TestWeakPtr, Assign) +{ + auto shared = rad::MakeShared(1); + rad::WeakPtr w1; + rad::WeakPtr w2; + + w1 = shared; + w2 = w1; + + EXPECT_EQ(shared.WeakCount(), 3u); + + w2 = shared; + + EXPECT_EQ(shared.WeakCount(), 3u); + + w1 = w2; + + EXPECT_EQ(shared.WeakCount(), 3u); +} + +TEST(TestWeakPtr, AssignMove) +{ + auto shared = rad::MakeShared(1); + auto o = shared; + + rad::WeakPtr w1; + rad::WeakPtr w2; + + w1 = Move(o); + w2 = Move(w1); + + EXPECT_EQ(shared.WeakCount(), 2u); + + w1 = shared; + w2 = Move(w1); + + EXPECT_EQ(shared.WeakCount(), 2u); +} + +TEST(TestWeakPtr, Reset) +{ + auto shared = rad::MakeShared(1); + + rad::WeakPtr w1(shared); + + shared = w1.Lock(); + + EXPECT_NE(shared, nullptr); + + w1.Reset(); + + shared = w1.Lock(); + + EXPECT_EQ(shared, nullptr); +} + +TEST(TestWeakPtr, Swap) +{ + auto s1 = rad::MakeShared(2); + auto s2 = rad::MakeShared(3); + + rad::WeakPtr w1(s1); + rad::WeakPtr w2(s2); + + w1.Swap(w1); + + s1 = w1.Lock(); + + EXPECT_EQ(*s1, 2); + + w1.Swap(w2); + + auto sa = w1.Lock(); + auto sb = w2.Lock(); + + EXPECT_EQ(*sa, 3); + EXPECT_EQ(*sb, 2); +} + +TEST(TestWeakPtr, UseCount) +{ + auto s1 = rad::MakeShared(2); + + rad::WeakPtr w1(s1); + + EXPECT_EQ(w1.UseCount(), 1u); + + auto s2 = s1; + + EXPECT_EQ(w1.UseCount(), 2u); +} + +TEST(TestWeakPtr, Expired) +{ + rad::WeakPtr weak; + + EXPECT_TRUE(weak.Expired()); + + { + auto shared = rad::MakeShared(2); + weak = shared; + + EXPECT_FALSE(weak.Expired()); + } + + EXPECT_TRUE(weak.Expired()); +} + +TEST(TestWeakPtr, Lock) +{ + rad::WeakPtr weak; + rad::SharedPtr p; + + EXPECT_TRUE(weak.Expired()); + + { + auto shared = rad::MakeShared(2); + weak = shared; + p = weak.Lock(); + + EXPECT_FALSE(weak.Expired()); + } + + EXPECT_FALSE(weak.Expired()); + + EXPECT_EQ(p.Get(), weak.Lock().Get()); + + p.Reset(); + + EXPECT_TRUE(weak.Expired()); +} + +TEST(TestWeakPtr, PolymorphicCtor) +{ + sptestobjs::Derived d; + sptestobjs::Base* b = &d; + sptestobjs::Extra* e = &d; + EXPECT_EQ(static_cast(&d), static_cast(b)); + EXPECT_NE(static_cast(b), static_cast(e)); + + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + { + auto ptr = rad::AllocateShared(alloc); + ptr->val = 1; + ptr->extra = 2; + + rad::WeakPtr bptr(ptr); + rad::WeakPtr eptr(ptr); + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(bptr.Lock()->val, 1u); + EXPECT_EQ(eptr.Lock()->extra, 2); + + rad::WeakPtr wptr(ptr); + + rad::WeakPtr bptr2(wptr); + rad::WeakPtr eptr2(wptr); + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(bptr2.Lock()->val, 1u); + EXPECT_EQ(eptr2.Lock()->extra, 2); + + rad::WeakPtr bptr3(Move(wptr)); + + EXPECT_EQ(wptr.Lock(), nullptr); + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(bptr3.Lock()->val, 1u); + } + + EXPECT_EQ(alloc.AllocCount(), 1u); + EXPECT_TRUE(alloc.VerifyCounts()); +} + +TEST(TestWeakPtr, PolymorphicAssign) +{ + radtest::StaticCountingAllocator alloc; + alloc.ResetCounts(); + + { + rad::WeakPtr eptr; + { + auto ptr = rad::AllocateShared(alloc); + ptr->extra = 2; + + eptr = ptr; + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(eptr.Lock()->extra, 2); + + eptr = ptr; + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(eptr.Lock()->extra, 2); + + rad::WeakPtr wptr(ptr); + + rad::WeakPtr bptr2; + rad::WeakPtr eptr2; + + bptr2 = wptr; + eptr2 = wptr; + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(eptr2.Lock()->extra, 2); + + bptr2 = wptr; + eptr2 = wptr; + + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(eptr2.Lock()->extra, 2); + + bptr2 = Move(wptr); + + EXPECT_EQ(wptr.Lock(), nullptr); + EXPECT_EQ(ptr.UseCount(), 1u); + EXPECT_EQ(eptr2.Lock()->extra, 2); + } + } + + EXPECT_EQ(alloc.AllocCount(), 1u); + EXPECT_TRUE(alloc.VerifyCounts()); +} + +TEST(TestAtomicSharedPtr, Construct) +{ + rad::AtomicSharedPtr aptr; + rad::SharedPtr ptr = aptr.Load(); + EXPECT_EQ(ptr, nullptr); + + rad::AtomicSharedPtr aptrTwo(nullptr); + ptr = aptrTwo.Load(); + EXPECT_EQ(ptr, nullptr); + + auto sptr = rad::MakeShared(123); + rad::AtomicSharedPtr aptrThree(sptr); + ptr = aptrThree.Load(); + EXPECT_EQ(ptr.Get(), sptr.Get()); +} + +TEST(TestAtomicSharedPtr, Store) +{ + rad::AtomicSharedPtr aptr; + auto sptr = rad::MakeShared(123); + aptr.Store(sptr); + auto ptr = aptr.Load(); + EXPECT_EQ(ptr.Get(), sptr.Get()); +} + +TEST(TestAtomicSharedPtr, Exchange) +{ + rad::AtomicSharedPtr aptr; + auto sptr = rad::MakeShared(123); + auto ptr = aptr.Exchange(sptr); + EXPECT_EQ(ptr, nullptr); + ptr = aptr.Exchange(ptr); + EXPECT_EQ(ptr.Get(), sptr.Get()); +} + +TEST(TestAtomicSharedPtr, AssignmentOperator) +{ + rad::AtomicSharedPtr aptr; + aptr = rad::MakeShared(123); + auto ptr = aptr.Load(); + EXPECT_EQ(*ptr, 123); +} + +namespace +{ +rad::SharedPtr AtomicConversionTester(rad::SharedPtr value) +{ + return value; +} +} // namespace + +TEST(TestAtomicSharedPtr, ConversionOperator) +{ + auto ptr = rad::MakeShared(123); + rad::AtomicSharedPtr aptr(ptr); + auto ptrTwo = AtomicConversionTester(aptr); + EXPECT_EQ(ptrTwo.Get(), ptr.Get()); +} + +TEST(TestAtomicWeakPtr, Construct) +{ + rad::AtomicWeakPtr aptr; + auto ptr = aptr.Load(); + EXPECT_EQ(ptr.Lock(), nullptr); + + auto sptr = rad::MakeShared(123); + rad::AtomicWeakPtr aptr2(sptr); + ptr = aptr2.Load(); + + sptr = ptr.Lock(); + EXPECT_EQ(*sptr, 123); + + rad::AtomicWeakPtr aptr3(ptr); + ptr = aptr3.Load(); + + sptr = ptr.Lock(); + EXPECT_EQ(*sptr, 123); +} + +TEST(TestAtomicWeakPtr, Store) +{ + rad::AtomicWeakPtr aptr; + auto sptr = rad::MakeShared(123); + aptr.Store(sptr); + auto ptr = aptr.Load(); + EXPECT_EQ(ptr.Lock().Get(), sptr.Get()); +} + +TEST(TestAtomicWeakPtr, Exchange) +{ + rad::AtomicWeakPtr aptr; + auto sptr = rad::MakeShared(123); + auto ptr = aptr.Exchange(sptr); + EXPECT_EQ(ptr.Lock(), nullptr); + ptr = aptr.Exchange(ptr); + EXPECT_EQ(ptr.Lock().Get(), sptr.Get()); +} + +TEST(TestAtomicWeakPtr, AssignmentOperator) +{ + auto sptr = rad::MakeShared(123); + rad::AtomicWeakPtr aptr; + aptr = sptr; + auto ptr = aptr.Load(); + EXPECT_EQ(*(ptr.Lock()), 123); + + rad::AtomicWeakPtr aptr2; + aptr2 = aptr.Load(); + ptr = aptr2.Load(); + EXPECT_EQ(*(ptr.Lock()), 123); +} + +TEST(TestAtomicWeakPtr, ConversionOperator) +{ + auto ptr = rad::MakeShared(123); + rad::AtomicWeakPtr aptr(ptr); + auto ptrTwo = AtomicConversionTester(aptr); + EXPECT_EQ(ptrTwo.Get(), ptr.Get()); +} + +namespace +{ +rad::WeakPtr AtomicConversionTesterWeak(rad::WeakPtr value) +{ + return value; +} +} // namespace + +TEST(TestAtomicWeakPtr, ConversionOperatorWeak) +{ + auto ptr = rad::MakeShared(123); + rad::AtomicWeakPtr aptr(ptr); + auto ptrTwo = AtomicConversionTesterWeak(aptr); + EXPECT_EQ(ptrTwo.Lock().Get(), ptr.Get()); +} diff --git a/test/test_Span.cpp b/test/test_Span.cpp new file mode 100644 index 0000000..e3cc554 --- /dev/null +++ b/test/test_Span.cpp @@ -0,0 +1,386 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "radiant/Span.h" +#include "radiant/Utility.h" +#include "test/TestThrow.h" + +static constexpr auto g_SpanString = rad::MakeSpan("Span String"); +static constexpr const uint8_t g_Bytes[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, + 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, + 0xcc, 0xdd, 0xee, 0xff }; +static constexpr auto g_SpanBytes = rad::MakeSpan(g_Bytes); + +RAD_S_ASSERT(sizeof(g_SpanString) == sizeof(void*)); +RAD_S_ASSERT(g_SpanString.Size() == rad::Size("Span String")); +RAD_S_ASSERT(sizeof(g_SpanBytes) == sizeof(void*)); +RAD_S_ASSERT(g_SpanBytes.Size() == rad::Size(g_Bytes)); + +// clang-format off + +RAD_S_ASSERT(noexcept(rad::Span("derp"))); + +RAD_S_ASSERT(noexcept(rad::Span())); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), 1))); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), 1))); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().~Span())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Data())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Size())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().SizeBytes())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Empty())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Front())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Back())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().AsBytes())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Subspan(1, 1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Subspan<1>())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().First(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().First<1>())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Last(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Last<1>())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().begin())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().end())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().rbegin())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().rend())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().operator[](1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().operator=(rad::DeclVal&>()))); + +RAD_S_ASSERT(noexcept(rad::Span())); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), 1))); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), 1))); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::Span(rad::DeclVal(), rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().~Span())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Data())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Size())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().SizeBytes())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Empty())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Front())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Back())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().AsBytes())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Subspan(1, 1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Subspan<1>())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().First(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().First<1>())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Last(1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().Last<1>())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().begin())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().end())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().rbegin())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().rend())); +RAD_S_ASSERT(noexcept(rad::DeclVal>().operator[](1))); +RAD_S_ASSERT(noexcept(rad::DeclVal>().operator=(rad::DeclVal&>()))); + +// clang-format on + +TEST(SpanTests, ConstructBySize) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + EXPECT_EQ(rad::Size(span), rad::Size(data)); + + EXPECT_EQ(span.Front(), 0u); + EXPECT_EQ(span.Back(), 10u); + EXPECT_EQ(span.Size(), rad::Size(data)); + + for (uint32_t i = 0; i < rad::Size(data); i++) + { + EXPECT_EQ(span[i], data[i]); + } + + uint32_t index = 0; + for (const auto& item : span) + { + EXPECT_EQ(item, data[index]); + index++; + } +} + +TEST(SpanTests, ConstructByPointers) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, data + rad::Size(data)); + + EXPECT_EQ(span.Front(), 0u); + EXPECT_EQ(span.Back(), 10u); + EXPECT_EQ(span.Size(), rad::Size(data)); + + for (uint32_t i = 0; i < rad::Size(data); i++) + { + EXPECT_EQ(span[i], data[i]); + } + + uint32_t index = 0; + for (const auto& item : span) + { + EXPECT_EQ(item, data[index]); + index++; + } +} + +TEST(SpanTests, LastStaticExtentByPointer) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, data + 3); + RAD_S_ASSERT(sizeof(span) == sizeof(void*)); + + EXPECT_EQ(span.Size(), 3u); + EXPECT_EQ(span.Data(), data); + + for (uint32_t i = 0; i < span.Size(); i++) + { + EXPECT_EQ(span[i], data[i]); + } +} + +TEST(SpanTests, FirstStaticExtent) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + auto sub = span.First<3>(); + RAD_S_ASSERT(sizeof(sub) == sizeof(void*)); + + EXPECT_EQ(sub.Front(), 0u); + EXPECT_EQ(sub.Back(), 2u); + EXPECT_EQ(sub.Size(), 3u); +} + +TEST(SpanTests, LastStaticExtent) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + auto sub = span.Last<3>(); + RAD_S_ASSERT(sizeof(sub) == sizeof(void*)); + + EXPECT_EQ(sub.Front(), 8u); + EXPECT_EQ(sub.Back(), 10u); + EXPECT_EQ(sub.Size(), 3u); +} + +TEST(SpanTests, FirstDynamicExtent) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + auto sub = span.First(3); + RAD_S_ASSERT(sizeof(sub) > sizeof(void*)); + + EXPECT_EQ(sub.Front(), 0u); + EXPECT_EQ(sub.Back(), 2u); + EXPECT_EQ(sub.Size(), 3u); +} + +TEST(SpanTests, LastDynamicExtent) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + auto sub = span.Last(3); + RAD_S_ASSERT(sizeof(sub) > sizeof(void*)); + + EXPECT_EQ(sub.Front(), 8u); + EXPECT_EQ(sub.Back(), 10u); + EXPECT_EQ(sub.Size(), 3u); +} + +TEST(SpanTests, SubspanStaticExtent) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + auto sub = span.Subspan<3, 4>(); + RAD_S_ASSERT(sizeof(sub) == sizeof(void*)); + + EXPECT_EQ(sub.Front(), 3u); + EXPECT_EQ(sub.Back(), 6u); + EXPECT_EQ(sub.Size(), 4u); +} + +TEST(SpanTests, SubspanDynamicExtent) +{ + const uint8_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + auto sub = span.Subspan(3, 4); + RAD_S_ASSERT(sizeof(sub) > sizeof(void*)); + + EXPECT_EQ(sub.Front(), 3u); + EXPECT_EQ(sub.Back(), 6u); + EXPECT_EQ(sub.Size(), 4u); +} + +TEST(SpanTests, SizeInBytes) +{ + const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + EXPECT_EQ(span.SizeBytes(), 44u); +} + +TEST(SpanTests, Iterate) +{ + const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + uint32_t i = 0; + for (auto it = span.begin(); it != span.end(); ++it) + { + EXPECT_EQ(*it, data[i++]); + } + EXPECT_EQ(i, rad::Size(data)); +} + +TEST(SpanTests, ReverseIterate) +{ + const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + int i = rad::Size(data); + for (auto it = span.rbegin(); it != span.rend(); ++it) + { + EXPECT_EQ(*it, data[--i]); + } + EXPECT_EQ(i, 0); +} + +TEST(SpanTests, IterateEmpty) +{ + rad::Span span; + + int i = 0; + for (auto it = span.begin(); it != span.end(); ++it) + { + i++; + } + EXPECT_EQ(i, 0); +} + +TEST(SpanTests, ReverseIterateEmpty) +{ + rad::Span span; + + int i = 0; + for (auto it = span.rbegin(); it != span.rend(); ++it) + { + i++; + } + EXPECT_EQ(i, 0); +} + +TEST(SpanTests, AsBytes) +{ + uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + for (auto& b : span.AsBytes()) + { + b = rad::Byte(1); + } + + uint32_t expected = 0x1010101; + for (const auto& d : data) + { + EXPECT_EQ(d, expected); + } + + rad::Span spanTwo(data, rad::Size(data)); + for (auto& v : spanTwo.AsBytes()) + { + EXPECT_EQ(v, rad::Byte(1)); + } +} + +TEST(SpanTests, Literal) +{ + auto string = rad::MakeSpan("derp"); + + EXPECT_EQ(string.Size(), 5u); + EXPECT_EQ(string[0], 'd'); + EXPECT_EQ(string[1], 'e'); + EXPECT_EQ(string[2], 'r'); + EXPECT_EQ(string[3], 'p'); +} + +TEST(SpanTests, LiteralConvert) +{ + auto string = rad::MakeSpan("derp"); + + rad::Span other(string); + + EXPECT_EQ(string.Size(), other.Size()); + EXPECT_EQ(string.Data(), other.Data()); + EXPECT_EQ(string[0], other[0]); + EXPECT_EQ(string[1], other[1]); + EXPECT_EQ(string[2], other[2]); + EXPECT_EQ(string[3], other[3]); +} + +TEST(SpanTests, DynamicConvertStatic) +{ + uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + rad::Span span(data, rad::Size(data)); + + rad::Span other(span); + + EXPECT_EQ(other.Size(), 3u); + EXPECT_EQ(span.Data(), other.Data()); + EXPECT_EQ(span[0], other[0]); + EXPECT_EQ(span[1], other[1]); + EXPECT_EQ(span[2], other[2]); +} + +TEST(SpanTests, MakeSpanStatic) +{ + uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + auto span = rad::MakeSpan(data); + RAD_S_ASSERT(sizeof(span) == sizeof(void*)); + RAD_S_ASSERT(span.Extent == 4u); + + EXPECT_EQ(span.Size(), 4u); + EXPECT_EQ(span.Data(), data); + EXPECT_EQ(span[0], data[0]); + EXPECT_EQ(span[1], data[1]); + EXPECT_EQ(span[2], data[2]); + EXPECT_EQ(span[3], data[3]); +} + +TEST(SpanTests, MakeSpanDynamic) +{ + uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + auto span = rad::MakeSpan(data, rad::Size(data)); + + EXPECT_EQ(span.Data(), data); + for (uint32_t i = 0; i < rad::Size(data); i++) + { + EXPECT_EQ(span[i], data[i]); + } +} + +TEST(SpanTests, MakeSpanDynamicByPointer) +{ + uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; + auto span = rad::MakeSpan(data, data + rad::Size(data)); + + EXPECT_EQ(span.Data(), data); + for (uint32_t i = 0; i < rad::Size(data); i++) + { + EXPECT_EQ(span[i], data[i]); + } +} diff --git a/test/test_TypeTraits.cpp b/test/test_TypeTraits.cpp new file mode 100644 index 0000000..629b077 --- /dev/null +++ b/test/test_TypeTraits.cpp @@ -0,0 +1,35 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "radiant/TypeTraits.h" + +struct A +{ +}; + +struct B : public A +{ +}; + +RAD_S_ASSERT((rad::IsLRefBindable)); +RAD_S_ASSERT((rad::IsLRefBindable)); +RAD_S_ASSERT(!(rad::IsLRefBindable)); +RAD_S_ASSERT((rad::IsLRefBindable)); + +RAD_S_ASSERT((rad::IsLRefBindable)); +RAD_S_ASSERT((rad::IsLRefBindable)); +RAD_S_ASSERT(!(rad::IsLRefBindable)); +RAD_S_ASSERT((rad::IsLRefBindable)); +RAD_S_ASSERT((!rad::IsLRefBindable)); +RAD_S_ASSERT((rad::IsLRefBindable)); diff --git a/test/test_TypeWrapper.cpp b/test/test_TypeWrapper.cpp new file mode 100644 index 0000000..f50becd --- /dev/null +++ b/test/test_TypeWrapper.cpp @@ -0,0 +1,499 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +// +// Disable nothrow assertions for unit testing +// +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 + +#include "gtest/gtest.h" + +#include "test/TestThrow.h" + +#include "radiant/TypeWrapper.h" + +template +using TW = rad::TypeWrapper; +using NTO = radtest::NonThrowingObject; +using TO = radtest::ThrowingObject; +using DNTO = radtest::DerivedNonThrowingObject; +using DTO = radtest::DerivedThrowingObject; + +// noexcept passthrough default ctor +RAD_S_ASSERT(noexcept(TW())); +RAD_S_ASSERT(!noexcept(TW())); + +// noexcept passthrough copy ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal()))); + +// noexcept passthrough move ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal()))); + +// noexcept passthrough initializer_list ctor +RAD_S_ASSERT(noexcept(TW({ 1, 2 }))); +RAD_S_ASSERT(!noexcept(TW({ 1, 2 }))); + +// noexcept passthrough wrapper default copy ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal&>()))); + +// noexcept passthrough wrapper default move ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal&&>()))); + +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal()))); + +// noexcept passthrough wrapper convertible ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal&>()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal&>()))); + +// noexcept passthrough wrapper convertible move ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal&&>()))); +RAD_S_ASSERT(!noexcept(TW(rad::DeclVal&&>()))); + +// noexcept passthrough forwarding ctor +RAD_S_ASSERT(noexcept(TW(1))); + +// noexcept passthrough type assign +RAD_S_ASSERT(noexcept(rad::DeclVal>() = rad::DeclVal())); +RAD_S_ASSERT(!noexcept(rad::DeclVal>() = rad::DeclVal())); + +// noexcept passthrough type move assign +RAD_S_ASSERT(noexcept(rad::DeclVal>() = rad::DeclVal())); +RAD_S_ASSERT(!noexcept(rad::DeclVal>() = rad::DeclVal())); + +// noexcept passthrough wrapper assign +RAD_S_ASSERT(noexcept(rad::DeclVal>() = rad::DeclVal&>())); +RAD_S_ASSERT(!noexcept(rad::DeclVal>() = rad::DeclVal&>())); + +// noexcept passthrough wrapper move assign +RAD_S_ASSERT(noexcept(rad::DeclVal>() = rad::DeclVal&&>())); +RAD_S_ASSERT(!noexcept(rad::DeclVal>() = rad::DeclVal&&>())); + +// noexcept l-ref ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); + +// noexcept const l-ref ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(TW(rad::DeclVal()))); + +// noexcept passthrough wrapper l-ref copy ctor +RAD_S_ASSERT(noexcept(TW(rad::DeclVal>()))); +RAD_S_ASSERT(noexcept(TW(rad::DeclVal>()))); + +// l-ref valid/invalid ctors +RAD_S_ASSERT((rad::IsCtor, TO&>)); +RAD_S_ASSERT((rad::IsCtor, DTO&>)); +RAD_S_ASSERT(!(rad::IsCtor, TO&&>)); +RAD_S_ASSERT(!(rad::IsCtor, const TO&>)); +RAD_S_ASSERT((rad::IsCtor, TO&>)); +RAD_S_ASSERT((rad::IsCtor, const TO&>)); +RAD_S_ASSERT((rad::IsCtor, const DTO&>)); + +RAD_S_ASSERT((rad::IsCtor, TW>)); +RAD_S_ASSERT((rad::IsCtor, const TW>)); +RAD_S_ASSERT(!(rad::IsCtor, const TW>)); +RAD_S_ASSERT((rad::IsCtor, TW>)); +RAD_S_ASSERT((rad::IsCtor, TW>)); +RAD_S_ASSERT((rad::IsCtor, const TW>)); + +TEST(TypeWrapperTests, DefaultCtor) +{ + EXPECT_THROW(TW(1), std::exception); + EXPECT_NO_THROW(TW()); +} + +TEST(TypeWrapperTests, Get) +{ + TW to(2); + EXPECT_EQ(to.Get(), 2); + + const TW cto(3); + EXPECT_EQ(cto.Get(), 3); +} + +TEST(TypeWrapperTests, TypeCopyCtor) +{ + TO to(2); + + TW twto(to); + EXPECT_EQ(twto.Get(), 2); + EXPECT_EQ(to, 2); + + TW twcto(to); + EXPECT_EQ(twcto.Get(), 2); + EXPECT_EQ(to, 2); + + const TW ctwto(to); + EXPECT_EQ(ctwto.Get(), 2); + EXPECT_EQ(to, 2); +} + +TEST(TypeWrapperTests, CopyInit) +{ + TW to = 1; + EXPECT_EQ(to, 1); +} + +TEST(TypeWrapperTests, CopyCtorFromRefTypeWrapper) +{ + int val = 2; + TW twri(val); + + TW twi(twri); + EXPECT_EQ(twi, val); +} + +TEST(TypeWrapperTests, TypeMoveCtor) +{ + TO to(2); + TW twto(rad::Move(to)); + EXPECT_EQ(twto.Get(), 2); + EXPECT_EQ(to, 0); + + TW twto2(TO(3)); + EXPECT_EQ(twto2.Get(), 3); +} + +#if RAD_ENABLE_STD + +TEST(TypeWrapperTests, TypeInitListCtor) +{ + TW twto({ 2, 3 }); + EXPECT_EQ(twto.Get(), 2); + EXPECT_EQ(twto, 2); + + TW twtoe({ 0 }); + EXPECT_EQ(twtoe.Get(), 0); + EXPECT_EQ(twtoe, 0); +} + +#endif // RAD_ENABLE_STD + +TEST(TypeWrapperTests, ConvertibleCtor) +{ + TW twdto(2); + TW twto(twdto); + EXPECT_EQ(twto.Get(), 2); +} + +TEST(TypeWrapperTests, ConvertibleMoveCtor) +{ + TW twto(TW(2)); + EXPECT_EQ(twto.Get(), 2); + + TW twto2(rad::Move(twto)); + EXPECT_EQ(twto.Get(), 0); + EXPECT_EQ(twto2, 2); +} + +TEST(TypeWrapperTests, TypeCopyAssign) +{ + int val = 2; + TW twto(3); + twto = val; + EXPECT_EQ(twto, 2); + twto = 4; + EXPECT_EQ(twto, 4); + + TO to(2); + twto = to; + EXPECT_EQ(twto, 2); + EXPECT_EQ(twto, to); + + TW twto2(0); + twto2 = to; + EXPECT_EQ(twto2, 2); + EXPECT_EQ(twto2, to); +} + +TEST(TypeWrapperTests, TypeMoveAssign) +{ + TO to(2); + + TW twto; + twto = rad::Move(to); + EXPECT_EQ(twto, 2); + EXPECT_EQ(to, 0); +} + +TEST(TypeWrapperTests, ConvertibleCopyAssign) +{ + TW to(2); + TW dto(3); + TW copy(0); + + copy = to; + EXPECT_EQ(copy, 2); + copy = dto; + EXPECT_EQ(copy, 3); +} + +TEST(TypeWrapperTests, ConvertibleMoveAssign) +{ + TW twto(0); + twto = TW{ 2 }; + EXPECT_EQ(twto, 2); + + TW other(3); + twto = rad::Move(other); + EXPECT_EQ(twto, 3); + EXPECT_EQ(other, 0); +} + +TEST(TypeWrapperTests, RefCtor) +{ + TO to(2); + TW twto(to); + EXPECT_EQ(&twto.Get(), &to); + EXPECT_EQ(twto, to); + + TW twcto(to); + EXPECT_EQ(&twcto.Get(), &to); + EXPECT_EQ(twcto, to); + + const TW ctwto(to); + EXPECT_EQ(&ctwto.Get(), &to); + EXPECT_EQ(ctwto, to); + + const TW ctwcto(to); + EXPECT_EQ(&ctwcto.Get(), &to); + EXPECT_EQ(ctwcto, to); +} + +TEST(TypeWrapperTests, RefCtorFromValueTypeWrapper) +{ + TW twto(2); + TW twcto(2); + + TW twrto(twto); + EXPECT_EQ(&twrto.Get(), &twto.Get()); + + TW twrtcto(twcto); + EXPECT_EQ(&twrtcto.Get(), &twcto.Get()); +} + +TEST(TypeWrapperTests, RefCopyCtor) +{ + TO to; + DTO dto; + TW twrto(to); + TW twrdto(dto); + const TW ctwrto(to); + + TW twto2(twrto); + EXPECT_EQ(&twto2.Get(), &to); + + TW twto3(twrdto); + EXPECT_EQ(&twto3.Get(), &dto); + + TW twcto(twrto); + EXPECT_EQ(&twcto.Get(), &to); + + const TW ctwcto(ctwrto); + EXPECT_EQ(&ctwcto.Get(), &to); +} + +TEST(TypeWrapperTests, RefMoveCtor) +{ + TO to; + TW twrto(to); + + TW twrto2(rad::Move(twrto)); + EXPECT_EQ(&twrto2.Get(), &to); + + TW twrto3(TW{ to }); + EXPECT_EQ(&twrto3.Get(), &to); +} + +TEST(TypeWrapperTests, RefAssignValue) +{ + TO to; + TO other; + TW rto(to); + EXPECT_EQ(&rto.Get(), &to); + rto = other; + EXPECT_EQ(&rto.Get(), &other); + + DTO dto; + rto = dto; + EXPECT_EQ(&rto.Get(), &dto); +} + +TEST(TypeWrapperTests, RefAssignRef) +{ + TO to; + TO other; + TO& rto(to); + + DTO dto; + DTO& rdto(dto); + + TW twrto(other); + EXPECT_EQ(&twrto.Get(), &other); + twrto = rto; + EXPECT_EQ(&twrto.Get(), &to); + + TW twrto2(other); + EXPECT_EQ(&twrto2.Get(), &other); + twrto2 = rdto; + EXPECT_EQ(&twrto2.Get(), &dto); +} + +TEST(TypeWrapperTests, RefCopyAssign) +{ + TO to; + TO other; + TW rto(to); + + TW rto2(other); + rto2 = rto; + EXPECT_EQ(&rto2.Get(), &to); + + TW crto(other); + crto = rto; + EXPECT_EQ(&crto.Get(), &to); +} + +TEST(TypeWrapperTests, RefMoveAssign) +{ + TO to; + TW twto(to); + TW twrto(rad::Move(twto)); + EXPECT_EQ(&twto.Get(), &to); + EXPECT_EQ(&twrto.Get(), &to); + + TW twcrto(TW{ to }); + EXPECT_EQ(&twcrto.Get(), &to); +} + +TEST(TypeWrapperTests, ComparisonEqual) +{ + int val = 2; + + TW twto(val); + TW twcto(2); + TW twrto(val); + TW twcrto(val); + + EXPECT_EQ(twto, 2); + EXPECT_EQ(2, twto); + EXPECT_EQ(twcto, 2); + EXPECT_EQ(2, twcto); + EXPECT_EQ(twto, twcto); + EXPECT_EQ(twcto, twto); + + EXPECT_EQ(twrto, twto); + EXPECT_EQ(twto, twrto); + EXPECT_EQ(twrto, val); + EXPECT_EQ(val, twrto); + EXPECT_EQ(twcrto, twrto); + EXPECT_EQ(twrto, twcrto); + EXPECT_EQ(twcrto, twto); + EXPECT_EQ(twto, twcrto); +} + +TEST(TypeWrapperTests, ComparisonNotEqual) +{ + int val = 4; + int other = 5; + int other2 = 6; + + TW twto(val); + TW twcto(2); + TW twrto(other); + TW twcrto(other2); + + EXPECT_NE(twto, 3); + EXPECT_NE(3, twto); + EXPECT_NE(twcto, 3); + EXPECT_NE(3, twcto); + EXPECT_NE(twto, twcto); + EXPECT_NE(twcto, twto); + + EXPECT_NE(twrto, twto); + EXPECT_NE(twto, twrto); + EXPECT_NE(twrto, val); + EXPECT_NE(val, twrto); + EXPECT_NE(twcrto, twrto); + EXPECT_NE(twrto, twcrto); + EXPECT_NE(twcrto, twto); + EXPECT_NE(twto, twcrto); +} + +TEST(TypeWrapperTests, ComparisonLessThan) +{ + int val = 4; + TW twto(2); + TW twcto(3); + + EXPECT_LT(twto, 3); + EXPECT_LT(1, twto); + EXPECT_LT(twcto, val); + EXPECT_LT(2, twcto); + EXPECT_LT(twto, twcto); +} + +TEST(TypeWrapperTests, ComparisonLessThanOrEqual) +{ + int val = 4; + int three = 3; + TW twto(2); + TW twcto(3); + + EXPECT_LE(twto, 3); + EXPECT_LE(twto, 2); + EXPECT_LE(1, twto); + EXPECT_LE(2, twto); + EXPECT_LE(twcto, val); + EXPECT_LE(twcto, three); + EXPECT_LE(2, twcto); + EXPECT_LE(3, twcto); + EXPECT_LE(twto, twcto); +} + +TEST(TypeWrapperTests, ComparisonGreaterThan) +{ + int val = 4; + TW twto(5); + TW twcto(6); + + EXPECT_GT(twto, 4); + EXPECT_GT(6, twto); + EXPECT_GT(twcto, val); + EXPECT_GT(7, twcto); + EXPECT_GT(twcto, twto); +} + +TEST(TypeWrapperTests, ComparisonGreaterThanOrEqual) +{ + int val = 1; + int three = 3; + TW twto(2); + TW twcto(3); + + EXPECT_GE(twto, 2); + EXPECT_GE(twto, 1); + EXPECT_GE(2, twto); + EXPECT_GE(3, twto); + EXPECT_GE(twcto, val); + EXPECT_GE(twcto, three); + EXPECT_GE(3, twcto); + EXPECT_GE(4, twcto); + EXPECT_GE(twcto, twto); +} diff --git a/test/test_Utility.cpp b/test/test_Utility.cpp new file mode 100644 index 0000000..89e1760 --- /dev/null +++ b/test/test_Utility.cpp @@ -0,0 +1,65 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +// +// Disable nothrow assertions for unit testing +// +#define RAD_ENABLE_NOTHROW_ASSERTIONS 0 + +#include "gtest/gtest.h" + +#include "radiant/Utility.h" + +#include "test/TestMove.h" + +RAD_S_ASSERT(noexcept(rad::Forward(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::Forward(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::Forward(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::Move(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::Move(rad::DeclVal()))); +RAD_S_ASSERT(noexcept(rad::MoveIfNoExcept(rad::DeclVal()))); + +TEST(Utility, Move) +{ + radtest::MoveTester::ResetCounts(); + radtest::MoveTester v1; + v1.value = 123; + radtest::MoveTester v2; + v2.value = 456; + + v2 = rad::Move(v1); + EXPECT_EQ(v2.value, 123u); + EXPECT_EQ(radtest::MoveTester::MoveCount(), 1u); + + radtest::MoveTester v3 = rad::Move(v2); + EXPECT_EQ(v3.value, 123u); + EXPECT_EQ(radtest::MoveTester::MoveCount(), 2u); +} + +TEST(Utility, MoveIfNoExcept) +{ + radtest::MoveTester::ResetCounts(); + radtest::MoveTester v1; + v1.value = 123; + radtest::MoveTester v2; + v2.value = 456; + + v2 = rad::MoveIfNoExcept(v1); + EXPECT_EQ(v2.value, 123u); + EXPECT_EQ(radtest::MoveTester::MoveCount(), 1u); + + radtest::MoveTester v3 = rad::MoveIfNoExcept(v2); + EXPECT_EQ(v3.value, 123u); + EXPECT_EQ(radtest::MoveTester::MoveCount(), 2u); +} diff --git a/test/test_Vector.cpp b/test/test_Vector.cpp new file mode 100644 index 0000000..de57504 --- /dev/null +++ b/test/test_Vector.cpp @@ -0,0 +1,1789 @@ +// Copyright 2023 The Radiant Authors. +// +// 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. + +#include "gtest/gtest.h" + +#include "test/TestAlloc.h" +#define RAD_DEFAULT_ALLOCATOR radtest::Allocator + +#include "radiant/Vector.h" + +#include + +struct VecTestStats +{ + int DtorCount = 0; + int DefaultCtorCount = 0; + int CtorCount = 0; + int CopyCtorCount = 0; + int MoveCtorCount = 0; + int CopyAssignCount = 0; + int MoveAssignCount = 0; + + void Reset() noexcept + { + DtorCount = 0; + DefaultCtorCount = 0; + CtorCount = 0; + CopyCtorCount = 0; + MoveCtorCount = 0; + CopyAssignCount = 0; + MoveAssignCount = 0; + } + + bool Empty() const noexcept + { + return (DtorCount == 0 && DefaultCtorCount == 0 && CtorCount == 0 && + CopyCtorCount == 0 && MoveCtorCount == 0 && + CopyAssignCount == 0 && MoveAssignCount == 0); + } +}; + +static VecTestStats g_stats; + +class VectorTests : public ::testing::Test +{ +public: + + void SetUp() override + { + g_stats.Reset(); + } + + void TearDown() override + { + EXPECT_TRUE(g_stats.Empty()); + } +}; + +class VecTester +{ +public: + + ~VecTester() + { + g_stats.DtorCount++; + m_stats.DtorCount++; + } + + VecTester() noexcept + { + g_stats.DefaultCtorCount++; + m_stats.DefaultCtorCount++; + } + + VecTester(int value) noexcept + : m_value(value) + { + g_stats.CtorCount++; + m_stats.CtorCount++; + } + + VecTester(const VecTester& o) noexcept + : m_value(o.m_value) + { + g_stats.CopyCtorCount++; + m_stats.CopyCtorCount++; + } + + VecTester(VecTester&& o) noexcept + : m_value(o.m_value) + { + g_stats.MoveCtorCount++; + m_stats.MoveCtorCount++; + } + + VecTester& operator=(const VecTester& o) noexcept + { + if (this != &o) + { + m_value = o.m_value; + } + + g_stats.CopyAssignCount++; + m_stats.CopyAssignCount++; + + return *this; + } + + VecTester& operator=(VecTester&& o) noexcept + { + if (this != &o) + { + m_value = o.m_value; + o.m_value = 0; + } + + g_stats.MoveAssignCount++; + m_stats.MoveAssignCount++; + + return *this; + } + + int m_value{ 0 }; + VecTestStats m_stats{}; +}; + +TEST(VectorTests, DefaultConstruct) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), 0u); +} + +TEST(VectorTests, InlineDefaultConstruct) +{ + rad::InlineVector vec; + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), 10u); +} + +TEST(VectorTests, AllocatorCopyConstruct) +{ + radtest::HeapAllocator heap; + radtest::AllocWrapper alloc(heap); + rad::Vector vec(alloc); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), 0u); + + EXPECT_EQ(heap.allocCount, 0); + EXPECT_EQ(heap.freeCount, 0); + EXPECT_EQ(vec.GetAllocator().base, &heap); +} + +TEST(VectorTests, AllocatorMoveConstruct) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), 0u); + + EXPECT_EQ(heap.allocCount, 0); + EXPECT_EQ(heap.freeCount, 0); + EXPECT_EQ(vec.GetAllocator().base, &heap); +} + +TEST(VectorTests, Reserve) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + EXPECT_TRUE(vec.Reserve(100).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 100u); + EXPECT_EQ(heap.allocCount, 1); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(50).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 100u); + EXPECT_EQ(heap.allocCount, 1); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(100).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 100u); + EXPECT_EQ(heap.allocCount, 1); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(150).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 150u); + EXPECT_EQ(heap.allocCount, 2); + EXPECT_EQ(heap.freeCount, 1); +} + +TEST(VectorTests, InlineReserve) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::InlineVector vec(heap); + + EXPECT_TRUE(vec.Reserve(5).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + EXPECT_EQ(heap.allocCount, 0); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(10).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + EXPECT_EQ(heap.allocCount, 0); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(100).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 100u); + EXPECT_EQ(heap.allocCount, 1); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(50).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 100u); + EXPECT_EQ(heap.allocCount, 1); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(100).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 100u); + EXPECT_EQ(heap.allocCount, 1); + EXPECT_EQ(heap.freeCount, 0); + + EXPECT_TRUE(vec.Reserve(150).IsOk()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), 150u); + EXPECT_EQ(heap.allocCount, 2); + EXPECT_EQ(heap.freeCount, 1); +} + +TEST(VectorTests, ReserveFail) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + heap.forceAllocFails = 1; + EXPECT_TRUE(vec.Reserve(100).IsErr()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), 0u); + EXPECT_EQ(heap.forceAllocFails, 0); + EXPECT_EQ(heap.allocCount, 0); +} + +TEST(VectorTests, InlineReserveFail) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::InlineVector vec(heap); + + heap.forceAllocFails = 1; + EXPECT_TRUE(vec.Reserve(100).IsErr()); + + EXPECT_TRUE(vec.Empty()); + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + EXPECT_EQ(heap.forceAllocFails, 0); + EXPECT_EQ(heap.allocCount, 0); +} + +TEST(VectorTests, PushBack) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + EXPECT_EQ(vec.Size(), 1u); + EXPECT_EQ(vec.At(0), 123); + + EXPECT_TRUE(vec.PushBack(456).IsOk()); + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec.At(0), 123); + EXPECT_EQ(vec.At(1), 456); + + EXPECT_TRUE(vec.PushBack(789).IsOk()); + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.At(0), 123); + EXPECT_EQ(vec.At(1), 456); + EXPECT_EQ(vec.At(2), 789); +} + +TEST(VectorTests, PushBackCopy) +{ + int value = 1337; + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(value).IsOk()); + EXPECT_EQ(value, 1337); + EXPECT_EQ(vec.Size(), 1u); + EXPECT_EQ(vec.At(0), 1337); + + EXPECT_TRUE(vec.PushBack(value).IsOk()); + EXPECT_EQ(value, 1337); + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec.At(0), 1337); + EXPECT_EQ(vec.At(1), 1337); + + EXPECT_TRUE(vec.PushBack(value).IsOk()); + EXPECT_EQ(value, 1337); + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.At(0), 1337); + EXPECT_EQ(vec.At(1), 1337); + EXPECT_EQ(vec.At(2), 1337); +} + +TEST(VectorTests, EmplaceBack) +{ + struct TestStruct + { + int First = 0; + bool Second = 0; + + TestStruct() noexcept = default; + + TestStruct(int One, bool Two) noexcept + : First(One), + Second(Two) + { + } + }; + + rad::Vector vec; + + EXPECT_TRUE(vec.EmplaceBack(123, false).IsOk()); + EXPECT_EQ(vec.Size(), 1u); + EXPECT_EQ(vec.At(0).First, 123); + EXPECT_EQ(vec.At(0).Second, false); + + EXPECT_TRUE(vec.EmplaceBack(456, true).IsOk()); + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec.At(1).First, 456); + EXPECT_EQ(vec.At(1).Second, true); + + EXPECT_TRUE(vec.EmplaceBack(789, false).IsOk()); + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.At(2).First, 789); + EXPECT_EQ(vec.At(2).Second, false); +} + +TEST(VectorTests, FrontBack) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + EXPECT_TRUE(vec.PushBack(456).IsOk()); + EXPECT_TRUE(vec.PushBack(789).IsOk()); + + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 789); + + const auto& constVec = vec; + + EXPECT_EQ(constVec.Front(), 123); + EXPECT_EQ(constVec.Back(), 789); +} + +TEST(VectorTests, At) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + EXPECT_TRUE(vec.PushBack(456).IsOk()); + EXPECT_TRUE(vec.PushBack(789).IsOk()); + + EXPECT_EQ(vec.At(0), 123); + EXPECT_EQ(vec.At(1), 456); + EXPECT_EQ(vec.At(2), 789); + + const auto& constVec = vec; + + EXPECT_EQ(constVec.At(0), 123); + EXPECT_EQ(constVec.At(1), 456); + EXPECT_EQ(constVec.At(2), 789); +} + +TEST(VectorTests, Subscript) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + EXPECT_TRUE(vec.PushBack(456).IsOk()); + EXPECT_TRUE(vec.PushBack(789).IsOk()); + + EXPECT_EQ(vec[0], 123); + EXPECT_EQ(vec[1], 456); + EXPECT_EQ(vec[2], 789); + + const auto& constVec = vec; + + EXPECT_EQ(constVec[0], 123); + EXPECT_EQ(constVec[1], 456); + EXPECT_EQ(constVec[2], 789); +} + +TEST(VectorTests, Data) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + EXPECT_TRUE(vec.PushBack(456).IsOk()); + EXPECT_TRUE(vec.PushBack(789).IsOk()); + + EXPECT_EQ(vec.Data()[0], 123); + EXPECT_EQ(vec.Data()[1], 456); + EXPECT_EQ(vec.Data()[2], 789); + + const auto& constVec = vec; + + EXPECT_EQ(constVec.Data()[0], 123); + EXPECT_EQ(constVec.Data()[1], 456); + EXPECT_EQ(constVec.Data()[2], 789); +} + +TEST(VectorTests, PopBack) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + EXPECT_TRUE(vec.PushBack(456).IsOk()); + EXPECT_TRUE(vec.PushBack(789).IsOk()); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.Back(), 789); + + vec.PopBack(); + + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec.Back(), 456); + + vec.PopBack(); + + EXPECT_EQ(vec.Size(), 1u); + EXPECT_EQ(vec.Back(), 123); + + vec.PopBack(); + + EXPECT_EQ(vec.Size(), 0u); +} + +TEST(VectorTests, Resize) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 0); + + EXPECT_TRUE(vec.Resize(4).IsOk()); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 0); + + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 0); + + EXPECT_TRUE(vec.Resize(6).IsOk()); + + EXPECT_EQ(vec.Size(), 6u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 0); +} + +TEST(VectorTests, ResizeCopy) +{ + int value = 1337; + + rad::Vector vec; + + EXPECT_TRUE(vec.PushBack(123).IsOk()); + + EXPECT_TRUE(vec.Resize(5, value).IsOk()); + + EXPECT_EQ(value, 1337); + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 1337); + + EXPECT_TRUE(vec.Resize(4, value).IsOk()); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 1337); + + EXPECT_TRUE(vec.Resize(5, value).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 1337); + + EXPECT_TRUE(vec.Resize(6, value).IsOk()); + + EXPECT_EQ(vec.Size(), 6u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 1337); +} + +TEST(VectorTests, Clear) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + + vec.Clear(); + + EXPECT_EQ(vec.Size(), 0u); +} + +TEST(VectorTests, ShrinkToFit) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + EXPECT_GE(vec.Capacity(), vec.Size()); + + vec.PopBack(); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_GE(vec.Capacity(), vec.Size()); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Capacity(), vec.Size()); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Capacity(), vec.Size()); + + vec.Clear(); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GT(vec.Capacity(), vec.Size()); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), vec.Size()); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), vec.Size()); +} + +TEST(VectorTests, ShrinkToFitInline) +{ + rad::InlineVector vec; + + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + vec.PopBack(); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + vec.Clear(); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + EXPECT_TRUE(vec.Resize(15).IsOk()); + + EXPECT_EQ(vec.Size(), 15u); + EXPECT_GE(vec.Capacity(), vec.Size()); + + vec.PopBack(); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 14u); + EXPECT_GE(vec.Capacity(), vec.Size()); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 14u); + EXPECT_GE(vec.Capacity(), vec.Size()); + + vec.Clear(); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_GE(vec.Capacity(), vec.Size()); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); + + // + // Shrink fits inline. + // + + EXPECT_TRUE(vec.Resize(12).IsOk()); + + vec.PopBack(); + vec.PopBack(); + + EXPECT_EQ(vec.Size(), 10u); + EXPECT_GT(vec.Capacity(), decltype(vec)::InlineCount); + + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(vec.Size(), 10u); + EXPECT_EQ(vec.Capacity(), decltype(vec)::InlineCount); +} + +TEST(VectorTests, AssignCopy) +{ + int value = 1337; + + rad::Vector vec; + + EXPECT_TRUE(vec.Assign(5, value).IsOk()); + + EXPECT_EQ(value, 1337); + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Front(), 1337); + EXPECT_EQ(vec.Back(), 1337); + + EXPECT_TRUE(vec.Assign(5, 123).IsOk()); + + EXPECT_EQ(vec.Size(), 5u); + EXPECT_EQ(vec.Front(), 123); + EXPECT_EQ(vec.Back(), 123); + + EXPECT_TRUE(vec.Assign(2, 456).IsOk()); + + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec.Front(), 456); + EXPECT_EQ(vec.Back(), 456); + + EXPECT_TRUE(vec.Assign(10, 789).IsOk()); + + EXPECT_EQ(vec.Size(), 10u); + EXPECT_EQ(vec.Front(), 789); + EXPECT_EQ(vec.Back(), 789); +} + +#if RAD_ENABLE_STD + +TEST(VectorTests, AssignInit) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.Front(), 1); + EXPECT_EQ(vec.Back(), 3); + + EXPECT_TRUE(vec.Assign({ 4, 5 }).IsOk()); + + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec.Front(), 4); + EXPECT_EQ(vec.Back(), 5); + + EXPECT_TRUE(vec.Assign({ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }).IsOk()); + + EXPECT_EQ(vec.Size(), 10u); + EXPECT_EQ(vec.Front(), 9); + EXPECT_EQ(vec.Back(), 0); +} + +TEST(VectorTests, AssignRange) +{ + rad::Vector other; + + EXPECT_TRUE(other.Assign({ 1, 2, 3 }).IsOk()); + + rad::Vector vec; + + EXPECT_TRUE(vec.Assign(other.ToSpan()).IsOk()); + + EXPECT_EQ(vec.Size(), other.Size()); + EXPECT_EQ(vec.Front(), other.Front()); + EXPECT_EQ(vec.Back(), other.Back()); +} + +TEST(VectorTests, Swap) +{ + rad::Vector vec; + rad::Vector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + EXPECT_TRUE(other.Assign({ 4, 5, 6, 7, 8, 9 }).IsOk()); + + vec.Swap(vec); + + vec.Swap(other); + + EXPECT_EQ(vec.Size(), 6u); + EXPECT_EQ(vec.Front(), 4); + EXPECT_EQ(vec.Back(), 9); + + EXPECT_EQ(other.Size(), 3u); + EXPECT_EQ(other.Front(), 1); + EXPECT_EQ(other.Back(), 3); +} + +TEST(VectorTests, InlineSwapBothInline) +{ + rad::InlineVector vec; + rad::InlineVector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + EXPECT_TRUE(other.Assign({ 4, 5, 6, 7 }).IsOk()); + + vec.Swap(vec); + + vec.Swap(other); + + EXPECT_EQ(vec.Size(), 4u); + EXPECT_EQ(vec.Front(), 4); + EXPECT_EQ(vec.Back(), 7); + + EXPECT_EQ(other.Size(), 3u); + EXPECT_EQ(other.Front(), 1); + EXPECT_EQ(other.Back(), 3); +} + +TEST(VectorTests, InlineSwapNeitherInline) +{ + rad::InlineVector vec; + rad::InlineVector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3, 4, 6 }).IsOk()); + EXPECT_TRUE(other.Assign({ 7, 8, 9, 10, 11, 12 }).IsOk()); + + vec.Swap(vec); + + vec.Swap(other); + + EXPECT_EQ(vec.Size(), 6u); + EXPECT_EQ(vec.Front(), 7); + EXPECT_EQ(vec.Back(), 12); + + EXPECT_EQ(other.Size(), 5u); + EXPECT_EQ(other.Front(), 1); + EXPECT_EQ(other.Back(), 6); +} + +TEST(VectorTests, InlineSwapDisjoint) +{ + rad::InlineVector vec; + rad::InlineVector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + EXPECT_TRUE(other.Assign({ 7, 8, 9, 10, 11, 12 }).IsOk()); + + vec.Swap(vec); + + vec.Swap(other); + + EXPECT_EQ(vec.Size(), 6u); + EXPECT_EQ(vec.Front(), 7); + EXPECT_EQ(vec.Back(), 12); + + EXPECT_EQ(other.Size(), 3u); + EXPECT_EQ(other.Front(), 1); + EXPECT_EQ(other.Back(), 3); + + vec.Swap(other); + + EXPECT_EQ(other.Size(), 6u); + EXPECT_EQ(other.Front(), 7); + EXPECT_EQ(other.Back(), 12); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.Front(), 1); + EXPECT_EQ(vec.Back(), 3); +} + +TEST(VectorTests, Copy) +{ + rad::Vector vec; + rad::Vector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_TRUE(vec.Copy(vec).IsOk()); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.Front(), 1); + EXPECT_EQ(vec.Back(), 3); + + EXPECT_TRUE(vec.Copy(other).IsOk()); + + EXPECT_EQ(vec.Size(), other.Size()); + EXPECT_EQ(vec.Front(), other.Front()); + EXPECT_EQ(vec.Back(), other.Back()); +} + +TEST(VectorTests, CopyOverwrite) +{ + rad::Vector vec; + rad::Vector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + EXPECT_TRUE(other.Assign({ 1, 2, 3, 4, 5 }).IsOk()); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.Front(), 1); + EXPECT_EQ(vec.Back(), 3); + + EXPECT_TRUE(vec.Copy(other).IsOk()); + + EXPECT_EQ(vec.Size(), other.Size()); + EXPECT_EQ(vec.Front(), other.Front()); + EXPECT_EQ(vec.Back(), other.Back()); +} + +TEST(VectorTests, Move) +{ + rad::Vector vec; + rad::Vector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + vec.Move(vec); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec.Front(), 1); + EXPECT_EQ(vec.Back(), 3); + + vec.Move(other); + + EXPECT_EQ(vec.Size(), 0u); + EXPECT_EQ(other.Size(), 3u); + EXPECT_EQ(other.Front(), 1); + EXPECT_EQ(other.Back(), 3); +} + +TEST(VectorTests, Double) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1.1, 2.2, 3.3 }).IsOk()); + + EXPECT_EQ(vec.At(0), 1.1); + EXPECT_EQ(vec.At(1), 2.2); + EXPECT_EQ(vec.At(2), 3.3); +} + +TEST(VectorTests, Float) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1.1f, 2.2f, 3.3f }).IsOk()); + + EXPECT_EQ(vec.At(0), 1.1f); + EXPECT_EQ(vec.At(1), 2.2f); + EXPECT_EQ(vec.At(2), 3.3f); +} + +TEST(VectorTests, RemoveBack) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_EQ(vec.RemoveBack(), 3); + EXPECT_EQ(vec.RemoveBack(), 2); + EXPECT_EQ(vec.RemoveBack(), 1); +} + +TEST(VectorTests, Seek) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_EQ(vec.Seek(0).Ok(), 1); + EXPECT_EQ(vec.Seek(1).Ok(), 2); + EXPECT_EQ(vec.Seek(2).Ok(), 3); + EXPECT_TRUE(vec.Seek(3).IsErr()); + + const auto& other = vec; + + EXPECT_EQ(other.Seek(0).Ok(), 1); + EXPECT_EQ(other.Seek(1).Ok(), 2); + EXPECT_EQ(other.Seek(2).Ok(), 3); + EXPECT_TRUE(other.Seek(3).IsErr()); +} + +TEST(VectorTests, SeekFront) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.SeekFront().IsErr()); + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_EQ(vec.SeekFront().Ok(), 1); + + const auto& other = vec; + + EXPECT_EQ(other.SeekFront(), 1); +} + +TEST(VectorTests, SeekBack) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.SeekBack().IsErr()); + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_EQ(vec.SeekBack().Ok(), 3); + + const auto& other = vec; + + EXPECT_EQ(other.SeekBack(), 3); +} + +TEST(VectorTests, MoveOperator) +{ + rad::Vector vec; + rad::Vector other; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + other = Move(vec); + + EXPECT_TRUE(vec.Empty()); + + EXPECT_EQ(other.Size(), 3u); + EXPECT_EQ(other[0], 1); + EXPECT_EQ(other[1], 2); + EXPECT_EQ(other[2], 3); +} + +TEST(VectorTests, AssignDown) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_TRUE(vec.Assign(1, 123).IsOk()); + + EXPECT_EQ(vec.Size(), 1u); + EXPECT_EQ(vec[0], 123); + + EXPECT_TRUE(vec.Assign(2, 123).IsOk()); + + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec[0], 123); + EXPECT_EQ(vec[1], 123); +} + +TEST(VectorTests, AssignOverlapping) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_EQ(vec.Assign(vec.ToSpan()).Err(), rad::Error::InvalidAddress); + EXPECT_EQ(vec.Assign(vec.ToSpan(0, 1)).Err(), rad::Error::InvalidAddress); + EXPECT_EQ(vec.Assign(vec.ToSpan(2, 1)).Err(), rad::Error::InvalidAddress); + EXPECT_EQ(vec.Assign(vec.ToSpan(2)).Err(), rad::Error::InvalidAddress); +} + +TEST(VectorTests, ResizeSame) +{ + rad::Vector vec; + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + EXPECT_TRUE(vec.Resize(3).IsOk()); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 2); + EXPECT_EQ(vec[2], 3); + + EXPECT_TRUE(vec.Resize(3, 123).IsOk()); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 2); + EXPECT_EQ(vec[2], 3); +} + +TEST(VectorTests, ShrinkToFitNoMemory) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + vec.PopBack(); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.ShrinkToFit().Err(), rad::Error::NoMemory); + + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 2); +} + +TEST(VectorTests, InlineShrinkToFitNoMemory) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + vec.PopBack(); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.ShrinkToFit().Err(), rad::Error::NoMemory); + + EXPECT_EQ(vec.Size(), 2u); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 2); +} + +TEST(VectorTests, CopyNoMemory) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + rad::Vector other(heap); + + EXPECT_TRUE(vec.Assign({ 1, 2, 3 }).IsOk()); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.Copy(other).Err(), rad::Error::NoMemory); + + EXPECT_EQ(vec.Size(), 3u); + EXPECT_EQ(vec[0], 1); + EXPECT_EQ(vec[1], 2); + EXPECT_EQ(vec[2], 3); +} + +#endif // RAD_ENABLE_STD + +TEST(VectorTests, ResizeNoMemory) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.Resize(3).Err(), rad::Error::NoMemory); + + EXPECT_TRUE(vec.Empty()); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.Resize(3, 123).Err(), rad::Error::NoMemory); + + EXPECT_TRUE(vec.Empty()); +} + +#if RAD_ENABLE_STD + +TEST(VectorTests, AssignNoMemory) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.Assign(3, 123).Err(), rad::Error::NoMemory); + + EXPECT_TRUE(vec.Empty()); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.Assign({ 1, 2, 3 }).Err(), rad::Error::NoMemory); + + EXPECT_TRUE(vec.Empty()); +} + +#endif // RAD_ENABLE_STD + +TEST(VectorTests, EmplaceBackNoMemory) +{ + using AllocWrap = radtest::AllocWrapper; + + radtest::HeapAllocator heap; + rad::Vector vec(heap); + + heap.forceAllocFails = 1; + EXPECT_EQ(vec.EmplaceBack(1).Err(), rad::Error::NoMemory); + + EXPECT_TRUE(vec.Empty()); +} + +#if RAD_ENABLE_STD + +TEST(VectorTests, EqualityOperators) +{ + rad::Vector left; + rad::Vector right; + + EXPECT_TRUE(left.Assign({ 1, 2, 3, 4 }).IsOk()); + EXPECT_TRUE(right.Assign({ 1, 2, 3, 4 }).IsOk()); + + EXPECT_TRUE(left == right); + EXPECT_TRUE(right == left); + EXPECT_FALSE(left != right); + EXPECT_FALSE(right != left); + + right.Clear(); + + EXPECT_FALSE(left == right); + EXPECT_FALSE(right == left); + EXPECT_TRUE(left != right); + EXPECT_TRUE(right != left); + + rad::InlineVector ilright; + + EXPECT_TRUE(ilright.Assign({ 1, 2, 3, 4 }).IsOk()); + + EXPECT_TRUE(left == ilright); + EXPECT_TRUE(ilright == left); + EXPECT_FALSE(left != ilright); + EXPECT_FALSE(ilright != left); + + ilright.Clear(); + + EXPECT_FALSE(left == ilright); + EXPECT_FALSE(ilright == left); + EXPECT_TRUE(left != ilright); + EXPECT_TRUE(ilright != left); + + EXPECT_TRUE(ilright.Assign({ 1, 1, 1, 1 }).IsOk()); + + EXPECT_FALSE(left == ilright); + EXPECT_FALSE(ilright == left); + EXPECT_TRUE(left != ilright); + EXPECT_TRUE(ilright != left); +} + +TEST(VectorTests, ComparisonOperators) +{ + rad::Vector left; + rad::Vector right; + + EXPECT_TRUE(left.Assign({ 1, 2, 3, 4 }).IsOk()); + EXPECT_TRUE(right.Assign({ 1, 2, 3, 4 }).IsOk()); + + EXPECT_FALSE(left < right); + EXPECT_FALSE(left > right); + EXPECT_TRUE(left <= right); + EXPECT_TRUE(left >= right); + + EXPECT_FALSE(right < left); + EXPECT_FALSE(right > left); + EXPECT_TRUE(right <= left); + EXPECT_TRUE(right >= left); + + EXPECT_TRUE(left.Assign({ 1, 1, 1, 1 }).IsOk()); + EXPECT_TRUE(right.Assign({ 2, 2, 2, 2 }).IsOk()); + + EXPECT_TRUE(left < right); + EXPECT_FALSE(left > right); + EXPECT_TRUE(left <= right); + EXPECT_FALSE(left >= right); + + EXPECT_FALSE(right < left); + EXPECT_TRUE(right > left); + EXPECT_FALSE(right <= left); + EXPECT_TRUE(right >= left); + + EXPECT_TRUE(left.Assign({ 1, 1, 1 }).IsOk()); + EXPECT_TRUE(right.Assign({ 1, 1, 1, 1 }).IsOk()); + + EXPECT_TRUE(left < right); + EXPECT_FALSE(left > right); + EXPECT_TRUE(left <= right); + EXPECT_FALSE(left >= right); + + EXPECT_FALSE(right < left); + EXPECT_TRUE(right > left); + EXPECT_FALSE(right <= left); + EXPECT_TRUE(right >= left); + + rad::InlineVector ilright; + + EXPECT_TRUE(ilright.Assign({ 1, 1, 1, 1 }).IsOk()); + + EXPECT_TRUE(left < right); + EXPECT_FALSE(left > right); + EXPECT_TRUE(left <= right); + EXPECT_FALSE(left >= right); + + EXPECT_FALSE(right < left); + EXPECT_TRUE(right > left); + EXPECT_FALSE(right <= left); + EXPECT_TRUE(right >= left); + + std::vector v; +} + +#endif // RAD_ENABLE_STD + +TEST(VectorTests, NonTrivReserve) +{ + g_stats.Reset(); + + rad::Vector vec; + + EXPECT_TRUE(vec.Reserve(10).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivResize) +{ + VecTester value(123); + rad::Vector vec; + + g_stats.Reset(); + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 5); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + EXPECT_TRUE(vec.Resize(5).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + EXPECT_TRUE(vec.Resize(2).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 3); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + EXPECT_TRUE(vec.Resize(2, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + EXPECT_TRUE(vec.Resize(5, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 3); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + EXPECT_TRUE(vec.Resize(10, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 5); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 5); + EXPECT_EQ(g_stats.MoveCtorCount, 5); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivAssign) +{ + VecTester value(123); + + rad::Vector vec; + + g_stats.Reset(); + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 10); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + value = 456; + + g_stats.Reset(); + EXPECT_TRUE(vec.Assign(20, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 10); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 20); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + EXPECT_TRUE(vec.Assign(5, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 20); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 5); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivClear) +{ + VecTester value(123); + + rad::Vector vec; + + g_stats.Reset(); + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 10); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); + vec.Clear(); + + EXPECT_EQ(g_stats.DtorCount, 10); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivPushBackLVal) +{ + VecTester value(123); + + rad::Vector vec; + + g_stats.Reset(); + EXPECT_TRUE(vec.PushBack(value).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 1); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivPushBackRVal) +{ + VecTester value(123); + + rad::Vector vec; + + g_stats.Reset(); + EXPECT_TRUE(vec.PushBack(rad::Move(value)).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 1); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivPushEmplaceBack) +{ + rad::Vector vec; + + g_stats.Reset(); + EXPECT_TRUE(vec.EmplaceBack(123).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 1); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivCopy) +{ + VecTester value(123); + + rad::Vector vec; + + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + rad::Vector other; + + g_stats.Reset(); + EXPECT_TRUE(vec.Copy(other).IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 10); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 10); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivMove) +{ + VecTester value(123); + + rad::Vector vec; + + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + rad::Vector other; + + g_stats.Reset(); + vec.Move(other); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivShrinkToFit) +{ + VecTester value(123); + + rad::Vector vec; + + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + vec.PopBack(); + + g_stats.Reset(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 9); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 9); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + while (!vec.Empty()) + { + vec.PopBack(); + } + + g_stats.Reset(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, NonTrivSwap) +{ + VecTester value(123); + + rad::Vector vec; + + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + rad::Vector other; + + g_stats.Reset(); + vec.Swap(other); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, InlineNonTrivShrinkToFit) +{ + VecTester value(123); + + rad::InlineVector vec; + + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + vec.PopBack(); + + g_stats.Reset(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 9); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 9); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + + g_stats.Reset(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 5); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 5); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + while (!vec.Empty()) + { + vec.PopBack(); + } + + g_stats.Reset(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + g_stats.Reset(); +} + +TEST(VectorTests, InlineNonTrivSwap) +{ + VecTester value(123); + + rad::InlineVector vec; + + EXPECT_TRUE(vec.Assign(10, value).IsOk()); + + rad::InlineVector other; + + EXPECT_TRUE(other.Assign(10, value).IsOk()); + + g_stats.Reset(); + vec.Swap(other); + + EXPECT_EQ(g_stats.DtorCount, 0); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 0); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + g_stats.Reset(); + vec.Swap(other); + + EXPECT_EQ(g_stats.DtorCount, 5); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 5); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 0); + + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + vec.PopBack(); + EXPECT_TRUE(vec.ShrinkToFit().IsOk()); + + g_stats.Reset(); + vec.Swap(other); + + EXPECT_EQ(g_stats.DtorCount, 5); + EXPECT_EQ(g_stats.DefaultCtorCount, 0); + EXPECT_EQ(g_stats.CtorCount, 0); + EXPECT_EQ(g_stats.CopyCtorCount, 0); + EXPECT_EQ(g_stats.MoveCtorCount, 10); + EXPECT_EQ(g_stats.CopyAssignCount, 0); + EXPECT_EQ(g_stats.MoveAssignCount, 5); + + g_stats.Reset(); +} + +struct VecTestStruct +{ + uint64_t UInt64; + double Double; +}; + +TEST(VectorTests, TrivialStructurePushBack) +{ + rad::Vector vec; + + VecTestStruct data; + for (uint32_t i = 0; i < 10; i++) + { + data.UInt64 = i; + data.Double = static_cast(i); + + EXPECT_TRUE(vec.PushBack(data).IsOk()); + } + + for (uint32_t i = 0; i < 10; i++) + { + auto& entry = vec.At(i); + EXPECT_EQ(entry.UInt64, i); + EXPECT_EQ(entry.Double, static_cast(i)); + } +} + +TEST(VectorTests, Pointers) +{ + rad::Vector vec; + + VecTestStruct data; + for (uint32_t i = 0; i < 10; i++) + { + data.UInt64 = i; + data.Double = static_cast(i); + + EXPECT_TRUE(vec.PushBack(&data).IsOk()); + } + + for (uint32_t i = 0; i < 10; i++) + { + auto& entry = vec.At(i); + + EXPECT_EQ(entry, &data); + EXPECT_EQ(entry->UInt64, 9); + EXPECT_EQ(entry->Double, static_cast(9)); + } +} diff --git a/tools/rad/README.md b/tools/rad/README.md new file mode 100644 index 0000000..285791b --- /dev/null +++ b/tools/rad/README.md @@ -0,0 +1,19 @@ +# Radiant Tooling Library + +The Radiant tooling library is a collection of tools for developing Radiant. +Development environment initialization, building, testing, code coverage, and +more are all handled through the tooling library. It is recommended to install +the library in the python environment. Then the command line interface can be +invoked directly. + +``` +python -m pip install -e ./tools/rad + +rad --help +``` + +Alternatively, the library may be invoked directly without installing it. + +``` +python ./tools/rad --help +``` diff --git a/tools/rad/__main__.py b/tools/rad/__main__.py new file mode 100644 index 0000000..c0bc16d --- /dev/null +++ b/tools/rad/__main__.py @@ -0,0 +1,25 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Command line interface for the Radiant project.""" + +from rad import cli + +# Enables the package to be execute ad-hoc without installing +# python tools/rad --help + +if __name__ == "__main__": + cli.main() diff --git a/tools/rad/pyproject.toml b/tools/rad/pyproject.toml new file mode 100644 index 0000000..1dd464c --- /dev/null +++ b/tools/rad/pyproject.toml @@ -0,0 +1,21 @@ +[project] +name = "rad" +description = "Radiant Tooling Library" +version = "0.1.0" +requires-python = ">=3.11" +authors = [{ name = "Radiant Authors" }] +dependencies = [ + "pre-commit", + "clang-format", + "pylint", + "black", + "lcov_cobertura", + "yamllint", +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project.scripts] +rad = "rad.cli:main" diff --git a/tools/rad/rad/__init__.py b/tools/rad/rad/__init__.py new file mode 100644 index 0000000..320fbc7 --- /dev/null +++ b/tools/rad/rad/__init__.py @@ -0,0 +1,19 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Radiant Tooling modules.""" + +__all__ = ["cli", "bazel", "coverage", "intellisense"] diff --git a/tools/rad/rad/bazel.py b/tools/rad/rad/bazel.py new file mode 100644 index 0000000..e935058 --- /dev/null +++ b/tools/rad/rad/bazel.py @@ -0,0 +1,292 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Utility functions to execute or query bazel""" + +import json +import logging +import os +import pathlib +import shutil +import subprocess +import sys +from lcov_cobertura.lcov_cobertura import LcovCobertura +from . import repo + + +class _Bazel: + """Class for managing information about and invoking bazel. Handles lazily + locating the bazel executable when needed. Should be accessed via the BAZEL + global variable.""" + + def __init__(self): + self._located_filename = None + + def run(self, args) -> bool: + """Runs bazel with the given arguments.""" + proc = subprocess.run([self.filename()] + args, check=False) + return proc.returncode == 0 + + def capture_output(self, args) -> str: + """Runs bazel with the given arguments and captures the output.""" + proc = subprocess.run( + [self.filename()] + args, capture_output=True, text=True, check=False + ) + return proc.stdout.strip() + + def filename(self) -> str: + """Returns the bazel filename.""" + if self._located_filename is None: + self._located_filename = self._locate_bazel() + return self._located_filename + + def _locate_bazel(self): + """Locates a suitable bazel executable.""" + files = [ + "bazelisk", + "bazel", + ] + for f in files: + bazel = shutil.which(f) + if bazel is not None: + return bazel + logging.error("bazel not found!") + raise FileNotFoundError("bazel not found!") + + +BAZEL = _Bazel() + + +def build(label, args=None): + """Builds using bazel with the given label and arguments.""" + if args is None: + args = [] + logging.info("starting build") + res = BAZEL.run(["build"] + args + [label]) + logging.log( + logging.INFO if res else logging.ERROR, + "build finished" if res else "build failed!", + ) + return res + + +def clean(expunge): + """Cleans the bazel workspace.""" + logging.info("starting clean") + res = BAZEL.run(["clean"] + (["--expunge"] if expunge else [])) + logging.log( + logging.INFO if res else logging.ERROR, + "clean finished" if res else "clean failed!", + ) + return res + + +def test(label, args=None): + """Runs tests using bazel with the given label and arguments.""" + if args is None: + args = [] + logging.info("starting test") + res = BAZEL.run(["test"] + args + [label]) + logging.log( + logging.INFO if res else logging.ERROR, + "test finished" if res else "test failed!", + ) + return res + + +def generate_compile_commands(): + """Generates compile_commands.json using bazel.""" + logging.info("generating compile_commands.json") + res = BAZEL.run(["run", "@hedron_compile_commands//:refresh_all"]) + logging.log( + logging.INFO if res else logging.ERROR, + "compile_commands.json generated" if res else "compile_commands.json failed!", + ) + return res + + +def coverage(label, output_xml=None, filters=None): + """Runs coverage using bazel.""" + try: + shutil.rmtree(pathlib.Path("bazel-out") / "_coverage") + except FileNotFoundError: + pass + + if filters is None: + filters = "radiant" + + command = ["coverage"] + + if sys.platform == "darwin": + # HACK: https://github.com/bazelbuild/bazel/issues/14970#issuecomment-1894565761 + command.extend( + [ + "--experimental_generate_llvm_lcov", + # pylint: disable=line-too-long + "--test_env=COVERAGE_GCOV_PATH=/Library/Developer/CommandLineTools/usr/bin/llvm-profdata", + "--test_env=LLVM_COV=/Library/Developer/CommandLineTools/usr/bin/llvm-cov", + "--copt=-ffile-compilation-dir=.", + ] + ) + + command.extend( + [ + "--instrumentation_filter=" + filters, + "--combined_report=lcov", + label, + ] + ) + + logging.info("starting coverage") + + if BAZEL.run(command) is False: + logging.error("coverage failed!") + return False + + logging.info("generating coverage.xml") + + lcov_path = repo.ROOT_PATH / "bazel-out" / "_coverage" / "_coverage_report.dat" + if output_xml is None: + coverage_path = repo.ROOT_PATH / "bazel-out" / "coverage.xml" + else: + coverage_path = output_xml + try: + with open(lcov_path, "r", encoding="utf-8") as lcov_file: + lcov_data = lcov_file.read() + lcov_cobertura = LcovCobertura( + lcov_data, str(repo.ROOT_PATH), demangle=True + ) + cobertura_xml = lcov_cobertura.convert() + with open(coverage_path, mode="wt", encoding="utf-8") as output_file: + output_file.write(cobertura_xml) + except IOError: + logging.error("unable to convert %s to cobertura xml", lcov_path) + return False + + logging.info("coverage finished") + return True + + +def get_execution_root(): + """Get the bazel execution_root value""" + return BAZEL.capture_output(["info", "execution_root"]) + + +def get_output_executables(): + """Get list of built executables""" + output = BAZEL.capture_output( + [ + "aquery", + 'mnemonic("CppLink", ...)', + "--output=jsonproto", + "--include_param_files=true", + "--include_artifacts=false", + ] + ) + j = json.loads(output) + execs = [] + for action in j["actions"]: + for arg in action["arguments"]: + if arg.startswith("/OUT:") and arg.endswith(".exe"): + execs.append(arg[5:]) + return execs + + +def get_output_labels(): + """Get list of target labels that generate a binary""" + output = BAZEL.capture_output( + [ + "aquery", + 'mnemonic("CppLink", ...)', + "--output=jsonproto", + "--include_artifacts=true", + ] + ) + j = json.loads(output) + labels = [] + for target in j["targets"]: + labels.append(target["label"]) + return labels + + +def _get_cpplink_executable(args): + """Extract the output executable path from the given CppLink arguments""" + for arg in args: + if arg.startswith("/OUT:") and arg.endswith(".exe"): + return arg[5:] + return None + + +def get_label_executables(label): + """Get the result executables from the given label""" + output = BAZEL.capture_output( + [ + "aquery", + f'mnemonic("CppLink", (outputs(".*exe", deps({label}))))', + "--output=jsonproto", + ] + ) + exes = [] + j = json.loads(output) + for target in j["targets"]: + target_id = target["id"] + for action in j["actions"]: + if action["targetId"] == target_id: + exes.append(_get_cpplink_executable(action["arguments"])) + break + return sorted(exes) + + +def get_compile_commands(): + """Returns a list of commands. Each command being a list of arguments.""" + output = BAZEL.capture_output( + [ + "aquery", + 'mnemonic("CppCompile", ...)', + "--output=jsonproto", + "--include_param_files=true", + "--include_artifacts=false", + ] + ) + j = json.loads(output) + return [action["arguments"] for action in j["actions"]] + + +def get_includes(commands): + """Returns a list of includes extracted from all given commands. + Filters out "." and paths starting with "bazel-out".""" + includes = {} + prev = False + for cmd in commands: + for arg in cmd: + if arg.startswith("/I"): + includes[arg[2:]] = True + elif arg in ("-isystem", "-iquote"): + prev = True + elif prev: + includes[arg], prev = True, False + return list( + filter(lambda x: x != "." and not x.startswith("bazel-out"), includes.keys()) + ) + + +def maybe_create_external_link(): + """Create ./external (symlink/directory junction) to the bazel output + external directory if it doesn't already exist.""" + target = pathlib.Path(BAZEL.capture_output(["info", "output_base"])) / "external" + link = pathlib.Path("external") + if target.exists() and not link.exists(): + os.symlink(target, link, target_is_directory=True) diff --git a/tools/rad/rad/cli.py b/tools/rad/rad/cli.py new file mode 100644 index 0000000..5c76f1c --- /dev/null +++ b/tools/rad/rad/cli.py @@ -0,0 +1,326 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Radiant Development Tool command line interface.""" + +import argparse +import logging +import os +import pathlib +import subprocess +import sys + +import rad.bazel +import rad.coverage +import rad.intellisense +import rad.repo + + +def init(args): # pylint: disable=unused-argument + """Initializes the Radiant development environment.""" + print("Initializing Radiant Development Environment...") + logging.info("Setting up pre-commit hooks...") + subprocess.run( + [ + "pre-commit", + "install", + "--config", + rad.repo.ROOT_PATH / ".pre-commit-config.yaml", + ], + check=False, + ) + logging.info("Generating compile_commands.json...") + rad.bazel.generate_compile_commands() + # Disabled in favor of compile_commands.json. Required admin on Windows to + # initialize. Might be removed completely in the future. + # logging.info("Setting up vscode c_cpp_properties.json...") + # rad.intellisense.update_vscode_configurations() + + +def build(args): + """Builds the Radiant project.""" + if args.clean: + rad.bazel.clean(False) + output = { + logging.ERROR: [], + logging.WARNING: [], + logging.INFO: ["--subcommands=pretty_print"], + logging.DEBUG: ["--subcommands"], + } + verbosity = output[logging.root.level] + mode = ["-c", "opt"] if args.release else ["-c", "dbg"] + platform = [] + if os.name == "nt": + if args.win_x86: + platform = ["--platforms=:windows_x86"] + elif args.win_x64: + platform = ["--platforms=:windows_x64"] + elif args.win_arm: + platform = ["--platforms=:windows_arm"] + elif args.win_arm64: + platform = ["--platforms=:windows_arm64"] + clang = ["--repo_env=CC=clang"] if os.name != "nt" and args.clang else [] + nostd = ["--copt=-DRAD_NO_STD"] if args.no_std else [] + rad.bazel.build("//...", clang + mode + platform + nostd + verbosity) + + +def clean(args): + """Cleans the Radiant project.""" + rad.bazel.clean(args.expunge) + + +def get_label(args): + """Returns the label to use for the given arguments.""" + if args.label: + return args.label + if not args.select: + return "//..." + labels = rad.bazel.get_output_labels() + labels.insert(0, "//...") + for n, label in enumerate(labels): + print(f"{n + 1}) {label}") + n = int(input("Select: ")) - 1 + if n < 0 or n >= len(labels): + return "//..." + return labels[n] + + +def test(args): + """Runs the unit tests for the Radiant project.""" + if args.clean: + rad.bazel.clean(False) + output = { + logging.ERROR: ["--test_output=summary", "--test_summary=terse"], + logging.WARNING: ["--test_output=summary", "--test_summary=short"], + logging.INFO: ["--test_output=summary", "--test_summary=detailed"], + logging.DEBUG: ["--test_output=all", "--test_summary=detailed"], + } + params = output[logging.root.level] + if args.no_cache: + params.append("--nocache_test_results") + if args.no_std: + params.append("--copt=-DRAD_NO_STD") + rad.bazel.test(get_label(args), params) + + +def coverage(args): + """Generates coverage data for the Radiant project.""" + if args.clean: + rad.bazel.clean(False) + label = get_label(args) + if os.name == "nt": + exec_root = rad.bazel.get_execution_root() + pdb_prefix = pathlib.PurePath(exec_root) / "radiant" + rad.coverage.generate_coverage( + label, + args.output_xml, + filters="radiant\\*", + pdb_prefix=pdb_prefix, + pdb_prefix_replace="radiant", + ) + else: + rad.coverage.generate_coverage(label, args.output_xml, filters="radiant") + + +def lint(args): + """Runs lint checks for the Radiant project.""" + env = os.environ.copy() + if args.skip: + env["SKIP"] = args.skip + command = [ + "pre-commit", + "run", + "--config", + rad.repo.ROOT_PATH / ".pre-commit-config.yaml", + ] + if args.all_files: + command.append("--all-files") + subprocess.run(command, env=env, check=False) + + +def setup_logging(verbosity): + """Sets up the logging configuration.""" + levels = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG] + level = levels[min(verbosity, len(levels) - 1)] + logging.basicConfig( + level=level, + format="%(asctime)s %(levelname)-8s %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + + +def main(): + """Main entry point for the Radiant Development Tool.""" + global_parser = argparse.ArgumentParser(add_help=False) + global_parser.add_argument( + "-v", "--verbose", action="count", default=0, help="Increase verbosity level" + ) + parser = argparse.ArgumentParser( + prog="rad", + description="Radiant Development Tool", + parents=[global_parser], + ) + subparsers = parser.add_subparsers( + title="commands", + description="valid commands", + required=True, + ) + + # rad init + + init_praser = subparsers.add_parser( + "init", + help="Development initialization", + parents=[global_parser], + ) + init_praser.set_defaults(func=init) + + # rad build + + build_parser = subparsers.add_parser( + "build", + help="Build using bazel", + parents=[global_parser], + ) + build_parser.add_argument( + "--clean", action="store_true", help="Clean before building" + ) + build_parser.add_argument( + "--release", action="store_true", help="Builds release instead of debug" + ) + if os.name == "nt": + plat = build_parser.add_mutually_exclusive_group() + plat.add_argument( + "--win_x86", action="store_true", help="Cross compile for x86 target" + ) + plat.add_argument( + "--win_x64", action="store_true", help="Cross compile for x64 target" + ) + plat.add_argument( + "--win_arm", action="store_true", help="Cross compile for ARM target" + ) + plat.add_argument( + "--win_arm64", action="store_true", help="Cross compile for ARM64 target" + ) + else: + build_parser.add_argument( + "--clang", action="store_true", help="Use clang compiler" + ) + build_parser.add_argument( + "--no-std", + action="store_true", + help="Build with RAD_NO_STD", + ) + build_parser.set_defaults(func=build) + + # rad clean + + clean_parser = subparsers.add_parser( + "clean", + help="Clean using bazel", + parents=[global_parser], + ) + clean_parser.add_argument( + "--expunge", + action="store_true", + help="Removes entire working tree for bazel instance and stops the bazel server.", + ) + clean_parser.set_defaults(func=clean) + + # rad test + + test_parser = subparsers.add_parser( + "test", help="Build and execute unit tests", parents=[global_parser] + ) + test_parser.add_argument( + "--clean", action="store_true", help="Clean before building" + ) + test_parser.add_argument( + "--no-cache", + action="store_true", + help="Ignore cached test results, run all tests unconditionally.", + ) + test_parser.add_argument( + "--label", + required=False, + help="Test the given label", + ) + test_parser.add_argument( + "--select", + action="store_true", + help="Select a test label interactively", + ) + test_parser.add_argument( + "--no-std", + action="store_true", + help="Tests with RAD_NO_STD", + ) + test_parser.set_defaults(func=test) + + # rad coverage + + coverage_parser = subparsers.add_parser( + "coverage", + help="Generate coverage data", + parents=[global_parser], + ) + coverage_parser.add_argument( + "--clean", action="store_true", help="Clean before building" + ) + coverage_parser.add_argument( + "--output-xml", + default=rad.repo.ROOT_PATH / "bazel-out" / "coverage.xml", + help="Output coverage xml file (default: bazel-out/coverage.xml)", + ) + coverage_parser.add_argument( + "--label", + required=False, + help="Generate coverage for the given label", + ) + coverage_parser.add_argument( + "--select", + action="store_true", + help="Select a coverage label interactively", + ) + coverage_parser.set_defaults(func=coverage) + + # rad lint + + lint_parser = subparsers.add_parser( + "lint", + help="Run lint checks", + parents=[global_parser], + ) + lint_parser.add_argument( + "--all-files", + action="store_true", + help="Run lint checks on all files in repo", + ) + lint_parser.add_argument( + "--skip", + type=str, + required=False, + help="Skip the specified lint checks", + ) + lint_parser.set_defaults(func=lint) + + # parse arguments... + + args = parser.parse_args(sys.argv[1:]) + + setup_logging(args.verbose) + + args.func(args) diff --git a/tools/rad/rad/coverage.py b/tools/rad/rad/coverage.py new file mode 100644 index 0000000..7c133f5 --- /dev/null +++ b/tools/rad/rad/coverage.py @@ -0,0 +1,120 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Utility functions to generate coverage data for the Radiant project.""" + +import os +import pathlib +import subprocess +import logging +from xml.etree import ElementTree +from . import bazel + + +def _load_coverage_xml(path): + """Load colbertura xml from file""" + return ElementTree.parse(path) + + +def _merge_coverage_xml(dst, src): + """Combine dst and src colbertura xml with result in dst.""" + srccov = src.findall(".")[0] + dstcov = dst.findall(".")[0] + lines_covered = int(dstcov.get("lines-covered")) + int(srccov.get("lines-covered")) + lines_valid = int(dstcov.get("lines-valid")) + int(srccov.get("lines-valid")) + dstcov.set("lines-covered", str(lines_covered)) + dstcov.set("lines-valid", str(lines_valid)) + if lines_valid == 0: + dstcov.set("line-rate", "0.0") + else: + dstcov.set("line-rate", str(lines_covered / lines_valid)) + packages = dst.findall("packages") + if not packages or len(packages) > 1: + return + packages[0].extend(src.iter("package")) + + +def _opencppcoverage( + label, output_xml=None, srcs_glob=None, pdb_prefix=None, pdb_prefix_replace=None +): + """Execute OpenCppCoverage to generate the given output xml file in cobertura format""" + bazel.build(label) + executables = bazel.get_label_executables(label) + if not executables: + return + + output_xml = pathlib.Path(output_xml) + output = ["--export_type", "cobertura:" + str(output_xml)] + + srcs = [] + if srcs_glob: + srcs = ["--sources", srcs_glob] + + pdb_substitute = [] + if pdb_prefix: + pdb_substitute = [ + "--substitute_pdb_source_path", + str(pdb_prefix) + "?" + pdb_prefix_replace, + ] + + for exe in executables: + if not pathlib.Path(exe).exists(): + bazel.build(label) + break + + combined = None + res = True + logging.info("starting coverage") + for exe in executables: + logging.info("running coverage for %s", exe) + proc = subprocess.run( + ["OpenCppCoverage", "--quiet"] + + srcs + + pdb_substitute + + output + + ["--", exe], + stdout=subprocess.DEVNULL, + check=False, + ) + if proc.returncode != 0: + res = False + break + + xml = _load_coverage_xml(output_xml) + try: + os.remove(output_xml) + except FileNotFoundError: + pass + if not combined: + combined = xml + else: + _merge_coverage_xml(combined, xml) + logging.log( + logging.INFO if res else logging.ERROR, + "coverage finished" if res else "coverage failed!", + ) + if combined: + combined.write(output_xml, xml_declaration=True, method="xml", encoding="utf-8") + + +def generate_coverage( + label, output_xml=None, filters=None, pdb_prefix=None, pdb_prefix_replace=None +): + """Generate coverage data for the Radiant project.""" + if os.name == "nt": + _opencppcoverage(label, output_xml, filters, pdb_prefix, pdb_prefix_replace) + else: + bazel.coverage(label, output_xml, filters) diff --git a/tools/rad/rad/intellisense.py b/tools/rad/rad/intellisense.py new file mode 100644 index 0000000..b6bde80 --- /dev/null +++ b/tools/rad/rad/intellisense.py @@ -0,0 +1,119 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Updates the given configurations within .vscode/c_cpp_properties.json to +include the include paths extracted via bazel aquery.""" + +import os +import json +import shutil +import logging +from . import repo +from . import bazel + +_LINUX_C_CPP_PROPERTIES = """{ + "configurations": [ + { + "name": "debug", + "includePath": [ + "${workspaceFolder}" + ], + "intelliSenseMode": "linux-gcc-x64", + "compilerPath": "/usr/bin/gcc", + "cStandard": "c17", + "cppStandard": "c++14" + }, + { + "name": "release", + "includePath": [ + "${workspaceFolder}" + ], + "defines": [ + "NDEBUG" + ], + "intelliSenseMode": "linux-gcc-x64", + "compilerPath": "/usr/bin/gcc", + "cStandard": "c17", + "cppStandard": "c++14" + } + ], + "version": 4 +} +""" + + +def _maybe_generate_default_c_cpp_properties(): + """If .vscode/c_cpp_properties.json doesn't exist, generate a default.""" + dst = repo.ROOT_PATH / ".vscode" / "c_cpp_properties.json" + if dst.exists(): + logging.debug("c_cpp_properties.json already exists") + return + if os.name == "nt": + has_ewdkdir = os.environ.get("EWDKDIR", None) + if has_ewdkdir: + src = ( + repo.ROOT_PATH + / "external " + / "ewdk_cc_configured_toolchain" + / "c_cpp_properties.json" + ) + shutil.copy(src=src, dst=dst) + else: + pass + else: + with open(dst, "w", encoding="utf-8") as file: + file.write(_LINUX_C_CPP_PROPERTIES) + + +def _load_c_cpp_properties(): + """Loads the existing .vscode/c_cpp_properties.json""" + with open( + repo.ROOT_PATH / ".vscode" / "c_cpp_properties.json", "r", encoding="utf-8" + ) as file: + return json.load(file) + + +def _update_c_cpp_configs(configs, includes): + """Updates each given configurations's includePath section. This removes + and replaces each includePath that starts with ${workspaceFolder}, and + leaves all other paths.""" + includes = ["${workspaceFolder}"] + ["${workspaceFolder}/" + x for x in includes] + for config in configs["configurations"]: + paths = list( + filter( + lambda x: not x.startswith("${workspaceFolder}"), config["includePath"] + ) + ) + config["includePath"] = includes + paths + + +def _write_c_cpp_properties(configs): + """Writes the given configurations to""" + with open( + repo.ROOT_PATH / ".vscode" / "c_cpp_properties.json", "w", encoding="utf-8" + ) as file: + return json.dump(configs, file, indent=4) + + +def update_vscode_configurations(): + """Updates the configurations within c_cpp_properties.json to include the + include paths extracted via bazel aquery.""" + bazel.maybe_create_external_link() + includes = bazel.get_includes(bazel.get_compile_commands()) + _maybe_generate_default_c_cpp_properties() + configs = _load_c_cpp_properties() + _update_c_cpp_configs(configs, includes) + _write_c_cpp_properties(configs) diff --git a/tools/rad/rad/repo.py b/tools/rad/rad/repo.py new file mode 100644 index 0000000..588fe65 --- /dev/null +++ b/tools/rad/rad/repo.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Utility functions to interact with the Radiant repository.""" + +import pathlib + +ROOT_PATH = pathlib.Path(__file__).resolve().parents[3] diff --git a/tools/rad/setup.py b/tools/rad/setup.py new file mode 100644 index 0000000..1195220 --- /dev/null +++ b/tools/rad/setup.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python +# +# Copyright 2023 The Radiant Authors. +# +# 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. + +"""Setup script for Radiant Tooling.""" + +from setuptools import setup + +setup()