diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile index 42026362f..3d01d2e62 100644 --- a/.clusterfuzzlite/Dockerfile +++ b/.clusterfuzzlite/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/oss-fuzz-base/base-builder@sha256:5e1404dc68c77c2a8fdefd8c50f21d069aee193fe3648a9e4fe1ef4d45e3f732 +FROM gcr.io/oss-fuzz-base/base-builder@sha256:6c61e467be42d10170ab0c4715d25805829e7c97aa60ce7b408687a02cd40c2d RUN apt-get update && apt-get upgrade -y --no-install-recommends \ ninja-build \ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 5cfed44fe..b185aec90 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -eu -cmake --preset Fuzzing -cmake --build --preset Fuzzing +cmake --preset fuzzing +cmake --build --preset fuzzing -cp build/Fuzzing/infra/syntax/fuzz/infra.syntax_json_fuzzer $OUT/infra-syntax_json_fuzzer +cp build/fuzzing/infra/syntax/fuzz/infra.syntax_json_fuzzer $OUT/infra-syntax_json_fuzzer diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index df9f970f3..b91420f74 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ // This devcontainer has been set-up to run docker-from-docker scenarios as per // https://github.com/microsoft/vscode-dev-containers/tree/main/containers/docker-from-docker "name": "amp-devcontainer", - "image": "ghcr.io/philips-software/amp-devcontainer:2.2.0", + "image": "ghcr.io/philips-software/amp-devcontainer:2.5.0", "runArgs": ["--add-host=host.docker.internal:host-gateway"], "remoteEnv": { "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }, "mounts": [ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d6c8f95ff..8a8a9a1b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,30 +22,31 @@ jobs: name: Host Build & Test (ubuntu-latest) runs-on: ubuntu-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@6d1841ec156c39a52b1b23a810da917ab98da1f4 # v1.2.10 with: key: ${{ github.job }}-ubuntu-latest variant: sccache + - uses: seanmiddleditch/gha-setup-ninja@8b297075da4cd2a5f1fd21fe011b499edf06e9d2 # v4 - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 with: - configurePreset: "ContinuousIntegration" - buildPreset: "ContinuousIntegrationWithPackage" - testPreset: "ContinuousIntegration" + configurePreset: "host" + buildPreset: "host-Debug-WithPackage" + testPreset: "host" configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=sccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=sccache']" - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: emil - path: build/ContinuousIntegration/emil-*-Linux.tar.gz + path: build/host/emil-*-Linux.tar.gz if-no-files-found: error - name: Upload test logs if: ${{ failure() }} uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: test-logs - path: build/ContinuousIntegration/Testing/Temporary/ + path: build/host/Testing/Temporary/ host_build_test: name: Host Build & Test runs-on: ${{ matrix.os }} @@ -53,7 +54,7 @@ jobs: matrix: os: [macos-latest, windows-latest, windows-2019] steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@6d1841ec156c39a52b1b23a810da917ab98da1f4 # v1.2.10 @@ -62,16 +63,16 @@ jobs: variant: sccache - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 with: - configurePreset: "ContinuousIntegration" - buildPreset: "ContinuousIntegration" - testPreset: "ContinuousIntegration" + configurePreset: "host-single-Debug" + buildPreset: "host-single-Debug" + testPreset: "host-single-Debug" configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=sccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=sccache']" - name: Upload test logs if: ${{ failure() }} uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: test-logs - path: build/ContinuousIntegration/Testing/Temporary/ + path: build/host/Testing/Temporary/ embedded_build: name: Embedded Build runs-on: ubuntu-latest @@ -81,11 +82,11 @@ jobs: gcc: ["7-2018-q2", "8-2019-q3", "9-2020-q2", "10.3-2021.10"] configuration: ["RelWithDebInfo"] steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - name: Install GNU Arm Embedded Toolchain ${{ matrix.gcc }} - uses: carlosperate/arm-none-eabi-gcc-action@6a221d7c85f5b36647d6e9b25e874abf187409d3 # v1.7.1 + uses: carlosperate/arm-none-eabi-gcc-action@e9cd61b92edb079b14b2d0449b21f8a517121fe8 # v1.8.0 with: release: ${{ matrix.gcc }} - run: sudo apt-get update && sudo apt-get install ninja-build @@ -99,8 +100,8 @@ jobs: - run: mkdir install && mv emil-*/* install/ - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 with: - configurePreset: "Embedded" - buildPreset: "Embedded-${{ matrix.configuration }}" + configurePreset: "embedded" + buildPreset: "embedded-${{ matrix.configuration }}" configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" rtos: name: Embedded Build - RTOS @@ -110,11 +111,11 @@ jobs: matrix: rtos: ["FreeRTOS", "ThreadX"] steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - name: Install GNU Arm Embedded Toolchain "10.3-2021.10" - uses: carlosperate/arm-none-eabi-gcc-action@6a221d7c85f5b36647d6e9b25e874abf187409d3 # v1.7.1 + uses: carlosperate/arm-none-eabi-gcc-action@e9cd61b92edb079b14b2d0449b21f8a517121fe8 # v1.8.0 with: release: "10.3-2021.10" - run: sudo apt-get update && sudo apt-get install ninja-build @@ -128,6 +129,6 @@ jobs: - run: mkdir install && mv emil-*/* install/ - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 with: - configurePreset: "Embedded-${{ matrix.rtos }}" - buildPreset: "Embedded-${{ matrix.rtos }}-RelWithDebInfo" + configurePreset: "embedded-${{ matrix.rtos }}" + buildPreset: "embedded-${{ matrix.rtos }}-RelWithDebInfo" configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" diff --git a/.github/workflows/dependency-scanner.yml b/.github/workflows/dependency-scanner.yml index 36efe6a3c..f6936d907 100644 --- a/.github/workflows/dependency-scanner.yml +++ b/.github/workflows/dependency-scanner.yml @@ -16,16 +16,16 @@ jobs: contents: write pull-requests: write steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: philips-forks/cmake-dependency-submission@72880580a7cafc16145d82268f1892c0ece3da2a # main dependency-review: runs-on: ubuntu-latest - if: github.event_name == 'pull_request' + if: ${{ github.event_name == 'pull_request' }} needs: [dependency-submission] permissions: pull-requests: write steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: actions/dependency-review-action@6c5ccdad469c9f8a2996bfecaec55a631a347034 # v3.1.0 with: comment-summary-in-pr: true diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index 7da7a10ac..3ac1685c6 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -16,7 +16,7 @@ jobs: if: ${{ github.ref == 'refs/heads/main' }} steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 persist-credentials: false @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 persist-credentials: false @@ -69,7 +69,7 @@ jobs: name: Publish to GitHub Pages steps: - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: Retrieve Antora Site uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2 with: diff --git a/.github/workflows/linting-formatting.yml b/.github/workflows/linting-formatting.yml index a68c358f9..7293c1151 100644 --- a/.github/workflows/linting-formatting.yml +++ b/.github/workflows/linting-formatting.yml @@ -21,7 +21,7 @@ jobs: contents: read pull-requests: write steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - uses: DoozyX/clang-format-lint-action@a83a8fb7d371f66da7dd1c4f33a193023899494b # v0.16 @@ -42,21 +42,21 @@ jobs: pull-requests: write security-events: write steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 persist-credentials: false - - uses: oxsecurity/megalinter@fda6ac3a38be0e969820709ac16e442464e5a035 # v7.3.0 + - uses: oxsecurity/megalinter@b48455a119cc28045eee8f1e9d0a542a85e71f4f # v7.5.0 env: APPLY_FIXES: all VALIDATE_ALL_CODEBASE: true GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - uses: github/codeql-action/upload-sarif@701f152f28d4350ad289a5e31435e9ab6169a7ca # v2.21.6 - if: ${{ success() }} || ${{ failure() }} + - uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 + if: ${{ success() || failure() }} with: sarif_file: megalinter-reports/megalinter-report.sarif - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 - if: ${{ success() }} || ${{ failure() }} + if: ${{ success() || failure() }} with: name: linter path: | diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 0778da3c1..78a43de67 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -26,7 +26,7 @@ jobs: app_id: ${{ secrets.FOREST_RELEASER_APP_ID }} app_base64_private_key: ${{ secrets.FOREST_RELEASER_APP_PRIVATE_KEY_BASE64 }} auth_type: installation - - uses: google-github-actions/release-please-action@ca6063f4ed81b55db15b8c42d1b6f7925866342d # v3.7.11 + - uses: google-github-actions/release-please-action@4c5670f886fe259db4d11222f7dff41c1382304d # v3.7.12 id: release with: command: manifest @@ -43,7 +43,7 @@ jobs: matrix: os: [macos-latest, ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - uses: hendrikmuhs/ccache-action@6d1841ec156c39a52b1b23a810da917ab98da1f4 # v1.2.10 @@ -52,8 +52,8 @@ jobs: variant: sccache - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 with: - configurePreset: "Package" - buildPreset: "Package" + configurePreset: "host-single-MinSizeRel" + buildPreset: "release-package" configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=sccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=sccache']" - run: gh release upload ${{ needs.release_please.outputs.tag_name }} build/**/emil-*.zip --clobber shell: bash diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index a47c33359..c6e0973d5 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -21,16 +21,16 @@ jobs: actions: read contents: read steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - name: Analysis - uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0 + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 with: results_file: scorecards.sarif results_format: sarif repo_token: ${{ secrets.SCORECARD_READ_TOKEN }} publish_results: true - - uses: github/codeql-action/upload-sarif@701f152f28d4350ad289a5e31435e9ab6169a7ca # v2.21.6 + - uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 with: sarif_file: scorecards.sarif diff --git a/.github/workflows/social-interaction.yml b/.github/workflows/social-interaction.yml index f03c75c26..ab77aaa8c 100644 --- a/.github/workflows/social-interaction.yml +++ b/.github/workflows/social-interaction.yml @@ -14,7 +14,7 @@ jobs: pull-requests: write if: ${{ github.actor != 'dependabot[bot]' }} steps: - - uses: actions/first-interaction@1d8459ca65b335265f1285568221e229d45a995e + - uses: actions/first-interaction@1dbfe1ba5525b8257e1f259b09745bee346d62d8 continue-on-error: true with: repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 6853e071e..5c458a844 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -18,47 +18,42 @@ jobs: sonar: name: SonarCloud runs-on: ubuntu-latest + container: ghcr.io/philips-software/amp-devcontainer:2.5.0 env: - SONAR_SCANNER_VERSION: 4.7.0.2747 + SONAR_SCANNER_VERSION: 5.0.1.3006 SONAR_SERVER_URL: "https://sonarcloud.io" steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: fetch-depth: 0 # Disable shallow clone to enable blame information persist-credentials: false - - run: sudo apt-get update && sudo apt-get install --no-install-recommends jq ninja-build xsltproc - - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 # v4.7.0 - - uses: BSFishy/pip-action@8f2d471d809dc20b6ada98c91910b6ae6243f318 - with: - packages: gcovr==5.2 - - name: Install Sonar Scanner & Mull + - name: Install Sonar Scanner run: | wget -qN "https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip" unzip -qqo "sonar-scanner-cli-${{ env.SONAR_SCANNER_VERSION }}-linux.zip" echo "${PWD}/sonar-scanner-${{ env.SONAR_SCANNER_VERSION }}-linux/bin" >> "$GITHUB_PATH" - wget -qN https://github.com/mull-project/mull/releases/download/0.18.0/Mull-12-0.18.0-LLVM-12.0-ubuntu-20.04.deb - sudo dpkg -i Mull-12-0.18.0-LLVM-12.0-ubuntu-20.04.deb - uses: hendrikmuhs/ccache-action@6d1841ec156c39a52b1b23a810da917ab98da1f4 # v1.2.10 with: key: ${{ github.job }} max-size: 2G - name: Build & Collect Coverage run: | - cmake --preset Coverage -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - cmake --build --preset Coverage - GTEST_OUTPUT="xml:${PWD}/testresults/" ctest --preset Coverage - gcovr --sonarqube=coverage.xml --exclude-lines-by-pattern '.*assert\(.*\);|.*really_assert\(.*\);|.*std::abort();' --exclude-unreachable-branches --exclude-throw-branches -j 2 --exclude=.*/generated/.* --exclude=.*/examples/.* --exclude=.*/external/.* --exclude=.*/lwip/.* --exclude=.*/tracing/.* --exclude=.*/test/.* - - name: Build & Run Mutation Tests - run: | - cmake --preset MutationTesting -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - cmake --build --preset MutationTesting - ctest --preset MutationTesting + cmake --preset coverage -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + cmake --build --preset coverage + GTEST_OUTPUT="xml:${PWD}/testresults/" ctest --preset coverage + gcovr --sonarqube=coverage.xml --exclude-lines-by-pattern '.*assert\(.*\);|.*really_assert\(.*\);|.*std::abort();' --exclude-unreachable-branches --exclude-throw-branches -j "$(nproc)" --exclude=.*/generated/.* --exclude=.*/examples/.* --exclude=.*/external/.* --exclude=.*/lwip/.* --exclude=.*/tracing/.* --exclude=.*/test/.* + - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 + with: + configurePreset: "mutation-testing" + buildPreset: "mutation-testing" + testPreset: "mutation-testing" + configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" - name: Convert Results run: | { echo ''; xsltproc .github/formatters/gtest-to-generic-execution.xslt testresults/*.xml; echo ''; } | tee execution.xml > /dev/null jq -s 'reduce .[] as $item ({}; . * $item)' reports/mull/*.json > reports/mull/merged-mutation.json jq --arg workspace "$GITHUB_WORKSPACE" -f .github/formatters/mutation-report-to-generic-issue.jq reports/mull/merged-mutation.json > mutation-sonar.json - cp build/Coverage/compile_commands.json compile_commands.json + cp build/coverage/compile_commands.json compile_commands.json - name: Run Analysis # skip the analysis step for dependabot PRs since dependabot does not have access to secrets if: ${{ github.actor != 'dependabot[bot]' }} @@ -70,20 +65,22 @@ jobs: codeql: name: CodeQL runs-on: ubuntu-latest + container: ghcr.io/philips-software/amp-devcontainer:2.5.0 permissions: security-events: write steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: persist-credentials: false - - run: sudo apt-get update && sudo apt-get install ninja-build - uses: hendrikmuhs/ccache-action@6d1841ec156c39a52b1b23a810da917ab98da1f4 # v1.2.10 with: key: ${{ github.job }} - - uses: github/codeql-action/init@701f152f28d4350ad289a5e31435e9ab6169a7ca # v2.21.6 + - uses: github/codeql-action/init@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 with: languages: cpp - - run: | - cmake --preset ContinuousIntegration -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - cmake --build --preset ContinuousIntegration - - uses: github/codeql-action/analyze@701f152f28d4350ad289a5e31435e9ab6169a7ca # v2.21.6 + - uses: lukka/run-cmake@c2b72aff009141774c5a5fabe74ea46c8c04d9c4 # v10.6 + with: + configurePreset: "host" + buildPreset: "host-Debug" + configurePresetAdditionalArgs: "['-DCMAKE_C_COMPILER_LAUNCHER=ccache', '-DCMAKE_CXX_COMPILER_LAUNCHER=ccache']" + - uses: github/codeql-action/analyze@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5 diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index 209866b5d..d85172744 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -11,7 +11,7 @@ jobs: conventional_commit: runs-on: ubuntu-latest steps: - - uses: Namchee/conventional-pr@5d1221ff603c491a4abb1e614a0f4ded41d4b397 # v0.14.0 + - uses: Namchee/conventional-pr@93f510707dd4ef011c5e8fe7c94c93c59fc86d4c # v0.15.1 with: access_token: ${{ secrets.GITHUB_TOKEN }} body: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 34a1aacd3..e80f35eb0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,7 +88,7 @@ option(EMIL_BUILD_TESTS "Enable building the tests" ${EMIL_DEFAULTOPT}) option(EMIL_BUILD_EXAMPLES "Enable building the examples" ${EMIL_DEFAULTOPT}) option(EMIL_ENABLE_FUZZING "Enable building the fuzzing targets" Off) option(EMIL_ENABLE_DOCKER_TOOLS "Enable shift-left tools (e.g. linters, formatters) that are run using Docker" On) -option(EMIL_GENERATE_PACKAGE_CONFIG "Enable generation of package configuration and install files" ${EMIL_HOST_BUILD}) +option(EMIL_BUILD_ECHO_COMPILERS "Build and install the Protobuf ECHO compilers" ${EMIL_DEFAULTOPT}) # Enable or disable optional features. option(EMIL_INCLUDE_MBEDTLS "Include MbedTLS as part of EmIL" On) @@ -148,18 +148,12 @@ if (EMIL_STANDALONE) emil_folderize_all_targets() endif() -if (EMIL_GENERATE_PACKAGE_CONFIG) +if (EMIL_BUILD_ECHO_COMPILERS) function(generate_export_targets name) - # Generate and install export file install(EXPORT emil${name}Targets FILE emil${name}Targets.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/emil ) - - # Generate the export targets for the build tree - export(EXPORT emil${name}Targets - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/emil${name}Targets.cmake" - ) endfunction() foreach(target IN ITEMS Protobuf) @@ -184,20 +178,20 @@ if (EMIL_GENERATE_PACKAGE_CONFIG) "${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME} ) - - set(CPACK_GENERATOR "ZIP;TGZ") - set(CPACK_SOURCE_IGNORE_FILES ".vs/;.git/;build/") - set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") - set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") - set(CPACK_PACKAGE_VENDOR "Koninklijke Philips N.V") - set(CPACK_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") - set(CPACK_DEBIAN_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") - set(CPACK_RPM_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") - set(CPACK_PACKAGE_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}") - set(CPACK_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}") - set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}") - set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md") - set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") - - include(CPack) endif() + +set(CPACK_GENERATOR "ZIP;TGZ") +set(CPACK_SOURCE_IGNORE_FILES ".vs/;.git/;build/") +set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") +set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") +set(CPACK_PACKAGE_VENDOR "Koninklijke Philips N.V") +set(CPACK_PACKAGE_DESCRIPTION "${PROJECT_DESCRIPTION}") +set(CPACK_DEBIAN_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") +set(CPACK_RPM_PACKAGE_NAME "${CPACK_PACKAGE_NAME}") +set(CPACK_PACKAGE_HOMEPAGE_URL "${PROJECT_HOMEPAGE_URL}") +set(CPACK_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}") +set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${CPACK_PACKAGE_VENDOR}") +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.md") +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") + +include(CPack) diff --git a/CMakePresets.json b/CMakePresets.json index 99cdfaf78..1442446cf 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -12,51 +12,57 @@ } }, { - "name": "ContinuousIntegration", - "displayName": "Configuration for Continuous Integration", + "name": "host", + "displayName": "Configuration for Host Tooling and Tests", + "inherits": "defaults", + "cacheVariables": { + "CMAKE_CONFIGURATION_TYPES": "Debug;Release;RelWithDebInfo;MinSizeRel" + }, + "generator": "Ninja Multi-Config" + }, + { + "name": "host-single-Debug", + "displayName": "Configuration for Host Tooling and Tests, Single Config Generator, Debug", "inherits": "defaults", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", - "EMIL_ENABLE_DOCKER_TOOLS": "Off", - "EMIL_BUILD_EXAMPLES": "On" + "EMIL_ENABLE_DOCKER_TOOLS": "Off" } }, { - "name": "Package", - "displayName": "Configuration for CPack package generation", + "name": "host-single-MinSizeRel", + "displayName": "Configuration for Host Tooling and Tests, Single Config Generator, MinSizeRel", "inherits": "defaults", "cacheVariables": { "CMAKE_BUILD_TYPE": "MinSizeRel", - "EMIL_ENABLE_DOCKER_TOOLS": "Off", - "EMIL_BUILD_TESTS": "Off", - "EMIL_BUILD_EXAMPLES": "Off" + "EMIL_ENABLE_DOCKER_TOOLS": "Off" } }, { - "name": "Coverage", + "name": "coverage", "displayName": "Configuration for Code Coverage", - "inherits": "ContinuousIntegration", + "inherits": "host", "cacheVariables": { "EMIL_ENABLE_COVERAGE": "On" }, "generator": "Ninja" }, { - "name": "MutationTesting", + "name": "mutation-testing", "displayName": "Configuration for Mutation Testing", - "inherits": "ContinuousIntegration", + "inherits": "host", "cacheVariables": { - "CMAKE_C_COMPILER": "clang-12", - "CMAKE_CXX_COMPILER": "clang++-12", + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", "EMIL_ENABLE_MUTATION_TESTING": "On", - "EMIL_MUTATION_TESTING_RUNNER_ARGUMENTS": "--reporters;Elements;--report-dir;${sourceDir}/reports/mull" + "EMIL_MUTATION_TESTING_RUNNER_ARGUMENTS": "--allow-surviving;--reporters;Elements;--report-dir;${sourceDir}/reports/mull" }, "generator": "Ninja" }, { - "name": "Fuzzing", + "name": "fuzzing", "displayName": "Configuration for Fuzzing", - "inherits": "ContinuousIntegration", + "inherits": "host", "cacheVariables": { "CMAKE_C_COMPILER": "clang", "CMAKE_CXX_COMPILER": "clang++", @@ -67,18 +73,7 @@ "generator": "Ninja" }, { - "name": "Host", - "displayName": "Configuration for Host Tooling", - "inherits": "defaults", - "cacheVariables": { - "CMAKE_CONFIGURATION_TYPES": "Debug;Release;RelWithDebInfo;MinSizeRel", - "EMIL_BUILD_TESTS": "Off", - "EMIL_BUILD_EXAMPLES": "Off" - }, - "generator": "Ninja Multi-Config" - }, - { - "name": "HostClangMsvc", + "name": "host-ClangMsvc", "displayName": "Configuration for Host Tooling using clang-cl to target Windows", "inherits": "defaults", "toolchainFile": "${sourceDir}/cmake/toolchain-clang-x86_64-pc-windows-msvc.cmake", @@ -91,8 +86,8 @@ "generator": "Ninja Multi-Config" }, { - "name": "Embedded", - "displayName": "Configuration for Embedded", + "name": "embedded", + "displayName": "Configuration for embedded", "inherits": "defaults", "toolchainFile": "${sourceDir}/cmake/toolchain-arm-gcc-m4-fpv4-sp-d16.cmake", "cacheVariables": { @@ -102,18 +97,18 @@ "generator": "Ninja Multi-Config" }, { - "name": "Embedded-FreeRTOS", - "displayName": "Configuration for Embedded with FreeRTOS", - "inherits": "Embedded", + "name": "embedded-FreeRTOS", + "displayName": "Configuration for embedded with FreeRTOS", + "inherits": "embedded", "cacheVariables": { "EMIL_INCLUDE_FREERTOS": "On", "FREERTOS_CONFIG_FILE_DIRECTORY": "${sourceDir}/examples/threading/config" } }, { - "name": "Embedded-ThreadX", - "displayName": "Configuration for Embedded with ThreadX", - "inherits": "Embedded", + "name": "embedded-ThreadX", + "displayName": "Configuration for embedded with ThreadX", + "inherits": "embedded", "cacheVariables": { "EMIL_INCLUDE_THREADX": "On" } @@ -121,96 +116,96 @@ ], "buildPresets": [ { - "name": "ContinuousIntegration", + "name": "host-Debug", "configuration": "Debug", - "configurePreset": "ContinuousIntegration" + "configurePreset": "host" }, { - "name": "ContinuousIntegrationWithPackage", + "name": "host-Debug-WithPackage", "configuration": "Debug", - "configurePreset": "ContinuousIntegration", + "configurePreset": "host", "targets": ["all", "package"] }, { - "name": "Package", - "configuration": "MinSizeRel", - "configurePreset": "Package", - "targets": ["package"] + "name": "host-Release", + "configuration": "Release", + "configurePreset": "host" }, { - "name": "Coverage", - "configuration": "Debug", - "configurePreset": "Coverage" + "name": "host-RelWithDebInfo", + "configuration": "RelWithDebInfo", + "configurePreset": "host" }, { - "name": "MutationTesting", - "configuration": "Debug", - "configurePreset": "MutationTesting" + "name": "host-MinSizeRel", + "configuration": "MinSizeRel", + "configurePreset": "host" }, { - "name": "Fuzzing", + "name": "host-single-Debug", "configuration": "Debug", - "configurePreset": "Fuzzing" + "configurePreset": "host-single-Debug" }, { - "name": "Host-Debug", - "configuration": "Debug", - "configurePreset": "Host" + "name": "release-package", + "configuration": "MinSizeRel", + "configurePreset": "host-single-MinSizeRel", + "targets": ["package"] }, { - "name": "Host-Release", - "configuration": "Release", - "configurePreset": "Host" + "name": "coverage", + "configuration": "Debug", + "configurePreset": "coverage" }, { - "name": "Host-RelWithDebInfo", - "configuration": "RelWithDebInfo", - "configurePreset": "Host" + "name": "mutation-testing", + "configuration": "Debug", + "configurePreset": "mutation-testing" }, { - "name": "Host-MinSizeRel", - "configuration": "MinSizeRel", - "configurePreset": "Host" + "name": "fuzzing", + "configuration": "Debug", + "configurePreset": "fuzzing" }, { - "name": "HostClangMsvc-Debug", + "name": "host-ClangMsvc-Debug", "configuration": "Debug", - "configurePreset": "HostClangMsvc" + "configurePreset": "host-ClangMsvc" }, { - "name": "HostClangMsvc-RelWithDebInfo", + "name": "host-ClangMsvc-RelWithDebInfo", "configuration": "RelWithDebInfo", - "configurePreset": "HostClangMsvc" + "configurePreset": "host-ClangMsvc" }, { - "name": "Embedded-Debug", + "name": "embedded-Debug", "configuration": "Debug", - "configurePreset": "Embedded" + "configurePreset": "embedded" }, { - "name": "Embedded-Release", + "name": "embedded-Release", "configuration": "Release", - "configurePreset": "Embedded" + "configurePreset": "embedded" }, { - "name": "Embedded-RelWithDebInfo", + "name": "embedded-RelWithDebInfo", "configuration": "RelWithDebInfo", - "configurePreset": "Embedded" + "configurePreset": "embedded" }, { - "name": "Embedded-MinSizeRel", + "name": "embedded-MinSizeRel", "configuration": "MinSizeRel", - "configurePreset": "Embedded" + "configurePreset": "embedded" }, { - "name": "Embedded-FreeRTOS-RelWithDebInfo", + "name": "embedded-FreeRTOS-RelWithDebInfo", "configuration": "RelWithDebInfo", - "configurePreset": "Embedded-FreeRTOS" + "configurePreset": "embedded-FreeRTOS" }, { - "name": "Embedded-ThreadX-RelWithDebInfo", + "name": "embedded-ThreadX-RelWithDebInfo", "configuration": "RelWithDebInfo", - "configurePreset": "Embedded-ThreadX" + "configurePreset": "embedded-ThreadX" } ], "testPresets": [ @@ -226,20 +221,26 @@ } }, { - "name": "ContinuousIntegration", - "configurePreset": "ContinuousIntegration", + "name": "host", + "configurePreset": "host", + "configuration": "Debug", + "inherits": "defaults" + }, + { + "name": "host-single-Debug", + "configurePreset": "host-single-Debug", "configuration": "Debug", "inherits": "defaults" }, { - "name": "Coverage", - "configurePreset": "Coverage", + "name": "coverage", + "configurePreset": "coverage", "configuration": "Debug", "inherits": "defaults" }, { - "name": "MutationTesting", - "configurePreset": "MutationTesting", + "name": "mutation-testing", + "configurePreset": "mutation-testing", "configuration": "Debug", "inherits": "defaults", "output": { diff --git a/cmake/emil_build_for.cmake b/cmake/emil_build_for.cmake index 0aa6e90da..282ce81cb 100644 --- a/cmake/emil_build_for.cmake +++ b/cmake/emil_build_for.cmake @@ -88,3 +88,10 @@ function(emil_build_for target) endif() endfunction() + +function(emil_install target) + get_target_property(exclude ${target} EXCLUDE_FROM_ALL) + if (NOT ${exclude}) + install(TARGETS ${target} ${ARGN}) + endif() +endfunction() diff --git a/cmake/emil_test_helpers.cmake b/cmake/emil_test_helpers.cmake index 5be8b44f7..3336f6ee7 100644 --- a/cmake/emil_test_helpers.cmake +++ b/cmake/emil_test_helpers.cmake @@ -2,12 +2,10 @@ option(EMIL_ENABLE_COVERAGE "Enable compiler flags for code coverage measurement option(EMIL_ENABLE_MUTATION_TESTING "Enable compiler flags for mutation testing" Off) set(EMIL_MUTATION_TESTING_RUNNER_ARGUMENTS "" CACHE STRING "Additional arguments for the mutation testing runner") -function(emil_enable_testing) - include(GoogleTest) - +function(emil_fetch_googletest) FetchContent_Declare( googletest - GIT_REPOSITORY https://github.com/google/googletest.git + GIT_REPOSITORY https://github.com/google/googletest GIT_TAG release-1.12.1 ) @@ -18,6 +16,12 @@ function(emil_enable_testing) set_target_properties(gtest gtest_main gmock gmock_main PROPERTIES FOLDER External/GoogleTest) mark_as_advanced(BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS gmock_build_tests gtest_build_samples test_build_tests gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols) +endfunction() + +function(emil_enable_testing) + include(GoogleTest) + + emil_fetch_googletest() if (EMIL_ENABLE_COVERAGE) add_compile_options( @@ -34,19 +38,10 @@ function(emil_enable_testing) if (EMIL_ENABLE_MUTATION_TESTING) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE CLANG_VERSION) - - if(CLANG_VERSION VERSION_GREATER 15.0 OR CLANG_VERSION VERSION_EQUAL 15.0) - add_compile_options( - -g -O0 -grecord-command-line -fprofile-instr-generate -fcoverage-mapping - -fexperimental-new-pass-manager -fpass-plugin=/usr/lib/mull-ir-frontend - ) - else() - add_compile_options( - -g -O0 -grecord-command-line -fprofile-instr-generate -fcoverage-mapping - -fexperimental-new-pass-manager -fpass-plugin=/usr/lib/mull-ir-frontend-12 - ) - endif() + add_compile_options( + -g -O0 -grecord-command-line -fprofile-instr-generate -fcoverage-mapping + -fexperimental-new-pass-manager -fpass-plugin=/usr/lib/mull-ir-frontend + ) add_link_options(-fprofile-instr-generate) else() @@ -68,13 +63,7 @@ function(emil_add_test target) get_target_property(exclude ${target} EXCLUDE_FROM_ALL) if (NOT ${exclude}) if (EMIL_ENABLE_MUTATION_TESTING) - execute_process(COMMAND ${CMAKE_CXX_COMPILER} -dumpversion OUTPUT_VARIABLE CLANG_VERSION) - - if(CLANG_VERSION VERSION_GREATER 15.0 OR CLANG_VERSION VERSION_EQUAL 15.0) - add_test(NAME ${target} COMMAND mull-runner ${EMIL_MUTATION_TESTING_RUNNER_ARGUMENTS} $) - else() - add_test(NAME ${target} COMMAND mull-runner-12 ${EMIL_MUTATION_TESTING_RUNNER_ARGUMENTS} $) - endif() + add_test(NAME ${target} COMMAND mull-runner ${EMIL_MUTATION_TESTING_RUNNER_ARGUMENTS} $) else() add_test(NAME ${target} COMMAND ${target}) endif() diff --git a/external/crypto/mbedtls/CMakeLists.txt b/external/crypto/mbedtls/CMakeLists.txt index db0eb2144..e44c96cd6 100644 --- a/external/crypto/mbedtls/CMakeLists.txt +++ b/external/crypto/mbedtls/CMakeLists.txt @@ -11,12 +11,36 @@ set(ENABLE_TESTING Off CACHE INTERNAL "") set(ENABLE_PROGRAMS Off CACHE INTERNAL "") set(GEN_FILES Off CACHE INTERNAL "") +# mbedtls uses CMAKE_C_COMPILER_ID to determine the type of compiler used. +# When compiling with clang-cl, mbedtls thinks the clang compiler +# is used, and sets flags according to what clang expects as arguments. +# However, clang-cl expects MSVC like arguments. Therefore we have to trick mbedtls +# in to thinking MSVC is being used as a compiler. +# After making mbedtls available the original COMPILER_ID will be restored +set(ORIGINAL_C_COMPILER_ID ${CMAKE_C_COMPILER_ID}) +set(ORIGINAL_CXX_COMPILER_ID ${CMAKE_CXX_COMPILER_ID}) + +if (CMAKE_C_SIMULATE_ID) + set(CMAKE_C_COMPILER_ID ${CMAKE_C_SIMULATE_ID}) +endif() + +if (CMAKE_CXX_SIMULATE_ID) + set(CMAKE_CXX_COMPILER_ID ${CMAKE_CXX_SIMULATE_ID}) +endif() + FetchContent_MakeAvailable(mbedtls) +set(CMAKE_C_COMPILER_ID ${ORIGINAL_C_COMPILER_ID}) +set(CMAKE_CXX_COMPILER_ID ${ORIGINAL_CXX_COMPILER_ID}) + function(add_mbedtls_target_properties) foreach(target ${ARGN}) target_compile_options(${target} PUBLIC -DMBEDTLS_CONFIG_FILE="mbedtls/mbedtls_emil_config.h" + # see https://github.com/Mbed-TLS/mbedtls/pull/6966 + # mbedtls sets the -Wdocumentation flag, which is throwing warnings + # since clang-15 + $<$:-Wno-documentation> ) target_include_directories(${target} PUBLIC diff --git a/hal/interfaces/CMakeLists.txt b/hal/interfaces/CMakeLists.txt index f163c562f..0d94b4dcd 100644 --- a/hal/interfaces/CMakeLists.txt +++ b/hal/interfaces/CMakeLists.txt @@ -18,7 +18,6 @@ target_sources(hal.interfaces PRIVATE Can.cpp Can.hpp CommunicationConfigurator.hpp - Dac.hpp DigitalToAnalogPin.hpp Ethernet.hpp Flash.cpp diff --git a/hal/interfaces/Dac.hpp b/hal/interfaces/Dac.hpp deleted file mode 100644 index 930247590..000000000 --- a/hal/interfaces/Dac.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef HAL_DAC_HPP -#define HAL_DAC_HPP - -#include "infra/util/Unit.hpp" - -namespace hal -{ - template - class Dac - { - protected: - Dac() = default; - - Dac(const Dac&) = delete; - Dac& operator=(const Dac&) = delete; - ~Dac() = default; - - public: - virtual void SetOutput(infra::Quantity value) = 0; - }; - - class DacImplBase - { - protected: - ~DacImplBase() = default; - - public: - virtual void SetOutput(uint16_t value) = 0; - }; - - template - class DacConverter - : public Dac - , public Impl - { - public: - template - DacConverter(Args&&... args); - - void SetOutput(infra::Quantity value); - }; - - //// Implementation //// - - template - template - DacConverter::DacConverter(Args&&... args) - : Impl(std::forward(args)...) - {} - - template - void DacConverter::SetOutput(infra::Quantity value) - { - infra::Quantity convertedValue(value * infra::Quantity(1)); - Impl::SetOutput(convertedValue.Value()); - } -} - -#endif diff --git a/infra/stream/BufferingStreamReader.cpp b/infra/stream/BufferingStreamReader.cpp new file mode 100644 index 000000000..a2c2289ce --- /dev/null +++ b/infra/stream/BufferingStreamReader.cpp @@ -0,0 +1,107 @@ +#include "infra/stream/BufferingStreamReader.hpp" + +namespace infra +{ + BufferingStreamReader::BufferingStreamReader(infra::BoundedDeque& buffer, infra::StreamReaderWithRewinding& input) + : buffer(buffer) + , input(input) + {} + + BufferingStreamReader::~BufferingStreamReader() + { + StoreRemainder(); + } + + void BufferingStreamReader::Extract(infra::ByteRange range, infra::StreamErrorPolicy& errorPolicy) + { + if (index != buffer.size()) + { + Read(infra::Head(buffer.contiguous_range(buffer.begin() + index), range.size()), range); + // Perhaps the deque just wrapped around, try once more + Read(infra::Head(buffer.contiguous_range(buffer.begin() + index), range.size()), range); + } + + if (!range.empty()) + Read(input.ExtractContiguousRange(range.size()), range); + + errorPolicy.ReportResult(range.empty()); + } + + uint8_t BufferingStreamReader::Peek(infra::StreamErrorPolicy& errorPolicy) + { + auto range = PeekContiguousRange(0); + + errorPolicy.ReportResult(!range.empty()); + + if (range.empty()) + return 0; + else + return range.front(); + } + + infra::ConstByteRange BufferingStreamReader::ExtractContiguousRange(std::size_t max) + { + if (index < buffer.size()) + { + auto from = infra::Head(buffer.contiguous_range(buffer.begin() + index), max); + index += from.size(); + return from; + } + + return input.ExtractContiguousRange(max); + } + + infra::ConstByteRange BufferingStreamReader::PeekContiguousRange(std::size_t start) + { + if (index + start < buffer.size()) + return buffer.contiguous_range(buffer.begin() + index + start); + + return input.PeekContiguousRange(index + start - buffer.size()); + } + + bool BufferingStreamReader::Empty() const + { + return Available() == 0; + } + + std::size_t BufferingStreamReader::Available() const + { + return buffer.size() + input.Available(); + } + + std::size_t BufferingStreamReader::ConstructSaveMarker() const + { + return index; + } + + void BufferingStreamReader::Rewind(std::size_t marker) + { + if (index > buffer.size()) + { + auto rewindAmount = std::min(index - marker, index - buffer.size()); + input.Rewind(input.ConstructSaveMarker() - rewindAmount); + index -= rewindAmount; + } + + if (marker < buffer.size()) + index = marker; + } + + void BufferingStreamReader::Read(infra::ConstByteRange from, infra::ByteRange& to) + { + infra::Copy(from, infra::Head(to, from.size())); + to.pop_front(from.size()); + index += from.size(); + } + + void BufferingStreamReader::StoreRemainder() + { + std::size_t bufferDecrease = std::min(buffer.size(), index); + buffer.erase(buffer.begin(), buffer.begin() + bufferDecrease); + while (!input.Empty()) + { + auto range = input.ExtractContiguousRange(std::numeric_limits::max()); + buffer.insert(buffer.end(), range.begin(), range.end()); + } + } +} diff --git a/infra/stream/BufferingStreamReader.hpp b/infra/stream/BufferingStreamReader.hpp new file mode 100644 index 000000000..3df25964f --- /dev/null +++ b/infra/stream/BufferingStreamReader.hpp @@ -0,0 +1,39 @@ +#ifndef INFRA_BUFFERING_STREAM_READER_HPP +#define INFRA_BUFFERING_STREAM_READER_HPP + +#include "infra/stream/InputStream.hpp" +#include "infra/util/BoundedDeque.hpp" + +namespace infra +{ + // Usage: Everything that is not read from the inputData is stored into the buffer upon destruction of the BufferingStreamReader + // Any data already present in the buffer is read first from the reader + class BufferingStreamReader + : public infra::StreamReaderWithRewinding + { + public: + BufferingStreamReader(infra::BoundedDeque& buffer, infra::StreamReaderWithRewinding& input); + ~BufferingStreamReader() override; + + // Implementation of StreamReaderWithRewinding + void Extract(infra::ByteRange range, infra::StreamErrorPolicy& errorPolicy) override; + uint8_t Peek(infra::StreamErrorPolicy& errorPolicy) override; + infra::ConstByteRange ExtractContiguousRange(std::size_t max) override; + infra::ConstByteRange PeekContiguousRange(std::size_t start) override; + bool Empty() const override; + std::size_t Available() const override; + std::size_t ConstructSaveMarker() const override; + void Rewind(std::size_t marker) override; + + private: + void Read(infra::ConstByteRange range, infra::ByteRange& to); + void StoreRemainder(); + + private: + infra::BoundedDeque& buffer; + infra::StreamReaderWithRewinding& input; + std::size_t index = 0; + }; +} + +#endif diff --git a/infra/stream/BufferingStreamWriter.cpp b/infra/stream/BufferingStreamWriter.cpp new file mode 100644 index 000000000..c66445491 --- /dev/null +++ b/infra/stream/BufferingStreamWriter.cpp @@ -0,0 +1,65 @@ +#include "infra/stream/BufferingStreamWriter.hpp" + +namespace infra +{ + BufferingStreamWriter::BufferingStreamWriter(infra::BoundedDeque& buffer, infra::StreamWriter& output) + : buffer(buffer) + , output(output) + { + LoadRemainder(); + } + + void BufferingStreamWriter::Insert(infra::ConstByteRange range, infra::StreamErrorPolicy& errorPolicy) + { + auto first = infra::Head(range, output.Available()); + output.Insert(first, errorPolicy); + index += first.size(); + range.pop_front(first.size()); + + buffer.insert(buffer.end(), range.begin(), range.end()); + index += range.size(); + } + + std::size_t BufferingStreamWriter::Available() const + { + return output.Available() + buffer.max_size() - buffer.size(); + } + + std::size_t BufferingStreamWriter::ConstructSaveMarker() const + { + return index; + } + + std::size_t BufferingStreamWriter::GetProcessedBytesSince(std::size_t marker) const + { + return index - marker; + } + + [[noreturn]] infra::ByteRange BufferingStreamWriter::SaveState(std::size_t marker) + { + std::abort(); + } + + [[noreturn]] void BufferingStreamWriter::RestoreState(infra::ByteRange range) + { + std::abort(); + } + + [[noreturn]] infra::ByteRange BufferingStreamWriter::Overwrite(std::size_t marker) + { + std::abort(); + } + + void BufferingStreamWriter::LoadRemainder() + { + infra::StreamErrorPolicy errorPolicy; + auto from = infra::Head(buffer.contiguous_range(buffer.begin()), output.Available()); + output.Insert(from, errorPolicy); + buffer.erase(buffer.begin(), buffer.begin() + from.size()); + from = infra::Head(buffer.contiguous_range(buffer.begin()), output.Available()); + output.Insert(from, errorPolicy); + buffer.erase(buffer.begin(), buffer.begin() + from.size()); + + index = buffer.size(); + } +} diff --git a/infra/stream/BufferingStreamWriter.hpp b/infra/stream/BufferingStreamWriter.hpp new file mode 100644 index 000000000..79f2d4216 --- /dev/null +++ b/infra/stream/BufferingStreamWriter.hpp @@ -0,0 +1,36 @@ +#ifndef INFRA_BUFFERING_STREAM_WRITER_HPP +#define INFRA_BUFFERING_STREAM_WRITER_HPP + +#include "infra/stream/OutputStream.hpp" +#include "infra/util/BoundedDeque.hpp" + +namespace infra +{ + // Usage: Any data that does not fit into the output stream is written to the buffer + // Any data already present in the buffer is written to the output stream upon construction of BufferingStreamWriter + class BufferingStreamWriter + : public infra::StreamWriter + { + public: + BufferingStreamWriter(infra::BoundedDeque& buffer, infra::StreamWriter& output); + + // Implementation of StreamWriter + void Insert(infra::ConstByteRange range, infra::StreamErrorPolicy& errorPolicy) override; + std::size_t Available() const override; + std::size_t ConstructSaveMarker() const override; + std::size_t GetProcessedBytesSince(std::size_t marker) const override; + [[noreturn]] infra::ByteRange SaveState(std::size_t marker) override; + [[noreturn]] void RestoreState(infra::ByteRange range) override; + [[noreturn]] infra::ByteRange Overwrite(std::size_t marker) override; + + private: + void LoadRemainder(); + + private: + infra::BoundedDeque& buffer; + infra::StreamWriter& output; + std::size_t index = 0; + }; +} + +#endif diff --git a/infra/stream/CMakeLists.txt b/infra/stream/CMakeLists.txt index eab26fd3f..4b3537a71 100644 --- a/infra/stream/CMakeLists.txt +++ b/infra/stream/CMakeLists.txt @@ -13,6 +13,10 @@ target_sources(infra.stream PRIVATE BoundedVectorInputStream.hpp BoundedVectorOutputStream.cpp BoundedVectorOutputStream.hpp + BufferingStreamReader.cpp + BufferingStreamReader.hpp + BufferingStreamWriter.cpp + BufferingStreamWriter.hpp ByteInputStream.cpp ByteInputStream.hpp ByteOutputStream.cpp diff --git a/infra/stream/test/CMakeLists.txt b/infra/stream/test/CMakeLists.txt index 957be3ce4..c3bfcaece 100644 --- a/infra/stream/test/CMakeLists.txt +++ b/infra/stream/test/CMakeLists.txt @@ -12,6 +12,8 @@ target_sources(infra.stream_test PRIVATE StreamMock.hpp TestBoundedDequeInputStream.cpp TestBoundedVectorOutputStream.cpp + TestBufferingStreamReader.cpp + TestBufferingStreamWriter.cpp TestByteInputStream.cpp TestByteOutputStream.cpp TestCountingInputStream.cpp diff --git a/infra/stream/test/TestBufferingStreamReader.cpp b/infra/stream/test/TestBufferingStreamReader.cpp new file mode 100644 index 000000000..1d58c3b79 --- /dev/null +++ b/infra/stream/test/TestBufferingStreamReader.cpp @@ -0,0 +1,206 @@ +#include "infra/stream/BufferingStreamReader.hpp" +#include "infra/stream/test/StreamMock.hpp" +#include "gmock/gmock.h" + +class BufferingStreamReaderTest + : public testing::Test +{ +public: + ~BufferingStreamReaderTest() + { + EXPECT_CALL(input, Empty()).WillOnce(testing::Return(true)); + } + + void Extract(std::size_t amount) + { + std::vector data(amount, 0); + std::vector inputData; + EXPECT_CALL(input, ExtractContiguousRange(testing::_)).Times(testing::AnyNumber()).WillRepeatedly(testing::Invoke([&](std::size_t max) + { + inputData.resize(max); + return infra::MakeRange(inputData); + })); + reader.Extract(infra::MakeRange(data), errorPolicy); + } + + void ExpectBuffer(const std::vector contents) + { + EXPECT_EQ(infra::BoundedDeque::WithMaxSize<100>(contents.begin(), contents.end()), buffer); + } + + infra::BoundedDeque::WithMaxSize<4> buffer{ std::initializer_list{ 1, 2 } }; + testing::StrictMock input; + infra::StreamErrorPolicy errorPolicy{ infra::noFail }; + infra::BufferingStreamReader reader{ buffer, input }; +}; + +TEST_F(BufferingStreamReaderTest, Extract_from_empty_buffer) +{ + buffer.clear(); + std::array data; + EXPECT_CALL(input, ExtractContiguousRange(2)).WillOnce(testing::Return(infra::ConstByteRange())); + reader.Extract(data, errorPolicy); + EXPECT_TRUE(errorPolicy.Failed()); +} + +TEST_F(BufferingStreamReaderTest, Extract_from_buffer) +{ + std::array data; + reader.Extract(data, errorPolicy); + EXPECT_EQ((std::array{ 1, 2 }), data); + EXPECT_FALSE(errorPolicy.Failed()); +} + +TEST_F(BufferingStreamReaderTest, Extract_from_wrapped_buffer) +{ + buffer.push_back(3); + buffer.push_back(4); + buffer.pop_front(); + buffer.pop_front(); + buffer.push_back(5); + buffer.push_back(6); + + std::array data; + reader.Extract(data, errorPolicy); + EXPECT_EQ((std::array{ 3, 4, 5, 6 }), data); +} + +TEST_F(BufferingStreamReaderTest, Extract_from_buffer_and_input) +{ + std::array data; + std::array inputData{ 3, 4 }; + EXPECT_CALL(input, ExtractContiguousRange(2)).WillOnce(testing::Invoke([&](std::size_t max) + { + return infra::MakeRange(inputData); + })); + reader.Extract(data, errorPolicy); + EXPECT_EQ((std::array{ 1, 2, 3, 4 }), data); +} + +TEST_F(BufferingStreamReaderTest, PeekContiguous_range_from_buffer_head) +{ + EXPECT_EQ((std::array{ 1, 2 }), reader.PeekContiguousRange(0)); +} + +TEST_F(BufferingStreamReaderTest, PeekContiguous_range_from_buffer_1) +{ + EXPECT_EQ((std::array{ 2 }), reader.PeekContiguousRange(1)); +} + +TEST_F(BufferingStreamReaderTest, PeekContiguous_range_from_input) +{ + std::array inputData{ 3, 4 }; + EXPECT_CALL(input, PeekContiguousRange(0)).WillOnce(testing::Invoke([&](std::size_t start) + { + return infra::MakeRange(inputData); + })); + EXPECT_EQ((std::array{ 3, 4 }), reader.PeekContiguousRange(2)); +} + +TEST_F(BufferingStreamReaderTest, Peek_from_buffer) +{ + EXPECT_EQ(1, reader.Peek(errorPolicy)); + reader.ExtractContiguousRange(2); + + std::array inputData{ 3, 4 }; + EXPECT_CALL(input, PeekContiguousRange(0)).WillOnce(testing::Invoke([&](std::size_t start) + { + return infra::MakeRange(inputData); + })); + EXPECT_EQ(3, reader.Peek(errorPolicy)); +} + +TEST_F(BufferingStreamReaderTest, Available) +{ + EXPECT_CALL(input, Available()).WillOnce(testing::Return(1)); + EXPECT_EQ(3, reader.Available()); + + EXPECT_CALL(input, Available()).WillOnce(testing::Return(1)); + EXPECT_FALSE(reader.Empty()); +} + +TEST_F(BufferingStreamReaderTest, ConstructSaveMarker) +{ + EXPECT_EQ(0, reader.ConstructSaveMarker()); + + reader.ExtractContiguousRange(2); + EXPECT_EQ(2, reader.ConstructSaveMarker()); +} + +TEST_F(BufferingStreamReaderTest, Rewind_in_buffer) +{ + reader.ExtractContiguousRange(2); + reader.Rewind(1); + EXPECT_EQ(2, reader.Peek(errorPolicy)); +} + +TEST_F(BufferingStreamReaderTest, Rewind_from_input_to_input) +{ + Extract(4); + + EXPECT_CALL(input, ConstructSaveMarker()).WillOnce(testing::Return(100)); + EXPECT_CALL(input, Rewind(99)); + reader.Rewind(3); + std::array inputData2{ 4 }; + EXPECT_CALL(input, PeekContiguousRange(1)).WillOnce(testing::Invoke([&](std::size_t start) + { + return infra::MakeRange(inputData2); + })); + EXPECT_EQ(4, reader.Peek(errorPolicy)); +} + +TEST_F(BufferingStreamReaderTest, Rewind_from_input_to_buffer) +{ + Extract(4); + + EXPECT_CALL(input, ConstructSaveMarker()).WillOnce(testing::Return(100)); + EXPECT_CALL(input, Rewind(98)); + reader.Rewind(1); + EXPECT_EQ(2, reader.Peek(errorPolicy)); +} + +TEST_F(BufferingStreamReaderTest, destruction_reduces_buffer) +{ + Extract(1); + + EXPECT_CALL(input, Empty()).WillOnce(testing::Return(true)); + infra::ReConstruct(reader, buffer, input); + + ExpectBuffer({ 2 }); +} + +TEST_F(BufferingStreamReaderTest, destruction_consumes_buffer) +{ + Extract(3); + + EXPECT_CALL(input, Empty()).WillOnce(testing::Return(true)); + infra::ReConstruct(reader, buffer, input); + + ExpectBuffer({}); +} + +TEST_F(BufferingStreamReaderTest, destruction_stores_input) +{ + EXPECT_CALL(input, Empty()).WillOnce(testing::Return(false)).WillOnce(testing::Return(true)); + std::array inputData{ 3, 4 }; + EXPECT_CALL(input, ExtractContiguousRange(std::numeric_limits::max())).WillOnce(testing::Invoke([&](std::size_t max) + { + return infra::MakeRange(inputData); + })); + infra::ReConstruct(reader, buffer, input); + + ExpectBuffer({ 1, 2, 3, 4 }); +} + +TEST_F(BufferingStreamReaderTest, destruction_stores_input_until_empty) +{ + EXPECT_CALL(input, Empty()).WillOnce(testing::Return(false)).WillOnce(testing::Return(false)).WillOnce(testing::Return(true)); + std::array inputData{ 3 }; + EXPECT_CALL(input, ExtractContiguousRange(std::numeric_limits::max())).WillRepeatedly(testing::Invoke([&](std::size_t max) + { + return infra::MakeRange(inputData); + })); + infra::ReConstruct(reader, buffer, input); + + ExpectBuffer({ 1, 2, 3, 3 }); +} diff --git a/infra/stream/test/TestBufferingStreamWriter.cpp b/infra/stream/test/TestBufferingStreamWriter.cpp new file mode 100644 index 000000000..2bb6db290 --- /dev/null +++ b/infra/stream/test/TestBufferingStreamWriter.cpp @@ -0,0 +1,103 @@ +#include "infra/stream/BufferingStreamWriter.hpp" +#include "infra/stream/test/StreamMock.hpp" +#include "infra/util/test_helper/MemoryRangeMatcher.hpp" +#include "gmock/gmock.h" + +class BufferingStreamWriterTest + : public testing::Test +{ +public: + void ExpectBuffer(const std::vector contents) + { + EXPECT_EQ(infra::BoundedDeque::WithMaxSize<100>(contents.begin(), contents.end()), buffer); + } + + infra::BoundedDeque::WithMaxSize<4> buffer; + testing::StrictMock output; + infra::StreamErrorPolicy errorPolicy{ infra::noFail }; + infra::Execute execute{ [this]() + { + EXPECT_CALL(output, Available()).WillRepeatedly(testing::Return(0)); + EXPECT_CALL(output, Insert(testing::_, testing::_)).Times(2); + } }; + infra::BufferingStreamWriter writer{ buffer, output }; +}; + +TEST_F(BufferingStreamWriterTest, Insert_into_output) +{ + std::array data{ 3, 4 }; + EXPECT_CALL(output, Available()).WillOnce(testing::Return(10)); + EXPECT_CALL(output, Insert(infra::ContentsEqual(data), testing::Ref(errorPolicy))); + writer.Insert(data, errorPolicy); + + ExpectBuffer({}); + EXPECT_EQ(2, writer.ConstructSaveMarker()); +} + +TEST_F(BufferingStreamWriterTest, Insert_overflows_output) +{ + std::array data{ 3, 4 }; + EXPECT_CALL(output, Available()).WillOnce(testing::Return(1)); + EXPECT_CALL(output, Insert(infra::ByteRangeContentsEqual(infra::Head(infra::MakeRange(data), 1)), testing::Ref(errorPolicy))); + writer.Insert(data, errorPolicy); + + ExpectBuffer({ 4 }); + EXPECT_EQ(2, writer.ConstructSaveMarker()); +} + +TEST_F(BufferingStreamWriterTest, GetProcessedBytesSince) +{ + EXPECT_EQ(0, writer.GetProcessedBytesSince(0)); + + std::array data{ 3, 4 }; + EXPECT_CALL(output, Available()).WillOnce(testing::Return(10)); + EXPECT_CALL(output, Insert(infra::ContentsEqual(data), testing::Ref(errorPolicy))); + writer.Insert(data, errorPolicy); + + EXPECT_EQ(2, writer.GetProcessedBytesSince(0)); + EXPECT_EQ(1, writer.GetProcessedBytesSince(1)); +} + +TEST_F(BufferingStreamWriterTest, LoadRemainder_loads_from_buffer) +{ + std::array data{ 1, 2 }; + buffer.insert(buffer.end(), data.begin(), data.end()); + + EXPECT_CALL(output, Available()).WillOnce(testing::Return(2)).WillOnce(testing::Return(0)); + EXPECT_CALL(output, Insert(infra::ContentsEqual(data), testing::_)); + EXPECT_CALL(output, Insert(infra::ByteRangeContentsEqual(infra::ConstByteRange()), testing::_)); + infra::ReConstruct(writer, buffer, output); +} + +TEST_F(BufferingStreamWriterTest, LoadRemainder_loads_from_circular_buffer) +{ + std::array data1{ 1, 2 }; + std::array data2{ 3, 4 }; + std::array data3{ 5, 6 }; + buffer.insert(buffer.end(), data1.begin(), data1.end()); + buffer.insert(buffer.end(), data2.begin(), data2.end()); + buffer.pop_front(); + buffer.pop_front(); + buffer.insert(buffer.end(), data3.begin(), data3.end()); + + EXPECT_CALL(output, Available()).WillOnce(testing::Return(4)).WillOnce(testing::Return(2)); + EXPECT_CALL(output, Insert(infra::ContentsEqual(data2), testing::_)); + EXPECT_CALL(output, Insert(infra::ContentsEqual(data3), testing::_)); + infra::ReConstruct(writer, buffer, output); +} + +TEST_F(BufferingStreamWriterTest, LoadRemainder_constrained_by_output_writes_in_chunks) +{ + std::array data{ 1, 2 }; + buffer.insert(buffer.end(), data.begin(), data.end()); + + EXPECT_CALL(output, Available()).WillOnce(testing::Return(1)).WillOnce(testing::Return(0)); + EXPECT_CALL(output, Insert(infra::ByteRangeContentsEqual(infra::Head(infra::MakeRange(data), 1)), testing::_)); + EXPECT_CALL(output, Insert(infra::ByteRangeContentsEqual(infra::ConstByteRange()), testing::_)); + infra::ReConstruct(writer, buffer, output); + + EXPECT_CALL(output, Available()).WillOnce(testing::Return(1)).WillOnce(testing::Return(0)); + EXPECT_CALL(output, Insert(infra::ByteRangeContentsEqual(infra::DiscardHead(infra::MakeRange(data), 1)), testing::_)); + EXPECT_CALL(output, Insert(infra::ByteRangeContentsEqual(infra::ConstByteRange()), testing::_)); + infra::ReConstruct(writer, buffer, output); +} diff --git a/infra/syntax/ProtoFormatter.cpp b/infra/syntax/ProtoFormatter.cpp index efb630d04..7b3e9d99a 100644 --- a/infra/syntax/ProtoFormatter.cpp +++ b/infra/syntax/ProtoFormatter.cpp @@ -130,6 +130,12 @@ namespace infra PutBytes(bytes); } + void ProtoFormatter::PutLengthDelimitedSize(std::size_t size, uint32_t fieldNumber) + { + PutVarInt((fieldNumber << 3) | 2); + PutVarInt(size); + } + ProtoLengthDelimitedFormatter ProtoFormatter::LengthDelimitedFormatter(uint32_t fieldNumber) { return ProtoLengthDelimitedFormatter(*this, fieldNumber); diff --git a/infra/syntax/ProtoFormatter.hpp b/infra/syntax/ProtoFormatter.hpp index 45748075b..7c717ce3c 100644 --- a/infra/syntax/ProtoFormatter.hpp +++ b/infra/syntax/ProtoFormatter.hpp @@ -14,6 +14,7 @@ namespace infra { public: ProtoLengthDelimitedFormatter(ProtoFormatter& formatter, uint32_t fieldNumber); + ProtoLengthDelimitedFormatter(ProtoFormatter& formatter, uint32_t fieldNumber, std::size_t size); ProtoLengthDelimitedFormatter(const ProtoLengthDelimitedFormatter& other) = delete; ProtoLengthDelimitedFormatter(ProtoLengthDelimitedFormatter&& other) noexcept; ProtoLengthDelimitedFormatter& operator=(const ProtoLengthDelimitedFormatter& other) = delete; @@ -43,6 +44,7 @@ namespace infra void PutLengthDelimitedField(infra::ConstByteRange range, uint32_t fieldNumber); void PutStringField(infra::BoundedConstString string, uint32_t fieldNumber); void PutBytesField(infra::ConstByteRange bytes, uint32_t fieldNumber); + void PutLengthDelimitedSize(std::size_t size, uint32_t fieldNumber); ProtoLengthDelimitedFormatter LengthDelimitedFormatter(uint32_t fieldNumber); private: diff --git a/infra/syntax/ProtoParser.cpp b/infra/syntax/ProtoParser.cpp index 513ebf7a6..8b5e09fb5 100644 --- a/infra/syntax/ProtoParser.cpp +++ b/infra/syntax/ProtoParser.cpp @@ -116,7 +116,40 @@ namespace infra return result; } + struct MakeFullField + : infra::StaticVisitor + { + MakeFullField(infra::DataInputStream inputStream, infra::StreamErrorPolicy& formatErrorPolicy, uint32_t fieldNumber) + : inputStream(inputStream) + , formatErrorPolicy(formatErrorPolicy) + , fieldNumber(fieldNumber) + {} + + template + ProtoParser::Field operator()(T value) const + { + return { value, fieldNumber }; + } + + ProtoParser::Field operator()(PartialProtoLengthDelimited value) const + { + return { ProtoLengthDelimited(inputStream, formatErrorPolicy, value.length), fieldNumber }; + } + + private: + infra::DataInputStream inputStream; + infra::StreamErrorPolicy& formatErrorPolicy; + uint32_t fieldNumber; + }; + ProtoParser::Field ProtoParser::GetField() + { + auto [value, fieldNumber] = GetPartialField(); + MakeFullField visitor(input, formatErrorPolicy, fieldNumber); + return infra::ApplyVisitor(visitor, value); + } + + ProtoParser::PartialField ProtoParser::GetPartialField() { uint32_t x = static_cast(GetVarInt()); uint8_t type = x & 7; @@ -129,7 +162,7 @@ namespace infra case 1: return std::make_pair(GetFixed64(), fieldNumber); case 2: - return std::make_pair(ProtoLengthDelimited(input, formatErrorPolicy, static_cast(GetVarInt())), fieldNumber); + return std::make_pair(PartialProtoLengthDelimited{ static_cast(GetVarInt()) }, fieldNumber); case 5: return std::make_pair(GetFixed32(), fieldNumber); default: diff --git a/infra/syntax/ProtoParser.hpp b/infra/syntax/ProtoParser.hpp index 3cb6d8306..652041edc 100644 --- a/infra/syntax/ProtoParser.hpp +++ b/infra/syntax/ProtoParser.hpp @@ -34,10 +34,18 @@ namespace infra infra::StreamErrorPolicy& formatErrorPolicy; }; + struct PartialProtoLengthDelimited + { + uint32_t length; + }; + class ProtoParser { public: - using Field = std::pair, uint32_t>; + using FieldVariant = infra::Variant; + using Field = std::pair; + using PartialFieldVariant = infra::Variant; + using PartialField = std::pair; explicit ProtoParser(infra::DataInputStream inputStream); ProtoParser(infra::DataInputStream inputStream, infra::StreamErrorPolicy& formatErrorPolicy); @@ -48,6 +56,7 @@ namespace infra uint64_t GetFixed64(); Field GetField(); + PartialField GetPartialField(); void ReportFormatResult(bool ok); bool FormatFailed() const; diff --git a/infra/syntax/test/TestProtoFormatter.cpp b/infra/syntax/test/TestProtoFormatter.cpp index 6b34a148a..e42384893 100644 --- a/infra/syntax/test/TestProtoFormatter.cpp +++ b/infra/syntax/test/TestProtoFormatter.cpp @@ -76,6 +76,17 @@ TEST(ProtoFormatterTest, PutBytesField) EXPECT_EQ((std::array{ 4 << 3 | 2, 1, 5 }), stream.Writer().Processed()); } +TEST(ProtoFormatterTest, PutSubObjectOfKnownSize) +{ + infra::ByteOutputStream::WithStorage<20> stream; + infra::ProtoFormatter formatter(stream); + + formatter.PutLengthDelimitedSize(2, 4); + formatter.PutVarIntField(2, 4); + + EXPECT_EQ((std::array{ 4 << 3 | 2, 2, 4 << 3, 2 }), stream.Writer().Processed()); +} + TEST(ProtoFormatterTest, PutSubObject) { infra::ByteOutputStream::WithStorage<20> stream; diff --git a/infra/util/ConstructBin.cpp b/infra/util/ConstructBin.cpp index ce30b8fff..001ae1dce 100644 --- a/infra/util/ConstructBin.cpp +++ b/infra/util/ConstructBin.cpp @@ -32,6 +32,13 @@ namespace infra return *this; } + ConstructBin& ConstructBin::Repeat(std::size_t amount, std::initializer_list v) + { + for (size_t i = 0; i != amount; ++i) + contents.insert(contents.end(), v.begin(), v.end()); + return *this; + } + ConstructBin& ConstructBin::RepeatString(std::size_t amount, const std::string& v) { for (size_t i = 0; i != amount; ++i) diff --git a/infra/util/ConstructBin.hpp b/infra/util/ConstructBin.hpp index ba514fd4e..83631f5c4 100644 --- a/infra/util/ConstructBin.hpp +++ b/infra/util/ConstructBin.hpp @@ -17,6 +17,7 @@ namespace infra ConstructBin& operator()(std::initializer_list v); ConstructBin& Repeat(std::size_t amount, uint8_t v); + ConstructBin& Repeat(std::size_t amount, std::initializer_list v); ConstructBin& RepeatString(std::size_t amount, const std::string& v); template diff --git a/infra/util/WithStorage.hpp b/infra/util/WithStorage.hpp index 52f78eb9e..1b199ef7a 100644 --- a/infra/util/WithStorage.hpp +++ b/infra/util/WithStorage.hpp @@ -26,6 +26,8 @@ namespace infra WithStorage(); template WithStorage(InPlace, StorageArg&& storageArg, Args&&... args); + template + WithStorage(InPlace, std::initializer_list initializerList); template WithStorage(Arg&& arg, std::enable_if_t>>, std::nullptr_t> = nullptr); template @@ -89,6 +91,13 @@ namespace infra , Base(detail::StorageHolder::storage, std::forward(args)...) {} + template + template + WithStorage::WithStorage(InPlace, std::initializer_list initializerList) + : detail::StorageHolder(initializerList) + , Base(detail::StorageHolder::storage) + {} + template template WithStorage::WithStorage(Arg&& arg, std::enable_if_t>>, std::nullptr_t>) diff --git a/protobuf/CMakeLists.txt b/protobuf/CMakeLists.txt index da2d6f17d..e6fe5e065 100644 --- a/protobuf/CMakeLists.txt +++ b/protobuf/CMakeLists.txt @@ -1,5 +1,5 @@ -add_subdirectory(echo) add_subdirectory(echo_attributes) +add_subdirectory(echo) add_subdirectory(protoc_echo_plugin) add_subdirectory(protoc_echo_plugin_csharp) add_subdirectory(protoc_echo_plugin_java) diff --git a/protobuf/echo/CMakeLists.txt b/protobuf/echo/CMakeLists.txt index 168b25331..3f459aaeb 100644 --- a/protobuf/echo/CMakeLists.txt +++ b/protobuf/echo/CMakeLists.txt @@ -8,6 +8,11 @@ target_link_libraries(protobuf.echo PUBLIC target_sources(protobuf.echo PRIVATE Echo.cpp Echo.hpp + Proto.hpp + ProtoMessageReceiver.cpp + ProtoMessageReceiver.hpp + ProtoMessageSender.cpp + ProtoMessageSender.hpp ServiceForwarder.cpp ServiceForwarder.hpp TracingEcho.cpp diff --git a/protobuf/echo/Echo.hpp b/protobuf/echo/Echo.hpp index 81d04ba97..96a5e6a1d 100644 --- a/protobuf/echo/Echo.hpp +++ b/protobuf/echo/Echo.hpp @@ -1,12 +1,11 @@ #ifndef PROTOBUF_ECHO_HPP #define PROTOBUF_ECHO_HPP -#include "infra/syntax/ProtoFormatter.hpp" -#include "infra/syntax/ProtoParser.hpp" #include "infra/util/BoundedDeque.hpp" #include "infra/util/Compatibility.hpp" #include "infra/util/Function.hpp" #include "infra/util/Optional.hpp" +#include "protobuf/echo/Proto.hpp" #include "services/util/MessageCommunication.hpp" namespace services @@ -15,121 +14,6 @@ namespace services class Service; class ServiceProxy; - struct ProtoBool - {}; - - struct ProtoUInt32 - {}; - - struct ProtoInt32 - {}; - - struct ProtoUInt64 - {}; - - struct ProtoInt64 - {}; - - struct ProtoFixed32 - {}; - - struct ProtoFixed64 - {}; - - struct ProtoSFixed32 - {}; - - struct ProtoSFixed64 - {}; - - struct ProtoUnboundedString - {}; - - struct ProtoUnboundedBytes - {}; - - template - struct ProtoMessage - {}; - - template - struct ProtoEnum - {}; - - template - struct ProtoBytes - {}; - - template - struct ProtoString - {}; - - template - struct ProtoRepeated - {}; - - template - struct ProtoUnboundedRepeated - {}; - - void SerializeField(ProtoBool, infra::ProtoFormatter& formatter, bool value, uint32_t fieldNumber); - void SerializeField(ProtoUInt32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber); - void SerializeField(ProtoInt32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber); - void SerializeField(ProtoUInt64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber); - void SerializeField(ProtoInt64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber); - void SerializeField(ProtoFixed32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber); - void SerializeField(ProtoFixed64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber); - void SerializeField(ProtoSFixed32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber); - void SerializeField(ProtoSFixed64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber); - void SerializeField(ProtoUnboundedString, infra::ProtoFormatter& formatter, const std::string& value, uint32_t fieldNumber); - void SerializeField(ProtoUnboundedBytes, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber); - - template - void SerializeField(ProtoRepeated, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber); - template - void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber); - template - void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber); - template - void SerializeField(ProtoMessage, infra::ProtoFormatter& formatter, const U& value, uint32_t fieldNumber); - template - void SerializeField(ProtoEnum, infra::ProtoFormatter& formatter, T value, uint32_t fieldNumber); - template - void SerializeField(ProtoBytes, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber); - template - void SerializeField(ProtoString, infra::ProtoFormatter& formatter, infra::BoundedConstString value, uint32_t fieldNumber); - - void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::Field& field, bool& value); - void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint32_t& value); - void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int32_t& value); - void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint64_t& value); - void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int64_t& value); - void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint32_t& value); - void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint64_t& value); - void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int32_t& value); - void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int64_t& value); - void DeserializeField(ProtoUnboundedString, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::string& value); - void DeserializeField(ProtoUnboundedBytes, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::vector& value); - - template - void DeserializeField(ProtoRepeated, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedVector& value); - template - void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::vector& value); - template - void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::vector& value); - template - void DeserializeField(ProtoMessage, infra::ProtoParser& parser, infra::ProtoParser::Field& field, U& value); - template - void DeserializeField(ProtoEnum, infra::ProtoParser& parser, infra::ProtoParser::Field& field, T& value); - template - void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedVector& value); - template - void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::ConstByteRange& value); - template - void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedString& value); - template - void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedConstString& value); - class EchoErrorPolicy { protected: @@ -275,261 +159,6 @@ namespace services //// Implementation //// - inline void SerializeField(ProtoBool, infra::ProtoFormatter& formatter, bool value, uint32_t fieldNumber) - { - formatter.PutVarIntField(value, fieldNumber); - } - - inline void SerializeField(ProtoUInt32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber) - { - formatter.PutVarIntField(value, fieldNumber); - } - - inline void SerializeField(ProtoInt32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber) - { - formatter.PutVarIntField(value, fieldNumber); - } - - inline void SerializeField(ProtoUInt64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber) - { - formatter.PutVarIntField(value, fieldNumber); - } - - inline void SerializeField(ProtoInt64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber) - { - formatter.PutVarIntField(value, fieldNumber); - } - - inline void SerializeField(ProtoFixed32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber) - { - formatter.PutFixed32Field(value, fieldNumber); - } - - inline void SerializeField(ProtoFixed64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber) - { - formatter.PutFixed64Field(value, fieldNumber); - } - - inline void SerializeField(ProtoSFixed32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber) - { - formatter.PutFixed32Field(static_cast(value), fieldNumber); - } - - inline void SerializeField(ProtoSFixed64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber) - { - formatter.PutFixed64Field(static_cast(value), fieldNumber); - } - - inline void SerializeField(ProtoUnboundedString, infra::ProtoFormatter& formatter, const std::string& value, uint32_t fieldNumber) - { - formatter.PutStringField(value, fieldNumber); - } - - inline void SerializeField(ProtoUnboundedBytes, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber) - { - formatter.PutBytesField(value, fieldNumber); - } - - template - void SerializeField(ProtoRepeated, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber) - { - for (auto& v : value) - SerializeField(T(), formatter, v, fieldNumber); - } - - template - void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber) - { - for (auto& v : value) - SerializeField(T(), formatter, v, fieldNumber); - } - - template - void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber) - { - for (auto v : value) - SerializeField(T(), formatter, v, fieldNumber); - } - - template - void SerializeField(ProtoMessage, infra::ProtoFormatter& formatter, const U& value, uint32_t fieldNumber) - { - infra::ProtoLengthDelimitedFormatter nestedMessage(formatter, fieldNumber); - value.Serialize(formatter); - } - - template - void SerializeField(ProtoEnum, infra::ProtoFormatter& formatter, T value, uint32_t fieldNumber) - { - formatter.PutVarIntField(static_cast(value), fieldNumber); - } - - template - void SerializeField(ProtoBytes, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber) - { - formatter.PutBytesField(infra::MakeRange(value), fieldNumber); - } - - template - void SerializeField(ProtoString, infra::ProtoFormatter& formatter, infra::BoundedConstString value, uint32_t fieldNumber) - { - formatter.PutStringField(value, fieldNumber); - } - - inline void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::Field& field, bool& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = field.first.Get() != 0; - } - - inline void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint32_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = static_cast(field.first.Get()); - } - - inline void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int32_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = static_cast(field.first.Get()); - } - - inline void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint64_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = field.first.Get(); - } - - inline void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int64_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = static_cast(field.first.Get()); - } - - inline void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint32_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = field.first.Get(); - } - - inline void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, uint64_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = field.first.Get(); - } - - inline void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int32_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = static_cast(field.first.Get()); - } - - inline void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::Field& field, int64_t& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = static_cast(field.first.Get()); - } - - inline void DeserializeField(ProtoUnboundedString, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::string& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = field.first.Get().GetStdString(); - } - - inline void DeserializeField(ProtoUnboundedBytes, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::vector& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = field.first.Get().GetUnboundedBytes(); - } - - template - void DeserializeField(ProtoRepeated, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedVector& value) - { - parser.ReportFormatResult(!value.full()); - if (!value.full()) - { - value.emplace_back(); - DeserializeField(T(), parser, field, value.back()); - } - } - - template - void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::vector& value) - { - value.emplace_back(); - DeserializeField(T(), parser, field, value.back()); - } - - template - void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::Field& field, std::vector& value) - { - bool result{}; - DeserializeField(T(), parser, field, result); - value.push_back(result); - } - - template - void DeserializeField(ProtoMessage, infra::ProtoParser& parser, infra::ProtoParser::Field& field, U& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - { - infra::ProtoParser nestedParser = field.first.Get().Parser(); - value.Deserialize(nestedParser); - } - } - - template - void DeserializeField(ProtoEnum, infra::ProtoParser& parser, infra::ProtoParser::Field& field, T& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - value = static_cast(field.first.Get()); - } - - template - void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedVector& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - field.first.Get().GetBytes(value); - } - - template - void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::ConstByteRange& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - field.first.Get().GetBytesReference(value); - } - - template - void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedString& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - field.first.Get().GetString(value); - } - - template - void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::Field& field, infra::BoundedConstString& value) - { - parser.ReportFormatResult(field.first.Is()); - if (field.first.Is()) - field.first.Get().GetStringReference(value); - } - template template ServiceProxyResponseQueue::ServiceProxyResponseQueue(Container& container, Args&&... args) diff --git a/protobuf/echo/Proto.hpp b/protobuf/echo/Proto.hpp new file mode 100644 index 000000000..3fb0ae479 --- /dev/null +++ b/protobuf/echo/Proto.hpp @@ -0,0 +1,541 @@ +#ifndef PROTOBUF_PROTO_HPP +#define PROTOBUF_PROTO_HPP + +#include "infra/syntax/ProtoFormatter.hpp" +#include "infra/syntax/ProtoParser.hpp" +#include + +namespace services +{ + struct ProtoBool + {}; + + struct ProtoUInt32 + {}; + + struct ProtoInt32 + {}; + + struct ProtoUInt64 + {}; + + struct ProtoInt64 + {}; + + struct ProtoFixed32 + {}; + + struct ProtoFixed64 + {}; + + struct ProtoSFixed32 + {}; + + struct ProtoSFixed64 + {}; + + struct ProtoUnboundedString + {}; + + struct ProtoUnboundedBytes + {}; + + template + struct ProtoMessage + {}; + + template + struct ProtoEnum + {}; + + struct ProtoBytesBase + {}; + + template + struct ProtoBytes + : ProtoBytesBase + {}; + + struct ProtoStringBase + {}; + + template + struct ProtoString + : ProtoStringBase + {}; + + template + struct ProtoRepeatedBase + {}; + + template + struct ProtoRepeated + : ProtoRepeatedBase + {}; + + template + struct ProtoUnboundedRepeated + {}; + + void SerializeField(ProtoBool, infra::ProtoFormatter& formatter, bool value, uint32_t fieldNumber); + void SerializeField(ProtoUInt32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber); + void SerializeField(ProtoInt32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber); + void SerializeField(ProtoUInt64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber); + void SerializeField(ProtoInt64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber); + void SerializeField(ProtoFixed32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber); + void SerializeField(ProtoFixed64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber); + void SerializeField(ProtoSFixed32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber); + void SerializeField(ProtoSFixed64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber); + void SerializeField(ProtoUnboundedString, infra::ProtoFormatter& formatter, const std::string& value, uint32_t fieldNumber); + void SerializeField(ProtoUnboundedBytes, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber); + + template + void SerializeField(ProtoRepeated, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber); + template + void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber); + template + void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber); + template + void SerializeField(ProtoMessage, infra::ProtoFormatter& formatter, const U& value, uint32_t fieldNumber); + template + void SerializeField(ProtoEnum, infra::ProtoFormatter& formatter, T value, uint32_t fieldNumber); + template + void SerializeField(ProtoBytes, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber); + template + void SerializeField(ProtoString, infra::ProtoFormatter& formatter, infra::BoundedConstString value, uint32_t fieldNumber); + + void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, bool& value); + void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint32_t& value); + void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int32_t& value); + void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint64_t& value); + void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int64_t& value); + void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint32_t& value); + void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint64_t& value); + void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int32_t& value); + void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int64_t& value); + void DeserializeField(ProtoUnboundedString, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::string& value); + void DeserializeField(ProtoUnboundedBytes, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::vector& value); + + template + void DeserializeField(ProtoRepeated, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedVector& value); + template + void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::vector& value); + template + void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::vector& value); + template + void DeserializeField(ProtoMessage, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, U& value); + template + void DeserializeField(ProtoEnum, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, T& value); + template + void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedVector& value); + template + void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::ConstByteRange& value); + template + void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedString& value); + template + void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedConstString& value); + + void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, bool& value); + void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value); + void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value); + void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value); + void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value); + void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value); + void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value); + void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value); + void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value); + + template + struct MessageDepth + { + static constexpr uint32_t value = 0; + }; + + template<> + struct MessageDepth + { + static constexpr uint32_t value = 1; + }; + + template<> + struct MessageDepth + { + static constexpr uint32_t value = 1; + }; + + template + struct MessageDepth> + { + static constexpr uint32_t value = 1; + }; + + template + struct MessageDepth> + { + static constexpr uint32_t value = 1; + }; + + template + struct MessageDepth> + { + static constexpr uint32_t value = 1; + }; + + template + struct MessageDepth> + { + static constexpr uint32_t value = 1; + }; + + template + struct Max; + + template + struct Max + { + static constexpr uint32_t value = V; + }; + + template + struct Max + { + static constexpr uint32_t value = std::max(V, Max::value); + }; + + template + struct MaxFieldsDepth; + + template + struct MaxFieldsDepth> + { + static constexpr uint32_t value = Max>::value...>::value; + }; + + template + struct MessageDepth> + { + static constexpr uint32_t value = MaxFieldsDepth>::value + 1; + }; + + //// Implementation //// + + inline void SerializeField(ProtoBool, infra::ProtoFormatter& formatter, bool value, uint32_t fieldNumber) + { + formatter.PutVarIntField(value, fieldNumber); + } + + inline void SerializeField(ProtoUInt32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber) + { + formatter.PutVarIntField(value, fieldNumber); + } + + inline void SerializeField(ProtoInt32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber) + { + formatter.PutVarIntField(value, fieldNumber); + } + + inline void SerializeField(ProtoUInt64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber) + { + formatter.PutVarIntField(value, fieldNumber); + } + + inline void SerializeField(ProtoInt64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber) + { + formatter.PutVarIntField(value, fieldNumber); + } + + inline void SerializeField(ProtoFixed32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber) + { + formatter.PutFixed32Field(value, fieldNumber); + } + + inline void SerializeField(ProtoFixed64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber) + { + formatter.PutFixed64Field(value, fieldNumber); + } + + inline void SerializeField(ProtoSFixed32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber) + { + formatter.PutFixed32Field(static_cast(value), fieldNumber); + } + + inline void SerializeField(ProtoSFixed64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber) + { + formatter.PutFixed64Field(static_cast(value), fieldNumber); + } + + inline void SerializeField(ProtoUnboundedString, infra::ProtoFormatter& formatter, const std::string& value, uint32_t fieldNumber) + { + formatter.PutStringField(value, fieldNumber); + } + + inline void SerializeField(ProtoUnboundedBytes, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber) + { + formatter.PutBytesField(value, fieldNumber); + } + + template + void SerializeField(ProtoRepeated, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber) + { + for (auto& v : value) + SerializeField(T(), formatter, v, fieldNumber); + } + + template + void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber) + { + for (auto& v : value) + SerializeField(T(), formatter, v, fieldNumber); + } + + template + void SerializeField(ProtoUnboundedRepeated, infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber) + { + for (auto v : value) + SerializeField(T(), formatter, v, fieldNumber); + } + + template + void SerializeField(ProtoMessage, infra::ProtoFormatter& formatter, const U& value, uint32_t fieldNumber) + { + infra::ProtoLengthDelimitedFormatter nestedMessage(formatter, fieldNumber); + value.Serialize(formatter); + } + + template + void SerializeField(ProtoEnum, infra::ProtoFormatter& formatter, T value, uint32_t fieldNumber) + { + formatter.PutVarIntField(static_cast(value), fieldNumber); + } + + template + void SerializeField(ProtoBytes, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber) + { + formatter.PutBytesField(infra::MakeRange(value), fieldNumber); + } + + template + void SerializeField(ProtoString, infra::ProtoFormatter& formatter, infra::BoundedConstString value, uint32_t fieldNumber) + { + formatter.PutStringField(value, fieldNumber); + } + + inline void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, bool& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get() != 0; + } + + inline void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get(); + } + + inline void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get(); + } + + inline void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, uint64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get(); + } + + inline void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, int64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoUnboundedString, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::string& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get().GetStdString(); + } + + inline void DeserializeField(ProtoUnboundedBytes, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::vector& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get().GetUnboundedBytes(); + } + + template + void DeserializeField(ProtoRepeated, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedVector& value) + { + parser.ReportFormatResult(!value.full()); + if (!value.full()) + { + value.emplace_back(); + DeserializeField(T(), parser, field, value.back()); + } + } + + template + void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::vector& value) + { + value.emplace_back(); + DeserializeField(T(), parser, field, value.back()); + } + + template + void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, std::vector& value) + { + bool result{}; + DeserializeField(T(), parser, field, result); + value.push_back(result); + } + + template + void DeserializeField(ProtoMessage, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, U& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + { + infra::ProtoParser nestedParser = field.Get().Parser(); + value.Deserialize(nestedParser); + } + } + + template + void DeserializeField(ProtoEnum, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, T& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + template + void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedVector& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + field.Get().GetBytes(value); + } + + template + void DeserializeField(ProtoBytes, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::ConstByteRange& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + field.Get().GetBytesReference(value); + } + + template + void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedString& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + field.Get().GetString(value); + } + + template + void DeserializeField(ProtoString, infra::ProtoParser& parser, infra::ProtoParser::FieldVariant& field, infra::BoundedConstString& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + field.Get().GetStringReference(value); + } + + inline void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, bool& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get() != 0; + } + + inline void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get(); + } + + inline void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get(); + } + + inline void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = field.Get(); + } + + inline void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + inline void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } +} + +#endif diff --git a/protobuf/echo/ProtoMessageReceiver.cpp b/protobuf/echo/ProtoMessageReceiver.cpp new file mode 100644 index 000000000..1c2e14a9e --- /dev/null +++ b/protobuf/echo/ProtoMessageReceiver.cpp @@ -0,0 +1,142 @@ +#include "protobuf/echo/ProtoMessageReceiver.hpp" +#include "infra/stream/BufferingStreamReader.hpp" + +namespace services +{ + ProtoMessageReceiverBase::ProtoMessageReceiverBase(infra::BoundedVector>>& stack) + : stack(stack) + {} + + void ProtoMessageReceiverBase::Feed(infra::StreamReaderWithRewinding& data) + { + infra::BufferingStreamReader reader{ buffer, data }; + + while (true) + { + infra::LimitedStreamReaderWithRewinding limitedReader(reader, stack.back().first); + infra::DataInputStream::WithErrorPolicy stream{ limitedReader, infra::softFail }; + + auto available = limitedReader.Available(); + + auto& current = stack.back(); + current.second(stream); + + if (stream.Failed() || reader.Empty()) + break; + + if (¤t != &stack.front()) + { + current.first -= available - limitedReader.Available(); + + if (current.first == 0) + stack.pop_back(); + } + } + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, bool& value) const + { + services::DeserializeField(ProtoBool(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value) const + { + services::DeserializeField(ProtoUInt32(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value) const + { + services::DeserializeField(ProtoInt32(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value) const + { + services::DeserializeField(ProtoUInt64(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value) const + { + services::DeserializeField(ProtoInt64(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value) const + { + services::DeserializeField(ProtoFixed32(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value) const + { + services::DeserializeField(ProtoFixed64(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value) const + { + services::DeserializeField(ProtoSFixed32(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value) const + { + services::DeserializeField(ProtoSFixed64(), parser, field, value); + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoStringBase, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, infra::BoundedString& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + { + auto stringSize = field.Get().length; + stack.emplace_back(stringSize, [&value](const infra::DataInputStream& stream) + { + while (!stream.Empty()) + value.append(infra::ByteRangeAsString(stream.ContiguousRange())); + }); + value.clear(); + } + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoUnboundedString, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, std::string& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + { + auto stringSize = field.Get().length; + stack.emplace_back(stringSize, [&value](const infra::DataInputStream& stream) + { + while (!stream.Empty()) + value.append(infra::ByteRangeAsStdString(stream.ContiguousRange())); + }); + value.clear(); + } + } + + void ProtoMessageReceiverBase::DeserializeField(ProtoBytesBase, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, infra::BoundedVector& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + { + auto bytesSize = field.Get().length; + stack.emplace_back(bytesSize, [&value](const infra::DataInputStream& stream) + { + while (!stream.Empty()) + { + auto range = stream.ContiguousRange(); + value.insert(value.end(), range.begin(), range.end()); + } + }); + value.clear(); + } + } + + void ProtoMessageReceiverBase::ConsumeUnknownField(infra::ProtoParser::PartialField& field) + { + if (field.first.Is()) + { + auto size = field.first.Get().length; + stack.emplace_back(size, [](const infra::DataInputStream& stream) + { + while (!stream.Empty()) + stream.ContiguousRange(); + }); + } + } +} diff --git a/protobuf/echo/ProtoMessageReceiver.hpp b/protobuf/echo/ProtoMessageReceiver.hpp new file mode 100644 index 000000000..b127695b4 --- /dev/null +++ b/protobuf/echo/ProtoMessageReceiver.hpp @@ -0,0 +1,173 @@ +#ifndef PROTOBUF_PROTO_MESSAGE_RECEIVER_HPP +#define PROTOBUF_PROTO_MESSAGE_RECEIVER_HPP + +#include "infra/syntax/ProtoParser.hpp" +#include "infra/util/BoundedDeque.hpp" +#include "infra/util/BoundedVector.hpp" +#include "protobuf/echo/Proto.hpp" + +namespace services +{ + class ProtoMessageReceiverBase + { + public: + explicit ProtoMessageReceiverBase(infra::BoundedVector>>& stack); + + void Feed(infra::StreamReaderWithRewinding& data); + + protected: + template + void FeedForMessage(const infra::DataInputStream& stream, Message& message); + + private: + template + bool DeserializeFields(infra::ProtoParser::PartialField& field, infra::ProtoParser& parser, Message& message, std::index_sequence); + + template + bool DeserializeSingleField(infra::ProtoParser::PartialField& field, infra::ProtoParser& parser, Message& message); + + void DeserializeField(ProtoBool, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, bool& value) const; + void DeserializeField(ProtoUInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value) const; + void DeserializeField(ProtoInt32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value) const; + void DeserializeField(ProtoUInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value) const; + void DeserializeField(ProtoInt64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value) const; + void DeserializeField(ProtoFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint32_t& value) const; + void DeserializeField(ProtoFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, uint64_t& value) const; + void DeserializeField(ProtoSFixed32, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int32_t& value) const; + void DeserializeField(ProtoSFixed64, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, int64_t& value) const; + + void DeserializeField(ProtoStringBase, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, infra::BoundedString& value); + void DeserializeField(ProtoUnboundedString, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, std::string& value); + void DeserializeField(ProtoBytesBase, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, infra::BoundedVector& value); + + template + void DeserializeField(ProtoEnum, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Enum& value) const; + template + void DeserializeField(ProtoMessage, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Message& value); + template + void DeserializeField(ProtoRepeatedBase, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Type& value) const; + template + void DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Type& value) const; + + void ConsumeUnknownField(infra::ProtoParser::PartialField& field); + + private: + infra::BoundedDeque::WithMaxSize<32> buffer; + infra::BoundedVector>>& stack; + }; + + template + class ProtoMessageReceiver + : public ProtoMessageReceiverBase + { + public: + ProtoMessageReceiver(); + + Message message; + + private: + infra::BoundedVector>>::WithMaxSize>::value + 1> stack{ { std::pair>{ std::numeric_limits::max(), [this](const infra::DataInputStream& stream) + { + FeedForMessage(stream, message); + } } } }; + }; +} + +//// Implementation //// + +namespace services +{ + template + void ProtoMessageReceiverBase::FeedForMessage(const infra::DataInputStream& stream, Message& message) + { + infra::ProtoParser parser{ stream }; + + while (!stream.Empty()) + { + auto marker = static_cast(stream.Reader()).ConstructSaveMarker(); + auto field = parser.GetPartialField(); + + if (stream.Failed()) + { + static_cast(stream.Reader()).Rewind(marker); + break; + } + + auto stackSize = stack.size(); + if (!DeserializeFields(field, parser, message, std::make_index_sequence{})) + ConsumeUnknownField(field); + + if (stackSize != stack.size()) + { + stream.Failed(); + return; + } + } + } + + template + bool ProtoMessageReceiverBase::DeserializeFields(infra::ProtoParser::PartialField& field, infra::ProtoParser& parser, Message& message, std::index_sequence) + { + return (DeserializeSingleField(field, parser, message) || ...); + } + + template + bool ProtoMessageReceiverBase::DeserializeSingleField(infra::ProtoParser::PartialField& field, infra::ProtoParser& parser, Message& message) + { + if (field.second == Message::template fieldNumber) + { + DeserializeField(typename Message::template ProtoType(), parser, field.first, message.Get(std::integral_constant())); + return true; + } + + return false; + } + + template + void ProtoMessageReceiverBase::DeserializeField(ProtoEnum, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Enum& value) const + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + value = static_cast(field.Get()); + } + + template + void ProtoMessageReceiverBase::DeserializeField(ProtoMessage, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Message& value) + { + parser.ReportFormatResult(field.Is()); + if (field.Is()) + { + auto messageSize = field.Get().length; + stack.emplace_back(messageSize, [this, &value](const infra::DataInputStream& stream) + { + FeedForMessage(stream, value); + }); + infra::ReConstruct(value); + } + } + + template + void ProtoMessageReceiverBase::DeserializeField(ProtoRepeatedBase, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Type& value) const + { + parser.ReportFormatResult(!value.full()); + if (!value.full()) + { + value.emplace_back(); + DeserializeField(ProtoType(), parser, field, value.back()); + } + } + + template + void ProtoMessageReceiverBase::DeserializeField(ProtoUnboundedRepeated, infra::ProtoParser& parser, infra::ProtoParser::PartialFieldVariant& field, Type& value) const + { + value.emplace_back(); + DeserializeField(ProtoType(), parser, field, value.back()); + } + + template + ProtoMessageReceiver::ProtoMessageReceiver() + : ProtoMessageReceiverBase(stack) + {} +} + +#endif diff --git a/protobuf/echo/ProtoMessageSender.cpp b/protobuf/echo/ProtoMessageSender.cpp new file mode 100644 index 000000000..5f013d486 --- /dev/null +++ b/protobuf/echo/ProtoMessageSender.cpp @@ -0,0 +1,132 @@ +#include "protobuf/echo/ProtoMessageSender.hpp" +#include "infra/stream/BufferingStreamWriter.hpp" + +namespace services +{ + ProtoMessageSenderBase::ProtoMessageSenderBase(infra::BoundedVector>>& stack) + : stack(stack) + {} + + void ProtoMessageSenderBase::Fill(infra::DataOutputStream output) + { + infra::BufferingStreamWriter writer{ buffer, output.Writer() }; + + while (!stack.empty()) + { + infra::DataOutputStream::WithErrorPolicy stream{ writer }; + + auto& [index, callback] = stack.back(); + bool retry = false; + auto result = callback(stream, index, retry, output.Writer()); + if (result) + stack.pop_back(); + else if (!retry) + break; + } + } + + bool ProtoMessageSenderBase::SerializeField(ProtoBool, infra::ProtoFormatter& formatter, bool value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoBool(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoUInt32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoUInt32(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoInt32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoInt32(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoUInt64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoUInt64(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoInt64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoInt64(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoFixed32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoFixed32(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoSFixed32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoSFixed32(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoFixed64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoFixed64(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoSFixed64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoSFixed64(), formatter, value, fieldNumber); + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoStringBase, infra::ProtoFormatter& formatter, const infra::BoundedString& value, uint32_t fieldNumber, bool& retry) const + { + formatter.PutVarInt((fieldNumber << 3) | 2); + formatter.PutVarInt(value.size()); + + stack.emplace_back(0, [&value](infra::DataOutputStream& stream, uint32_t& index, const bool&, const infra::StreamWriter&) + { + auto range = infra::Head(infra::DiscardHead(infra::StringAsByteRange(value), index), stream.Available()); + stream << range; + index += range.size(); + return index == value.size(); + }); + + retry = true; + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoUnboundedString, infra::ProtoFormatter& formatter, const std::string& value, uint32_t fieldNumber, bool& retry) const + { + formatter.PutVarInt((fieldNumber << 3) | 2); + formatter.PutVarInt(value.size()); + + stack.emplace_back(0, [&value](infra::DataOutputStream& stream, uint32_t& index, const bool&, const infra::StreamWriter&) + { + auto range = infra::Head(infra::DiscardHead(infra::StdStringAsByteRange(value), index), stream.Available()); + stream << range; + index += range.size(); + return index == value.size(); + }); + + retry = true; + return true; + } + + bool ProtoMessageSenderBase::SerializeField(ProtoBytesBase, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber, bool& retry) const + { + formatter.PutVarInt((fieldNumber << 3) | 2); + formatter.PutVarInt(value.size()); + + stack.emplace_back(0, [&value](infra::DataOutputStream& stream, uint32_t& index, const bool&, const infra::StreamWriter&) + { + auto range = infra::Head(infra::DiscardHead(infra::MakeRange(value), index), stream.Available()); + stream << range; + index += range.size(); + return index == value.size(); + }); + + retry = true; + return true; + } +} diff --git a/protobuf/echo/ProtoMessageSender.hpp b/protobuf/echo/ProtoMessageSender.hpp new file mode 100644 index 000000000..930c3df9f --- /dev/null +++ b/protobuf/echo/ProtoMessageSender.hpp @@ -0,0 +1,180 @@ +#ifndef PROTOBUF_PROTO_MESSAGE_SENDER_HPP +#define PROTOBUF_PROTO_MESSAGE_SENDER_HPP + +#include "infra/stream/CountingOutputStream.hpp" +#include "infra/syntax/ProtoFormatter.hpp" +#include "infra/util/BoundedDeque.hpp" +#include "infra/util/BoundedVector.hpp" +#include "protobuf/echo/Proto.hpp" + +namespace services +{ + class ProtoMessageSenderBase + { + public: + explicit ProtoMessageSenderBase(infra::BoundedVector>>& stack); + + void Fill(infra::DataOutputStream output); + + protected: + template + bool FillForMessage(infra::DataOutputStream& stream, const Message& message, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter) const; + + private: + template + bool SerializeFields(infra::ProtoFormatter& formatter, const Message& message, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter, std::index_sequence) const; + + template + bool SerializeSingleField(infra::ProtoFormatter& formatter, const Message& message, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter) const; + + bool SerializeField(ProtoBool, infra::ProtoFormatter& formatter, bool value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoUInt32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoInt32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoUInt64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoInt64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoFixed32, infra::ProtoFormatter& formatter, uint32_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoSFixed32, infra::ProtoFormatter& formatter, int32_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoFixed64, infra::ProtoFormatter& formatter, uint64_t value, uint32_t fieldNumber, const bool& retry) const; + bool SerializeField(ProtoSFixed64, infra::ProtoFormatter& formatter, int64_t value, uint32_t fieldNumber, const bool& retry) const; + + bool SerializeField(ProtoStringBase, infra::ProtoFormatter& formatter, const infra::BoundedString& value, uint32_t fieldNumber, bool& retry) const; + bool SerializeField(ProtoUnboundedString, infra::ProtoFormatter& formatter, const std::string& value, uint32_t fieldNumber, bool& retry) const; + bool SerializeField(ProtoBytesBase, infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber, bool& retry) const; + + template + bool SerializeField(ProtoEnum, infra::ProtoFormatter& formatter, const Enum& value, uint32_t fieldNumber, const bool& retry) const; + template + bool SerializeField(ProtoMessage, infra::ProtoFormatter& formatter, const Message& value, uint32_t fieldNumber, bool& retry) const; + template + bool SerializeField(ProtoRepeatedBase, const infra::ProtoFormatter& formatter, const infra::BoundedVector& value, uint32_t fieldNumber, bool& retry) const; + template + bool SerializeField(ProtoUnboundedRepeated, const infra::ProtoFormatter& formatter, const std::vector& value, uint32_t fieldNumber, bool& retry) const; + + private: + infra::BoundedDeque::WithMaxSize<32> buffer; + infra::BoundedVector>>& stack; + }; + + template + class ProtoMessageSender + : public ProtoMessageSenderBase + { + public: + explicit ProtoMessageSender(const Message& message); + + private: + const Message& message; + infra::BoundedVector>>::WithMaxSize>::value + 1> stack{ { std::pair>{ 0, [this](infra::DataOutputStream& stream, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter) + { + return FillForMessage(stream, message, index, retry, finalWriter); + } } } }; + }; + + //// Implementation //// + + template + bool ProtoMessageSenderBase::FillForMessage(infra::DataOutputStream& stream, const Message& message, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter) const + { + infra::ProtoFormatter formatter{ stream }; + + return SerializeFields(formatter, message, index, retry, finalWriter, std::make_index_sequence{}); + } + + template + bool ProtoMessageSenderBase::SerializeFields(infra::ProtoFormatter& formatter, const Message& message, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter, std::index_sequence) const + { + return (SerializeSingleField(formatter, message, index, retry, finalWriter) && ...); + } + + template + bool ProtoMessageSenderBase::SerializeSingleField(infra::ProtoFormatter& formatter, const Message& message, uint32_t& index, bool& retry, const infra::StreamWriter& finalWriter) const + { + if (finalWriter.Available() == 0) + return false; + + if (index == I) + { + if (!SerializeField(typename Message::template ProtoType(), formatter, message.Get(std::integral_constant()), Message::template fieldNumber, retry)) + return false; + + index = I + 1; + return !retry; + } + + return true; + } + + template + bool ProtoMessageSenderBase::SerializeField(ProtoEnum, infra::ProtoFormatter& formatter, const Enum& value, uint32_t fieldNumber, [[maybe_unused]] const bool& retry) const + { + services::SerializeField(ProtoEnum(), formatter, value, fieldNumber); + return true; + } + + template + bool ProtoMessageSenderBase::SerializeField(ProtoMessage, infra::ProtoFormatter& formatter, const Message& value, uint32_t fieldNumber, bool& retry) const + { + infra::DataOutputStream::WithWriter countingStream; + infra::ProtoFormatter countingFormatter{ countingStream }; + value.Serialize(countingFormatter); + formatter.PutLengthDelimitedSize(countingStream.Writer().Processed(), fieldNumber); + + stack.emplace_back(0, [this, &value](infra::DataOutputStream& stream, uint32_t& index, bool& retry2, const infra::StreamWriter& finalWriter) + { + return FillForMessage(stream, value, index, retry2, finalWriter); + }); + + retry = true; + return true; + } + + template + bool ProtoMessageSenderBase::SerializeField(ProtoRepeatedBase, const infra::ProtoFormatter&, const infra::BoundedVector& value, uint32_t fieldNumber, bool& retry) const + { + stack.emplace_back(0, [this, &value, fieldNumber](infra::DataOutputStream& stream, uint32_t& index, const bool&, const infra::StreamWriter& finalWriter) + { + infra::ProtoFormatter formatter{ stream }; + + for (; index != value.size(); ++index) + { + if (finalWriter.Available() == 0) + return false; + services::SerializeField(ProtoType(), formatter, value[index], fieldNumber); + } + + return true; + }); + + retry = true; + return true; + } + + template + bool ProtoMessageSenderBase::SerializeField(ProtoUnboundedRepeated, const infra::ProtoFormatter&, const std::vector& value, uint32_t fieldNumber, bool& retry) const + { + stack.emplace_back(0, [this, &value, fieldNumber](infra::DataOutputStream& stream, uint32_t& index, const bool&, const infra::StreamWriter& finalWriter) + { + infra::ProtoFormatter formatter{ stream }; + + for (; index != value.size(); ++index) + { + if (finalWriter.Available() == 0) + return false; + services::SerializeField(ProtoType(), formatter, value[index], fieldNumber); + } + + return true; + }); + + retry = true; + return true; + } + + template + ProtoMessageSender::ProtoMessageSender(const Message& message) + : ProtoMessageSenderBase(stack) + , message(message) + {} +} + +#endif diff --git a/protobuf/echo/test/CMakeLists.txt b/protobuf/echo/test/CMakeLists.txt index 3176af5dc..9dd11414e 100644 --- a/protobuf/echo/test/CMakeLists.txt +++ b/protobuf/echo/test/CMakeLists.txt @@ -2,8 +2,12 @@ add_executable(protobuf.echo_test) emil_build_for(protobuf.echo_test BOOL EMIL_BUILD_TESTS) emil_add_test(protobuf.echo_test) +protocol_buffer_echo_cpp(protobuf.echo_test TestMessages.proto) + target_sources(protobuf.echo_test PRIVATE TestEchoServiceResponseQueue.cpp + TestProtoMessageReceiver.cpp + TestProtoMessageSender.cpp TestServiceForwarder.cpp ) diff --git a/protobuf/protoc_echo_plugin/test/TestMessages.proto b/protobuf/echo/test/TestMessages.proto similarity index 92% rename from protobuf/protoc_echo_plugin/test/TestMessages.proto rename to protobuf/echo/test/TestMessages.proto index 8f931978e..5588d0a11 100644 --- a/protobuf/protoc_echo_plugin/test/TestMessages.proto +++ b/protobuf/echo/test/TestMessages.proto @@ -64,7 +64,7 @@ message TestRepeatedString { } message TestBytes { - bytes value = 1 [(bytes_size) = 10]; + bytes value = 1 [(bytes_size) = 50]; } message TestUnboundedBytes { @@ -72,7 +72,11 @@ message TestUnboundedBytes { } message TestRepeatedUInt32 { - repeated uint32 value = 1 [(array_size) = 10]; + repeated uint32 value = 1 [(array_size) = 50]; +} + +message TestUnboundedRepeatedUInt32 { + repeated uint32 value = 1; } message TestMessageWithMessageField { @@ -168,3 +172,8 @@ service TestService2 rpc Search (TestString) returns (Nothing) { option (method_id) = 1; } } + +message TestBoolWithBytes { + bytes b = 1 [(bytes_size) = 30]; + bool value = 2; +} diff --git a/protobuf/echo/test/TestProtoMessageReceiver.cpp b/protobuf/echo/test/TestProtoMessageReceiver.cpp new file mode 100644 index 000000000..fe7a83fc0 --- /dev/null +++ b/protobuf/echo/test/TestProtoMessageReceiver.cpp @@ -0,0 +1,222 @@ +#include "generated/echo/TestMessages.pb.hpp" +#include "infra/stream/StdVectorInputStream.hpp" +#include "protobuf/echo/ProtoMessageReceiver.hpp" +#include + +TEST(ProtoMessageReceiverTest, parse_bool) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 1 }); + receiver.Feed(data); + + EXPECT_EQ(true, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_unknown_simple_field) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 2 << 3, 1, 1 << 3, 1 }); + receiver.Feed(data); + + EXPECT_EQ(true, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_unknown_length_delimited) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 1, (2 << 3) | 2, 2, 1 << 3, 0 }); + receiver.Feed(data); + + EXPECT_EQ(true, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_incomplete_bool) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 8 }); + receiver.Feed(data); + + EXPECT_EQ(false, receiver.message.value); + + infra::StdVectorInputStreamReader::WithStorage data2(infra::inPlace, std::initializer_list{ 1 }); + receiver.Feed(data2); + + EXPECT_EQ(true, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_uint32) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 5 }); + receiver.Feed(data); + + EXPECT_EQ(5, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_int32) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1 }); + receiver.Feed(data); + + EXPECT_EQ(-1, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_uint64) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 5 }); + receiver.Feed(data); + + EXPECT_EQ(5, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_int64) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1 }); + receiver.Feed(data); + + EXPECT_EQ(-1, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_fixed32) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 13, 0x90, 0x91, 0x0f, 0 }); + receiver.Feed(data); + + EXPECT_EQ(1020304, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_fixed64) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 9, 0x64, 0xc8, 0x0c, 0xce, 0xcb, 0x5c, 0x00, 0x00 }); + receiver.Feed(data); + + EXPECT_EQ(102030405060708, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_sfixed32) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 13, 0xff, 0xff, 0xff, 0xff }); + receiver.Feed(data); + + EXPECT_EQ(-1, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_sfixed64) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); + receiver.Feed(data); + + EXPECT_EQ(-1, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_enum) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 1 }); + receiver.Feed(data); + + EXPECT_EQ(test_messages::Enumeration::val1, receiver.message.e); +} + +TEST(ProtoMessageReceiverTest, parse_repeated_uint32) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 5, 1 << 3, 6 }); + receiver.Feed(data); + + EXPECT_EQ((infra::BoundedVector::WithMaxSize<10>{ { 5, 6 } }), receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_unbounded_repeated_uint32) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 1 << 3, 5, 1 << 3, 6 }); + receiver.Feed(data); + + EXPECT_EQ((std::vector{ { 5, 6 } }), receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_incomplete_int) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }); + receiver.Feed(data); + EXPECT_EQ(0, receiver.message.value); + + infra::StdVectorInputStreamReader::WithStorage data2(infra::inPlace, std::initializer_list{ 0xff, 1 }); + receiver.Feed(data2); + EXPECT_EQ(-1, receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_string) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 10, 4, 'a', 'b', 'c', 'd' }); + receiver.Feed(data); + + EXPECT_EQ("abcd", receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_std_string) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 10, 4, 'a', 'b', 'c', 'd' }); + receiver.Feed(data); + + EXPECT_EQ("abcd", receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_bytes) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ 10, 2, 5, 6 }); + receiver.Feed(data); + + EXPECT_EQ((infra::BoundedVector::WithMaxSize<10>{ { 5, 6 } }), receiver.message.value); +} + +TEST(ProtoMessageReceiverTest, parse_message) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ (1 << 3) | 2, 2, 1 << 3, 5 }); + receiver.Feed(data); + + EXPECT_EQ((test_messages::TestMessageWithMessageField(test_messages::TestUInt32(5))), receiver.message.message); +} + +TEST(ProtoMessageReceiverTest, parse_more_nested_message) +{ + services::ProtoMessageReceiver receiver; + + infra::StdVectorInputStreamReader::WithStorage data(infra::inPlace, std::initializer_list{ (1 << 3) | 2, 2, 1 << 3, 5, (2 << 3) | 2, 2, 2 << 3, 10 }); + receiver.Feed(data); + + EXPECT_EQ((test_messages::TestMoreNestedMessage({ 5 }, { 10 })), receiver.message); +} diff --git a/protobuf/echo/test/TestProtoMessageSender.cpp b/protobuf/echo/test/TestProtoMessageSender.cpp new file mode 100644 index 000000000..3cefc4282 --- /dev/null +++ b/protobuf/echo/test/TestProtoMessageSender.cpp @@ -0,0 +1,222 @@ +#include "generated/echo/TestMessages.pb.hpp" +#include "infra/stream/BoundedVectorOutputStream.hpp" +#include "infra/stream/ByteOutputStream.hpp" +#include "infra/stream/StdVectorOutputStream.hpp" +#include "infra/util/ConstructBin.hpp" +#include "protobuf/echo/ProtoMessageSender.hpp" +#include + +class ProtoMessageSenderTest + : public testing::Test +{ +public: + template + void ExpectFill(const std::vector& data, services::ProtoMessageSender& sender) + { + infra::StdVectorOutputStream::WithStorage stream; + sender.Fill(stream); + + EXPECT_EQ(data, stream.Storage()); + } +}; + +TEST_F(ProtoMessageSenderTest, format_bool) +{ + test_messages::TestBool message{ true }; + services::ProtoMessageSender sender(message); + + ExpectFill({ 1 << 3, 1 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_uint32) +{ + test_messages::TestUInt32 message(5); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 1 << 3, 5 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_int32) +{ + test_messages::TestInt32 message(-1); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_uint64) +{ + test_messages::TestUInt64 message(5); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 1 << 3, 5 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_int64) +{ + test_messages::TestInt64 message(-1); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 1 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_fixed32) +{ + test_messages::TestFixed32 message(1020304); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 13, 0x90, 0x91, 0x0f, 0 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_fixed64) +{ + test_messages::TestFixed64 message(102030405060708); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 9, 0x64, 0xc8, 0x0c, 0xce, 0xcb, 0x5c, 0x00, 0x00 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_sfixed32) +{ + test_messages::TestSFixed32 message(-1); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 13, 0xff, 0xff, 0xff, 0xff }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_sfixed64) +{ + test_messages::TestSFixed64 message(-1); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, sender); +} + +TEST_F(ProtoMessageSenderTest, partially_format_bool) +{ + test_messages::TestBool message{ true }; + services::ProtoMessageSender sender(message); + + infra::ByteOutputStream::WithStorage<1> partialStream; + sender.Fill(partialStream); + infra::StdVectorOutputStream::WithStorage stream; + sender.Fill(stream); + + EXPECT_EQ((std::array{ 1 << 3 }), partialStream.Storage()); + EXPECT_EQ((std::vector{ 1 }), stream.Storage()); +} + +TEST_F(ProtoMessageSenderTest, format_enum) +{ + test_messages::TestEnum message{ test_messages::Enumeration::val1 }; + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 1 << 3, 1 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_repeated_uint32) +{ + test_messages::TestRepeatedUInt32 message; + message.value.push_back(5); + message.value.push_back(6); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 1 << 3, 5, 1 << 3, 6 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_unbounded_repeated_uint32) +{ + test_messages::TestUnboundedRepeatedUInt32 message; + message.value.push_back(5); + message.value.push_back(6); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 1 << 3, 5, 1 << 3, 6 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_string) +{ + test_messages::TestString message{ "abcd" }; + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 10, 4, 'a', 'b', 'c', 'd' }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_std_string) +{ + test_messages::TestStdString message{ "abcd" }; + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 10, 4, 'a', 'b', 'c', 'd' }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_bytes) +{ + test_messages::TestBytes message; + message.value.push_back(5); + message.value.push_back(6); + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 10, 2, 5, 6 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_message) +{ + test_messages::TestMessageWithMessageField message{ 5 }; + services::ProtoMessageSender sender{ message }; + + ExpectFill({ 1 << 3 | 2, 2, 1 << 3, 5 }, sender); +} + +TEST_F(ProtoMessageSenderTest, format_more_nested_message) +{ + test_messages::TestMoreNestedMessage message{ { 5 }, { 10 } }; + services::ProtoMessageSender sender{ message }; + + ExpectFill({ (1 << 3) | 2, 2, 1 << 3, 5, (2 << 3) | 2, 2, 2 << 3, 10 }, sender); +} + +TEST_F(ProtoMessageSenderTest, dont_format_on_buffer_full) +{ + test_messages::TestBoolWithBytes message; + message.b.insert(message.b.begin(), 30, static_cast(5)); + message.value = true; + services::ProtoMessageSender sender{ message }; + + infra::ByteOutputStream::WithStorage<1> partialStream; + sender.Fill(partialStream); + + infra::StdVectorOutputStream::WithStorage stream; + sender.Fill(stream); + EXPECT_EQ((std::vector{ 30, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2 << 3, 1 }), stream.Storage()); +} + +TEST_F(ProtoMessageSenderTest, format_many_repeated_uint32) +{ + test_messages::TestRepeatedUInt32 message; + message.value.insert(message.value.end(), 50, static_cast(5)); + services::ProtoMessageSender sender{ message }; + + infra::ByteOutputStream::WithStorage<10> partialStream; + sender.Fill(partialStream); + EXPECT_EQ((std::array{ 1 << 3, 5, 1 << 3, 5, 1 << 3, 5, 1 << 3, 5, 1 << 3, 5 }), partialStream.Storage()); + + infra::StdVectorOutputStream::WithStorage stream; + sender.Fill(stream); + EXPECT_EQ(infra::ConstructBin().Repeat(45, { 1 << 3, 5 }).Vector(), stream.Storage()); +} + +TEST_F(ProtoMessageSenderTest, format_many_bytes) +{ + test_messages::TestBytes message; + message.value.insert(message.value.end(), 50, static_cast(5)); + services::ProtoMessageSender sender{ message }; + + infra::ByteOutputStream::WithStorage<10> partialStream; + sender.Fill(partialStream); + EXPECT_EQ((std::array{ 10, 50, 5, 5, 5, 5, 5, 5, 5, 5 }), partialStream.Storage()); + + infra::StdVectorOutputStream::WithStorage stream; + sender.Fill(stream); + EXPECT_EQ((std::vector{ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 }), stream.Storage()); +} diff --git a/protobuf/echo/test_doubles/ServiceStub.cpp b/protobuf/echo/test_doubles/ServiceStub.cpp index a9216d6ee..90a16384e 100644 --- a/protobuf/echo/test_doubles/ServiceStub.cpp +++ b/protobuf/echo/test_doubles/ServiceStub.cpp @@ -19,7 +19,7 @@ namespace services switch (field.second) { case 1: - DeserializeField(services::ProtoUInt32(), parser, field, value); + DeserializeField(services::ProtoUInt32(), parser, field.first, value); break; default: if (field.first.Is()) diff --git a/protobuf/protoc_echo_plugin/CMakeLists.txt b/protobuf/protoc_echo_plugin/CMakeLists.txt index c9f8b14e7..19fce22c6 100644 --- a/protobuf/protoc_echo_plugin/CMakeLists.txt +++ b/protobuf/protoc_echo_plugin/CMakeLists.txt @@ -26,8 +26,8 @@ if (EMIL_HOST_BUILD) if (NOT CMAKE_CROSSCOMPILING) add_executable(protobuf.protoc_echo_plugin) - emil_build_for(protobuf.protoc_echo_plugin BOOL EMIL_STANDALONE) - install(TARGETS protobuf.protoc_echo_plugin EXPORT emilProtobufTargets DESTINATION bin) + emil_build_for(protobuf.protoc_echo_plugin BOOL EMIL_BUILD_ECHO_COMPILERS) + emil_install(protobuf.protoc_echo_plugin EXPORT emilProtobufTargets DESTINATION bin) target_link_libraries(protobuf.protoc_echo_plugin PUBLIC protobuf.protoc_echo_plugin_lib diff --git a/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.cpp b/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.cpp index 493f2b7bc..3a35ff3a0 100644 --- a/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.cpp +++ b/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.cpp @@ -319,7 +319,7 @@ namespace application , prefix(prefix) {} - void MessageTypeMapGenerator::Run(Entities& formatter) + void MessageTypeMapGenerator::Run(Entities& formatter) const { auto typeMapNamespace = std::make_shared("detail"); @@ -331,15 +331,21 @@ namespace application { auto typeMapSpecialization = std::make_shared(MessageName() + "TypeMap"); typeMapSpecialization->TemplateSpecialization(google::protobuf::SimpleItoa(std::distance(message->fields.data(), &field))); - typeMapSpecialization->Add(std::make_shared("ProtoType", field->protoType)); + AddTypeMapProtoType(*field, *typeMapSpecialization); AddTypeMapType(*field, *typeMapSpecialization); + AddTypeMapFieldNumber(*field, *typeMapSpecialization); typeMapNamespace->Add(typeMapSpecialization); } formatter.Add(typeMapNamespace); } - void MessageTypeMapGenerator::AddTypeMapType(EchoField& field, Entities& entities) + void MessageTypeMapGenerator::AddTypeMapProtoType(const EchoField& field, Entities& entities) const + { + entities.Add(std::make_shared("ProtoType", field.protoType)); + } + + void MessageTypeMapGenerator::AddTypeMapType(const EchoField& field, Entities& entities) const { std::string result; StorageTypeVisitor visitor(result); @@ -347,6 +353,11 @@ namespace application entities.Add(std::make_shared("Type", result)); } + void MessageTypeMapGenerator::AddTypeMapFieldNumber(const EchoField& field, Entities& entities) const + { + entities.Add(std::make_shared("fieldNumber", "static const uint32_t", google::protobuf::SimpleItoa(field.number))); + } + std::string MessageTypeMapGenerator::MessageName() const { return prefix + message->name + MessageSuffix(); @@ -357,27 +368,12 @@ namespace application return ""; } - void MessageReferenceTypeMapGenerator::Run(Entities& formatter) + void MessageReferenceTypeMapGenerator::AddTypeMapProtoType(const EchoField& field, Entities& entities) const { - auto typeMapNamespace = std::make_shared("detail"); - - auto typeMapDeclaration = std::make_shared(MessageName() + "TypeMap"); - typeMapDeclaration->TemplateParameter("std::size_t fieldIndex"); - typeMapNamespace->Add(typeMapDeclaration); - - for (auto& field : message->fields) - { - auto typeMapSpecialization = std::make_shared(MessageName() + "TypeMap"); - typeMapSpecialization->TemplateSpecialization(google::protobuf::SimpleItoa(std::distance(message->fields.data(), &field))); - typeMapSpecialization->Add(std::make_shared("ProtoType", field->protoReferenceType)); - AddTypeMapType(*field, *typeMapSpecialization); - typeMapNamespace->Add(typeMapSpecialization); - } - - formatter.Add(typeMapNamespace); + entities.Add(std::make_shared("ProtoType", field.protoReferenceType)); } - void MessageReferenceTypeMapGenerator::AddTypeMapType(EchoField& field, Entities& entities) + void MessageReferenceTypeMapGenerator::AddTypeMapType(const EchoField& field, Entities& entities) const { std::string result; ParameterReferenceTypeVisitor visitor(result); @@ -476,12 +472,16 @@ namespace application { auto typeMap = std::make_shared("public"); + auto numberOfFields = std::make_shared("numberOfFields", "static const uint32_t", google::protobuf::SimpleItoa(message->fields.size())); + typeMap->Add(numberOfFields); auto protoTypeUsing = std::make_shared("ProtoType", "typename " + TypeMapName() + "::ProtoType"); protoTypeUsing->TemplateParameter("std::size_t fieldIndex"); typeMap->Add(protoTypeUsing); auto typeUsing = std::make_shared("Type", "typename " + TypeMapName() + "::Type"); typeUsing->TemplateParameter("std::size_t fieldIndex"); typeMap->Add(typeUsing); + auto fieldNumber = std::make_shared("fieldNumber", "template static const uint32_t", TypeMapName() + "::fieldNumber"); + typeMap->Add(fieldNumber); classFormatter->Add(typeMap); } @@ -493,9 +493,12 @@ namespace application for (auto& field : message->fields) { auto index = std::distance(message->fields.data(), &field); - auto function = std::make_shared("Get", "return " + field->name + ";\n", ClassName() + "::Type<" + google::protobuf::SimpleItoa(index) + ">&", 0); - function->Parameter("std::integral_constant"); - getters->Add(function); + auto functionGet = std::make_shared("Get", "return " + field->name + ";\n", ClassName() + "::Type<" + google::protobuf::SimpleItoa(index) + ">&", 0); + functionGet->Parameter("std::integral_constant"); + getters->Add(functionGet); + auto functionConstGet = std::make_shared("Get", "return " + field->name + ";\n", "const " + ClassName() + "::Type<" + google::protobuf::SimpleItoa(index) + ">&", Function::fConst); + functionConstGet->Parameter("std::integral_constant"); + getters->Add(functionConstGet); } classFormatter->Add(getters); @@ -618,7 +621,7 @@ namespace application for (auto& field : message->fields) printer.Print(R"(case $constant$: - DeserializeField($type$(), parser, field, $name$); + DeserializeField($type$(), parser, field.first, $name$); break; )", "constant", field->constantName, "type", field->protoType, "name", field->name); @@ -740,9 +743,12 @@ namespace application for (auto& field : message->fields) { auto index = std::distance(message->fields.data(), &field); - auto function = std::make_shared("Get", "return " + field->name + ";\n", ClassName() + "::Type<" + google::protobuf::SimpleItoa(index) + ">&", 0); - function->Parameter("std::integral_constant"); - getters->Add(function); + auto functionGet = std::make_shared("Get", "return " + field->name + ";\n", ClassName() + "::Type<" + google::protobuf::SimpleItoa(index) + ">&", 0); + functionGet->Parameter("std::integral_constant"); + getters->Add(functionGet); + auto functionConstGet = std::make_shared("Get", "return " + field->name + ";\n", ClassName() + "::Type<" + google::protobuf::SimpleItoa(index) + ">", Function::fConst); + functionConstGet->Parameter("std::integral_constant"); + getters->Add(functionConstGet); } classFormatter->Add(getters); diff --git a/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.hpp b/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.hpp index 81c3676f6..c611e71c2 100644 --- a/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.hpp +++ b/protobuf/protoc_echo_plugin/ProtoCEchoPlugin.hpp @@ -55,10 +55,12 @@ namespace application MessageTypeMapGenerator& operator=(const MessageTypeMapGenerator& other) = delete; ~MessageTypeMapGenerator() = default; - void Run(Entities& formatter); + void Run(Entities& formatter) const; protected: - virtual void AddTypeMapType(EchoField& field, Entities& entities); + virtual void AddTypeMapProtoType(const EchoField& field, Entities& entities) const; + virtual void AddTypeMapType(const EchoField& field, Entities& entities) const; + void AddTypeMapFieldNumber(const EchoField& field, Entities& entities) const; std::string MessageName() const; virtual std::string MessageSuffix() const; @@ -73,10 +75,9 @@ namespace application public: using MessageTypeMapGenerator::MessageTypeMapGenerator; - void Run(Entities& formatter); - protected: - void AddTypeMapType(EchoField& field, Entities& entities) override; + void AddTypeMapProtoType(const EchoField& field, Entities& entities) const override; + void AddTypeMapType(const EchoField& field, Entities& entities) const override; std::string MessageSuffix() const override; }; diff --git a/protobuf/protoc_echo_plugin/test/CMakeLists.txt b/protobuf/protoc_echo_plugin/test/CMakeLists.txt index e301e87a6..9e2eed725 100644 --- a/protobuf/protoc_echo_plugin/test/CMakeLists.txt +++ b/protobuf/protoc_echo_plugin/test/CMakeLists.txt @@ -2,7 +2,7 @@ add_executable(protobuf.protoc_echo_plugin_test) emil_build_for(protobuf.protoc_echo_plugin_test BOOL EMIL_BUILD_TESTS) emil_add_test(protobuf.protoc_echo_plugin_test) -protocol_buffer_echo_cpp(protobuf.protoc_echo_plugin_test TestMessages.proto) +protocol_buffer_echo_cpp(protobuf.protoc_echo_plugin_test ../../echo/test/TestMessages.proto) target_sources(protobuf.protoc_echo_plugin_test PRIVATE TestCppFormatter.cpp diff --git a/protobuf/protoc_echo_plugin/test/TestProtoCEchoPlugin.cpp b/protobuf/protoc_echo_plugin/test/TestProtoCEchoPlugin.cpp index 83aa9272e..5bf5bbf8f 100644 --- a/protobuf/protoc_echo_plugin/test/TestProtoCEchoPlugin.cpp +++ b/protobuf/protoc_echo_plugin/test/TestProtoCEchoPlugin.cpp @@ -159,6 +159,28 @@ TEST(ProtoCEchoPluginTest, deserialize_bool) EXPECT_EQ(true, message.value); } +TEST(ProtoCEchoPluginTest, serialize_enum) +{ + test_messages::TestEnum message; + message.e = test_messages::Enumeration::val1; + + infra::ByteOutputStream::WithStorage<100> stream; + infra::ProtoFormatter formatter(stream); + message.Serialize(formatter); + + EXPECT_EQ((std::array{ 8, 1 }), stream.Writer().Processed()); +} + +TEST(ProtoCEchoPluginTest, deserialize_enum) +{ + std::array data{ 8, 1 }; + infra::ByteInputStream stream(data); + infra::ProtoParser parser(stream); + + test_messages::TestEnum message(parser); + EXPECT_EQ(test_messages::Enumeration::val1, message.e); +} + TEST(ProtoCEchoPluginTest, serialize_string) { test_messages::TestString message; @@ -265,6 +287,32 @@ TEST(ProtoCEchoPluginTest, deserialize_bytes) EXPECT_EQ(value, message.value); } +TEST(ProtoCEchoPluginTest, serialize_unbounded_bytes) +{ + test_messages::TestUnboundedBytes message; + message.value.push_back(5); + message.value.push_back(6); + + infra::ByteOutputStream::WithStorage<100> stream; + infra::ProtoFormatter formatter(stream); + message.Serialize(formatter); + + EXPECT_EQ((std::array{ 10, 2, 5, 6 }), stream.Writer().Processed()); +} + +TEST(ProtoCEchoPluginTest, deserialize_unbounded_bytes) +{ + std::array data{ 10, 2, 5, 6 }; + infra::ByteInputStream stream(data); + infra::ProtoParser parser(stream); + + test_messages::TestUnboundedBytes message(parser); + std::vector value; + value.push_back(5); + value.push_back(6); + EXPECT_EQ(value, message.value); +} + TEST(ProtoCEchoPluginTest, serialize_uint32) { test_messages::TestUInt32 message; @@ -334,6 +382,31 @@ TEST(ProtoCEchoPluginTest, deserialize_repeated_uint32) EXPECT_EQ(6, message.value[1]); } +TEST(ProtoCEchoPluginTest, serialize_unbounded_repeated_uint32) +{ + test_messages::TestUnboundedRepeatedUInt32 message; + message.value.push_back(5); + message.value.push_back(6); + + infra::ByteOutputStream::WithStorage<100> stream; + infra::ProtoFormatter formatter(stream); + message.Serialize(formatter); + + EXPECT_EQ((std::array{ 1 << 3, 5, 1 << 3, 6 }), stream.Writer().Processed()); +} + +TEST(ProtoCEchoPluginTest, deserialize_unbounded_repeated_uint32) +{ + std::array data{ 1 << 3, 5, 1 << 3, 6 }; + infra::ByteInputStream stream(data); + infra::ProtoParser parser(stream); + + test_messages::TestUnboundedRepeatedUInt32 message(parser); + EXPECT_EQ(2, message.value.size()); + EXPECT_EQ(5, message.value[0]); + EXPECT_EQ(6, message.value[1]); +} + TEST(ProtoCEchoPluginTest, serialize_message) { test_messages::TestMessageWithMessageField message; diff --git a/protobuf/protoc_echo_plugin_csharp/CMakeLists.txt b/protobuf/protoc_echo_plugin_csharp/CMakeLists.txt index e4a223263..8e6ae63c8 100644 --- a/protobuf/protoc_echo_plugin_csharp/CMakeLists.txt +++ b/protobuf/protoc_echo_plugin_csharp/CMakeLists.txt @@ -1,7 +1,7 @@ if (EMIL_HOST_BUILD AND NOT CMAKE_CROSSCOMPILING) add_executable(protobuf.protoc_echo_plugin_csharp) - emil_build_for(protobuf.protoc_echo_plugin_csharp BOOL EMIL_STANDALONE) - install(TARGETS protobuf.protoc_echo_plugin_csharp EXPORT emilProtobufTargets DESTINATION bin) + emil_build_for(protobuf.protoc_echo_plugin_csharp BOOL EMIL_BUILD_ECHO_COMPILERS) + emil_install(protobuf.protoc_echo_plugin_csharp EXPORT emilProtobufTargets DESTINATION bin) target_link_libraries(protobuf.protoc_echo_plugin_csharp PUBLIC protobuf.protoc_echo_plugin_lib diff --git a/protobuf/protoc_echo_plugin_java/CMakeLists.txt b/protobuf/protoc_echo_plugin_java/CMakeLists.txt index 4e8182fc4..3d1ef6b4a 100644 --- a/protobuf/protoc_echo_plugin_java/CMakeLists.txt +++ b/protobuf/protoc_echo_plugin_java/CMakeLists.txt @@ -1,7 +1,7 @@ if (EMIL_HOST_BUILD AND NOT CMAKE_CROSSCOMPILING) add_executable(protobuf.protoc_echo_plugin_java) - emil_build_for(protobuf.protoc_echo_plugin_java BOOL EMIL_STANDALONE) - install(TARGETS protobuf.protoc_echo_plugin_java EXPORT emilProtobufTargets DESTINATION bin) + emil_build_for(protobuf.protoc_echo_plugin_java BOOL EMIL_BUILD_ECHO_COMPILERS) + emil_install(protobuf.protoc_echo_plugin_java EXPORT emilProtobufTargets DESTINATION bin) target_link_libraries(protobuf.protoc_echo_plugin_java PUBLIC protobuf.protoc_echo_plugin_lib diff --git a/services/echo_console/CMakeLists.txt b/services/echo_console/CMakeLists.txt index eb292ad84..43ba34bb1 100644 --- a/services/echo_console/CMakeLists.txt +++ b/services/echo_console/CMakeLists.txt @@ -1,6 +1,6 @@ if (EMIL_HOST_BUILD) add_executable(services.echo_console ${EMIL_EXCLUDE_FROM_ALL}) - install(TARGETS services.echo_console EXPORT emilProtobufTargets DESTINATION bin) + emil_install(services.echo_console DESTINATION bin) target_link_libraries(services.echo_console PUBLIC args diff --git a/services/network/test_doubles/Certificates.cpp b/services/network/test_doubles/Certificates.cpp index 304f07a74..bffa0ff77 100644 --- a/services/network/test_doubles/Certificates.cpp +++ b/services/network/test_doubles/Certificates.cpp @@ -53,7 +53,7 @@ namespace services "-----END CERTIFICATE-----\r\n"; const char testServerKeyData[] = - "-----BEGIN RSA PRIVATE KEY-----\r\n" + "-----BEGIN RSA PRIVATE KEY-----\r\n" //NOSONAR "MIIEogIBAAKCAQEArcYgmszNxJu5pfLpploIw12iIfAyZWK6PZ1/6zlifaR1v0KL\r\n" "x08aLSQAOAvnb8jmfVzkBwpt3hYVm69gWdTJcFpRXMuZ1nIur7HEDQXtzrgvmV6v\r\n" "d8Eu7ngoGfHijP9eiuwxoYNiMmbyvTM2hYnRn5WllOnbK5OBldnh0Vawh/XErMO/\r\n" @@ -105,7 +105,7 @@ namespace services "-----END CERTIFICATE-----\r\n"; const char testClientKeyData[] = - "-----BEGIN RSA PRIVATE KEY-----\r\n" + "-----BEGIN RSA PRIVATE KEY-----\r\n" //NOSONAR "MIIEowIBAAKCAQEAqNdp/w5eM4io5Ki/amQC8FAuhhdk1H3ELm11MmkYTLXJBsSc\r\n" "RCLIdiyM3Ten/I3bZtI0Ak7yvubQnozeBZig3usBBapfOvx5adCSsZfwGjgsqLF+\r\n" "zQ2maEd15pHSRPPXBAJ++4ZpxdVkfr0r/ZRIxWJwvyxtK3N0BJfUcop1J2fa3cZx\r\n" diff --git a/services/util/CMakeLists.txt b/services/util/CMakeLists.txt index 9e8191af2..ab5588d3d 100644 --- a/services/util/CMakeLists.txt +++ b/services/util/CMakeLists.txt @@ -58,6 +58,8 @@ target_sources(services.util PRIVATE MessageCommunicationWindowed.hpp RepeatingButton.cpp RepeatingButton.hpp + SerialCommunicationLoopback.cpp + SerialCommunicationLoopback.hpp Sha256.hpp SignalLed.cpp SignalLed.hpp diff --git a/services/util/MessageCommunicationSecured.cpp b/services/util/MessageCommunicationSecured.cpp index 48a6591c0..32a5be95b 100644 --- a/services/util/MessageCommunicationSecured.cpp +++ b/services/util/MessageCommunicationSecured.cpp @@ -1,4 +1,5 @@ #include "services/util/MessageCommunicationSecured.hpp" +#include "mbedtls/version.h" #include namespace services @@ -71,7 +72,12 @@ namespace services return; receiveBuffer.clear(); + +#if MBEDTLS_VERSION_MAJOR < 3 + really_assert(mbedtls_gcm_starts(&receiveContext, MBEDTLS_GCM_DECRYPT, reinterpret_cast(receiveIv.data()), receiveIv.size(), nullptr, 0) == 0); +#else really_assert(mbedtls_gcm_starts(&receiveContext, MBEDTLS_GCM_DECRYPT, reinterpret_cast(receiveIv.data()), receiveIv.size()) == 0); +#endif while (stream.Available() != blockSize) { @@ -80,14 +86,22 @@ namespace services stream >> infra::MakeRange(encrypted); receiveBuffer.resize(receiveBuffer.size() + encrypted.size()); +#if MBEDTLS_VERSION_MAJOR < 3 + really_assert(mbedtls_gcm_update(&receiveContext, encrypted.size(), encrypted.data(), receiveBuffer.data() + receiveBuffer.size() - encrypted.size()) == 0); +#else std::size_t processedSize = 0; really_assert(mbedtls_gcm_update(&receiveContext, encrypted.data(), encrypted.size(), receiveBuffer.data() + receiveBuffer.size() - encrypted.size(), receiveBuffer.size(), &processedSize) == 0); receiveBuffer.resize(receiveBuffer.size() - encrypted.size() + processedSize); +#endif } std::array computedMac; +#if MBEDTLS_VERSION_MAJOR < 3 + really_assert(mbedtls_gcm_finish(&receiveContext, reinterpret_cast(computedMac.data()), computedMac.size()) == 0); +#else std::size_t processedSize = 0; really_assert(mbedtls_gcm_finish(&receiveContext, nullptr, 0, &processedSize, reinterpret_cast(computedMac.data()), computedMac.size()) == 0); +#endif std::array receivedMac; stream >> infra::MakeRange(receivedMac); @@ -101,15 +115,28 @@ namespace services void MessageCommunicationSecured::SendMessageStreamReleased() { +#if MBEDTLS_VERSION_MAJOR < 3 + really_assert(mbedtls_gcm_starts(&sendContext, MBEDTLS_GCM_ENCRYPT, reinterpret_cast(sendIv.data()), sendIv.size(), nullptr, 0) == 0); +#else really_assert(mbedtls_gcm_starts(&sendContext, MBEDTLS_GCM_ENCRYPT, reinterpret_cast(sendIv.data()), sendIv.size()) == 0); +#endif +#if MBEDTLS_VERSION_MAJOR < 3 + really_assert(mbedtls_gcm_update(&sendContext, sendBuffer.size(), sendBuffer.data(), sendBuffer.data()) == 0); +#else std::size_t processedSize = 0; really_assert(mbedtls_gcm_update(&sendContext, sendBuffer.data(), sendBuffer.size(), sendBuffer.data(), sendBuffer.size(), &processedSize) == 0); +#endif + sendBuffer.resize(sendBuffer.size() + blockSize); +#if MBEDTLS_VERSION_MAJOR < 3 + really_assert(mbedtls_gcm_finish(&sendContext, sendBuffer.data() + sendBuffer.size() - blockSize, blockSize) == 0); +#else std::size_t moreProcessedSize = 0; really_assert(mbedtls_gcm_finish(&sendContext, sendBuffer.data() + processedSize, sendBuffer.size() - processedSize - blockSize, &moreProcessedSize, sendBuffer.data() + sendBuffer.size() - blockSize, blockSize) == 0); really_assert(processedSize + moreProcessedSize + blockSize == sendBuffer.size()); +#endif infra::DataOutputStream::WithErrorPolicy stream(*sendWriter); stream << infra::MakeRange(sendBuffer); diff --git a/services/util/SerialCommunicationLoopback.cpp b/services/util/SerialCommunicationLoopback.cpp new file mode 100644 index 000000000..c0e166e90 --- /dev/null +++ b/services/util/SerialCommunicationLoopback.cpp @@ -0,0 +1,42 @@ +#include "services/util/SerialCommunicationLoopback.hpp" +#include "hal/interfaces/SerialCommunication.hpp" +#include "infra/event/EventDispatcher.hpp" + +namespace services +{ + SerialCommunicationLoopback::SerialCommunicationLoopback() + : server(&client) + , client(&server) + {} + + hal::SerialCommunication& SerialCommunicationLoopback::Server() + { + return server; + } + + hal::SerialCommunication& SerialCommunicationLoopback::Client() + { + return client; + } + + SerialCommunicationLoopback::SerialCommunicationLoopbackPeer::SerialCommunicationLoopbackPeer(SerialCommunicationLoopbackPeer* other) + : other{ *other } + {} + + void SerialCommunicationLoopback::SerialCommunicationLoopbackPeer::SendData(infra::ConstByteRange data, infra::Function actionOnCompletion) + { + this->data = data; + this->actionOnCompletion = actionOnCompletion; + + infra::EventDispatcher::Instance().Schedule([this] + { + other.dataReceived(this->data); + this->actionOnCompletion(); + }); + } + + void SerialCommunicationLoopback::SerialCommunicationLoopbackPeer::ReceiveData(infra::Function dataReceived) + { + this->dataReceived = dataReceived; + } +} diff --git a/services/util/SerialCommunicationLoopback.hpp b/services/util/SerialCommunicationLoopback.hpp new file mode 100644 index 000000000..5b63250b2 --- /dev/null +++ b/services/util/SerialCommunicationLoopback.hpp @@ -0,0 +1,43 @@ +#ifndef SERVICES_SERIAL_COMMUNICATION_LOOPBACK_HPP +#define SERVICES_SERIAL_COMMUNICATION_LOOPBACK_HPP + +#include "hal/interfaces/SerialCommunication.hpp" + +namespace services +{ + + class SerialCommunicationLoopback + { + public: + SerialCommunicationLoopback(); + + hal::SerialCommunication& Server(); + hal::SerialCommunication& Client(); + + private: + class SerialCommunicationLoopbackPeer + : public hal::SerialCommunication + { + public: + // pointer instead of reference to avoid defining a copy constructor + // pass by reference generates a warning with clang and -Wunitialized + explicit SerialCommunicationLoopbackPeer(SerialCommunicationLoopbackPeer* other); + + void SendData(infra::ConstByteRange data, infra::Function actionOnCompletion) override; + void ReceiveData(infra::Function dataReceived) override; + + private: + SerialCommunicationLoopbackPeer& other; + + infra::ConstByteRange data; + infra::Function actionOnCompletion; + infra::Function dataReceived; + }; + + private: + SerialCommunicationLoopbackPeer server; + SerialCommunicationLoopbackPeer client; + }; +} + +#endif diff --git a/services/util/test/CMakeLists.txt b/services/util/test/CMakeLists.txt index 3a5634451..7a307a65a 100644 --- a/services/util/test/CMakeLists.txt +++ b/services/util/test/CMakeLists.txt @@ -14,6 +14,7 @@ target_link_libraries(services.util_test PUBLIC hal.interfaces_test_doubles infra.timer_test_helper infra.util_test_helper + infra.event_test_helper ) target_sources(services.util_test PRIVATE @@ -39,6 +40,7 @@ target_sources(services.util_test PRIVATE TestMessageCommunicationSecured.cpp TestMessageCommunicationWindowed.cpp TestRepeatingButton.cpp + TestSerialCommunicationLoopback.cpp TestSignalLed.cpp TestSpiMasterWithChipSelect.cpp TestSpiMultipleAccess.cpp diff --git a/services/util/test/TestSerialCommunicationLoopback.cpp b/services/util/test/TestSerialCommunicationLoopback.cpp new file mode 100644 index 000000000..748bf5db8 --- /dev/null +++ b/services/util/test/TestSerialCommunicationLoopback.cpp @@ -0,0 +1,63 @@ +#include "hal/interfaces/SerialCommunication.hpp" +#include "infra/event/test_helper/EventDispatcherFixture.hpp" +#include "infra/util/ByteRange.hpp" +#include "infra/util/Function.hpp" +#include "infra/util/test_helper/MockCallback.hpp" +#include "services/util/SerialCommunicationLoopback.hpp" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +class SerialCommunicationLoopbackTest + : public testing::Test + , public infra::EventDispatcherFixture +{ +public: + services::SerialCommunicationLoopback SerialCommunicationLoopback; + hal::SerialCommunication& server{ SerialCommunicationLoopback.Server() }; + hal::SerialCommunication& client{ SerialCommunicationLoopback.Client() }; +}; + +TEST_F(SerialCommunicationLoopbackTest, SendFromServerReceiveByClient) +{ + infra::ConstByteRange data = infra::MakeStringByteRange("hello"); + infra::VerifyingFunctionMock clientReceiveCallback{ data }; + + client.ReceiveData(clientReceiveCallback); + server.SendData(data, infra::emptyFunction); + + ExecuteAllActions(); +} + +TEST_F(SerialCommunicationLoopbackTest, SendFromClientReceiveByServer) +{ + infra::ConstByteRange data = infra::MakeStringByteRange("world"); + infra::VerifyingFunctionMock serverReceiveCallback{ data }; + + server.ReceiveData(serverReceiveCallback); + client.SendData(data, infra::emptyFunction); + + ExecuteAllActions(); +} + +TEST_F(SerialCommunicationLoopbackTest, ActionOnCompletionAfterDataReceivedByOtherPeer) +{ + infra::ConstByteRange data = infra::MakeStringByteRange("Hello World!"); + infra::MockCallback dataReceivedMockCallback; + infra::MockCallback actionOnCompletionMockCallback; + + server.ReceiveData([&dataReceivedMockCallback](infra::ConstByteRange data) + { + dataReceivedMockCallback.callback(data); + }); + + client.SendData(data, [&actionOnCompletionMockCallback] + { + actionOnCompletionMockCallback.callback(); + }); + + testing::InSequence sequence; + EXPECT_CALL(dataReceivedMockCallback, callback(data)); + EXPECT_CALL(actionOnCompletionMockCallback, callback()); + + ExecuteAllActions(); +} diff --git a/upgrade/pack_builder/SupportedTargets.cpp b/upgrade/pack_builder/SupportedTargets.cpp index f376b6127..e4bf44d6a 100644 --- a/upgrade/pack_builder/SupportedTargets.cpp +++ b/upgrade/pack_builder/SupportedTargets.cpp @@ -14,9 +14,16 @@ namespace application return *this; } + SupportedTargetsBuilder& SupportedTargetsBuilder::Order(uint8_t order) + { + this->order.Emplace(order); + return *this; + } + SupportedTargetsBuilder& SupportedTargetsBuilder::AddCmd(const SupportedTargets::Target& target) { AddToMandatoryWhenNecessary(target); + AddInOrder(target); targets.cmd.emplace_back(target); return *this; } @@ -24,6 +31,7 @@ namespace application SupportedTargetsBuilder& SupportedTargetsBuilder::AddHex(const SupportedTargets::Target& target) { AddToMandatoryWhenNecessary(target); + AddInOrder(target); targets.hex.emplace_back(target); return *this; } @@ -31,6 +39,7 @@ namespace application SupportedTargetsBuilder& SupportedTargetsBuilder::AddElf(const SupportedTargets::Target& target, uint32_t offset) { AddToMandatoryWhenNecessary(target); + AddInOrder(target); targets.elf.emplace_back(target, offset); return *this; } @@ -38,6 +47,7 @@ namespace application SupportedTargetsBuilder& SupportedTargetsBuilder::AddBin(const SupportedTargets::Target& target, uint32_t offset) { AddToMandatoryWhenNecessary(target); + AddInOrder(target); targets.bin.emplace_back(target, offset); return *this; } @@ -45,7 +55,19 @@ namespace application void SupportedTargetsBuilder::AddToMandatoryWhenNecessary(const SupportedTargets::Target& target) { if (mandatory) + { targets.mandatory.emplace_back(target); + mandatory = false; + } + } + + void SupportedTargetsBuilder::AddInOrder(const SupportedTargets::Target& target) + { + if (order) + { + targets.order[*order].emplace_back(target); + order = infra::none; + } } SupportedTargetsBuilder SupportedTargets::Create() diff --git a/upgrade/pack_builder/SupportedTargets.hpp b/upgrade/pack_builder/SupportedTargets.hpp index 256e23b4e..acbd4cbc8 100644 --- a/upgrade/pack_builder/SupportedTargets.hpp +++ b/upgrade/pack_builder/SupportedTargets.hpp @@ -1,6 +1,8 @@ #ifndef UPGRADE_SUPPORTED_TARGETS_HPP #define UPGRADE_SUPPORTED_TARGETS_HPP +#include "infra/util/Optional.hpp" +#include #include #include #include @@ -43,6 +45,11 @@ namespace application return mandatory; } + const auto& OrderOfTargets() const + { + return order; + } + private: std::vector cmd; std::vector hex; @@ -50,6 +57,7 @@ namespace application std::vector bin; std::vector mandatory; + std::map> order; }; class SupportedTargetsBuilder @@ -62,6 +70,7 @@ namespace application SupportedTargetsBuilder& Mandatory(); SupportedTargetsBuilder& Optional(); + SupportedTargetsBuilder& Order(uint8_t order); SupportedTargetsBuilder& AddCmd(const SupportedTargets::Target& target); SupportedTargetsBuilder& AddHex(const SupportedTargets::Target& target); @@ -70,10 +79,12 @@ namespace application private: void AddToMandatoryWhenNecessary(const SupportedTargets::Target& target); + void AddInOrder(const SupportedTargets::Target& target); private: SupportedTargets targets; bool mandatory{ false }; + infra::Optional order; }; } diff --git a/upgrade/pack_builder/test/TestSupportedTargets.cpp b/upgrade/pack_builder/test/TestSupportedTargets.cpp index 659112c61..e8fabe12a 100644 --- a/upgrade/pack_builder/test/TestSupportedTargets.cpp +++ b/upgrade/pack_builder/test/TestSupportedTargets.cpp @@ -35,3 +35,40 @@ TEST(SupportedTargetsTest, should_add_mandatory_targets) EXPECT_EQ(2, targets.HexTargets().size()); EXPECT_EQ("application", targets.MandatoryTargets()[0]); } + +TEST(SupportedTargetsTest, should_add_target_to_mandatory_only_if_specified) +{ + application::SupportedTargets targets = application::SupportedTargets::Create() + .Mandatory() + .AddHex("application") + .AddHex("data"); + + EXPECT_EQ(1, targets.MandatoryTargets().size()); + EXPECT_EQ("application", targets.MandatoryTargets()[0]); +} + +TEST(SupportedTargetsTest, should_add_targets_in_order) +{ + application::SupportedTargets targets = application::SupportedTargets::Create() + .Order(2) + .AddHex("application") + .Order(1) + .AddHex("data"); + const auto& orderedTargets = targets.OrderOfTargets(); + + EXPECT_EQ(2, orderedTargets.size()); + EXPECT_EQ("data", orderedTargets.at(1).at(0)); + EXPECT_EQ("application", orderedTargets.at(2).at(0)); +} + +TEST(SupportedTargetsTest, should_add_target_to_order_only_if_specified) +{ + application::SupportedTargets targets = application::SupportedTargets::Create() + .Order(1) + .AddHex("data") + .AddHex("application"); + const auto& orderedTargets = targets.OrderOfTargets(); + + EXPECT_EQ(1, orderedTargets.size()); + EXPECT_EQ("data", orderedTargets.at(1).at(0)); +} diff --git a/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.cpp b/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.cpp index e6156e9a4..0abacb399 100644 --- a/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.cpp +++ b/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.cpp @@ -20,6 +20,14 @@ namespace main_ {} }; + struct IncorrectOrderOfTargetException + : std::runtime_error + { + explicit IncorrectOrderOfTargetException(const std::string& target) + : std::runtime_error("Incorrect order of target: " + target) + {} + }; + UpgradePackBuilderFacade::UpgradePackBuilderFacade(const application::UpgradePackBuilder::HeaderInfo& headerInfo) : headerInfo(headerInfo) {} @@ -51,12 +59,44 @@ namespace main_ builder.WriteUpgradePack(outputFilename, fileSystem); } + infra::Optional UpgradePackBuilderFacade::GetOrder(const std::string& targetName, const std::map>& orderedTargets) const + { + for (const auto& targets : orderedTargets) + { + const auto targetPos = std::find(targets.second.begin(), targets.second.end(), targetName); + if (targetPos != targets.second.end()) + return infra::MakeOptional(targets.first); + } + return infra::none; + } + + bool UpgradePackBuilderFacade::IsTargetInOrder(const std::string& target, const std::map>& orderedTargets) const + { + const auto orderToAdd = GetOrder(target, orderedTargets); + + return orderToAdd == infra::none || currentOrderOfTarget <= *orderToAdd; + } + + void UpgradePackBuilderFacade::UpdateCurrentOrderOfTarget(const std::string& target, const std::map>& orderedTargets) + { + const auto orderToAdd = GetOrder(target, orderedTargets); + + if (orderToAdd != infra::none) + currentOrderOfTarget = std::max(currentOrderOfTarget, *orderToAdd); + } + std::vector> UpgradePackBuilderFacade::CreateInputs(const application::SupportedTargets& supportedTargets, const TargetAndFiles& requestedTargets, application::InputFactory& factory) { std::vector> inputs; + const auto& orderedTargets = supportedTargets.OrderOfTargets(); for (const auto& [target, file, address] : requestedTargets) + { + UpdateCurrentOrderOfTarget(target, orderedTargets); + if (!IsTargetInOrder(target, orderedTargets)) + throw IncorrectOrderOfTargetException(target); inputs.push_back(factory.CreateInput(target, file, address)); + } for (const auto& mandatoryTarget : supportedTargets.MandatoryTargets()) { diff --git a/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.hpp b/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.hpp index d808ff906..cae51436f 100644 --- a/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.hpp +++ b/upgrade/pack_builder_instantiations/UpgradePackBuilderFacade.hpp @@ -36,8 +36,14 @@ namespace main_ virtual void PostBuilder(application::UpgradePackBuilder& builder, application::ImageSigner& signer, const BuildOptions& buildOptions); private: + infra::Optional GetOrder(const std::string& targetName, const std::map>& orderedTargets) const; + bool IsTargetInOrder(const std::string& target, const std::map>& orderedTargets) const; + void UpdateCurrentOrderOfTarget(const std::string& target, const std::map>& orderedTargets); std::vector> CreateInputs(const application::SupportedTargets& supportedTargets, const TargetAndFiles& requestedTargets, application::InputFactory& factory); + private: + uint8_t currentOrderOfTarget = 0; + protected: application::UpgradePackBuilder::HeaderInfo headerInfo; }; diff --git a/upgrade/security_key_generator/CMakeLists.txt b/upgrade/security_key_generator/CMakeLists.txt index 885f1ba2c..660983433 100644 --- a/upgrade/security_key_generator/CMakeLists.txt +++ b/upgrade/security_key_generator/CMakeLists.txt @@ -1,6 +1,6 @@ add_executable(upgrade.security_key_generator) emil_build_for(upgrade.security_key_generator HOST All PREREQUISITE_BOOL EMIL_STANDALONE) -install(TARGETS upgrade.security_key_generator EXPORT emilUpgradeTargets DESTINATION bin) +emil_install(upgrade.security_key_generator EXPORT emilUpgradeTargets DESTINATION bin) target_link_libraries(upgrade.security_key_generator PUBLIC args