├── .clang-format ├── .clang-tidy ├── .github └── workflows │ ├── ci.yml │ ├── ci_conan_profile │ ├── codeql.yml │ ├── coverage-comment-pr.yml │ └── coverage.yml ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING.adoc ├── LICENSE ├── NOTICE.adoc ├── README.md ├── conanfile.py ├── coverage.sh ├── docs └── design │ └── utils │ └── CallbackConnection.md ├── include └── up-cpp │ ├── README.md │ ├── client │ └── usubscription │ │ └── v3 │ │ └── Consumer.h │ ├── communication │ ├── NotificationSink.h │ ├── NotificationSource.h │ ├── Publisher.h │ ├── RpcClient.h │ ├── RpcServer.h │ └── Subscriber.h │ ├── datamodel │ ├── builder │ │ ├── Payload.h │ │ ├── UMessage.h │ │ └── Uuid.h │ ├── constants │ │ └── UuidConstants.h │ ├── serializer │ │ ├── UUri.h │ │ └── Uuid.h │ └── validator │ │ ├── UMessage.h │ │ ├── UUri.h │ │ └── Uuid.h │ ├── transport │ └── UTransport.h │ └── utils │ ├── CallbackConnection.h │ ├── CyclicQueue.h │ ├── Expected.h │ ├── IpAddress.h │ ├── ProtoConverter.h │ ├── ThreadPool.h │ └── base64.h ├── lint ├── clang-format.sh ├── clang-tidy.sh └── valgrind.sh ├── src ├── client │ └── usubscription │ │ └── v3 │ │ └── Consumer.cpp ├── communication │ ├── NotificationSink.cpp │ ├── NotificationSource.cpp │ ├── Publisher.cpp │ ├── RpcClient.cpp │ ├── RpcServer.cpp │ └── Subscriber.cpp ├── datamodel │ ├── builder │ │ ├── Payload.cpp │ │ ├── UMessage.cpp │ │ └── Uuid.cpp │ ├── serializer │ │ ├── UUri.cpp │ │ └── Uuid.cpp │ └── validator │ │ ├── UMessage.cpp │ │ ├── UUri.cpp │ │ └── Uuid.cpp ├── transport │ └── UTransport.cpp └── utils │ ├── IpAddress.cpp │ ├── ProtoConverter.cpp │ ├── ThreadPool.cpp │ └── base64.cpp └── test ├── CMakeLists.txt ├── coverage ├── client │ └── usubscription │ │ └── v3 │ │ └── ConsumerTest.cpp ├── communication │ ├── NotificationSinkTest.cpp │ ├── NotificationSourceTest.cpp │ ├── PublisherTest.cpp │ ├── RpcClientTest.cpp │ ├── RpcServerTest.cpp │ └── SubscriberTest.cpp ├── datamodel │ ├── PayloadBuilderTest.cpp │ ├── UMessageBuilderTest.cpp │ ├── UMessageValidatorTest.cpp │ ├── UUriSerializerTest.cpp │ ├── UUriValidatorTest.cpp │ ├── UuidBuilderTest.cpp │ ├── UuidSerializerTest.cpp │ └── UuidValidatorTest.cpp ├── transport │ └── UTransportTest.cpp └── utils │ ├── CallbackConnectionTest.cpp │ ├── CyclicQueueTest.cpp │ ├── ExpectedTest.cpp │ ├── IpAddressTest.cpp │ ├── ThreadPoolTest.cpp │ └── base64Test.cpp ├── extra ├── NotificationTest.cpp ├── PublisherSubscriberTest.cpp ├── RpcClientServerTest.cpp └── UTransportMockTest.cpp ├── include └── UTransportMock.h └── sanitizers ├── valgrind_exclude_test_dhat.txt ├── valgrind_exclude_test_helgrind.txt ├── valgrind_exclude_test_memcheck.txt └── valgrind_exclude_test_threadcheck.txt /.clang-format: -------------------------------------------------------------------------------- 1 | # Part of the uprocotol project, under the Apache License v2.0 2 | # See /LICENSE for license information. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | --- 6 | BasedOnStyle: Google 7 | AllowShortBlocksOnASingleLine: 'false' 8 | AllowShortIfStatementsOnASingleLine: Never 9 | AllowShortLoopsOnASingleLine: 'false' 10 | DerivePointerAlignment: 'false' 11 | ExperimentalAutoDetectBinPacking: 'false' 12 | FixNamespaceComments: 'true' 13 | PointerAlignment: Left 14 | 15 | TabWidth: 4 16 | IndentWidth: 4 17 | AccessModifierOffset: -4 18 | 19 | # There are *arguments* that tabs are better for accessibility (allows 20 | # customization to meet individual needs _and_ represents single character on 21 | # alternate display devices), but first-hand accounts / discussions of 22 | # preferences are not easily found at the moment. 23 | # 24 | # Regardless, tabs are used for the time being to allow greater flexibility 25 | # in configuration for the broader community. 26 | UseTab: ForIndentation 27 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | # Part of the uprocotol project, under the Apache License v2.0 2 | # See /LICENSE for license information. 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | --- 6 | # - modernize-use-nodiscard is disabled because it only fixes const methods, 7 | # not non-const, which yields distracting results on accessors. 8 | # - performance-unnecessary-value-param is disabled because it duplicate 9 | # modernize-pass-by-value. 10 | Checks: > 11 | -*, 12 | bugprone-*, 13 | #-bugprone-easily-swappable-parameters, 14 | #-bugprone-narrowing-conversions, 15 | cert-*, 16 | -cert-msc50-cpp, 17 | -cert-msc51-cpp, 18 | cppcoreguidelines-*, 19 | -cppcoreguidelines-avoid-non-const-global-variables, 20 | -cppcoreguidelines-special-member-functions, 21 | -cppcoreguidelines-pro-type-reinterpret-cast, 22 | google-*, 23 | #-google-readability-todo, 24 | #-google-runtime-int, 25 | llvm-header-guard, 26 | hicpp-exception-baseclass, 27 | hicpp-no-assembler, 28 | misc-confusable-identifiers, 29 | misc-const-correctness, 30 | misc-definitions-in-headers, 31 | misc-header-include-cycle, 32 | misc-include-cleaner, 33 | misc-misplaced-const, 34 | misc-non-private-member-variables-in-classes, 35 | misc-redundant-expression, 36 | misc-static-assert, 37 | misc-unconventional-assign-operator, 38 | misc-uniqueptr-reset-release, 39 | misc-unused-*, 40 | misc-use-anonymous-namespace, 41 | modernize-*, 42 | -modernize-use-trailing-return-type, 43 | performance-*, 44 | -performance-avoid-endl, 45 | portability-*, 46 | -portability-restrict-system-includes, 47 | readability-*, 48 | #-readability-convert-member-functions-to-static, 49 | -readability-identifier-length, 50 | -readability-suspicious-call-argument, 51 | WarningsAsErrors: true 52 | CheckOptions: 53 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 54 | - { 55 | key: readability-identifier-naming.ConstexprVariableCase, 56 | value: UPPER_CASE, 57 | } 58 | - { key: readability-identifier-naming.NamespaceCase, value: lower_case } 59 | - { key: readability-identifier-naming.StructCase, value: CamelCase } 60 | - { 61 | key: readability-identifier-naming.TemplateParameterCase, 62 | value: CamelCase, 63 | } 64 | - { key: readability-identifier-naming.TypeAliasCase, value: CamelCase } 65 | - { key: readability-identifier-naming.TypedefCase, value: CamelCase } 66 | - { key: readability-identifier-naming.UnionCase, value: CamelCase } 67 | - { key: readability-identifier-naming.VariableCase, value: lower_case } 68 | - { key: readability-identifier-naming.ParameterCase, value: lower_case } 69 | - { key: readability-identifier-naming.ClassMemberCase, value: lower_case } 70 | - { key: readability-identifier-naming.ClassMemberSuffix, value: _ } 71 | -------------------------------------------------------------------------------- /.github/workflows/ci_conan_profile: -------------------------------------------------------------------------------- 1 | [settings] 2 | arch=x86_64 3 | build_type=Release 4 | compiler=gcc 5 | compiler.cppstd=gnu17 6 | compiler.libcxx=libstdc++11 7 | compiler.version=11 8 | os=Linux 9 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main", "v1.0_up-v1.6.0" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: '22 5 * * 5' 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze (${{ matrix.language }}) 14 | runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} 15 | timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} 16 | permissions: 17 | # required for all workflows 18 | security-events: write 19 | 20 | # required to fetch internal or private CodeQL packs 21 | packages: read 22 | 23 | # only required for workflows in private repositories 24 | actions: read 25 | contents: read 26 | 27 | strategy: 28 | fail-fast: false 29 | matrix: 30 | include: 31 | - language: c-cpp 32 | build-mode: manual 33 | steps: 34 | - if: matrix.build-mode == 'manual' 35 | name: Install Conan 36 | id: conan 37 | uses: turtlebrowser/get-conan@main 38 | with: 39 | version: 2.3.2 40 | 41 | - name: Checkout repository 42 | uses: actions/checkout@v4 43 | with: 44 | path: up-cpp 45 | 46 | # Initializes the CodeQL tools for scanning. 47 | - name: Initialize CodeQL 48 | uses: github/codeql-action/init@v3 49 | with: 50 | languages: ${{ matrix.language }} 51 | build-mode: ${{ matrix.build-mode }} 52 | 53 | - if: matrix.build-mode == 'manual' 54 | name: Install conan CI profile 55 | shell: bash 56 | run: | 57 | conan profile detect 58 | cp up-cpp/.github/workflows/ci_conan_profile "$(conan profile path default)" 59 | conan profile show 60 | 61 | - if: matrix.build-mode == 'manual' 62 | name: Fetch up-core-api conan recipe 63 | uses: actions/checkout@v4 64 | with: 65 | path: up-conan-recipes 66 | repository: eclipse-uprotocol/up-conan-recipes 67 | 68 | - if: matrix.build-mode == 'manual' 69 | name: Build up-core-api conan package 70 | shell: bash 71 | run: | 72 | conan create --version 1.6.0-alpha4 up-conan-recipes/up-core-api/release 73 | 74 | - if: matrix.build-mode == 'manual' 75 | name: Build up-cpp with tests 76 | shell: bash 77 | run: | 78 | cd up-cpp 79 | conan install --build=missing . 80 | cmake --preset conan-release -DCMAKE_EXPORT_COMPILE_COMMANDS=yes 81 | cd build/Release 82 | cmake --build . -- -j 83 | 84 | - name: Perform CodeQL Analysis 85 | uses: github/codeql-action/analyze@v3 86 | with: 87 | category: "/language:${{matrix.language}}" 88 | checkout_path: up-cpp 89 | -------------------------------------------------------------------------------- /.github/workflows/coverage-comment-pr.yml: -------------------------------------------------------------------------------- 1 | name: Add coverage comment to PR 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Test and Coverage"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | add-coverage-comment: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | pull-requests: write 14 | if: > 15 | github.event.workflow_run.event == 'pull_request' && 16 | github.event.workflow_run.conclusion == 'success' 17 | steps: 18 | - name: 'Download artifact' 19 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 20 | with: 21 | script: | 22 | var artifacts = await github.rest.actions.listWorkflowRunArtifacts({ 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | run_id: ${{github.event.workflow_run.id }}, 26 | }); 27 | var matchArtifact = artifacts.data.artifacts.filter((artifact) => { 28 | return artifact.name == "pr-comment" 29 | })[0]; 30 | var download = await github.rest.actions.downloadArtifact({ 31 | owner: context.repo.owner, 32 | repo: context.repo.repo, 33 | artifact_id: matchArtifact.id, 34 | archive_format: 'zip', 35 | }); 36 | var fs = require('fs'); 37 | fs.writeFileSync('${{github.workspace}}/pr-comment.zip', Buffer.from(download.data)); 38 | - run: unzip pr-comment.zip 39 | 40 | - name: 'Comment on PR' 41 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 42 | with: 43 | github-token: ${{ secrets.GITHUB_TOKEN }} 44 | script: | 45 | var fs = require('fs'); 46 | 47 | const issue_number = Number(fs.readFileSync('./pr-number.txt')); 48 | const body = fs.readFileSync('./body.txt', { encoding: 'utf8', flag: 'r' }); 49 | 50 | await github.rest.issues.createComment({ 51 | owner: context.repo.owner, 52 | repo: context.repo.repo, 53 | issue_number: issue_number, 54 | body: body 55 | }); 56 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Test and Coverage 2 | 3 | on: 4 | push: 5 | branches: [ "main", "v1.0_up-v1.6.0" ] 6 | pull_request: 7 | branches: ["**"] 8 | 9 | jobs: 10 | test: 11 | name: Generate Test Coverage Report 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - name: Install Conan 16 | id: conan 17 | uses: turtlebrowser/get-conan@main 18 | with: 19 | version: 2.3.2 20 | 21 | - name: Fetch up-cpp 22 | uses: actions/checkout@v4 23 | with: 24 | path: up-cpp 25 | 26 | - name: Install conan CI profile 27 | shell: bash 28 | run: | 29 | conan profile detect 30 | cp up-cpp/.github/workflows/ci_conan_profile "$(conan profile path default)" 31 | conan profile show 32 | 33 | - name: Install gcovr 34 | run: sudo apt-get install -y gcovr 35 | 36 | - name: Fetch up-core-api conan recipe 37 | uses: actions/checkout@v4 38 | with: 39 | path: up-conan-recipes 40 | repository: eclipse-uprotocol/up-conan-recipes 41 | 42 | - name: Build up-core-api conan package 43 | shell: bash 44 | run: | 45 | conan create --version 1.6.0-alpha4 up-conan-recipes/up-core-api/release 46 | 47 | - name: Build up-cpp with tests 48 | shell: bash 49 | run: | 50 | cd up-cpp 51 | conan install --build=missing . 52 | cd build/Release 53 | cmake -S ../../ -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Coverage 54 | cmake --build . -- -j 55 | 56 | - name: Run all tests 57 | shell: bash 58 | run: | 59 | cd up-cpp/build/Release 60 | chmod +x bin/* 61 | ctest || true 62 | 63 | - name: Run Coverage report 64 | shell: bash 65 | run: | 66 | cd up-cpp/build/Release 67 | mkdir -p ../Coverage 68 | gcovr -r ../../ --html --html-details -o ../Coverage/index.html -e '.*test.*' --gcov-ignore-parse-errors negative_hits.warn_once_per_file 69 | cd .. 70 | echo "Coverage report can be found here: ../Coverage/index.html" 71 | 72 | - name: Extract and Print Coverage Percentage 73 | shell: bash 74 | run: | 75 | cd up-cpp/build/Coverage 76 | COVERAGE_PERCENTAGE=$(grep -oP '>\K[0-9.]+(?=%)' index.html | head -n 1) 77 | export COVERAGE_PERCENTAGE=$(printf "%.2f" "$COVERAGE_PERCENTAGE") 78 | echo "COVERAGE_PERCENTAGE= $COVERAGE_PERCENTAGE" >> $GITHUB_ENV 79 | 80 | 81 | 82 | - name: Upload coverage report 83 | uses: actions/upload-artifact@v4 84 | if: success() || failure() 85 | with: 86 | name: coverage-report 87 | path: 'up-cpp/build/Coverage' 88 | 89 | - name: Generate coverage comment 90 | uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 91 | with: 92 | script: | 93 | const fs = require('fs'); 94 | 95 | fs.mkdirSync('./pr-comment', { recursive: true }); 96 | 97 | const COVERAGE_PERCENTAGE = `${{ env.COVERAGE_PERCENTAGE }}`; 98 | 99 | const COVERAGE_REPORT_PATH = `https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}/`; 100 | 101 | var pr_number = `${{ github.event.number }}`; 102 | var body = ` 103 | Code coverage report is ready! :chart_with_upwards_trend: 104 | 105 | - **Code Coverage Percentage:** ${COVERAGE_PERCENTAGE}% 106 | - **Code Coverage Report:** [View Coverage Report](${COVERAGE_REPORT_PATH}) 107 | `; 108 | 109 | fs.writeFileSync('./pr-comment/pr-number.txt', pr_number); 110 | fs.writeFileSync('./pr-comment/body.txt', body); 111 | 112 | - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 113 | with: 114 | name: pr-comment 115 | path: pr-comment/ 116 | 117 | 118 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* 2 | build/* 3 | .vscode/settings.json 4 | .vscode 5 | CMakeUserPresets.json 6 | *.swp 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | # 3 | # See the NOTICE file(s) distributed with this work for additional 4 | # information regarding copyright ownership. 5 | # 6 | # This program and the accompanying materials are made available under the 7 | # terms of the Apache License Version 2.0 which is available at 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # SPDX-License-Identifier: Apache-2.0 11 | 12 | cmake_minimum_required(VERSION 3.10.0) 13 | project(up-cpp VERSION 1.1.0 LANGUAGES CXX DESCRIPTION "C++ API for uProtocol") 14 | 15 | find_package(protobuf REQUIRED) 16 | find_package(spdlog REQUIRED) 17 | find_package(up-core-api REQUIRED) 18 | 19 | message("* Adding build types...") 20 | list(APPEND CMAKE_CONFIGURATION_TYPES Release Coverage) 21 | list(REMOVE_DUPLICATES CMAKE_CONFIGURATION_TYPES) 22 | set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING 23 | "Add the code coverage configuration." 24 | FORCE) 25 | message(" Available build types are now: ${CMAKE_CONFIGURATION_TYPES}") 26 | 27 | message("* Current build type is: ${CMAKE_BUILD_TYPE}") 28 | 29 | # Settings for Gcc for all builds 30 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 31 | 32 | # Modify settings based on build type 33 | if(CMAKE_BUILD_TYPE STREQUAL Coverage) 34 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") 35 | set(CMAKE_BUILD_TYPE Release) 36 | endif() 37 | message("* Current Compiler Flags: ${CMAKE_CXX_FLAGS}") 38 | 39 | # This is the root CMakeLists.txt file; We can set project wide settings here 40 | # TODO: Is this needed? 41 | if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) 42 | set(CMAKE_CXX_STANDARD 17) 43 | # place libraries in a lib directory and executables in a bin directory, 44 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 45 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 46 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 47 | endif() 48 | 49 | file(GLOB_RECURSE SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") 50 | 51 | add_library(${PROJECT_NAME} ${SRC_FILES}) 52 | add_library(up-cpp::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 53 | 54 | target_include_directories(${PROJECT_NAME} 55 | PUBLIC 56 | $ 57 | $ 58 | $ 59 | ${up-core-api_INCLUDE_DIR} 60 | ${protobuf_INCLUDE_DIR} 61 | ${spdlog_INCLUDE_DIR}) 62 | 63 | set_property(TARGET ${PROJECT_NAME} PROPERTY POSITION_INDEPENDENT_CODE ON) 64 | 65 | target_compile_options(${PROJECT_NAME} PUBLIC 66 | -Wall 67 | -Wswitch-enum 68 | -Wcast-qual 69 | -Wuninitialized 70 | -Wconversion 71 | -pedantic 72 | -Werror 73 | ) 74 | 75 | target_link_libraries(${PROJECT_NAME} 76 | PRIVATE 77 | up-core-api::up-core-api 78 | protobuf::libprotobuf 79 | spdlog::spdlog 80 | gcov) 81 | 82 | enable_testing() 83 | add_subdirectory(test) 84 | 85 | INSTALL(TARGETS ${PROJECT_NAME}) 86 | INSTALL(DIRECTORY include DESTINATION .) 87 | -------------------------------------------------------------------------------- /CONTRIBUTING.adoc: -------------------------------------------------------------------------------- 1 | = Contributing to Eclipse uProtocol 2 | 3 | Thanks for your interest in this project. Contributions are welcome! 4 | 5 | == Developer resources 6 | 7 | Information regarding source code management, builds, coding standards, and 8 | more. 9 | 10 | https://projects.eclipse.org/proposals/eclipse-uprotocol 11 | 12 | The project maintains the following source code repositories 13 | 14 | * https://github.com/eclipse-uprotocol 15 | 16 | == Eclipse Contributor Agreement 17 | 18 | Before your contribution can be accepted by the project team contributors must 19 | electronically sign the Eclipse Contributor Agreement (ECA). 20 | 21 | * http://www.eclipse.org/legal/ECA.php 22 | 23 | Commits that are provided by non-committers must have a Signed-off-by field in 24 | the footer indicating that the author is aware of the terms by which the 25 | contribution has been provided to the project. The non-committer must 26 | additionally have an Eclipse Foundation account and must have a signed Eclipse 27 | Contributor Agreement (ECA) on file. 28 | 29 | For more information, please see the Eclipse Committer Handbook: 30 | https://www.eclipse.org/projects/handbook/#resources-commit 31 | 32 | == Contact 33 | 34 | Contact the project developers via the project's "dev" list. 35 | 36 | * https://accounts.eclipse.org/mailing-list/uprotocol-dev -------------------------------------------------------------------------------- /NOTICE.adoc: -------------------------------------------------------------------------------- 1 | = Notices for Eclipse uProtocol C++ Library 2 | 3 | This content is produced and maintained by the Eclipse uProtocol project. 4 | 5 | * Project home: https://projects.eclipse.org/projects/automotive.uprotocol 6 | 7 | == Trademarks 8 | 9 | Eclipse uProtocol is trademark of the Eclipse Foundation. 10 | Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. 11 | 12 | == Copyright 13 | 14 | All content is the property of the respective authors or their employers. 15 | For more information regarding authorship of content, please consult the 16 | listed source code repository logs. 17 | 18 | == Declared Project Licenses 19 | 20 | This program and the accompanying materials are made available under the 21 | terms of the or the Apache License, Version 2.0 22 | which is available at https://www.apache.org/licenses/LICENSE-2.0. 23 | 24 | SPDX-License-Identifier: Apache-2.0 25 | 26 | == Third-party Content 27 | 28 | The following are libraries used by this project: 29 | 30 | * http://protobuf.dev 31 | * https://github.com/google/googletest 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uProtocol C++ Interface Library (up-cpp) 2 | 3 | [![CI](https://github.com/eclipse-uprotocol/up-cpp/actions/workflows/ci.yml/badge.svg)](https://github.com/eclipse-uprotocol/up-cpp/actions/workflows/ci.yml) 4 | 5 | ## Welcome! 6 | 7 | This library provides the primary interface to uProtocol for uEntity 8 | applications. The primary point of interaction for most applications will be the 9 | layer 2 "communications" objects. 10 | 11 | In addition to `up-cpp`, a uEntity will also need at least on transport 12 | implementation, such as [up-transport-zenoh-cpp][zenoh-transport-repo]. 13 | 14 | *_IMPORTANT NOTE:_ This project is under active development* 15 | 16 | ## Getting Started 17 | 18 | ### Requirements: 19 | - Compiler: GCC/G++ 11 or Clang 13 20 | - Conan : 1.59 or latest 2.x 21 | 22 | #### Conan packages 23 | 24 | Using the recipes found in [up-conan-recipes][conan-recipe-repo], build these 25 | Conan packages: 26 | 27 | 1. [up-core-api][spec-repo]: `conan create --version 1.6.0-alpha4 --build=missing up-core-api/release` 28 | 29 | **NOTE:** all `conan` commands in this document use Conan 2.x syntax. Please 30 | adjust accordingly when using Conan 1.x. 31 | 32 | ## How to Use the Library 33 | 34 | To add up-cpp to your conan build dependencies, place following in your 35 | [conanfile.txt][conan-txt-reference]: 36 | 37 | ``` 38 | [requires] 39 | up-cpp/[~1.0] 40 | 41 | [generators] 42 | CMakeDeps 43 | CMakeToolchain 44 | 45 | [layout] 46 | cmake_layout 47 | ``` 48 | 49 | **NOTE:** If using conan version 1.59 Ensure that the conan profile is 50 | configured to use ABI 11 (libstdc++11: New ABI) standards according to 51 | [the Conan documentation for managing gcc ABIs][conan-abi-docs]. 52 | 53 | ## Building locally 54 | 55 | The following steps are only required for developers to locally build and test 56 | up-cpp, If you are making a project that uses up-cpp, follow the steps in the 57 | [How to Use the Library](#how-to-use-the-library) section above. 58 | 59 | ### With Conan for dependencies 60 | 61 | From the root of this repo, run: 62 | 63 | ```bash 64 | conan install --build=missing . 65 | cmake --preset conan-release 66 | cd build/Release 67 | cmake --build . -- -j 68 | ``` 69 | 70 | Once the build completes, tests can be run with `ctest`. 71 | 72 | Debug builds can be generated by changing those steps to: 73 | 74 | ```bash 75 | conan install --build=missing --settings=build_type=Debug . 76 | cmake --preset conan-debug 77 | cd build/Debug 78 | cmake --build . -- -j 79 | ``` 80 | 81 | ### With Conan for QNX 82 | 83 | Before building **up-cpp** we need to build all dependencies from **up-conan-recipes**: 84 | 85 | Please follow instruction for QNX build in file [up-conan-recipes/README.md](https://github.com/eclipse-uprotocol/up-conan-recipes/blob/main/README.md) 86 | 87 | Pre-requisite: 88 | 89 | * Build and install all **QNX build** dependencies from up-conan-recipes 90 | - https://github.com/eclipse-uprotocol/up-conan-recipes 91 | 92 | ```bash 93 | # setup path to up-conan-recipes 94 | export QNX_CONAN_ROOT= 95 | 96 | # Install conan toolchain for QNX target 97 | # 98 | # : nto-7.1-aarch64-le, nto-7.1-x86_64, nto-8.0-aarch64-le, nto-8.0-x86_64 99 | # : 1.0.0-rc0, 1.0.0, 1.0.1-rc1, 1.0.1 100 | # 101 | conan install -pr:h=$QNX_CONAN_ROOT/tools/profiles/ --version= --build=missing . 102 | 103 | cmake --preset conan-release 104 | 105 | cmake --build build/Release -- -j 106 | 107 | # all tests you can find under build/Release/bin/ 108 | # copy test binaries to your QNX target 109 | ``` 110 | 111 | ### Generate UT Coverage 112 | 113 | To get code coverage, perform the steps above, but replace `cmake --preset...` with 114 | ``` 115 | cd build/Release 116 | cmake ../../ -DCMAKE_TOOLCHAIN_FILE=generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Coverage 117 | ``` 118 | Once the tests complete, the Unit Test Coverage report can be generated from the base up-cpp folder with: ./Coverage.sh 119 | ``` 120 | ./coverage.sh 121 | ``` 122 | 123 | ### With dependencies installed as system libraries 124 | 125 | **TODO** Verify steps for pure cmake build without Conan. 126 | 127 | ### Creating the Conan package 128 | 129 | See: [up-conan-recipes][conan-recipe-repo] 130 | 131 | ## Show your support 132 | 133 | Give a ⭐️ if this project helped you! 134 | 135 | [zenoh-transport-repo]: https://github.com/eclipse-uprotocol/up-transport-zenoh-cpp 136 | [conan-recipe-repo]: https://github.com/eclipse-uprotocol/up-conan-recipes 137 | [spec-repo]: https://github.com/eclipse-uprotocol/up-spec 138 | [conan-abi-docs]: https://docs.conan.io/en/1.60/howtos/manage_gcc_abi.html 139 | [conan-txt-reference]: https://docs.conan.io/2/reference/conanfile_txt.html 140 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile 2 | from conan.tools.cmake import CMakeToolchain, cmake_layout, CMakeDeps 3 | 4 | class upCoreApiRecipe(ConanFile): 5 | name = "up-cpp" 6 | 7 | # Optional metadata 8 | license = "Apache-2.0" 9 | author = "Contributors to the Eclipse Foundation " 10 | url = "https://github.com/eclipse-uprotocol/up-cpp" 11 | description = "This library provides a C++ uProtocol API for the development of uEntities" 12 | topics = ("automotive", "iot", "uprotocol", "messaging") 13 | 14 | # Binary configuration 15 | settings = "os", "compiler", "build_type", "arch" 16 | 17 | def requirements(self): 18 | self.requires("protobuf/3.21.12") 19 | self.requires("spdlog/1.13.0") 20 | self.requires("up-core-api/[~1.6, include_prerelease]") 21 | self.test_requires("gtest/1.14.0") 22 | 23 | def layout(self): 24 | cmake_layout(self) 25 | 26 | def generate(self): 27 | deps = CMakeDeps(self) 28 | deps.generate() 29 | tc = CMakeToolchain(self) 30 | #all warnings have to be fixed 31 | tc.cache_variables["CMAKE_CXX_FLAGS_INIT"] = "-Wno-error=unused-but-set-variable -Wno-error=pedantic -Wno-error=conversion" 32 | tc.generate() 33 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Unit Test Coverage report generation. Must compile and run ctest before running this." 3 | 4 | mkdir ./build/Coverage 5 | 6 | gcovr -r . --html --html-details -o ./build/Coverage/index.html -e '.*test.*' 7 | 8 | xdg-open ./build/Coverage/index.html 9 | cd .. 10 | echo "Coverage report can be found here: ./build/Coverage/index.html" 11 | 12 | -------------------------------------------------------------------------------- /include/up-cpp/README.md: -------------------------------------------------------------------------------- 1 | # uProtocol C++ Interface 2 | 3 | The core uProtocol interface in C++ is divided into layers corresponding to the 4 | layers defined in the [uProtocol specifications](https://github.com/eclipse-uprotocol/up-spec). 5 | Each layer is represented as a folder containing headers. 6 | 7 | ## L0: Datamodel 8 | 9 | The API for interacting with uProtocol data is provided in the `datamodel/` 10 | folder. This is unofficially "Layer Zero" of uProtocol. 11 | 12 | `datamodel/` contains: 13 | 14 | * `builder/` - Interfaces for building common configurations of uProtocol objects 15 | * `serializer/` - Functions for serializing and deserializing uProtocol objects to formats _other than_ protobuf 16 | * `validator/` - Functions for inspecting and validating the content of uProtocol objects 17 | 18 | ## L1: Transport 19 | 20 | The abstract API in `transport/` is for sending and receiving uProtocol 21 | messages over a transport protocol, such as Zenoh or SOME/IP. 22 | 23 | ## L2: Communication 24 | 25 | uEntities building on uProtocol will typically use the APIs in `communication/`. 26 | These provide specific communication models, such as pub/sub, RPC, and 27 | notifications. 28 | 29 | These headers are the primary interface for uEntities. 30 | 31 | Client classes are implmented entirely on top of the Transport API, and should 32 | not require customization or re-implementation on a per-transport basis. 33 | At construction time, a `shared_ptr` is required for these classes 34 | to operate. 35 | -------------------------------------------------------------------------------- /include/up-cpp/communication/NotificationSink.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_COMMUNICATION_NOTIFICATIONSINK_H 13 | #define UP_CPP_COMMUNICATION_NOTIFICATIONSINK_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace uprotocol::communication { 26 | 27 | /// @brief Interface for uEntities to receive notifications. 28 | /// 29 | /// Like all L2 client APIs, the NotificationSink is a wrapper on top of the L1 30 | /// UTransport API; in this instance, it provides for the notification 31 | /// receiving half of the notification model. 32 | struct NotificationSink { 33 | using ListenCallback = transport::UTransport::ListenCallback; 34 | using SinkOrStatus = 35 | utils::Expected, v1::UStatus>; 36 | using ListenHandle = transport::UTransport::ListenHandle; 37 | 38 | /// @brief Create a notification sink to receive notifications. 39 | /// 40 | /// The sink will remain active so long as the NotificationSink is held. 41 | /// Resetting the unique_ptr for the NotificationSink will automatically 42 | /// unregister the callback. 43 | /// 44 | /// @param transport Shared pointer to a transport instance. 45 | /// @param callback Called when a notification is received from the source. 46 | /// @param source_filter Notifications matching this source pattern will 47 | /// be passed to the callback. 48 | /// 49 | /// @remarks The transport's entity URI will be used as a sink filter when 50 | /// the callback is registered with the transport. 51 | /// 52 | /// @throws datamodel::validator::uri::InvalidUUri if the source_filter 53 | /// is an invalid pattern for matching notification messages. 54 | /// @throws transport::NullTransport if the transport pointer is nullptr. 55 | /// 56 | /// @returns 57 | /// * unique_ptr to a NotificationSink if the callback was connected 58 | /// successfully. 59 | /// * UStatus containing an error state otherwise. 60 | [[nodiscard]] static SinkOrStatus create( 61 | const std::shared_ptr& transport, 62 | ListenCallback&& callback, const v1::UUri& source_filter); 63 | 64 | /// @note DEPRECATED 65 | /// @brief Create a notification sink to receive notifications. 66 | /// 67 | /// Now a wrapper for create(transport, callback, source_filter) 68 | /// 69 | /// @param sink URI of this uE. Must be transport->getEntityUri(). 70 | /// @param callback Called when a notification is received. 71 | /// @param source_filter Notifications matching this pattern will be 72 | /// forwarded to the callback. MUST HAVE A VALUE. 73 | /// 74 | /// @throws InvalidUUri if sink is not transport->getEntityUri(). 75 | /// @throws InvalidUUri if source_filter has no value. 76 | [[deprecated( 77 | "See alternate overload of " 78 | "create()")]] [[nodiscard]] static SinkOrStatus 79 | create(const std::shared_ptr& transport, 80 | const v1::UUri& sink, ListenCallback&& callback, 81 | std::optional&& source_filter); 82 | 83 | ~NotificationSink() = default; 84 | 85 | /// @brief Constructs a notification listener connected to a given 86 | /// transport. 87 | /// 88 | /// @param transport Transport to receive notifications on. 89 | /// @param listener Handle for a callback connected and listening for 90 | /// notifications. 91 | /// 92 | /// @throws std::invalid_argument if listener is not connected. 93 | NotificationSink(std::shared_ptr transport, 94 | ListenHandle&& listener); 95 | 96 | private: 97 | std::shared_ptr transport_; 98 | ListenHandle listener_; 99 | 100 | // Allow the protected constructor for this class to be used in make_unique 101 | // inside of subscribe() 102 | friend std::unique_ptr std::make_unique< 103 | NotificationSink, std::shared_ptr, ListenHandle>( 104 | std::shared_ptr&&, ListenHandle&&); 105 | }; 106 | 107 | } // namespace uprotocol::communication 108 | 109 | #endif // UP_CPP_COMMUNICATION_NOTIFICATIONSINK_H 110 | -------------------------------------------------------------------------------- /include/up-cpp/communication/NotificationSource.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_COMMUNICATION_NOTIFICATIONSOURCE_H 13 | #define UP_CPP_COMMUNICATION_NOTIFICATIONSOURCE_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace uprotocol::communication { 28 | /// @brief Interface for uEntities to receive notifications. 29 | /// 30 | /// Like all L2 client APIs, the NotificationSource is a wrapper on top of the 31 | /// L1 UTransport API; in this instance, it provides for the notification 32 | /// sending half of the notification model. 33 | struct NotificationSource { 34 | /// @brief Constructs a notification source connected to a given transport. 35 | /// 36 | /// @post An internal UMessageBuilder will be configured based on the 37 | /// provided attributes. 38 | /// 39 | /// @param transport Transport to publish messages on. 40 | /// @param sink URI of this uE. The authority and entity will be replaced 41 | /// automatically with those found in the transport's default. 42 | /// @param sink URI of the uE notifications will be sent to. 43 | /// @param payload_format (Optional) If sending a payload, this sets the 44 | /// format that will be expected when notify() is 45 | /// called. Empty response payloads can only be sent 46 | /// if this was not set. 47 | /// @param priority All sent notifications will be assigned this priority. 48 | /// @param ttl How long messages will be valid from the time notify() is 49 | /// called. 50 | NotificationSource(std::shared_ptr transport, 51 | v1::UUri&& source, v1::UUri&& sink, 52 | std::optional payload_format = {}, 53 | std::optional priority = {}, 54 | std::optional ttl = {}); 55 | 56 | /// @brief Send a notification to the selected sink. 57 | /// 58 | /// @param A Payload builder containing the payload to be sent with the 59 | /// notification. 60 | v1::UStatus notify(datamodel::builder::Payload&&) const; 61 | 62 | /// @brief Send a notification to the selected sink. 63 | /// 64 | /// This can only be called if no payload format was provided at 65 | /// construction time. 66 | v1::UStatus notify() const; 67 | 68 | private: 69 | std::shared_ptr transport_; 70 | datamodel::builder::UMessageBuilder notify_builder_; 71 | }; 72 | 73 | } // namespace uprotocol::communication 74 | 75 | #endif // UP_CPP_COMMUNICATION_NOTIFICATIONSOURCE_H 76 | -------------------------------------------------------------------------------- /include/up-cpp/communication/Publisher.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_COMMUNICATION_PUBLISHER_H 13 | #define UP_CPP_COMMUNICATION_PUBLISHER_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace uprotocol::communication { 28 | 29 | /// @brief Interface for uEntities to publish messages out to subscribers 30 | /// 31 | /// Like all L2 client APIs, the Publisher is a wrapper on top of the L1 32 | /// UTransport API; in this instance, it is the publisher half of the pub/sub 33 | /// model. 34 | struct Publisher { 35 | /// @brief Constructs a publisher connected to a given transport. 36 | /// 37 | /// @post An internal UMessageBuilder will be configured based on the 38 | /// provided attributes. 39 | /// 40 | /// @param transport Transport to publish messages on. 41 | /// @param topic URI representing the topic messages will be published to. 42 | /// @param format UPayloadFormat of data that will be published. 43 | /// @param priority All published messages will be assigned this priority. 44 | /// @param ttl How long published messages will be valid from the time 45 | /// publish() is called. 46 | Publisher(std::shared_ptr transport, 47 | v1::UUri&& topic, v1::UPayloadFormat format, 48 | std::optional priority = {}, 49 | std::optional ttl = {}); 50 | 51 | /// @brief Publish a payload to this Publisher's topic. 52 | /// 53 | /// @param A Payload builder containing the payload to be published. 54 | [[nodiscard]] v1::UStatus publish(datamodel::builder::Payload&&) const; 55 | 56 | ~Publisher() = default; 57 | 58 | private: 59 | std::shared_ptr transport_; 60 | datamodel::builder::UMessageBuilder publish_builder_; 61 | }; 62 | 63 | } // namespace uprotocol::communication 64 | 65 | #endif // UP_CPP_COMMUNICATION_PUBLISHER_H 66 | -------------------------------------------------------------------------------- /include/up-cpp/communication/RpcServer.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_COMMUNICATION_RPCSERVER_H 13 | #define UP_CPP_COMMUNICATION_RPCSERVER_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace uprotocol::communication { 31 | 32 | /// @brief Interface for uEntities to receive and respond to RPC requests. 33 | /// 34 | /// Like all L2 client APIs, the RpcServer is a wrapper on top of the L1 35 | /// UTransport API; in this instance, it is the request-handling half of the 36 | /// RPC model. 37 | struct RpcServer { 38 | /// @brief Callback function signature for implementing the RPC method. 39 | /// 40 | /// Callbacks can (optionally) return a Payload builder containing data 41 | /// to include in the response message. The payload can only be omitted 42 | /// if the payload format was not specified when the RpcServer was created. 43 | using RpcCallback = 44 | std::function( 45 | const v1::UMessage&)>; 46 | 47 | using ServerOrStatus = 48 | utils::Expected, v1::UStatus>; 49 | 50 | /// @brief Creates an RPC server. 51 | /// 52 | /// The callback will remain registered so long as the RpcServer is held. 53 | /// Resetting the unique_ptr to the RpcServer will automatically disconnect 54 | /// the callback. 55 | /// 56 | /// @param transport Transport to offer the RPC method through. 57 | /// @param method_name URI representing the name clients will use to invoke 58 | /// the RPC method. 59 | /// @param callback Method that will be called when requests are received. 60 | /// @param payload_format (Optional) If sending a payload, this sets the 61 | /// format that will be expected when the callback 62 | /// returns. Empty response payloads can only be 63 | /// sent if this was not set. 64 | /// @param ttl (Optional) Time response will be valid from the moment 65 | /// respond() is called. Note that the original request's TTL 66 | /// may also still apply. 67 | /// 68 | /// @returns 69 | /// * unique_ptr to a RpcServer if the callback was connected 70 | /// successfully. 71 | /// * UStatus containing an error state otherwise. 72 | static ServerOrStatus create( 73 | std::shared_ptr transport, 74 | const v1::UUri& method_name, RpcCallback&& callback, 75 | std::optional payload_format = {}, 76 | std::optional ttl = {}); 77 | 78 | ~RpcServer() = default; 79 | 80 | protected: 81 | /// @brief Constructs an RPC server connected to a given transport. 82 | /// 83 | /// @param transport Transport to offer the RPC method through. 84 | /// @param method URI representing the name clients will use to invoke 85 | /// the RPC method. 86 | /// @param payload_format (Optional) If sending a payload, this sets the 87 | /// format that will be expected when the callback 88 | /// returns. Empty response payloads can only be 89 | /// sent if this was not set. 90 | /// @param ttl (Optional) Time response will be valid from the moment 91 | /// respond() is called. Note that the original request's TTL 92 | /// may also still apply. 93 | explicit RpcServer(std::shared_ptr transport, 94 | std::optional format = {}, 95 | std::optional ttl = {}); 96 | 97 | /// @brief Allows std::make_unique to directly access RpcServer's private 98 | /// constructor. 99 | /// 100 | /// @param transport The transport layer abstraction for the RPC server. 101 | /// @param payload_format (Optional) Specifies the payload format, if any. 102 | /// @param ttl (Optional) Specifies the time-to-live (TTL) value, if any. 103 | friend std::unique_ptr 104 | std::make_unique, 105 | std::optional, 106 | std::optional>( 107 | std::shared_ptr&&, 108 | std::optional&&, 109 | std::optional&&); 110 | 111 | /// @brief Connects the RPC callback method and returns the status from 112 | /// UTransport::registerListener. 113 | /// 114 | /// @param callback Method that will be called when requests are received. 115 | /// 116 | /// @returns OK if connected successfully, error status otherwise. 117 | [[nodiscard]] v1::UStatus connect(const v1::UUri& method, 118 | RpcCallback&& callback); 119 | 120 | private: 121 | /// @brief Transport instance that will be used for communication 122 | std::shared_ptr transport_; 123 | 124 | /// @brief TTL to use for responses, if set at construction time 125 | std::optional ttl_; 126 | 127 | /// @brief RPC callback method 128 | RpcCallback callback_; 129 | 130 | /// @brief Format of the payload that will be expected in responses 131 | std::optional expected_payload_format_; 132 | 133 | /// @brief Handle to the connected callback for the RPC method wrapper 134 | transport::UTransport::ListenHandle callback_handle_; 135 | }; 136 | 137 | } // namespace uprotocol::communication 138 | 139 | #endif // UP_CPP_COMMUNICATION_RPCSERVER_H 140 | -------------------------------------------------------------------------------- /include/up-cpp/communication/Subscriber.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_COMMUNICATION_SUBSCRIBER_H 13 | #define UP_CPP_COMMUNICATION_SUBSCRIBER_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | namespace uprotocol::communication { 23 | 24 | /// @brief Interface for uEntities to subscribe published topics 25 | /// 26 | /// Like all L2 client APIs, the functions in Subscriber are a wrapper on top 27 | /// of the L1 UTransport API; in this instance, they are the subscriber half of 28 | /// the pub/sub model. 29 | struct Subscriber { 30 | using SubscriberOrStatus = 31 | utils::Expected, v1::UStatus>; 32 | using ListenCallback = transport::UTransport::ListenCallback; 33 | using ListenHandle = transport::UTransport::ListenHandle; 34 | 35 | /// @brief Subscribes to a topic. 36 | /// 37 | /// The subscription will remain active so long as the Subscriber is held. 38 | /// Resetting the unique_ptr for the Subscriber will automatically 39 | /// unregister the callback. 40 | /// 41 | /// @param transport Transport to register with. 42 | /// @param topic UUri of the topic to listen on. 43 | /// @param callback Function to be called when a message is published ot the 44 | /// subscribed topic. 45 | /// 46 | /// @returns * A unique_ptr to a Subscriber if the callback was 47 | /// successfully registered. 48 | /// * A UStatus with the appropriate failure code otherwise. 49 | [[nodiscard]] static SubscriberOrStatus subscribe( 50 | std::shared_ptr transport, const v1::UUri& topic, 51 | ListenCallback&& callback); 52 | 53 | protected: 54 | /// @brief Constructor 55 | /// 56 | /// @param transport Transport this subscriber is connected to. 57 | /// @param subscription Handle to the callback registered with UTransport. 58 | Subscriber(std::shared_ptr transport, 59 | ListenHandle&& subscription); 60 | 61 | private: 62 | std::shared_ptr transport_; 63 | ListenHandle subscription_; 64 | // Allow the protected constructor for this class to be used in make_unique 65 | // inside of subscribe() 66 | friend std::unique_ptr std::make_unique< 67 | Subscriber, std::shared_ptr, ListenHandle>( 68 | std::shared_ptr&&, ListenHandle&&); 69 | }; 70 | 71 | } // namespace uprotocol::communication 72 | 73 | #endif // UP_CPP_COMMUNICATION_SUBSCRIBER_H 74 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/builder/Payload.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_BUILDER_PAYLOAD_H 13 | #define UP_CPP_DATAMODEL_BUILDER_PAYLOAD_H 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace uprotocol::datamodel::builder { 24 | 25 | /// @brief Interface for preparing payloads for inclusion in a UMessage 26 | /// 27 | /// Allows for implicit conversions at interfaces that require a payload. 28 | struct Payload { 29 | /// @brief Protobuf uses std::string to represent the Bytes type from 30 | /// messages. 31 | using PbBytes = std::string; 32 | 33 | /// @brief A serialized payload as a pairing of bytes and format. 34 | using Serialized = std::tuple; 35 | 36 | /// @brief The two types of data that can be stored in a Serialized payload. 37 | enum PayloadType { Data, Format }; 38 | 39 | /// @brief Constructs a Payload builder with the payload populated by 40 | /// a serialized protobuf. 41 | /// 42 | /// @tparam ProtobufT Automatically inferred protobuf message type. 43 | /// Must be derived from google::protobuf::message. 44 | /// @param A protobuf message that will be serialized using its 45 | /// SerializeToString() method. 46 | /// 47 | /// @remarks The UPayloadFormat will automatically be set to 48 | /// UPAYLOAD_FORMAT_PROTOBUF 49 | template 50 | explicit Payload(const ProtobufT& message) { 51 | std::string serialized; 52 | message.SerializeToString(&serialized); 53 | payload_ = 54 | std::make_tuple(std::move(serialized), 55 | v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); 56 | } 57 | 58 | /// @brief Creates a Payload builder with the payload populated by the 59 | /// result of Serializer::serialize(data). 60 | /// 61 | /// This interface would be invoked something like this: 62 | /// 63 | /// builder::Payload(ToPayload(), foo); 64 | /// 65 | /// @note Serializer types should only contain static methods. As such, 66 | /// there would be nothing to construct and the temporary s argument 67 | /// would compile out. 68 | /// 69 | /// @tparam Serializer An object capable of serializing ValueT. Must 70 | /// provide a `static Serialized serialize(ValueT)` 71 | /// method. This will be inferred from the parameters. 72 | /// @tparam ValueT Automatically inferred unserialized payload value 73 | /// type, serializable using the Serializer. 74 | /// 75 | /// @throws std::out_of_range If the serialized payload format is not valid 76 | /// for v1::UPayloadFormat 77 | /// 78 | /// @param s An instance of Serializer. Note that only the static 79 | /// `Serializer::serialize(data)` will be called - the s instance 80 | /// will compile out. 81 | /// @param data Data to be serialized and stored. 82 | template 83 | Payload(Serializer s [[maybe_unused]], const ValueT& data) { 84 | auto serialized_data = Serializer::serialize(data); 85 | if (!UPayloadFormat_IsValid( 86 | std::get(serialized_data))) { 87 | throw std::out_of_range("Invalid Serializer payload format"); 88 | } 89 | payload_ = std::move(serialized_data); 90 | } 91 | 92 | /// @brief Creates a Payload builder with a provided pre-serialized data. 93 | /// 94 | /// @param value_bytes A byte array containing the serialized payload. 95 | /// @param format The data format of the payload in value_bytes. 96 | /// 97 | /// @throws std::out_of_range If format is not valid for v1::UPayloadFormat 98 | Payload(const std::vector& value_bytes, v1::UPayloadFormat format); 99 | 100 | /// @brief Creates a Payload builder with a provided pre-serialized data. 101 | /// 102 | /// @param value A string containing the serialized payload. 103 | /// @param format The data format of the payload in value_bytes. 104 | /// 105 | /// @throws std::out_of_range If format is not valid for v1::UPayloadFormat 106 | /// 107 | /// @note This would typically be used for UPAYLOAD_FORMAT_TEXT or 108 | /// UPAYLOAD_FORMAT_JSON, but can be used for other payload formats. 109 | Payload(const std::string& value, v1::UPayloadFormat format); 110 | 111 | /// @brief Creates a Payload builder with a provided pre-serialized data. 112 | /// 113 | /// The contents of value will be moved into the Payload object. 114 | /// 115 | /// @param value A string containing the serialized payload. 116 | /// @param format The data format of the payload in value_bytes. 117 | /// 118 | /// @throws std::out_of_range If format is not valid for v1::UPayloadFormat 119 | /// 120 | /// @note This would typically be used for UPAYLOAD_FORMAT_TEXT or 121 | /// UPAYLOAD_FORMAT_JSON, but can be used for other payload formats. 122 | Payload(std::string&& value, v1::UPayloadFormat format); 123 | 124 | /// @brief Creates a Payload builder with a provided pre-serialized data. 125 | /// 126 | /// The contents of value will be moved into the Payload object. 127 | /// 128 | /// @param A pairing of pre-serialized data and a format. 129 | /// 130 | /// @throws std::out_of_range If the serialized payload format is not valid 131 | /// for v1::UPayloadFormat 132 | explicit Payload(Serialized&&); 133 | 134 | /// @brief Creates a Payload builder with a provided protobuf::Any. 135 | /// 136 | /// The contents of value will be moved into the Payload object. 137 | /// 138 | /// @param An initialized google::protobuf::Any object.. 139 | explicit Payload(const google::protobuf::Any&); 140 | 141 | /// @brief Move constructor. 142 | Payload(Payload&&) noexcept; 143 | 144 | /// @brief Copy constructor. 145 | Payload(const Payload&); 146 | 147 | Payload& operator=(Payload&&) noexcept; 148 | Payload& operator=(const Payload&); 149 | 150 | /// @brief This exception indicates build() or move() has been called after 151 | /// move() has already been called. 152 | struct PayloadMoved : public std::runtime_error { 153 | // Inherit constructors 154 | using std::runtime_error::runtime_error; 155 | 156 | PayloadMoved(PayloadMoved&&) noexcept; 157 | PayloadMoved& operator=(PayloadMoved&&) noexcept; 158 | 159 | PayloadMoved(const PayloadMoved&); 160 | PayloadMoved& operator=(const PayloadMoved&); 161 | }; 162 | 163 | /// @brief Get a reference to the internal data from this builder. 164 | /// 165 | /// @throws PayloadMoved if called after buildMove() has already been 166 | /// called. 167 | [[nodiscard]] const Serialized& buildCopy() const; 168 | 169 | /// @brief Get an xvalue of the internal data that can be moved into a 170 | /// UMessage. 171 | /// 172 | /// @post This Payload builder will no longer be valid. Calling 173 | /// `buildCopy()` 174 | /// or `movePayload()` after this will result in an exception. 175 | /// 176 | /// @throws PayloadMoved if called after buildMove() has already been 177 | /// called. 178 | [[nodiscard]] Serialized buildMove() &&; 179 | 180 | private: 181 | Serialized payload_; 182 | bool moved_{false}; 183 | }; 184 | 185 | } // namespace uprotocol::datamodel::builder 186 | 187 | #endif // UP_CPP_DATAMODEL_BUILDER_PAYLOAD_H 188 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/builder/Uuid.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_BUILDER_UUID_H 13 | #define UP_CPP_DATAMODEL_BUILDER_UUID_H 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | namespace uprotocol::datamodel::builder { 23 | 24 | /// @brief Builder class designed to build UUID v8 objects for uProtocol. 25 | /// 26 | /// @see https://github.com/eclipse-uprotocol/up-spec/blob/main/basics/uuid.adoc 27 | struct UuidBuilder { 28 | /// @brief Get a UuidBuilder in the default, production mode. 29 | /// 30 | /// @remarks This should be used in most cases. 31 | /// 32 | /// @returns A UuidBuilder in the default, production mode. 33 | static UuidBuilder getBuilder(); 34 | 35 | /// @brief Get a UuidBuilder in the test mode. 36 | /// 37 | /// The testing mode of UuidBuilder allows for the time and random sources 38 | /// to be replaced such that deterministic tests can be written in 39 | /// situations where the normal behavior of UuidBuilder would interfere. 40 | /// 41 | /// @remarks The provided UuidBuilder starts with an identical state to 42 | /// one returned by getBuilder() with one difference: it will 43 | /// allow customization of its behavior through the `withX()` 44 | /// interfaces. 45 | /// 46 | /// @returns A UuidBuilder in the test mode. 47 | static UuidBuilder getTestBuilder(); 48 | 49 | /// @brief Sets the time source for a UuidBuilder in test mode. 50 | /// 51 | /// @post All built UUIDs will use the provided function to get time 52 | /// values instead of calling `std::chrono::system_clock::now()` 53 | /// 54 | /// @note This can only be used with a UuidBuilder created with the 55 | /// getTestBuilder() interface. 56 | /// 57 | /// @param A callable that returns a time_point. Will be called whenever 58 | /// build() is called to populate the time field in the built UUID. 59 | /// 60 | /// @throws std::domain_error If called on a non-test UuidBuilder. 61 | /// @returns A reference to thus UuidBuilder. 62 | UuidBuilder& withTimeSource( 63 | std::function&&); 64 | 65 | /// @brief Sets the random value source for a UuidBuilder in test mode. 66 | /// 67 | /// @post All built UUIDs will use the provided function to get random 68 | /// values instead of using a true random source. 69 | /// 70 | /// @note This can only be used with a UuidBuilder created with the 71 | /// getTestBuilder() interface. 72 | /// 73 | /// @param A callable that returns a uint64_t. Will be called whenever 74 | /// build() is called in place of the default random value source. 75 | /// 76 | /// @throws std::domain_error If called on a non-test UuidBuilder. 77 | /// @returns A reference to thus UuidBuilder. 78 | UuidBuilder& withRandomSource(std::function&&); 79 | 80 | /// @brief Creates a uProtocol UUID based on the builder's current state. 81 | /// 82 | /// @remarks As part of the UUID v7/v8 spec, there is a shared state for 83 | /// all UUID builders within a process. Test builders can override 84 | /// this with the withIndependentState() interface. 85 | v1::UUID build(); 86 | 87 | private: 88 | explicit UuidBuilder(bool testing); 89 | 90 | const bool testing_{false}; 91 | std::function time_source_; 92 | std::function random_source_; 93 | }; 94 | 95 | } // namespace uprotocol::datamodel::builder 96 | 97 | #endif // UP_CPP_DATAMODEL_BUILDER_UUID_H 98 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/constants/UuidConstants.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_CONSTANTS_UUIDCONSTANTS_H 13 | #define UP_CPP_DATAMODEL_CONSTANTS_UUIDCONSTANTS_H 14 | 15 | #include 16 | 17 | namespace uprotocol::datamodel { 18 | 19 | // Masks and shifts for various UUID fields 20 | constexpr uint64_t UUID_TIMESTAMP_MASK = 0xFFFFFFFFFFFF; 21 | constexpr uint64_t UUID_TIMESTAMP_SHIFT = 16; 22 | constexpr uint64_t UUID_VERSION_MASK = 0xF; 23 | constexpr uint64_t UUID_VERSION_SHIFT = 12; 24 | constexpr uint64_t UUID_VARIANT_MASK = 0x3; 25 | constexpr uint64_t UUID_VARIANT_SHIFT = 62; 26 | constexpr uint64_t UUID_RANDOM_A_MASK = 0xFFF; 27 | constexpr uint64_t UUID_RANDOM_B_MASK = 0x3FFFFFFFFFFFFFFF; 28 | 29 | // Constants for UUID version and variant 30 | constexpr uint64_t UUID_VERSION_7 = 7; 31 | constexpr uint64_t UUID_VARIANT_RFC4122 = 2; 32 | 33 | // Other constants 34 | constexpr uint32_t UUID_BYTE_SIZE = 16; 35 | constexpr uint32_t UUID_PART_SIZE = 4; 36 | constexpr uint32_t HEX_BASE = 16; 37 | constexpr uint64_t MASK_32_BITS = 0xFFFFFFFF; 38 | constexpr uint64_t MASK_16_BITS = 0xFFFF; 39 | constexpr uint64_t MASK_14_BITS = 0x3FFF; 40 | 41 | // number of digits needed to represent a given number of bits in base 16 42 | constexpr uint64_t LEN_16_BITS_IN_HEX = 4; 43 | constexpr uint64_t LEN_32_BITS_IN_HEX = 8; 44 | constexpr uint64_t LEN_48_BITS_IN_HEX = 12; 45 | 46 | // number of characters a valid uuid 47 | constexpr uint64_t TOTAL_UUID_LENGTH = 36; 48 | constexpr uint64_t LEN_MSB_IN_HEX = 8; 49 | constexpr uint64_t LEN_LSB_IN_HEX = 4; 50 | constexpr uint64_t LEN_VCANT_IN_HEX = 4; 51 | constexpr uint64_t LEN_VARR_IN_HEX = 4; 52 | constexpr uint64_t LEN_RAND_IN_HEX = 8; 53 | 54 | // number of bits represented by a single hex character 55 | constexpr uint64_t LEN_HEX_TO_BIT = 4; 56 | 57 | // number of bits to represent uint64 58 | constexpr uint64_t LEN_UINT64_IN_BIT = sizeof(uint64_t) * 8; 59 | 60 | // expected positions of the '-' separators in a valid uuid 61 | constexpr uint64_t POS_FIRST_SEPARATOR = LEN_MSB_IN_HEX; 62 | constexpr uint64_t POS_SECOND_SEPARATOR = 63 | POS_FIRST_SEPARATOR + LEN_LSB_IN_HEX + 1; 64 | constexpr uint64_t POS_THIRD_SEPARATOR = 65 | POS_SECOND_SEPARATOR + LEN_VCANT_IN_HEX + 1; 66 | constexpr uint64_t POS_FOURTH_SEPARATOR = 67 | POS_THIRD_SEPARATOR + LEN_VARR_IN_HEX + 1; 68 | 69 | } // namespace uprotocol::datamodel 70 | 71 | #endif // UP_CPP_DATAMODEL_CONSTANTS_UUIDCONSTANTS_H 72 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/serializer/UUri.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_SERIALIZER_UURI_H 13 | #define UP_CPP_DATAMODEL_SERIALIZER_UURI_H 14 | 15 | #include 16 | 17 | #include 18 | 19 | /// @brief Collection of interfaces for converting uprotocol::v1::UUri objects 20 | /// between protobuf and alternative representations. 21 | namespace uprotocol::datamodel::serializer::uri { 22 | 23 | /// @brief Converts to and from a human-readable string representation of UUri 24 | /// according to the UUri spec. 25 | struct AsString { 26 | [[nodiscard]] static std::string serialize(const v1::UUri&); 27 | [[nodiscard]] static v1::UUri deserialize(const std::string&); 28 | }; 29 | 30 | } // namespace uprotocol::datamodel::serializer::uri 31 | 32 | #endif // UP_CPP_DATAMODEL_SERIALIZER_UURI_H 33 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/serializer/Uuid.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_SERIALIZER_UUID_H 13 | #define UP_CPP_DATAMODEL_SERIALIZER_UUID_H 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | /// @brief Collection of interfaces for converting uprotocol::v1::UUID objects 22 | /// between protobuf and alternative representations. 23 | /// 24 | /// @remarks No `AsPayload` is provided because v1::UUID is a protobuf message. 25 | /// As such, it can be automatically serialized with builder::Payload. 26 | namespace uprotocol::datamodel::serializer::uuid { 27 | 28 | /// @brief Converts to and from a human-readable string representation of UUID 29 | struct AsString { 30 | [[nodiscard]] static std::string serialize(const v1::UUID&); 31 | [[nodiscard]] static v1::UUID deserialize(const std::string&); 32 | }; 33 | 34 | /// @brief Converts to and from byte vector representation of UUID 35 | struct AsBytes { 36 | [[nodiscard]] static std::vector serialize(const v1::UUID&); 37 | [[nodiscard]] static v1::UUID deserialize(const std::vector&); 38 | }; 39 | 40 | } // namespace uprotocol::datamodel::serializer::uuid 41 | 42 | #endif // UP_CPP_DATAMODEL_SERIALIZER_UUID_H 43 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/validator/UMessage.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_VALIDATOR_UMESSAGE_H 13 | #define UP_CPP_DATAMODEL_VALIDATOR_UMESSAGE_H 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | /// @brief Validators for UMessage objects. 22 | /// 23 | /// See 24 | /// https://github.com/eclipse-uprotocol/up-spec/blob/main/basics/uattributes.adoc 25 | namespace uprotocol::datamodel::validator::message { 26 | 27 | enum class Reason { 28 | /// @brief The ID does not pass UUID validity checks 29 | BAD_ID, 30 | /// @brief The TTL, if present, indicates the ID has expired 31 | ID_EXPIRED, 32 | /// @brief The Priority, if set, is not within the allowable range 33 | PRIORITY_OUT_OF_RANGE, 34 | /// @brief The Payload Format is not within the allowable range 35 | PAYLOAD_FORMAT_OUT_OF_RANGE, 36 | /// @brief Source URI did not pass validity checks 37 | BAD_SOURCE_URI, 38 | /// @brief Sink URI did not pass validity checks 39 | BAD_SINK_URI, 40 | /// @brief TTL is set to an invalid value (e.g. 0) 41 | INVALID_TTL, 42 | /// @brief A field was set that is not allowed for the validated mode 43 | DISALLOWED_FIELD_SET, 44 | /// @brief The Request ID did not match the ID of the request message 45 | REQID_MISMATCH, 46 | /// @brief The Priority did not match the Priority of the request message 47 | PRIORITY_MISMATCH, 48 | /// @brief The source and sink from a request must be swapped in a response 49 | URI_MISMATCH, 50 | /// @brief The message type was set to UNSPECIFIED (isValid() only) 51 | UNSPECIFIED_MESSAGE_TYPE, 52 | /// @brief The message type is outside the allowable range (isValid() only) 53 | INVALID_MESSAGE_TYPE, 54 | /// @brief The type set in the message is incorrect for the validated mode 55 | WRONG_MESSAGE_TYPE 56 | }; 57 | 58 | /// @brief Get a descriptive message for a reason code. 59 | std::string_view message(Reason); 60 | 61 | /// @brief Return type for validity checks. 62 | /// 63 | /// The recommended usage of these checks and return types looks something 64 | /// like this: 65 | /// 66 | /// auto [valid, maybe_reason] = isValidRpcRequest(request); 67 | /// if (valid) { 68 | /// // Do something with the message 69 | /// } else if (maybe_reason) { 70 | /// log(message(*maybe_reason); 71 | /// } 72 | using ValidationResult = std::tuple>; 73 | 74 | /// @brief Checks if UMessage is a valid UMessage of any format. 75 | /// 76 | /// A UMessage is valid if any of these are true: 77 | /// 78 | /// * isValidRpcRequest() 79 | /// * isValidRpcResponse() 80 | /// * isValidPublish() 81 | /// * isValidNotification() 82 | [[nodiscard]] ValidationResult isValid(const v1::UMessage&); 83 | 84 | /// @brief Checks if common attributes for all UMessage types are valid 85 | /// 86 | /// These checks must pass: 87 | /// * The message ID must be a valid UUID 88 | /// * If TTL is specified, the ID must not be expired 89 | /// * If Priority is specified, it is within the range of UPriority 90 | /// * Payload Format must be within the range of UPayloadFormat 91 | [[nodiscard]] ValidationResult areCommonAttributesValid(const v1::UMessage&); 92 | 93 | /// @brief Checks if UMessage is valid for invoking an RPC method 94 | /// 95 | /// In addition to all common attributes being valid, these checks must pass: 96 | /// * Message type must be UMESSAGE_TYPE_REQUEST 97 | /// * Message source must pass uri::isValidRpcResponse() 98 | /// * Message sink must pass uri::isValidRpcMethod() 99 | /// * Message priority must be UPRIORITY_CS4 or higher 100 | /// * Message ttl must be set and greater than zero 101 | /// * Message must not set commstatus 102 | /// * Message must not set reqid 103 | [[nodiscard]] ValidationResult isValidRpcRequest(const v1::UMessage&); 104 | 105 | /// @brief Checks if UMessage is a valid response 106 | /// 107 | /// In addition to all common attributes being valid, these checks must pass: 108 | /// * Message type must be UMESSAGE_TYPE_RESPONSE 109 | /// * Message source must pass uri::isValidRpcMethod() 110 | /// * Message sink must pass uri::isValidRpcResponse() 111 | /// * Message reqid must be set to a valid, unexpired UUID 112 | /// * Message priority must be UPRIORITY_CS4 or higher 113 | /// * Message must not set permission_level 114 | /// * Message must not set token 115 | [[nodiscard]] ValidationResult isValidRpcResponse(const v1::UMessage&); 116 | 117 | /// @brief Checks if UMessage is a valid response to specific RPC request 118 | /// 119 | /// In addition to all checks in isValidRpcResponse() passing: 120 | /// * Message reqid must be the ID from the request message 121 | /// * Message priority must be the priority from the request message 122 | [[nodiscard]] ValidationResult isValidRpcResponseFor( 123 | const v1::UMessage& request, const v1::UMessage& response); 124 | 125 | /// @brief Checks if UMessage is valid for publishing to a topic 126 | /// 127 | /// In addition to all common attributes being valid, these checks must pass: 128 | /// * Message type must be UMESSAGE_TYPE_PUBLISH 129 | /// * Message source must pass uri::isValidTopic() 130 | /// * Message must not set sink 131 | /// * Message must not set commstatus 132 | /// * Message must not set reqid 133 | /// * Message must not set permission_level 134 | /// * Message must not set token 135 | [[nodiscard]] ValidationResult isValidPublish(const v1::UMessage&); 136 | 137 | /// @brief Checks if UMessage is valid for sending a notification 138 | /// 139 | /// In addition to all common attributes being valid, these checks must pass: 140 | /// * Message type must be UMESSAGE_TYPE_NOTIFICATION 141 | /// * Message source must pass uri::isValidNotification() 142 | /// * Message sink must pass uri::isValidNotification() 143 | /// * Message must not set commstatus 144 | /// * Message must not set reqid 145 | /// * Message must not set permission_level 146 | /// * Message must not set token 147 | [[nodiscard]] ValidationResult isValidNotification(const v1::UMessage&); 148 | 149 | /// @brief This exception indicates that a UMessage object was provided that 150 | /// did not contain valid UMessage data or was the wrong type. 151 | /// 152 | /// @remarks Generally used by L2 client interfaces. Not used by checks in this 153 | /// file that return ValidationResult. 154 | struct InvalidUMessage : public std::invalid_argument { 155 | // Inherit constructors 156 | using std::invalid_argument::invalid_argument; 157 | 158 | InvalidUMessage(InvalidUMessage&&) noexcept; 159 | InvalidUMessage& operator=(InvalidUMessage&&) noexcept; 160 | 161 | InvalidUMessage(const InvalidUMessage&); 162 | InvalidUMessage& operator=(const InvalidUMessage&); 163 | }; 164 | 165 | } // namespace uprotocol::datamodel::validator::message 166 | 167 | #endif // UP_CPP_DATAMODEL_VALIDATOR_UMESSAGE_H 168 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/validator/UUri.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_VALIDATOR_UURI_H 13 | #define UP_CPP_DATAMODEL_VALIDATOR_UURI_H 14 | 15 | #include 16 | 17 | #include 18 | 19 | /// @brief Validators for UUri objects. 20 | namespace uprotocol::datamodel::validator::uri { 21 | 22 | enum class Reason { 23 | /// @brief The URI is completely empty 24 | EMPTY, 25 | /// @brief The version is 0 (reserved) 26 | RESERVED_VERSION, 27 | /// @brief The resource is 0 for non-RPC-response URIs (reserved) 28 | RESERVED_RESOURCE, 29 | /// @brief The URI contains a wildcard in a position that is not allowed 30 | /// for this particular form. 31 | DISALLOWED_WILDCARD, 32 | /// @brief The resource ID is not in the allowed range for this URI form. 33 | BAD_RESOURCE_ID, 34 | /// @brief The URI has a blank (local) authority name 35 | LOCAL_AUTHORITY, 36 | /// @brief uE Major Version is greater than uint8_t max 37 | VERSION_OVERFLOW, 38 | /// @brief Resource ID is greater than uint16_t max 39 | RESOURCE_OVERFLOW, 40 | /// @brief Authority Name is longer than 128 characters 41 | AUTHORITY_TOO_LONG 42 | }; 43 | 44 | /// @brief Get a descriptive message for a reason code. 45 | std::string_view message(Reason); 46 | 47 | /// @brief Return type for validity checks. 48 | /// 49 | /// The recommended usage of these checks and return types looks something 50 | /// like this: 51 | /// 52 | /// auto [valid, maybe_reason] = isValidRpcMethod(uri); 53 | /// if (valid) { 54 | /// // Do something with the URI 55 | /// } else if (maybe_reason) { 56 | /// log(message(*maybe_reason); 57 | /// } 58 | using ValidationResult = std::tuple>; 59 | 60 | /// @note DEPRECATED - This check can produce misleading results. It doesn't 61 | /// handle filters with wildcards. It also is not very useful when 62 | /// checking URI fields in messages where a message type is already 63 | /// available. In those cases, the appropriate type-specfic check should 64 | /// be used instead. 65 | /// 66 | /// @brief Checks if UUri is a valid UUri for use as an attribute in a message 67 | /// 68 | /// A UUri is valid if: 69 | /// 70 | /// * It is not empty 71 | /// * it is valid for at least one of: 72 | /// * isValidRpcMethod() 73 | /// * isValidRpcResponse() 74 | /// * isValidPublishTopic() 75 | /// * isValidNotification() 76 | [[deprecated( 77 | "Use isValidFilter or a message-type-specific " 78 | "validator")]] [[nodiscard]] ValidationResult 79 | isValid(const v1::UUri&); 80 | 81 | /// @brief Checks if a UUri is valid as a source_filter or sink_filter when 82 | /// registering a listener with a transport. 83 | [[nodiscard]] ValidationResult isValidFilter(const v1::UUri&); 84 | 85 | /// @brief Checks if UUri is valid for invoking an RPC method 86 | /// 87 | /// The UUri must not be blank, no field can be a wildcard, and 88 | /// resource_id must be in the range [0x0001, 0x7FFF]. 89 | [[nodiscard]] ValidationResult isValidRpcMethod(const v1::UUri&); 90 | 91 | /// @brief Checks if UUri is a valid sink for responding to an RPC request. 92 | /// 93 | /// The UUri must not be blank, no field can be a wildcard, and 94 | /// resource_id must be 0. 95 | [[nodiscard]] ValidationResult isValidRpcResponse(const v1::UUri&); 96 | 97 | /// @brief Checks if UUri is valid as an entity URI for a UTransport. 98 | /// 99 | /// @note The requirements for this URI are identical to isValidRpcResponse() 100 | /// except that the authority name is not allowed to be blank. 101 | [[nodiscard]] ValidationResult isValidDefaultEntity(const v1::UUri&); 102 | 103 | /// @note DEPRECATED 104 | /// @see isValidDefaultEntity() 105 | /// @brief Checks if UUri is valid as a default source on a UTransport. 106 | [[deprecated( 107 | "Use isValidDefaultEntity instead")]] [[nodiscard]] ValidationResult 108 | isValidDefaultSource(const v1::UUri&); 109 | 110 | /// @brief Checks if UUri is valid for publishing to a topic, OR as a source 111 | /// and sink for sending notifications, OR as a sink for receiving 112 | /// notifications. 113 | /// 114 | /// The UUri must not be blank, no field can be a wildcard, and 115 | /// resource_id must be in the range [0x8000, 0xFFFE]. 116 | [[nodiscard]] ValidationResult isValidPublishTopic(const v1::UUri&); 117 | 118 | /// @brief Checks if UUri is valid for notification source. 119 | /// 120 | /// The UUri must not be blank, no field can be a wildcard, and 121 | /// resource_id must be in the range [0x8000, 0xFFFE]. 122 | [[nodiscard]] ValidationResult isValidNotificationSource(const v1::UUri&); 123 | 124 | /// @brief Checks if UUri is valid for notification sink. 125 | /// 126 | /// The UUri must not be blank, no field can be a wildcard, and 127 | /// resource_id must be 0. 128 | [[nodiscard]] ValidationResult isValidNotificationSink(const v1::UUri&); 129 | 130 | /// @brief Checks if UUri is valid as a subscription to a published topic or 131 | /// as a source filter when subscribing to a notification. 132 | /// 133 | /// The UUri must not be blank, and resource_id, if not a wildcard, 134 | /// must be in the range [0x8000, 0xFFFE]. 135 | [[nodiscard]] ValidationResult isValidSubscription(const v1::UUri&); 136 | 137 | /// @brief Checks if a URI is empty. 138 | /// 139 | /// An Empty URI is one where all of these conditions are met: 140 | /// 141 | /// * The authority name has zero length or contains only whitespace 142 | /// characters. 143 | /// * The uE ID is 0 144 | /// * The uE major version is 0 145 | /// * The resource ID is 0 146 | [[nodiscard]] ValidationResult isEmpty(const v1::UUri&); 147 | 148 | /// @brief Checks if a UUri is local 149 | /// 150 | /// This is just a check for a zero-length authority name string. 151 | [[nodiscard]] bool isLocal(const v1::UUri&); 152 | 153 | /// @brief Checks if a UUri has a wildcard authority name. 154 | /// 155 | /// Checks if a UUri has a wildcard authority name, returns true if yes. 156 | [[nodiscard]] bool has_wildcard_authority(const v1::UUri& uuri); 157 | 158 | /// @brief Checks if a UUri has a wildcard service id. 159 | /// 160 | /// Checks if a UUri has a wildcard service id, returns true if yes. 161 | [[nodiscard]] bool has_wildcard_service_id(const v1::UUri& uuri); 162 | 163 | /// @brief Checks if a UUri has a wildcard service instance id. 164 | /// 165 | /// Checks if a UUri has a wildcard service instance id, returns true if yes. 166 | [[nodiscard]] bool has_wildcard_service_instance_id(const v1::UUri& uuri); 167 | 168 | /// @brief Checks if a UUri has a wildcard version. 169 | /// 170 | /// Checks if a UUri has a wildcard version, returns true if yes. 171 | [[nodiscard]] bool has_wildcard_version(const v1::UUri& uuri); 172 | 173 | /// @brief Checks if a UUri has a wildcard resoruce id. 174 | /// 175 | /// Checks if a UUri has a wildcard resoruce id, returns true if yes. 176 | [[nodiscard]] bool has_wildcard_resource_id(const v1::UUri& uuri); 177 | 178 | /// @brief Checks if a UUri uses no wildcards 179 | /// 180 | /// Checks for all types of wildcards, returns true if no wildcards are found. 181 | [[nodiscard]] bool verify_no_wildcards(const v1::UUri&); 182 | 183 | /// @brief This exception indicates that a UUri object was provided that 184 | /// did not contain valid UUri data. 185 | /// 186 | /// @remarks Generally used by L2 client interfaces. Not used by checks that 187 | /// return ValidationResult. 188 | struct InvalidUUri : public std::invalid_argument { 189 | // Forward constructors 190 | template 191 | explicit InvalidUUri(Args&&... args) 192 | : std::invalid_argument(std::forward(args)...) {} 193 | 194 | InvalidUUri(InvalidUUri&&) noexcept; 195 | InvalidUUri& operator=(InvalidUUri&&) noexcept; 196 | 197 | InvalidUUri(const InvalidUUri&); 198 | InvalidUUri& operator=(const InvalidUUri&); 199 | }; 200 | 201 | } // namespace uprotocol::datamodel::validator::uri 202 | 203 | #endif // UP_CPP_DATAMODEL_VALIDATOR_UURI_H 204 | -------------------------------------------------------------------------------- /include/up-cpp/datamodel/validator/Uuid.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_DATAMODEL_VALIDATOR_UUID_H 13 | #define UP_CPP_DATAMODEL_VALIDATOR_UUID_H 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | // @brief Collection of functions for validating and inspecting UUIDs. 24 | namespace uprotocol::datamodel::validator::uuid { 25 | 26 | enum class Reason { 27 | /// @brief The UUID could not be interpreted as a uP v8 UUID 28 | WRONG_VERSION, 29 | /// @brief The UUID variant is not a supported variant 30 | UNSUPPORTED_VARIANT, 31 | /// @brief The time in the UUID is in the future 32 | FROM_THE_FUTURE, 33 | /// @brief For a given TTL, the UUID has already expired 34 | EXPIRED 35 | }; 36 | 37 | /// @brief Get a descriptive message for a reason code. 38 | std::string_view message(Reason); 39 | 40 | /// @brief Return type for validity checks. 41 | /// 42 | /// The recommended usage of these checks and return types looks something 43 | /// like this: 44 | /// 45 | /// auto [valid, maybe_reason] = isUuid(uuid); 46 | /// if (valid) { 47 | /// // Do something with the UUID 48 | /// } else if (maybe_reason) { 49 | /// log(message(*maybe_reason); 50 | /// } 51 | using ValidationResult = std::tuple>; 52 | 53 | /// @name Validity checks 54 | /// @{ 55 | /// @brief Checks if the provided UUID contains valid uP v8 UUID data. 56 | /// @returns True if the UUID has valid UUID data, false otherwise. 57 | ValidationResult isUuid(const v1::UUID&); 58 | 59 | /// @brief Checks if the provided UUID has expired based on the given TTL. 60 | /// @throws InvalidUuid if the UUID does not contain valid UUID data 61 | /// @returns True if the difference between the current system time and 62 | /// the the timestamp in the UUID is greater than the TTL. 63 | ValidationResult isExpired(const v1::UUID& uuid, std::chrono::milliseconds ttl); 64 | /// @} 65 | 66 | /// @name Inspection utilities 67 | /// @{ 68 | /// @brief Gets the version field from a UUID object 69 | /// @throws InvalidUuid if the UUID does not contain valid UUID data 70 | /// @returns The UUID's version 71 | uint8_t getVersion(const v1::UUID&); 72 | 73 | /// @brief Gets the variant field from a UUID object 74 | /// @throws InvalidUuid if the UUID does not contain valid UUID data 75 | /// @returns The UUID's variant 76 | uint8_t getVariant(const v1::UUID&); 77 | 78 | /// @brief Gets the timestamp field from a UUID object 79 | /// @throws InvalidUuid if the UUID does not contain valid UUID data 80 | /// @returns The UUID's timestamp as a chrono::time_point for the system 81 | /// clock. 82 | std::chrono::system_clock::time_point getTime(const v1::UUID& uuid); 83 | 84 | /// @brief Gets the difference between a UUID's timestamp and the current 85 | /// time according to the system clock/ 86 | /// @throws InvalidUuid if the UUID does not contain valid UUID data 87 | /// @returns The age of the UUID in milliseconds 88 | std::chrono::milliseconds getElapsedTime(const v1::UUID& uuid); 89 | 90 | /// @brief Gets the time remaining before the UUID expires, based on the 91 | /// given TTL. 92 | /// @throws InvalidUuid if the UUID does not contain valid UUID data 93 | /// @returns Remaining time (ttl - getElapsedTime(uuid)) in milliseconds 94 | std::chrono::milliseconds getRemainingTime(const v1::UUID& uuid, 95 | std::chrono::milliseconds ttl); 96 | /// @} 97 | 98 | /// @brief This exception indicates that a UUID object was provided that 99 | /// did not contain valid UUID data. 100 | struct InvalidUuid : public std::invalid_argument { 101 | // Inherit constructors 102 | using std::invalid_argument::invalid_argument; 103 | 104 | // Adding a constructor for std::string_view 105 | explicit InvalidUuid(std::string_view message) 106 | : std::invalid_argument(std::string(message)) {} 107 | 108 | InvalidUuid(InvalidUuid&&) noexcept = default; 109 | InvalidUuid& operator=(InvalidUuid&&) noexcept = default; 110 | 111 | InvalidUuid(const InvalidUuid&) = default; 112 | InvalidUuid& operator=(const InvalidUuid&) = default; 113 | }; 114 | 115 | } // namespace uprotocol::datamodel::validator::uuid 116 | 117 | #endif // UP_CPP_DATAMODEL_VALIDATOR_UUID_H 118 | -------------------------------------------------------------------------------- /include/up-cpp/utils/CyclicQueue.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_UTILS_CYCLICQUEUE_H 13 | #define UP_CPP_UTILS_CYCLICQUEUE_H 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace uprotocol::utils { 20 | 21 | /// @brief Queue that enforces a maximum size by evicting the oldest entry to 22 | /// make room for new ones. 23 | template 24 | class CyclicQueue final { 25 | public: 26 | explicit CyclicQueue(size_t max_size); 27 | 28 | CyclicQueue(const CyclicQueue&) = delete; 29 | CyclicQueue& operator=(const CyclicQueue&) = delete; 30 | 31 | virtual ~CyclicQueue() = default; 32 | 33 | void push(T&& data) noexcept; 34 | void push(const T& data) noexcept; 35 | 36 | bool isFull() const noexcept; 37 | bool isEmpty() const noexcept; 38 | 39 | // Blocking pop() 40 | bool pop(T& popped_value) noexcept; 41 | // Non-blocking pop() 42 | bool tryPop(T& popped_value) noexcept; 43 | // Time-limited blocking pop()s 44 | bool tryPopFor(T& popped_value, std::chrono::milliseconds limit) noexcept; 45 | bool tryPopUntil(T& popped_value, 46 | std::chrono::system_clock::time_point when) noexcept; 47 | 48 | size_t size() const noexcept; 49 | 50 | void clear() noexcept; 51 | 52 | private: 53 | size_t queueMaxSize_; 54 | mutable std::mutex mutex_; 55 | std::condition_variable conditionVariable_; 56 | std::queue queue_; 57 | }; 58 | 59 | } // namespace uprotocol::utils 60 | 61 | #endif // UP_CPP_UTILS_CYCLICQUEUE_H 62 | -------------------------------------------------------------------------------- /include/up-cpp/utils/Expected.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_UTILS_EXPECTED_H 13 | #define UP_CPP_UTILS_EXPECTED_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace uprotocol::utils { 21 | 22 | // A sentinel keeping watch for when we switch to C++23 and gain access to 23 | // the real std::expected. Our limited copy should be replaced immediately. 24 | static_assert(!__has_cpp_attribute(__cpp_lib_expected), 25 | "Replace uprotocol::utils::Expected with std::expected"); 26 | 27 | /// @name Temporary substitute for std::expected 28 | /// @remarks See the reference for std::expected: 29 | /// https://en.cppreference.com/w/cpp/utility/expected 30 | /// No further documentation is provided in this file. 31 | /// @{ 32 | struct BadExpectedAccess : public std::runtime_error { 33 | template 34 | explicit BadExpectedAccess(Args&&... args) 35 | : std::runtime_error(std::forward(args)...) {} 36 | }; 37 | 38 | /// @brief Required tagging type for cases where expected and unexpected type 39 | /// are identical. 40 | template 41 | class Unexpected { 42 | public: 43 | constexpr Unexpected(const Unexpected&) = default; 44 | constexpr Unexpected(Unexpected&&) noexcept = default; 45 | 46 | constexpr explicit Unexpected(const E& rhs) : storage_(rhs) {} 47 | constexpr explicit Unexpected(E&& rhs) : storage_(std::move(rhs)) {} 48 | 49 | constexpr const E& error() const& noexcept { return storage_; } 50 | constexpr E&& error() && noexcept { return std::move(storage_); } 51 | 52 | private: 53 | E storage_; 54 | }; 55 | 56 | /// @brief A stripped-down version of std::expected from C++23. 57 | template 58 | class Expected { 59 | public: 60 | constexpr explicit Expected(T arg) : storage_(std::forward(arg)) {} 61 | // It E and T are the same type, this can cause problems. Previously, this 62 | // was in use by implicit conversion 63 | // constexpr explicit Expected(E arg) : 64 | // storage_(std::forward>(Unexpected(arg))) {} 65 | constexpr explicit Expected(Unexpected arg) 66 | : storage_(std::forward>(arg)) {} 67 | 68 | constexpr Expected(const Expected&) = default; 69 | constexpr Expected(Expected&&) noexcept = default; 70 | 71 | [[nodiscard]] constexpr bool has_value() const noexcept { 72 | return std::holds_alternative(storage_); 73 | } 74 | 75 | constexpr explicit operator bool() const noexcept { 76 | return std::holds_alternative(storage_); 77 | } 78 | 79 | template 80 | constexpr T value_or(X&& v) const& noexcept { 81 | return has_value() ? std::get(storage_) 82 | : static_cast(std::forward(v)); 83 | } 84 | 85 | constexpr const T& value() const& { 86 | if (!has_value()) { 87 | throw BadExpectedAccess( 88 | "Attempt to access value() when unexpected."); 89 | } 90 | return std::get(storage_); 91 | } 92 | 93 | constexpr T value() && { 94 | if (!has_value()) { 95 | throw BadExpectedAccess( 96 | "Attempt to access value() when unexpected."); 97 | } 98 | return std::move(std::get(storage_)); 99 | } 100 | 101 | constexpr const E& error() const& { 102 | if (has_value()) { 103 | throw BadExpectedAccess( 104 | "Attempt to access error() when not unexpected."); 105 | } 106 | return std::get>(storage_).error(); 107 | } 108 | 109 | constexpr E error() && { 110 | if (has_value()) { 111 | throw BadExpectedAccess( 112 | "Attempt to access error() when not unexpected."); 113 | } 114 | return std::move(std::get>(storage_)).error(); 115 | } 116 | 117 | constexpr const T& operator*() const { 118 | if (!has_value()) { 119 | throw BadExpectedAccess( 120 | "Attempt to dereference expected value when unexpected."); 121 | } 122 | return std::get(storage_); 123 | } 124 | 125 | constexpr const T* operator->() const { 126 | if (!has_value()) { 127 | throw BadExpectedAccess( 128 | "Attempt to dereference expected pointer when unexpected."); 129 | } 130 | return &std::get(storage_); 131 | } 132 | 133 | private: 134 | std::variant> storage_; 135 | 136 | static_assert(!std::is_void_v, 137 | "We don't allow T==void (unlike std::expected)"); 138 | 139 | static_assert( 140 | std::is_destructible_v && !std::is_array_v && 141 | !std::is_reference_v, 142 | "Expected requires T to meet the C++ 'destructable' requirement"); 143 | }; 144 | 145 | /// @} 146 | 147 | } // namespace uprotocol::utils 148 | 149 | #endif // UP_CPP_UTILS_EXPECTED_H -------------------------------------------------------------------------------- /include/up-cpp/utils/IpAddress.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_UTILS_IPADDRESS_H 13 | #define UP_CPP_UTILS_IPADDRESS_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace uprotocol::utils { 22 | 23 | /// @brief Utility for converting IP addresses between representations and for 24 | /// normalizing string representations. 25 | /// 26 | /// IP addresses can be represented as either human-readable strings or binary 27 | /// numbers, and may be either IPv4 or IPv6. Additionally, each address family 28 | /// has multiple allowable string representations. For example these IPv6 29 | /// addresses are all the same: 30 | /// 31 | /// * `2001::C0FF:EE01` 32 | /// * `2001:0000:0000:0000:0000:0000:c0ff:ee01` 33 | /// * `2001:0000:0000:0000::0000:192.255.238.1` 34 | /// 35 | /// This class provides three utilities: 36 | /// 37 | /// 1. Given a string, it will detect if it contains an IP address and which 38 | /// family (IPv4 or IPv6) it contains. 39 | /// 2. Given a string IP address, it will convert the address to its binary 40 | /// representation. 41 | /// 3. Given a binary representation of an IP address, it will convert it 42 | /// to a string. 43 | /// 4. In all cases, it will produce a consistent, normalized string 44 | /// representation of an IP address. 45 | /// 46 | /// Point #4 is particularly important in any scenario where a direct string 47 | /// match is used on an address, such as in the Zenoh URI <-> Topic mapping 48 | /// (https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l1/zenoh.adoc). 49 | struct IpAddress { 50 | /// @brief Describes the type / family of an IP address 51 | enum class Type { 52 | /// @brief For IPv4 family addresses 53 | IpV4, 54 | /// @brief For IPv6 family addresses 55 | IpV6, 56 | /// @brief Used when constructed from a string that is not an IP 57 | Invalid 58 | }; 59 | 60 | /// @brief Constructs an IP address from a string representation of 61 | /// an address. 62 | explicit IpAddress(std::string_view ip_string); 63 | 64 | /// @brief Constructs an IP address from a binary representation of 65 | /// an address. 66 | IpAddress(std::vector const& ip_bytes, Type type); 67 | 68 | /// @brief Get the type of this IP address. 69 | [[nodiscard]] Type getType() const; 70 | 71 | /// @brief Gets the nomalized string representation of this IP address. 72 | [[nodiscard]] const std::string& getString() const; 73 | 74 | /// @brief Gets the binary representation of this IP address. 75 | [[nodiscard]] const std::vector& getBytes() const; 76 | 77 | /// @brief Gets the binary representation of this IP address, wrapped in a 78 | /// string-like container to better interface with protobuf. 79 | /// 80 | /// Protobuf uses std::string as a generic byte container, so this can be 81 | /// useful for embedding compact, binary representations of IP addresses 82 | /// into a protobuf message. 83 | [[nodiscard]] std::string getBytesString() const; 84 | 85 | /// @brief Number of bytes in IPv4 address. 86 | static constexpr uint8_t IP_V4_ADDRESS_BYTES = 4; 87 | 88 | /// @brief Number of bytes in IPv6 address. 89 | static constexpr uint8_t IP_V6_ADDRESS_BYTES = 16; 90 | 91 | private: 92 | /// @brief Updates the state of this instance from the value of the 93 | /// ipString_ field. 94 | void fromString(); 95 | 96 | /// @brief Updates the state of this instance from the value of the 97 | /// ipBytes_ field. 98 | void fromBytes(); 99 | 100 | /// @brief Type of the IP addess contained in this instance. 101 | Type type_{Type::Invalid}; 102 | 103 | /// @brief IP address in byte format. 104 | std::vector ipBytes_{}; 105 | 106 | /// @brief IP address in string format. 107 | std::string ipString_{}; 108 | 109 | /// @name String container compatibility checks 110 | /// @{ 111 | using Bytes = typename std::decay_t::value_type; 112 | using StrBytes = typename std::string_view::value_type; 113 | using StrBytesPtr = typename std::string_view::const_pointer; 114 | 115 | static_assert( 116 | sizeof(StrBytes) == sizeof(Bytes), 117 | "Mismatch in size between string_view value type and ipBytes_ " 118 | "value type invalidates reinterpret_cast used in constructor."); 119 | /// @} 120 | }; 121 | 122 | } // namespace uprotocol::utils 123 | 124 | #endif // UP_CPP_UTILS_IPADDRESS_H 125 | -------------------------------------------------------------------------------- /include/up-cpp/utils/ProtoConverter.h: -------------------------------------------------------------------------------- 1 | #ifndef UP_CPP_UTILS_PROTOCONVERTER_H 2 | #define UP_CPP_UTILS_PROTOCONVERTER_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace uprotocol::utils { 11 | using uprotocol::core::usubscription::v3::SubscribeAttributes; 12 | using uprotocol::core::usubscription::v3::SubscriberInfo; 13 | using uprotocol::core::usubscription::v3::SubscriptionRequest; 14 | using uprotocol::core::usubscription::v3::UnsubscribeRequest; 15 | 16 | struct ProtoConverter { 17 | /// @brief Converts std::chrono::time_point to google::protobuf::Timestamp 18 | /// 19 | /// @param tp the time point to convert 20 | /// @return the converted google::protobuf::Timestamp 21 | static google::protobuf::Timestamp ConvertToProtoTimestamp( 22 | const std::chrono::system_clock::time_point& tp); 23 | 24 | /// @brief Builds a SubscriberInfo from the given parameters 25 | /// 26 | /// @param entity_uri the UUri of the entity subscribing 27 | /// @return the built SubscriberInfo 28 | static SubscriberInfo BuildSubscriberInfo(const v1::UUri& entity_uri); 29 | 30 | /// @brief Builds a SubscribeAttributes from the given parameters 31 | /// 32 | /// @param when_expire the optional time point when the subscription expires 33 | /// @param subscription_details the details of the subscription 34 | /// @param sample_period_ms the optional sample period in milliseconds 35 | /// @return the built SubscribeAttributes 36 | static SubscribeAttributes BuildSubscribeAttributes( 37 | std::optional when_expire, 38 | std::optional subscription_details, 39 | std::optional sample_period_ms); 40 | 41 | /// @brief Builds a SubscriptionRequest from the given parameters 42 | /// 43 | /// @param subscription_topic the UUri of the topic to subscribe to 44 | /// @param attributes the SubscribeAttributes for the subscription 45 | /// @return the built SubscriptionRequest 46 | static SubscriptionRequest BuildSubscriptionRequest( 47 | const v1::UUri& subscription_topic, 48 | std::optional attributes = {}); 49 | 50 | /// @brief Builds a UnsubscribeRequest from the given parameters 51 | /// 52 | /// @param subscription_topic the UUri of the topic to unsubscribe from 53 | /// @return the built UnsubscribeRequest 54 | static UnsubscribeRequest BuildUnSubscribeRequest( 55 | const v1::UUri& subscription_topic); 56 | }; 57 | }; // namespace uprotocol::utils 58 | #endif // UP_CPP_UTILS_PROTOCONVERTER_H 59 | -------------------------------------------------------------------------------- /include/up-cpp/utils/ThreadPool.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_UTILS_THREADPOOL_H 13 | #define UP_CPP_UTILS_THREADPOOL_H 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace uprotocol::utils { 26 | 27 | class ThreadPool { 28 | public: 29 | ThreadPool(const ThreadPool&) = delete; 30 | ThreadPool(ThreadPool&&) = delete; 31 | 32 | ThreadPool& operator=(const ThreadPool&) = delete; 33 | ThreadPool& operator=(ThreadPool&&) = delete; 34 | 35 | ThreadPool(size_t max_queue_size, size_t max_num_of_threads, 36 | std::chrono::milliseconds task_timeout); 37 | 38 | ~ThreadPool(); 39 | 40 | // Submit a function to be executed asynchronously by the pool 41 | template 42 | auto submit(F&& f, Args&&... args) -> std::future; 43 | 44 | private: 45 | CyclicQueue> queue_; 46 | bool terminate_; 47 | size_t maxNumOfThreads_; 48 | std::atomic numOfThreads_; 49 | std::vector> threads_; 50 | std::mutex mutex_; 51 | const std::chrono::milliseconds timeout_; 52 | }; 53 | } // namespace uprotocol::utils 54 | 55 | #endif // UP_CPP_UTILS_THREADPOOL_H 56 | -------------------------------------------------------------------------------- /include/up-cpp/utils/base64.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UP_CPP_UTILS_BASE64_H 13 | #define UP_CPP_UTILS_BASE64_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | /// @brief Utilities for encoding / decoding data in Base 64 format. 21 | namespace uprotocol::utils::base64 { 22 | 23 | /// @brief Encode a string of bytes to base64. 24 | std::string encode(std::string_view); 25 | 26 | /// @brief Encode a vector of bytes to base64. 27 | std::string encode(const std::vector&); 28 | 29 | /// @brief Decode a base64 string to a string of bytes. 30 | std::string decodeAsString(std::string_view); 31 | 32 | /// @brief Decode a base64 string to a vector of bytes. 33 | std::vector decodeAsBytes(std::string_view); 34 | 35 | /// @brief Given a string of bytes, calculate the length needed for a 36 | /// base64 encoding of that string. 37 | size_t encodedLen(std::string_view); 38 | 39 | /// @brief Given a vector of bytes, calculate the length needed for a 40 | /// base64 encoding of that vector. 41 | size_t encodedLen(std::vector); 42 | 43 | /// @brief Given a base64 encoded string, calculate the length of the 44 | /// decoded data. 45 | size_t decodedLen(std::string_view); 46 | 47 | } // namespace uprotocol::utils::base64 48 | 49 | #endif // UP_CPP_UTILS_BASE64_H 50 | -------------------------------------------------------------------------------- /lint/clang-format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROJECT_ROOT="$(realpath "$(dirname "$0")/../")" 4 | 5 | if [ -n "$(which clang-format-13)" ]; then 6 | # NOTE: Using clang-format-13 in CI system, too 7 | FORMATTER=clang-format-13 8 | elif [ -n "$(which clang-format)" ]; then 9 | echo "Did not find clang-format-13. Trying clang-format. Results may not" 10 | echo "match formatting in GitHub CI process." 11 | FORMATTER=clang-format 12 | else 13 | echo "Could not find clang-format. Please make sure it is installed" 1>&2 14 | exit 2 15 | fi 16 | 17 | echo "Running $FORMATTER on all files in '$PROJECT_ROOT'" 18 | shopt -s globstar 19 | 20 | pushd "$PROJECT_ROOT" > /dev/null 21 | for f in **/*.h **/*.cpp; do 22 | if [[ ! ("$f" =~ "build/") ]]; then 23 | echo 24 | echo "Checking file '$f'" 25 | $FORMATTER -i "$f" 26 | fi 27 | done 28 | popd > /dev/null 29 | -------------------------------------------------------------------------------- /lint/clang-tidy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | PROJECT_ROOT="$(realpath "$(dirname "$0")/../")" 4 | 5 | if [ -n "$(which clang-tidy-13)" ]; then 6 | # NOTE: Using clang-tidy-13 in CI system, too 7 | LINTER=clang-tidy-13 8 | elif [ -n "$(which clang-tidy)" ]; then 9 | echo "Did not find clang-tidy-13. Trying clang-tidy. Results may not" 10 | echo "match formatting in GitHub CI process." 11 | LINTER=clang-tidy 12 | else 13 | echo "Could not find clang-tidy. Please make sure it is installed" 1>&2 14 | exit 2 15 | fi 16 | 17 | usage() { 18 | echo "$(basename "$0") path/to/compile_commands.json [source_to_lint]" 1>&2 19 | echo 1>&2 20 | echo " compile_commands.json" 1>&2 21 | echo " Produced during a cmake build when configured with the" 1>&2 22 | echo " -DCMAKE_EXPORT_COMPILE_COMMANDS=yes flag" 1>&2 23 | echo 1>&2 24 | echo " source_to_lint (optional)" 1>&2 25 | echo " Source file to run clang-tidy against. If not specified," 1>&2 26 | echo " all source files in the repo will be scanned." 1>&2 27 | } 28 | 29 | if [ "$1" == "-h" ] || [ "$1" == "--help" ] || [ "$1" == "/h" ] || [ "$1" == "/?" ]; then 30 | usage 31 | exit 32 | fi 33 | 34 | compile_database="$1" 35 | 36 | if [ -z "$compile_database" ]; then 37 | echo "No compile database specified. Make sure cmake was configured" 1>&2 38 | echo "with '-DCMAKE_EXPORT_COMPILE_COMMANDS=yes' and re-run the build" 1>&2 39 | echo 1>&2 40 | echo "Usage:" 1>&2 41 | usage 42 | exit 1 43 | elif [ ! -f "$compile_database" ]; then 44 | echo "Compile database file not found. Make sure cmake was configured" 1>&2 45 | echo "with '-DCMAKE_EXPORT_COMPILE_COMMANDS=yes' and re-run the build" 1>&2 46 | echo 1>&2 47 | echo "Usage:" 1>&2 48 | usage 49 | exit 1 50 | fi 51 | 52 | compile_database="$(realpath "$compile_database")" 53 | 54 | target_source="$2" 55 | 56 | if [ -z "$target_source" ]; then 57 | echo "Running $LINTER on all files in '$PROJECT_ROOT'" 58 | shopt -s globstar 59 | 60 | pushd "$PROJECT_ROOT" > /dev/null 61 | for f in include/**/*.h src/**/*.cpp test/coverage/**/*.cpp test/extra/**/*.cpp test/include/**/*.h; do 62 | if [[ ! ("$f" =~ "build/") ]]; then 63 | echo 64 | echo "Checking file '$f'" 65 | $LINTER -p "$(dirname "$compile_database")" "$f" 66 | fi 67 | done 68 | popd > /dev/null 69 | exit 70 | fi 71 | 72 | if [ ! -f "$target_source" ]; then 73 | echo "Target source file '$target_source' not found." 1>&2 74 | echo 1>&2 75 | echo "Usage:" 1>&2 76 | usage 77 | exit 1 78 | fi 79 | 80 | echo "Running $LINTER on '$target_source'" 81 | $LINTER -p "$(dirname "$compile_database")" "$target_source" 82 | -------------------------------------------------------------------------------- /lint/valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_binary=$1 4 | 5 | if [ ! -x "$test_binary" ]; then 6 | if [ -e "$test_binary" ]; then 7 | echo "Specified test binary is not executable" 1>&2 8 | else 9 | echo "Specified test binary does not exist" 1>&2 10 | fi 11 | fi 12 | 13 | echo "LEAK CHECK" 1>&2 14 | echo "==========" 1>&2 15 | valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./$test_binary --gtest_brief=1 > /dev/null 16 | echo 1>&2 17 | 18 | echo "DRD" 1>&2 19 | echo "===" 1>&2 20 | valgrind --tool=drd ./$test_binary --gtest_brief=1 > /dev/null 21 | echo 1>&2 22 | 23 | echo "HELGRIND" 1>&2 24 | echo "========" 1>&2 25 | valgrind --tool=helgrind ./$test_binary --gtest_brief=1 > /dev/null 26 | echo 1>&2 27 | 28 | echo "DHAT" 1>&2 29 | echo "====" 1>&2 30 | valgrind --tool=dhat ./$test_binary --gtest_brief=1 > /dev/null 31 | -------------------------------------------------------------------------------- /src/client/usubscription/v3/Consumer.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | 14 | #include 15 | 16 | namespace uprotocol::client::usubscription::v3 { 17 | 18 | Consumer::Consumer(std::shared_ptr transport, 19 | v1::UUri subscription_topic, 20 | ConsumerOptions consumer_options) 21 | : transport_(std::move(transport)), 22 | subscription_topic_(std::move(subscription_topic)), 23 | consumer_options_(std::move(consumer_options)), 24 | rpc_client_(nullptr) { 25 | // Initialize uSubscriptionUUriBuilder_ 26 | uSubscriptionUUriBuilder_ = USubscriptionUUriBuilder(); 27 | } 28 | 29 | [[nodiscard]] Consumer::ConsumerOrStatus Consumer::create( 30 | std::shared_ptr transport, 31 | const v1::UUri& subscription_topic, ListenCallback&& callback, 32 | v1::UPriority priority, std::chrono::milliseconds subscription_request_ttl, 33 | ConsumerOptions consumer_options) { 34 | auto consumer = std::make_unique( 35 | std::forward>(transport), 36 | std::forward(subscription_topic), 37 | std::forward(consumer_options)); 38 | 39 | // Attempt to connect create notification sink for updates. 40 | auto status = consumer->createNotificationSink(); 41 | if (status.code() == v1::UCode::OK) { 42 | status = consumer->subscribe(priority, subscription_request_ttl, 43 | std::move(callback)); 44 | if (status.code() == v1::UCode::OK) { 45 | return ConsumerOrStatus(std::move(consumer)); 46 | } 47 | return ConsumerOrStatus(utils::Unexpected(status)); 48 | } 49 | // If connection fails, return the error status. 50 | return ConsumerOrStatus(utils::Unexpected(status)); 51 | } 52 | 53 | v1::UStatus Consumer::createNotificationSink() { 54 | auto notification_sink_callback = [this](const v1::UMessage& update) { 55 | if (update.has_payload()) { 56 | Update data; 57 | if (data.ParseFromString(update.payload())) { 58 | if (data.topic().SerializeAsString() == 59 | subscription_topic_.SerializeAsString()) { 60 | subscription_update_ = std::move(data); 61 | } 62 | } 63 | } 64 | }; 65 | 66 | auto notification_topic = uSubscriptionUUriBuilder_.getNotificationUri(); 67 | 68 | auto result = communication::NotificationSink::create( 69 | transport_, std::move(notification_sink_callback), notification_topic); 70 | 71 | if (result.has_value()) { 72 | noficationSinkHandle_ = std::move(result).value(); 73 | v1::UStatus status; 74 | status.set_code(v1::UCode::OK); 75 | return status; 76 | } 77 | return result.error(); 78 | } 79 | 80 | SubscriptionRequest Consumer::buildSubscriptionRequest() { 81 | auto attributes = utils::ProtoConverter::BuildSubscribeAttributes( 82 | consumer_options_.when_expire, consumer_options_.subscription_details, 83 | consumer_options_.sample_period_ms); 84 | 85 | auto subscription_request = utils::ProtoConverter::BuildSubscriptionRequest( 86 | subscription_topic_, attributes); 87 | return subscription_request; 88 | } 89 | 90 | v1::UStatus Consumer::subscribe( 91 | v1::UPriority priority, std::chrono::milliseconds subscription_request_ttl, 92 | ListenCallback&& callback) { 93 | rpc_client_ = std::make_unique( 94 | transport_, uSubscriptionUUriBuilder_.getServiceUriWithResourceId(1), 95 | priority, subscription_request_ttl); 96 | 97 | auto on_response = [this](const auto& maybe_response) { 98 | if (maybe_response.has_value() && 99 | maybe_response.value().has_payload()) { 100 | SubscriptionResponse response; 101 | if (response.ParseFromString(maybe_response.value().payload())) { 102 | if (response.topic().SerializeAsString() == 103 | subscription_topic_.SerializeAsString()) { 104 | subscription_response_ = response; 105 | } 106 | } 107 | } 108 | }; 109 | 110 | SubscriptionRequest const subscription_request = buildSubscriptionRequest(); 111 | auto payload = datamodel::builder::Payload(subscription_request); 112 | 113 | rpc_handle_ = 114 | rpc_client_->invokeMethod(std::move(payload), std::move(on_response)); 115 | 116 | // Create a L2 subscription 117 | auto result = communication::Subscriber::subscribe( 118 | transport_, subscription_topic_, std::move(callback)); 119 | 120 | if (result.has_value()) { 121 | subscriber_ = std::move(result).value(); 122 | v1::UStatus status; 123 | status.set_code(v1::UCode::OK); 124 | return status; 125 | } 126 | return result.error(); 127 | } 128 | 129 | UnsubscribeRequest Consumer::buildUnsubscriptionRequest() { 130 | auto unsubscribe_request = 131 | utils::ProtoConverter::BuildUnSubscribeRequest(subscription_topic_); 132 | return unsubscribe_request; 133 | } 134 | 135 | void Consumer::unsubscribe(v1::UPriority priority, 136 | std::chrono::milliseconds request_ttl) { 137 | rpc_client_ = std::make_unique( 138 | transport_, uSubscriptionUUriBuilder_.getServiceUriWithResourceId(2), 139 | priority, request_ttl); 140 | 141 | auto on_response = [](const auto& maybe_response) { 142 | if (!maybe_response.has_value()) { 143 | // Do something as this means sucessfully unsubscribed. 144 | } 145 | }; 146 | 147 | UnsubscribeRequest const unsubscribe_request = buildUnsubscriptionRequest(); 148 | auto payload = datamodel::builder::Payload(unsubscribe_request); 149 | 150 | rpc_handle_ = 151 | rpc_client_->invokeMethod(std::move(payload), std::move(on_response)); 152 | 153 | subscriber_.reset(); 154 | } 155 | 156 | } // namespace uprotocol::client::usubscription::v3 -------------------------------------------------------------------------------- /src/communication/NotificationSink.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/communication/NotificationSink.h" 13 | 14 | #include 15 | 16 | #include "up-cpp/datamodel/validator/UUri.h" 17 | 18 | namespace uprotocol::communication { 19 | namespace UriValidator = datamodel::validator::uri; 20 | 21 | NotificationSink::SinkOrStatus NotificationSink::create( 22 | const std::shared_ptr& transport, 23 | ListenCallback&& callback, const uprotocol::v1::UUri& source_filter) { 24 | // Standard check - transport pointer cannot be null 25 | if (!transport) { 26 | throw transport::NullTransport("transport cannot be null"); 27 | } 28 | 29 | // NOTE: isValidSubscription() is documented as 30 | // "valid ... as a source filter when subscribing to a notification" 31 | auto [source_ok, bad_source_reason] = 32 | UriValidator::isValidSubscription(source_filter); 33 | if (!source_ok) { 34 | throw UriValidator::InvalidUUri( 35 | "Source filter is not a valid notification source URI | " + 36 | std::string(UriValidator::message(*bad_source_reason))); 37 | } 38 | 39 | auto listener = transport->registerListener( 40 | std::move(callback), source_filter, transport->getEntityUri()); 41 | 42 | if (!listener) { 43 | return SinkOrStatus(utils::Unexpected(listener.error())); 44 | } 45 | 46 | return SinkOrStatus(std::make_unique( 47 | transport, std::forward(std::move(listener).value()))); 48 | } 49 | 50 | // NOTE: deprecated 51 | NotificationSink::SinkOrStatus NotificationSink::create( 52 | const std::shared_ptr& transport, 53 | const uprotocol::v1::UUri& sink, ListenCallback&& callback, 54 | std::optional&& source_filter) { 55 | // Standard check - transport pointer cannot be null 56 | if (!transport) { 57 | throw transport::NullTransport("transport cannot be null"); 58 | } 59 | 60 | // With the adjustments to the API to match 1.6.0, the primary filter for 61 | // a notification is the _source_ URI. The sink will always be this entity's 62 | // URI from the transport. 63 | if (!source_filter) { 64 | throw UriValidator::InvalidUUri("Source filter must be provided"); 65 | } 66 | 67 | using MessageDifferencer = google::protobuf::util::MessageDifferencer; 68 | 69 | if (!MessageDifferencer::Equals(sink, transport->getEntityUri())) { 70 | throw UriValidator::InvalidUUri( 71 | "Sink filter must match transport->getEntityUri()"); 72 | } 73 | 74 | return create(transport, std::move(callback), *source_filter); 75 | } 76 | 77 | NotificationSink::NotificationSink( 78 | std::shared_ptr transport, 79 | NotificationSink::ListenHandle&& listener) 80 | : transport_(std::move(transport)), listener_(std::move(listener)) {} 81 | 82 | } // namespace uprotocol::communication 83 | -------------------------------------------------------------------------------- /src/communication/NotificationSource.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/communication/NotificationSource.h" 13 | 14 | namespace uprotocol::communication { 15 | 16 | using uprotocol::datamodel::builder::UMessageBuilder; 17 | 18 | NotificationSource::NotificationSource( 19 | std::shared_ptr transport, v1::UUri&& source, 20 | v1::UUri&& sink, std::optional payload_format, 21 | std::optional priority, 22 | std::optional ttl) 23 | : transport_(std::move(transport)), 24 | notify_builder_( 25 | UMessageBuilder::notification(std::move(source), std::move(sink)) 26 | .withPriority(priority.value_or(v1::UPriority::UPRIORITY_CS1))) { 27 | if (!transport_) { 28 | throw transport::NullTransport("transport cannot be null"); 29 | } 30 | 31 | if (payload_format.has_value()) { 32 | notify_builder_.withPayloadFormat(payload_format.value()); 33 | } 34 | 35 | if (ttl.has_value()) { 36 | notify_builder_.withTtl(ttl.value()); 37 | } 38 | } 39 | 40 | v1::UStatus NotificationSource::notify( 41 | datamodel::builder::Payload&& payload) const { 42 | auto message = notify_builder_.build(std::move(payload)); 43 | 44 | return transport_->send(message); 45 | } 46 | 47 | v1::UStatus NotificationSource::notify() const { 48 | auto message = notify_builder_.build(); 49 | if (!transport_) { 50 | throw transport::NullTransport("transport cannot be null"); 51 | } 52 | 53 | return transport_->send(message); 54 | } 55 | 56 | } // namespace uprotocol::communication -------------------------------------------------------------------------------- /src/communication/Publisher.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/communication/Publisher.h" 13 | 14 | #include 15 | 16 | namespace uprotocol::communication { 17 | using uprotocol::datamodel::builder::UMessageBuilder; 18 | 19 | Publisher::Publisher(std::shared_ptr transport, 20 | v1::UUri&& topic, v1::UPayloadFormat format, 21 | std::optional priority, 22 | std::optional ttl) 23 | : transport_(std::move(transport)), 24 | publish_builder_(UMessageBuilder::publish(std::move(topic))) { 25 | publish_builder_.withPayloadFormat(format).withPriority( 26 | priority.value_or(v1::UPriority::UPRIORITY_CS1)); 27 | 28 | if (!transport_) { 29 | throw transport::NullTransport("transport cannot be null"); 30 | } 31 | 32 | if (ttl.has_value()) { 33 | publish_builder_.withTtl(ttl.value()); 34 | } 35 | } 36 | 37 | v1::UStatus Publisher::publish(datamodel::builder::Payload&& payload) const { 38 | auto message = publish_builder_.build(std::move(payload)); 39 | if (!transport_) { 40 | throw transport::NullTransport("transport cannot be null"); 41 | } 42 | 43 | return transport_->send(message); 44 | } 45 | 46 | } // namespace uprotocol::communication -------------------------------------------------------------------------------- /src/communication/RpcServer.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/communication/RpcServer.h" 13 | 14 | namespace uprotocol::communication { 15 | 16 | namespace Validator = datamodel::validator; 17 | 18 | RpcServer::RpcServer(std::shared_ptr transport, 19 | std::optional format, 20 | std::optional ttl) 21 | : transport_(std::move(transport)), 22 | ttl_(ttl), 23 | expected_payload_format_(format) { 24 | if (!transport_) { 25 | throw transport::NullTransport("transport cannot be null"); 26 | } 27 | } 28 | 29 | RpcServer::ServerOrStatus RpcServer::create( 30 | std::shared_ptr transport, 31 | const v1::UUri& method_name, RpcCallback&& callback, 32 | std::optional payload_format, 33 | std::optional ttl) { 34 | // Validate the method name using a URI validator. 35 | auto [valid, reason] = Validator::uri::isValidRpcMethod(method_name); 36 | 37 | if (!transport) { 38 | throw transport::NullTransport("transport cannot be null"); 39 | } 40 | 41 | if (!valid) { 42 | // If the method name is invalid, return an error status. 43 | v1::UStatus status; 44 | status.set_code(v1::UCode::INVALID_ARGUMENT); 45 | status.set_message("Invalid rpc URI"); 46 | return ServerOrStatus(utils::Unexpected(status)); 47 | } 48 | 49 | // Validate the payload format, if provided. 50 | if (payload_format.has_value()) { 51 | if (!UPayloadFormat_IsValid(payload_format.value())) { 52 | // If the payload format is invalid, return an error status. 53 | v1::UStatus status; 54 | status.set_code(v1::UCode::OUT_OF_RANGE); 55 | status.set_message("Invalid payload format"); 56 | return ServerOrStatus(utils::Unexpected(status)); 57 | } 58 | } 59 | 60 | // Create the RpcServer instance with the provided parameters. 61 | auto server = std::make_unique( 62 | std::forward>(transport), 63 | std::forward>(payload_format), 64 | std::forward>(ttl)); 65 | 66 | // Attempt to connect the server with the provided method name and callback. 67 | auto status = server->connect(method_name, std::move(callback)); 68 | if (status.code() == v1::UCode::OK) { 69 | // If connection is successful, return the server instance. 70 | return ServerOrStatus(std::move(server)); 71 | } 72 | // If connection fails, return the error status. 73 | return ServerOrStatus(utils::Unexpected(status)); 74 | } 75 | 76 | v1::UStatus RpcServer::connect(const v1::UUri& method, RpcCallback&& callback) { 77 | callback_ = std::move(callback); 78 | 79 | if (!transport_) { 80 | throw transport::NullTransport("transport cannot be null"); 81 | } 82 | 83 | auto result = transport_->registerListener( 84 | // listener= 85 | [this](const v1::UMessage& request) { 86 | // Validate the request message using a RPC message validator. 87 | auto [valid, reason] = 88 | Validator::message::isValidRpcRequest(request); 89 | if (!valid) { 90 | return; 91 | } 92 | 93 | // Create a response message builder using the request message. 94 | auto builder = 95 | datamodel::builder::UMessageBuilder::response(request); 96 | 97 | // Call the RPC callback method with the request message. 98 | auto payload_data = callback_(request); 99 | 100 | if (ttl_.has_value()) { 101 | builder.withTtl(ttl_.value()); 102 | } 103 | 104 | if (expected_payload_format_.has_value()) { 105 | builder.withPayloadFormat(expected_payload_format_.value()); 106 | } 107 | 108 | // Check for payload data requirement based on expected format 109 | // presence 110 | if (!payload_data.has_value()) { 111 | // builder.build() verifies if payload format is required 112 | auto response = builder.build(); 113 | // Ignoring status code for transport send 114 | std::ignore = transport_->send(response); 115 | } else { 116 | // builder.build(payloadData) verifies if payload format 117 | // matches the expected 118 | auto response = builder.build(std::move(payload_data).value()); 119 | // Ignoring status code for transport send 120 | std::ignore = transport_->send(response); 121 | } 122 | }, 123 | // source_filter= 124 | []() { 125 | v1::UUri any_uri; 126 | any_uri.set_authority_name("*"); 127 | // Instance ID FFFF and UE ID FFFF for wildcard 128 | constexpr auto WILDCARD_INSTANCE_ID_WITH_WILDCARD_SERVICE_ID = 129 | 0xFFFFFFFF; 130 | constexpr auto VERSION_MAJOR_WILDCARD = 0xFF; 131 | constexpr auto RESOURCE_ID_WILDCARD = 0xFFFF; 132 | any_uri.set_ue_id(WILDCARD_INSTANCE_ID_WITH_WILDCARD_SERVICE_ID); 133 | any_uri.set_ue_version_major(VERSION_MAJOR_WILDCARD); 134 | any_uri.set_resource_id(RESOURCE_ID_WILDCARD); 135 | return any_uri; 136 | }(), 137 | // sink_filter= 138 | method); 139 | 140 | if (result.has_value()) { 141 | // If result is successful, extract the UTransport::ListenHandle and 142 | // assign it to callback_handle_ 143 | callback_handle_ = std::move(result).value(); 144 | v1::UStatus status; 145 | status.set_code(v1::UCode::OK); 146 | return status; 147 | } 148 | return result.error(); 149 | } 150 | 151 | } // namespace uprotocol::communication 152 | -------------------------------------------------------------------------------- /src/communication/Subscriber.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/communication/Subscriber.h" 13 | 14 | #include 15 | 16 | #include "up-cpp/datamodel/validator/UUri.h" 17 | 18 | namespace uprotocol::communication { 19 | namespace uri_validator = uprotocol::datamodel::validator::uri; 20 | 21 | [[nodiscard]] Subscriber::SubscriberOrStatus Subscriber::subscribe( 22 | std::shared_ptr transport, const v1::UUri& topic, 23 | ListenCallback&& callback) { 24 | // Standard check - transport pointer cannot be null 25 | if (!transport) { 26 | throw transport::NullTransport("transport cannot be null"); 27 | } 28 | 29 | auto [source_ok, bad_source_reason] = 30 | uri_validator::isValidSubscription(topic); 31 | if (!source_ok) { 32 | throw uri_validator::InvalidUUri( 33 | "Topic URI is not a valid topic subscription pattern | " + 34 | std::string(uri_validator::message(*bad_source_reason))); 35 | } 36 | 37 | auto handle = transport->registerListener(std::move(callback), topic); 38 | 39 | if (!handle) { 40 | return SubscriberOrStatus( 41 | utils::Unexpected(handle.error())); 42 | } 43 | 44 | return SubscriberOrStatus(std::make_unique( 45 | std::forward>(transport), 46 | std::forward(std::move(handle).value()))); 47 | } 48 | 49 | Subscriber::Subscriber(std::shared_ptr transport, 50 | ListenHandle&& subscription) 51 | : transport_(std::move(transport)), subscription_(std::move(subscription)) { 52 | // Constructor body. Any additional setup can go here. 53 | if (!transport_) { 54 | throw transport::NullTransport("transport cannot be null"); 55 | } 56 | } 57 | 58 | } // namespace uprotocol::communication 59 | -------------------------------------------------------------------------------- /src/datamodel/builder/Payload.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/datamodel/builder/Payload.h" 13 | namespace uprotocol::datamodel::builder { 14 | 15 | // Byte vector constructor 16 | Payload::Payload(const std::vector& value_bytes, 17 | const v1::UPayloadFormat format) { 18 | if (!UPayloadFormat_IsValid(format)) { 19 | throw std::out_of_range("Invalid Byte vector payload format"); 20 | } 21 | std::string serialized(value_bytes.begin(), value_bytes.end()); 22 | payload_ = std::make_tuple(std::move(serialized), format); 23 | } 24 | 25 | // String constructor 26 | Payload::Payload(const std::string& value, const v1::UPayloadFormat format) { 27 | if (!UPayloadFormat_IsValid(format)) { 28 | throw std::out_of_range("Invalid String payload format"); 29 | } 30 | payload_ = std::make_tuple(value, format); 31 | } 32 | 33 | // Move string constructor 34 | Payload::Payload(std::string&& value, const v1::UPayloadFormat format) { 35 | if (!UPayloadFormat_IsValid(format)) { 36 | throw std::out_of_range("Invalid RValue String payload format"); 37 | } 38 | payload_ = std::make_tuple(std::move(value), format); 39 | } 40 | 41 | // Move Serialized constructor 42 | Payload::Payload(Serialized&& serialized) { 43 | if (!UPayloadFormat_IsValid(std::get(serialized))) { 44 | throw std::out_of_range("Invalid RValue Serialized payload format"); 45 | } 46 | payload_ = std::move(serialized); 47 | } 48 | 49 | // google::protobuf::Any constructor 50 | Payload::Payload(const google::protobuf::Any& any) { 51 | payload_ = std::make_tuple( 52 | any.SerializeAsString(), 53 | uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); 54 | } 55 | 56 | // Move constructor 57 | Payload::Payload(Payload&& other) noexcept 58 | : payload_(std::move(other.payload_)), moved_(other.moved_) {} 59 | 60 | // Copy constructor 61 | Payload::Payload(const Payload& other) = default; 62 | 63 | // Move assignment operator 64 | Payload& Payload::operator=(Payload&& other) noexcept { 65 | payload_ = std::move(other.payload_); 66 | moved_ = other.moved_; 67 | return *this; 68 | } 69 | 70 | // Copy assignment operator 71 | Payload& Payload::operator=(const Payload& other) = default; 72 | 73 | Payload::PayloadMoved::PayloadMoved(PayloadMoved&& other) noexcept 74 | : std::runtime_error(std::move(other)) {} 75 | 76 | // PayloadMoved move assignment operator 77 | Payload::PayloadMoved& Payload::PayloadMoved::operator=( 78 | PayloadMoved&& other) noexcept { 79 | std::runtime_error::operator=(std::move(other)); 80 | return *this; 81 | } 82 | 83 | // PayloadMoved copy constructor 84 | Payload::PayloadMoved::PayloadMoved(const PayloadMoved& other) = default; 85 | 86 | // PayloadMoved copy assignment operator 87 | Payload::PayloadMoved& Payload::PayloadMoved::operator=( 88 | const PayloadMoved& other) = default; 89 | 90 | // buildCopy method 91 | [[nodiscard]] const Payload::Serialized& Payload::buildCopy() const { 92 | if (moved_) { 93 | throw PayloadMoved("Payload has been already moved"); 94 | } 95 | return payload_; 96 | } 97 | 98 | // buildMove method 99 | [[nodiscard]] Payload::Serialized Payload::buildMove() && { 100 | // Check if payload_ is in the "moved" state 101 | if (moved_) { 102 | throw PayloadMoved("Payload has been already moved"); 103 | } 104 | // Set payload_ to the "moved" state 105 | moved_ = true; 106 | return std::move(payload_); 107 | } 108 | } // namespace uprotocol::datamodel::builder 109 | -------------------------------------------------------------------------------- /src/datamodel/builder/Uuid.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/datamodel/builder/Uuid.h" 13 | 14 | #include 15 | #include 16 | 17 | #include "up-cpp/datamodel/constants/UuidConstants.h" 18 | 19 | namespace uprotocol::datamodel::builder { 20 | 21 | /* 22 | struct uuidbuilder::uuidsharedstate { 23 | std::mt19937_64 random_engine{std::random_device{}()}; 24 | }; 25 | */ 26 | 27 | UuidBuilder UuidBuilder::getBuilder() { return UuidBuilder(false); } 28 | 29 | UuidBuilder UuidBuilder::getTestBuilder() { return UuidBuilder(true); } 30 | 31 | UuidBuilder& UuidBuilder::withTimeSource( 32 | std::function&& time_source) { 33 | if (!testing_) { 34 | throw std::domain_error( 35 | "Cannot set time source on non-test UuidBuilder"); 36 | } 37 | time_source_ = std::move(time_source); 38 | return *this; 39 | } 40 | 41 | UuidBuilder& UuidBuilder::withRandomSource( 42 | std::function&& random_source) { 43 | if (!testing_) { 44 | throw std::domain_error( 45 | "Cannot set random source on non-test UuidBuilder"); 46 | } 47 | random_source_ = std::move(random_source); 48 | return *this; 49 | } 50 | 51 | v1::UUID UuidBuilder::build() { 52 | v1::UUID uuid; 53 | auto now = time_source_ ? time_source_() : std::chrono::system_clock::now(); 54 | auto unix_ts_ms = 55 | std::chrono::time_point_cast(now); 56 | std::mt19937_64 gen{std::random_device{}()}; 57 | std::uniform_int_distribution dist_a(0, UUID_RANDOM_A_MASK); 58 | std::uniform_int_distribution dist_b(0, UUID_RANDOM_B_MASK); 59 | 60 | uint64_t msb = static_cast(unix_ts_ms.time_since_epoch().count()) 61 | << UUID_TIMESTAMP_SHIFT; 62 | msb |= static_cast(UUID_VERSION_7) << UUID_VERSION_SHIFT; 63 | msb |= 64 | (random_source_ ? random_source_() : dist_a(gen)) & UUID_RANDOM_A_MASK; 65 | 66 | uint64_t lsb = 67 | (random_source_ ? random_source_() : dist_b(gen)) & UUID_RANDOM_B_MASK; 68 | // set the Variant to 10b 69 | lsb |= static_cast(UUID_VARIANT_RFC4122) << UUID_VARIANT_SHIFT; 70 | 71 | uuid.set_msb(msb); 72 | uuid.set_lsb(lsb); 73 | return uuid; 74 | } 75 | 76 | UuidBuilder::UuidBuilder(bool testing) 77 | : testing_(testing), time_source_(nullptr), random_source_(nullptr) {} 78 | 79 | } // namespace uprotocol::datamodel::builder -------------------------------------------------------------------------------- /src/datamodel/serializer/UUri.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | #include "up-cpp/datamodel/serializer/UUri.h" 12 | 13 | #include 14 | #include 15 | 16 | #include "up-cpp/datamodel/validator/UUri.h" 17 | 18 | namespace uprotocol::datamodel::serializer::uri { 19 | 20 | std::string AsString::serialize(const v1::UUri& uri) { 21 | using uprotocol::datamodel::validator::uri::InvalidUUri; 22 | using uprotocol::datamodel::validator::uri::isLocal; 23 | using uprotocol::datamodel::validator::uri::isValidFilter; 24 | 25 | // isValidFilter is the most permissive of the validators 26 | auto [valid, reason] = isValidFilter(uri); 27 | if (!valid) { 28 | throw InvalidUUri("Invalid UUri For Serialization | " + 29 | std::string(message(*reason))); 30 | } 31 | std::stringstream ss; 32 | ss << std::hex << std::uppercase; 33 | 34 | if (!isLocal(uri)) { 35 | ss << "//" << uri.authority_name(); 36 | } 37 | ss << "/" << uri.ue_id() << "/" << uri.ue_version_major() << "/" 38 | << uri.resource_id(); 39 | 40 | return std::move(ss).str(); 41 | } 42 | 43 | std::string_view extractSegment(std::string_view& uri_view) { 44 | constexpr std::string_view SEGMENT_SEPARATOR = "/"; 45 | const auto end = uri_view.find(SEGMENT_SEPARATOR); 46 | if (end == std::string_view::npos) { 47 | throw std::invalid_argument("Could not extract segment from '" + 48 | std::string(uri_view) + 49 | "' with separator '" + "/" + "'"); 50 | } 51 | auto segment = uri_view.substr(0, end); 52 | uri_view = uri_view.substr(end + 1); 53 | return segment; 54 | } 55 | 56 | uint32_t segmentToUint32(const std::string_view& segment) { 57 | uint32_t value = 0; 58 | constexpr auto HEX_BASE = 16; 59 | auto [end, ec] = std::from_chars( 60 | segment.data(), segment.data() + segment.size(), value, HEX_BASE); 61 | const bool convert_ok = 62 | (ec == std::errc{}) && (end == segment.data() + segment.size()); 63 | if (!convert_ok) { 64 | throw std::invalid_argument("Failed to convert segment to number: " + 65 | std::string(segment)); 66 | } 67 | return value; 68 | } 69 | 70 | uprotocol::v1::UUri AsString::deserialize(const std::string& uri_as_string) { 71 | if (uri_as_string.empty()) { 72 | throw std::invalid_argument("Cannot deserialize empty string"); 73 | } 74 | 75 | constexpr std::string_view SCHEMA_PREFIX = "up://"; 76 | constexpr std::string_view REMOTE_PREFIX = "//"; 77 | constexpr std::string_view SEGMENT_SEPARATOR = "/"; 78 | 79 | // Operating on a string view to avoid copies and reallocations 80 | std::string_view uri_view(uri_as_string); 81 | 82 | // Extract and convert the rest of the URI string 83 | v1::UUri uri; 84 | 85 | // Verify start and extract Authority, if present 86 | // With up:// schema 87 | if (uri_view.substr(0, SCHEMA_PREFIX.size()) == SCHEMA_PREFIX) { 88 | // Advance past the prefix 89 | uri_view = uri_view.substr(SCHEMA_PREFIX.size()); 90 | uri.set_authority_name(std::string(extractSegment(uri_view))); 91 | // with // remote prefix 92 | } else if (uri_view.substr(0, REMOTE_PREFIX.size()) == REMOTE_PREFIX) { 93 | // Advance past the prefix 94 | uri_view = uri_view.substr(REMOTE_PREFIX.size()); 95 | uri.set_authority_name(std::string(extractSegment(uri_view))); 96 | 97 | // with / local prefix 98 | } else if (uri_view.substr(0, SEGMENT_SEPARATOR.size()) == 99 | SEGMENT_SEPARATOR) { 100 | // Advance past the prefix 101 | uri_view = uri_view.substr(SEGMENT_SEPARATOR.size()); 102 | 103 | // Missing required prefix 104 | } else { 105 | throw std::invalid_argument( 106 | "Did not find expected URI start in string: '" + 107 | std::string(uri_view) + "'"); 108 | } 109 | uri.set_ue_id(segmentToUint32(extractSegment(uri_view))); 110 | uri.set_ue_version_major(segmentToUint32(extractSegment(uri_view))); 111 | uri.set_resource_id(segmentToUint32(uri_view)); 112 | 113 | { 114 | using uprotocol::datamodel::validator::uri::InvalidUUri; 115 | using uprotocol::datamodel::validator::uri::isValidFilter; 116 | // isValidFilter is the most permissive of the validators 117 | auto [valid, reason] = isValidFilter(uri); 118 | if (!valid) { 119 | throw InvalidUUri("Invalid UUri For DeSerialization | " + 120 | std::string(message(*reason))); 121 | } 122 | } 123 | return uri; 124 | } 125 | } // namespace uprotocol::datamodel::serializer::uri 126 | -------------------------------------------------------------------------------- /src/datamodel/serializer/Uuid.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/datamodel/serializer/Uuid.h" 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "up-cpp/datamodel/constants/UuidConstants.h" 21 | 22 | namespace { 23 | constexpr uint64_t RANDOM_B_SHIFT = 48; 24 | constexpr size_t MSB_HIGH = 0; 25 | constexpr size_t MSB_LOW = 4; 26 | constexpr size_t LSB_HIGH = 8; 27 | constexpr size_t LSB_LOW = 12; 28 | } // namespace 29 | 30 | namespace uprotocol::datamodel::serializer::uuid { 31 | 32 | std::string AsString::serialize(const uprotocol::v1::UUID& uuid) { 33 | // Extracting the parts of the UUIDv7 34 | uint64_t unix_ts_ms = 35 | (uuid.msb() >> UUID_TIMESTAMP_SHIFT) & UUID_TIMESTAMP_MASK; 36 | uint8_t ver = (uuid.msb() >> UUID_VERSION_SHIFT) & UUID_VERSION_MASK; 37 | uint16_t rand_a = uuid.msb() & UUID_RANDOM_A_MASK; 38 | uint8_t var = (uuid.lsb() >> UUID_VARIANT_SHIFT) & UUID_VARIANT_MASK; 39 | uint64_t rand_b = uuid.lsb() & UUID_RANDOM_B_MASK; 40 | 41 | // Formatting the UUIDv8 in the traditional format 42 | std::stringstream ss; 43 | ss << std::hex << std::setfill('0') << std::setw(LEN_32_BITS_IN_HEX) 44 | << ((unix_ts_ms >> LEN_LSB_IN_HEX * LEN_HEX_TO_BIT) & 45 | MASK_32_BITS) // First 32 bits of timestamp 46 | << "-" << std::setw(LEN_16_BITS_IN_HEX) 47 | << ((unix_ts_ms)&MASK_16_BITS) // Next 16 bits of timestamp i.e. last 16 48 | // bits of ts 49 | << "-" << std::setw(LEN_16_BITS_IN_HEX) 50 | << (((ver & UUID_VERSION_MASK) << UUID_VERSION_SHIFT) | 51 | (rand_a & 52 | UUID_RANDOM_A_MASK)) // Last 16 bits of timestamp and version 53 | << "-" << std::setw(LEN_16_BITS_IN_HEX) 54 | << (((var & UUID_VARIANT_MASK) << (LEN_HEX_TO_BIT * 3 + 2)) | 55 | ((rand_b >> RANDOM_B_SHIFT) & MASK_14_BITS)) // Variant and randb 56 | << "-" << std::setw(LEN_48_BITS_IN_HEX) 57 | << (rand_b & UUID_TIMESTAMP_MASK); // Random number 58 | 59 | return std::move(ss).str(); 60 | } 61 | 62 | uprotocol::v1::UUID AsString::deserialize(const std::string& str) { 63 | // Check if the UUID string is in the correct format 64 | // Format : 12345678-1234-1234-1234-123456789ABC 65 | // Index : 012345678901234567890123456789012345 66 | // Layout : ***msb**-lsb*-vcnt-varr-***RAND***** 67 | // msb - timestamp most significant bits (32 bits) 68 | // lsb - timestamp least significant bits (16 bits) 69 | // v - version (4 bits) 70 | // cnt - counter (12 bits) 71 | // var - variant (2 bits) 72 | // RAND - random (62 bits) 73 | // Please check UP-spec for UUID formatting: 74 | // https://github.com/eclipse-uprotocol/up-spec/blob/main/basics/uuid.adoc 75 | 76 | if (str.length() != TOTAL_UUID_LENGTH || str[POS_FIRST_SEPARATOR] != '-' || 77 | str[POS_SECOND_SEPARATOR] != '-' || str[POS_THIRD_SEPARATOR] != '-' || 78 | str[POS_FOURTH_SEPARATOR] != '-') { 79 | throw std::invalid_argument("Invalid UUID string format"); 80 | } 81 | 82 | uprotocol::v1::UUID uuid; 83 | uint64_t unix_ts_ms = 0; 84 | uint8_t ver = 0; 85 | uint16_t rand_a = 0; 86 | uint8_t var = 0; 87 | uint64_t rand_b = 0; 88 | 89 | try { 90 | // Extract the parts from the UUID string 91 | unix_ts_ms = 92 | std::stoull(str.substr(0, LEN_MSB_IN_HEX), nullptr, HEX_BASE) 93 | << LEN_LSB_IN_HEX * LEN_HEX_TO_BIT; 94 | unix_ts_ms |= 95 | std::stoull(str.substr(POS_FIRST_SEPARATOR + 1, LEN_LSB_IN_HEX), 96 | nullptr, HEX_BASE); 97 | 98 | uint16_t msb_low = static_cast( 99 | std::stoul(str.substr(POS_SECOND_SEPARATOR + 1, LEN_VCANT_IN_HEX), 100 | nullptr, HEX_BASE)); 101 | ver = (msb_low >> LEN_HEX_TO_BIT * 3) & UUID_VERSION_MASK; 102 | rand_a = msb_low & UUID_RANDOM_A_MASK; 103 | 104 | uint16_t var_randb = static_cast( 105 | std::stoul(str.substr(POS_THIRD_SEPARATOR + 1, LEN_VARR_IN_HEX), 106 | nullptr, HEX_BASE)); 107 | var = (var_randb >> (LEN_HEX_TO_BIT * 3 + 2)) & UUID_VARIANT_MASK; 108 | rand_b = static_cast(var_randb & MASK_14_BITS) 109 | << RANDOM_B_SHIFT; 110 | rand_b |= std::stoull(str.substr(POS_FOURTH_SEPARATOR + 1), nullptr, 111 | HEX_BASE); 112 | } catch (const std::exception& e) { 113 | throw std::invalid_argument("Invalid UUID string format"); 114 | } 115 | 116 | // Reconstruct the UUID 117 | uuid.set_msb((unix_ts_ms << UUID_TIMESTAMP_SHIFT) | 118 | ((static_cast(ver) << UUID_VERSION_SHIFT)) | rand_a); 119 | uuid.set_lsb((static_cast(var) << UUID_VARIANT_SHIFT) | rand_b); 120 | 121 | return uuid; 122 | } 123 | 124 | // Serialization function 125 | std::vector AsBytes::serialize(const v1::UUID& uuid) { 126 | std::vector bytes(UUID_BYTE_SIZE); 127 | 128 | uint32_t msb_high = 129 | htonl(static_cast(uuid.msb() >> LEN_UINT64_IN_BIT / 2)); 130 | uint32_t msb_low = htonl(static_cast(uuid.msb() & MASK_32_BITS)); 131 | uint32_t lsb_high = 132 | htonl(static_cast(uuid.lsb() >> LEN_UINT64_IN_BIT / 2)); 133 | uint32_t lsb_low = htonl(static_cast(uuid.lsb() & MASK_32_BITS)); 134 | 135 | std::memcpy(&bytes[MSB_HIGH], &msb_high, UUID_PART_SIZE); 136 | std::memcpy(&bytes[MSB_LOW], &msb_low, UUID_PART_SIZE); 137 | std::memcpy(&bytes[LSB_HIGH], &lsb_high, UUID_PART_SIZE); 138 | std::memcpy(&bytes[LSB_LOW], &lsb_low, UUID_PART_SIZE); 139 | 140 | return bytes; 141 | } 142 | 143 | v1::UUID AsBytes::deserialize(const std::vector& bytes) { 144 | if (bytes.size() != UUID_BYTE_SIZE) { 145 | throw std::invalid_argument("Invalid UUID byte array size"); 146 | } 147 | 148 | uint32_t msb_high = 0; 149 | uint32_t msb_low = 0; 150 | uint32_t lsb_high = 0; 151 | uint32_t lsb_low = 0; 152 | 153 | std::memcpy(&msb_high, &bytes[MSB_HIGH], UUID_PART_SIZE); 154 | std::memcpy(&msb_low, &bytes[MSB_LOW], UUID_PART_SIZE); 155 | std::memcpy(&lsb_high, &bytes[LSB_HIGH], UUID_PART_SIZE); 156 | std::memcpy(&lsb_low, &bytes[LSB_LOW], UUID_PART_SIZE); 157 | 158 | uint64_t msb = 159 | (static_cast(ntohl(msb_high)) << LEN_UINT64_IN_BIT / 2) | 160 | ntohl(msb_low); 161 | uint64_t lsb = 162 | (static_cast(ntohl(lsb_high)) << LEN_UINT64_IN_BIT / 2) | 163 | ntohl(lsb_low); 164 | 165 | v1::UUID uuid; 166 | uuid.set_msb(msb); 167 | uuid.set_lsb(lsb); 168 | 169 | return uuid; 170 | } 171 | 172 | } // namespace uprotocol::datamodel::serializer::uuid 173 | -------------------------------------------------------------------------------- /src/datamodel/validator/UUri.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/datamodel/validator/UUri.h" 13 | 14 | namespace { 15 | 16 | constexpr size_t AUTHORITY_SPEC_MAX_LENGTH = 128; 17 | // TODO(max) try to find a better name 18 | constexpr auto START_OF_TOPICS = 0x8000; 19 | constexpr auto MAX_RESOURCE_ID = 0xFFFF; 20 | 21 | using uprotocol::datamodel::validator::uri::Reason; 22 | using uprotocol::datamodel::validator::uri::ValidationResult; 23 | 24 | ValidationResult uriCommonValidChecks(const uprotocol::v1::UUri& uuri) { 25 | if (uuri.ue_version_major() == 0) { 26 | return {false, Reason::RESERVED_VERSION}; 27 | } 28 | 29 | if (uuri.ue_version_major() > std::numeric_limits::max()) { 30 | return {false, Reason::VERSION_OVERFLOW}; 31 | } 32 | 33 | if (uuri.resource_id() > std::numeric_limits::max()) { 34 | return {false, Reason::RESOURCE_OVERFLOW}; 35 | } 36 | 37 | if (uuri.authority_name().size() > AUTHORITY_SPEC_MAX_LENGTH) { 38 | return {false, Reason::AUTHORITY_TOO_LONG}; 39 | } 40 | 41 | return {true, {}}; 42 | } 43 | 44 | } // namespace 45 | 46 | namespace uprotocol::datamodel::validator::uri { 47 | 48 | std::string_view message(Reason reason) { 49 | switch (reason) { 50 | case Reason::EMPTY: 51 | return "The URI authority_name is empty or whitespace."; 52 | case Reason::RESERVED_VERSION: 53 | return "The version is 0 (reserved)."; 54 | case Reason::RESERVED_RESOURCE: 55 | return "The resource is 0 for non-RPC-response URIs."; 56 | case Reason::DISALLOWED_WILDCARD: 57 | return "The URI contains a wildcard in one or more fields that is " 58 | "not allowed for this form."; 59 | case Reason::BAD_RESOURCE_ID: 60 | return "The resource ID is not in the allowed range for this URI " 61 | "form."; 62 | case Reason::LOCAL_AUTHORITY: 63 | return "Local (blank) authority names are not allowed here."; 64 | case Reason::VERSION_OVERFLOW: 65 | return "uE Major Version is greater than uint8 max"; 66 | case Reason::RESOURCE_OVERFLOW: 67 | return "Resource ID is greater than uint16 max"; 68 | case Reason::AUTHORITY_TOO_LONG: 69 | return "Authority is longer than 128 characters (spec max length)"; 70 | default: 71 | return "Unknown reason."; 72 | } 73 | } 74 | 75 | bool has_wildcard_authority(const v1::UUri& uuri) { 76 | return uuri.authority_name() == "*"; 77 | } 78 | 79 | bool has_wildcard_service_id(const v1::UUri& uuri) { 80 | constexpr auto LOWER_16_BIT_MASK = 0xFFFF; 81 | return (uuri.ue_id() & LOWER_16_BIT_MASK) == LOWER_16_BIT_MASK; 82 | } 83 | 84 | bool has_wildcard_service_instance_id(const v1::UUri& uuri) { 85 | constexpr auto UPPER_16_BIT_MASK = 0xFFFF0000; 86 | return (uuri.ue_id() & UPPER_16_BIT_MASK) == UPPER_16_BIT_MASK; 87 | } 88 | 89 | bool has_wildcard_version(const v1::UUri& uuri) { 90 | constexpr auto LOWER_8_BIT_MASK = 0xFF; 91 | return uuri.ue_version_major() == LOWER_8_BIT_MASK; 92 | } 93 | 94 | bool has_wildcard_resource_id(const v1::UUri& uuri) { 95 | constexpr auto LOWER_16_BIT_MASK = 0xFFFF; 96 | return uuri.resource_id() == LOWER_16_BIT_MASK; 97 | } 98 | 99 | bool verify_no_wildcards(const v1::UUri& uuri) { 100 | return !has_wildcard_authority(uuri) && !has_wildcard_service_id(uuri) && 101 | !has_wildcard_service_instance_id(uuri) && 102 | !has_wildcard_version(uuri) && !has_wildcard_resource_id(uuri); 103 | } 104 | 105 | ValidationResult isValid(const v1::UUri& uuri) { 106 | { 107 | auto [valid, reason] = isValidRpcMethod(uuri); 108 | if (valid) { 109 | return {true, std::nullopt}; 110 | } 111 | } 112 | 113 | { 114 | auto [valid, reason] = isValidRpcResponse(uuri); 115 | if (valid) { 116 | return {true, std::nullopt}; 117 | } 118 | } 119 | 120 | { 121 | auto [valid, reason] = isValidPublishTopic(uuri); 122 | if (valid) { 123 | return {true, std::nullopt}; 124 | } 125 | } 126 | 127 | { 128 | auto [valid, reason] = isValidNotificationSource(uuri); 129 | if (valid) { 130 | return {true, std::nullopt}; 131 | } 132 | } 133 | 134 | return isValidNotificationSink(uuri); 135 | } 136 | 137 | ValidationResult isValidFilter(const v1::UUri& uuri) { 138 | auto [is_empty, empty_reason] = isEmpty(uuri); 139 | if (is_empty) { 140 | return {false, Reason::EMPTY}; 141 | } 142 | 143 | auto [common_valid, common_fail_reason] = uriCommonValidChecks(uuri); 144 | if (!common_valid) { 145 | return {common_valid, common_fail_reason}; 146 | } 147 | 148 | return {true, {}}; 149 | } 150 | 151 | ValidationResult isValidRpcMethod(const v1::UUri& uuri) { 152 | // disallow wildcards 153 | if (!verify_no_wildcards(uuri)) { 154 | return {false, Reason::DISALLOWED_WILDCARD}; 155 | } 156 | 157 | // check resource ID [0x0001, 0x7FFF] 158 | if (uuri.resource_id() == 0 || uuri.resource_id() >= START_OF_TOPICS) { 159 | return {false, Reason::BAD_RESOURCE_ID}; 160 | } 161 | 162 | return {true, std::nullopt}; 163 | } 164 | 165 | ValidationResult isValidRpcResponse(const v1::UUri& uuri) { 166 | // disallow wildcards 167 | if (!verify_no_wildcards(uuri)) { 168 | return {false, Reason::DISALLOWED_WILDCARD}; 169 | } 170 | 171 | if (uuri.resource_id() != 0) { 172 | return {false, Reason::BAD_RESOURCE_ID}; 173 | } 174 | return {true, std::nullopt}; 175 | } 176 | 177 | ValidationResult isValidDefaultEntity(const v1::UUri& uuri) { 178 | if (isLocal(uuri)) { 179 | return {false, Reason::LOCAL_AUTHORITY}; 180 | } 181 | 182 | return isValidRpcResponse(uuri); 183 | } 184 | 185 | ValidationResult isValidDefaultSource(const v1::UUri& uuri) { 186 | return isValidDefaultEntity(uuri); 187 | } 188 | 189 | ValidationResult isValidPublishTopic(const v1::UUri& uuri) { 190 | // disallow wildcards 191 | if (!verify_no_wildcards(uuri)) { 192 | return {false, Reason::DISALLOWED_WILDCARD}; 193 | } 194 | 195 | if ((uuri.resource_id() < START_OF_TOPICS) || 196 | (uuri.resource_id() > MAX_RESOURCE_ID)) { 197 | return {false, Reason::BAD_RESOURCE_ID}; 198 | } 199 | 200 | return {true, std::nullopt}; 201 | } 202 | 203 | ValidationResult isValidNotificationSource(const v1::UUri& uuri) { 204 | // disallow wildcards 205 | if (!verify_no_wildcards(uuri)) { 206 | return {false, Reason::DISALLOWED_WILDCARD}; 207 | } 208 | 209 | if ((uuri.resource_id() < START_OF_TOPICS) || 210 | (uuri.resource_id() > MAX_RESOURCE_ID)) { 211 | return {false, Reason::BAD_RESOURCE_ID}; 212 | } 213 | 214 | return {true, std::nullopt}; 215 | } 216 | 217 | ValidationResult isValidNotificationSink(const v1::UUri& uuri) { 218 | // disallow wildcards 219 | if (!verify_no_wildcards(uuri)) { 220 | return {false, Reason::DISALLOWED_WILDCARD}; 221 | } 222 | 223 | if (uuri.resource_id() != 0) { 224 | return {false, Reason::BAD_RESOURCE_ID}; 225 | } 226 | 227 | return {true, std::nullopt}; 228 | } 229 | 230 | ValidationResult isValidSubscription(const v1::UUri& uuri) { 231 | if (uuri.resource_id() < START_OF_TOPICS || 232 | uuri.resource_id() > MAX_RESOURCE_ID) { 233 | return {false, Reason::BAD_RESOURCE_ID}; 234 | } 235 | 236 | return {true, std::nullopt}; 237 | } 238 | 239 | ValidationResult isEmpty(const v1::UUri& uuri) { 240 | if (!std::all_of(uuri.authority_name().begin(), uuri.authority_name().end(), 241 | isspace)) { 242 | return {false, Reason::EMPTY}; 243 | } 244 | 245 | if (uuri.ue_id() != 0) { 246 | return {false, Reason::RESERVED_RESOURCE}; 247 | } 248 | 249 | if (uuri.ue_version_major() != 0) { 250 | return {false, Reason::RESERVED_VERSION}; 251 | } 252 | 253 | if (uuri.resource_id() != 0) { 254 | return {false, Reason::BAD_RESOURCE_ID}; 255 | } 256 | 257 | return {true, std::nullopt}; 258 | } 259 | 260 | bool isLocal(const v1::UUri& uuri) { return (uuri.authority_name().empty()); } 261 | 262 | } // namespace uprotocol::datamodel::validator::uri 263 | -------------------------------------------------------------------------------- /src/datamodel/validator/Uuid.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/datamodel/validator/Uuid.h" 13 | 14 | #include "up-cpp/datamodel/constants/UuidConstants.h" 15 | 16 | using namespace std::chrono_literals; 17 | 18 | namespace uprotocol::datamodel { 19 | 20 | using Milliseconds = std::chrono::milliseconds; 21 | 22 | std::chrono::system_clock::time_point getUuidTimestamp( 23 | const uprotocol::v1::UUID& uuid) { 24 | uint64_t msb = uuid.msb(); 25 | uint64_t timestamp = (msb >> UUID_TIMESTAMP_SHIFT) & UUID_TIMESTAMP_MASK; 26 | 27 | return std::chrono::system_clock::time_point(Milliseconds(timestamp)); 28 | } 29 | 30 | uint8_t internalGetVersion(const uprotocol::v1::UUID& uuid) { 31 | return (uuid.msb() >> UUID_VERSION_SHIFT) & UUID_VERSION_MASK; 32 | } 33 | 34 | uint8_t internalGetVariant(const uprotocol::v1::UUID& uuid) { 35 | return (uuid.lsb() >> UUID_VARIANT_SHIFT) & UUID_VARIANT_MASK; 36 | } 37 | 38 | } // namespace uprotocol::datamodel 39 | 40 | namespace uprotocol::datamodel::validator::uuid { 41 | 42 | std::string_view message(Reason reason) { 43 | switch (reason) { 44 | case Reason::WRONG_VERSION: 45 | return "Invalid UUID version"; 46 | case Reason::UNSUPPORTED_VARIANT: 47 | return "Unsupported UUID variant"; 48 | case Reason::FROM_THE_FUTURE: 49 | return "UUID timestamp is from the future"; 50 | case Reason::EXPIRED: 51 | return "UUID has expired"; 52 | default: 53 | return "Unknown reason"; 54 | } 55 | } 56 | 57 | ValidationResult isUuid(const uprotocol::v1::UUID& uuid) { 58 | uint8_t version = internalGetVersion(uuid); 59 | if (version != UUID_VERSION_7) { 60 | return {false, Reason::WRONG_VERSION}; 61 | } 62 | 63 | uint8_t variant = internalGetVariant(uuid); 64 | if (variant != UUID_VARIANT_RFC4122) { 65 | return {false, Reason::UNSUPPORTED_VARIANT}; 66 | } 67 | 68 | auto timestamp = getUuidTimestamp(uuid); 69 | auto current_time = std::chrono::system_clock::now(); 70 | 71 | if (timestamp > current_time) { 72 | return {false, Reason::FROM_THE_FUTURE}; 73 | } 74 | 75 | return {true, std::nullopt}; 76 | } 77 | 78 | ValidationResult isExpired(const uprotocol::v1::UUID& uuid, 79 | std::chrono::milliseconds ttl) { 80 | auto [valid, reason] = isUuid(uuid); 81 | if (!valid) { 82 | return {false, reason}; 83 | } 84 | 85 | auto timestamp = getUuidTimestamp(uuid); 86 | auto current_time = std::chrono::system_clock::now(); 87 | 88 | if ((current_time - timestamp) > ttl) { 89 | return {true, Reason::EXPIRED}; 90 | } 91 | 92 | return {false, std::nullopt}; 93 | } 94 | 95 | uint8_t getVersion(const uprotocol::v1::UUID& uuid) { 96 | auto [valid, reason] = isUuid(uuid); 97 | if (!valid) { 98 | throw InvalidUuid(message(reason.value())); 99 | } 100 | return internalGetVersion(uuid); 101 | } 102 | 103 | uint8_t getVariant(const uprotocol::v1::UUID& uuid) { 104 | auto [valid, reason] = isUuid(uuid); 105 | if (!valid) { 106 | throw InvalidUuid(message(reason.value())); 107 | } 108 | return internalGetVariant(uuid); 109 | } 110 | 111 | std::chrono::system_clock::time_point getTime(const uprotocol::v1::UUID& uuid) { 112 | auto [valid, reason] = isUuid(uuid); 113 | if (!valid) { 114 | throw InvalidUuid(message(reason.value())); 115 | } 116 | return getUuidTimestamp(uuid); 117 | } 118 | 119 | std::chrono::milliseconds getElapsedTime(const uprotocol::v1::UUID& uuid) { 120 | auto [valid, reason] = isUuid(uuid); 121 | if (!valid) { 122 | throw InvalidUuid(message(reason.value())); 123 | } 124 | 125 | auto current_time = std::chrono::system_clock::now(); 126 | auto uuid_time = getTime(uuid); 127 | 128 | return std::chrono::duration_cast(current_time - uuid_time); 129 | } 130 | 131 | std::chrono::milliseconds getRemainingTime(const uprotocol::v1::UUID& uuid, 132 | std::chrono::milliseconds ttl) { 133 | auto elapsed_time = getElapsedTime(uuid); 134 | return std::max(ttl - elapsed_time, 0ms); 135 | } 136 | 137 | } // namespace uprotocol::datamodel::validator::uuid 138 | -------------------------------------------------------------------------------- /src/transport/UTransport.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/transport/UTransport.h" 13 | 14 | #include 15 | 16 | #include "up-cpp/datamodel/validator/UMessage.h" 17 | #include "up-cpp/datamodel/validator/UUri.h" 18 | #include "up-cpp/utils/Expected.h" 19 | 20 | namespace uprotocol::transport { 21 | 22 | namespace uri_validator = uprotocol::datamodel::validator::uri; 23 | namespace message_validator = uprotocol::datamodel::validator::message; 24 | 25 | UTransport::UTransport(v1::UUri entity_uri) 26 | : entity_uri_(std::move(entity_uri)) { 27 | auto [uri_ok, reason] = uri_validator::isValidDefaultEntity(entity_uri_); 28 | if (!uri_ok) { 29 | throw uri_validator::InvalidUUri( 30 | "Transport's entity URI is not a valid URI | " + 31 | std::string(uri_validator::message(*reason))); 32 | } 33 | } 34 | 35 | v1::UStatus UTransport::send(const v1::UMessage& message) { 36 | auto [msgOk, reason] = message_validator::isValid(message); 37 | if (!msgOk) { 38 | throw message_validator::InvalidUMessage( 39 | "Invalid UMessage | " + 40 | std::string(message_validator::message(*reason))); 41 | } 42 | 43 | return sendImpl(message); 44 | } 45 | 46 | UTransport::HandleOrStatus UTransport::registerListener( 47 | ListenCallback&& listener, const v1::UUri& source_filter, 48 | uint16_t sink_resource_filter) { 49 | auto sink_filter = getEntityUri(); 50 | sink_filter.set_resource_id(sink_resource_filter); 51 | return registerListener(std::move(listener), source_filter, 52 | std::move(sink_filter)); 53 | } 54 | 55 | UTransport::HandleOrStatus UTransport::registerListener( 56 | ListenCallback&& listener, const v1::UUri& source_filter, 57 | std::optional&& sink_filter) { 58 | if (!sink_filter) { 59 | // Handle the special case of publish-like messages where only a source 60 | // address is provided. 61 | auto [source_ok, bad_source_reason] = 62 | uri_validator::isValidSubscription(source_filter); 63 | if (!source_ok) { 64 | throw uri_validator::InvalidUUri( 65 | "source_filter is not a valid URI | " + 66 | std::string(uri_validator::message(*bad_source_reason))); 67 | } 68 | } else { 69 | auto [source_ok, bad_source_reason] = 70 | uri_validator::isValidFilter(source_filter); 71 | if (!source_ok) { 72 | throw uri_validator::InvalidUUri( 73 | "source_filter is not a valid URI | " + 74 | std::string(uri_validator::message(*bad_source_reason))); 75 | } 76 | 77 | auto [sink_ok, bad_sink_reason] = 78 | uri_validator::isValidFilter(*sink_filter); 79 | if (!sink_ok) { 80 | throw uri_validator::InvalidUUri( 81 | "sink_filter is not a valid URI | " + 82 | std::string(uri_validator::message(*bad_sink_reason))); 83 | } 84 | } 85 | 86 | auto [handle, callable] = CallbackConnection::establish( 87 | std::move(listener), [this](auto conn) { cleanupListener(conn); }); 88 | 89 | v1::UStatus status = registerListenerImpl( 90 | std::move(callable), source_filter, std::move(sink_filter)); 91 | 92 | if (status.code() == v1::UCode::OK) { 93 | return HandleOrStatus(std::move(handle)); 94 | } 95 | return HandleOrStatus(utils::Unexpected(status)); 96 | } 97 | 98 | // NOTE: deprecated 99 | utils::Expected 100 | UTransport::registerListener(const v1::UUri& sink_or_topic_filter, 101 | ListenCallback&& listener, 102 | std::optional&& source_filter) { 103 | if (!source_filter) { 104 | // Workaround for deprecated API not fully reflecting the use cases 105 | // defined in the uprotocol spec: an unset source_filter is treated 106 | // as meaning that the sink_filter is a topic to subscribe to. This 107 | // will then pass the sink_filter as a source filter to the updated 108 | // registerListener(), which is then called with sink_filter unset. 109 | return registerListener(std::move(listener), sink_or_topic_filter, 110 | std::move(source_filter)); 111 | } 112 | v1::UUri sink_filter_copy = sink_or_topic_filter; 113 | return registerListener(std::move(listener), *source_filter, 114 | std::move(sink_filter_copy)); 115 | } 116 | 117 | const v1::UUri& UTransport::getEntityUri() const { return entity_uri_; } 118 | 119 | const v1::UUri& UTransport::getDefaultSource() const { return getEntityUri(); } 120 | 121 | void UTransport::cleanupListener(const CallableConn& listener) { 122 | static_cast(listener); 123 | } 124 | 125 | } // namespace uprotocol::transport 126 | -------------------------------------------------------------------------------- /src/utils/IpAddress.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/utils/IpAddress.h" 13 | -------------------------------------------------------------------------------- /src/utils/ProtoConverter.cpp: -------------------------------------------------------------------------------- 1 | #include "up-cpp/utils/ProtoConverter.h" 2 | 3 | namespace uprotocol::utils { 4 | google::protobuf::Timestamp ProtoConverter::ConvertToProtoTimestamp( 5 | const std::chrono::system_clock::time_point& tp) { 6 | constexpr auto NANOSECONDS_PER_SECOND = 1000000000LL; 7 | google::protobuf::Timestamp timestamp; 8 | auto duration = tp.time_since_epoch(); 9 | auto seconds = 10 | std::chrono::duration_cast(duration).count(); 11 | auto nanoseconds = 12 | std::chrono::duration_cast(duration).count() % 13 | NANOSECONDS_PER_SECOND; 14 | 15 | timestamp.set_seconds(seconds); 16 | timestamp.set_nanos(static_cast(nanoseconds)); 17 | 18 | return timestamp; 19 | } 20 | 21 | // SubscriberInfo builder 22 | SubscriberInfo ProtoConverter::BuildSubscriberInfo(const v1::UUri& entity_uri) { 23 | SubscriberInfo subscriber_info; 24 | 25 | // Create a new instance of UUri and copy the contents from entity_uri 26 | *subscriber_info.mutable_uri() = entity_uri; 27 | 28 | return subscriber_info; 29 | } 30 | 31 | // SubscribeAttributes builder 32 | SubscribeAttributes ProtoConverter::BuildSubscribeAttributes( 33 | std::optional when_expire, 34 | std::optional subscription_details, 35 | std::optional sample_period_ms) { 36 | SubscribeAttributes attributes; 37 | 38 | if (when_expire.has_value()) { 39 | *attributes.mutable_expire() = 40 | ConvertToProtoTimestamp(when_expire.value()); 41 | } 42 | 43 | if (subscription_details.has_value()) { 44 | attributes.add_details()->CopyFrom(subscription_details.value()); 45 | } 46 | 47 | if (sample_period_ms.has_value()) { 48 | attributes.set_sample_period_ms( 49 | static_cast(sample_period_ms.value().count())); 50 | } 51 | 52 | return attributes; 53 | } 54 | 55 | // SubscriptionRequest builder 56 | SubscriptionRequest ProtoConverter::BuildSubscriptionRequest( 57 | const v1::UUri& subscription_topic, 58 | std::optional attributes) { 59 | SubscriptionRequest subscription_request; 60 | *subscription_request.mutable_topic() = subscription_topic; 61 | 62 | // Use mutable attributes if provided 63 | if (attributes.has_value()) { 64 | *subscription_request.mutable_attributes() = 65 | std::move(attributes.value()); 66 | } 67 | 68 | return subscription_request; 69 | } 70 | 71 | UnsubscribeRequest ProtoConverter::BuildUnSubscribeRequest( 72 | const v1::UUri& subscription_topic) { 73 | UnsubscribeRequest unsubscribe_request; 74 | *unsubscribe_request.mutable_topic() = subscription_topic; 75 | 76 | return unsubscribe_request; 77 | } 78 | 79 | } // namespace uprotocol::utils 80 | -------------------------------------------------------------------------------- /src/utils/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/utils/ThreadPool.h" 13 | -------------------------------------------------------------------------------- /src/utils/base64.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include "up-cpp/utils/base64.h" 13 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | # 3 | # See the NOTICE file(s) distributed with this work for additional 4 | # information regarding copyright ownership. 5 | # 6 | # This program and the accompanying materials are made available under the 7 | # terms of the Apache License Version 2.0 which is available at 8 | # https://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # SPDX-License-Identifier: Apache-2.0 11 | 12 | enable_testing() 13 | 14 | find_package(GTest REQUIRED) 15 | include(GoogleTest) 16 | 17 | # Copy exclude files to the binary directory 18 | file(COPY 19 | ${CMAKE_CURRENT_SOURCE_DIR}/sanitizers/valgrind_exclude_test_memcheck.txt 20 | ${CMAKE_CURRENT_SOURCE_DIR}/sanitizers/valgrind_exclude_test_threadcheck.txt 21 | ${CMAKE_CURRENT_SOURCE_DIR}/sanitizers/valgrind_exclude_test_helgrind.txt 22 | ${CMAKE_CURRENT_SOURCE_DIR}/sanitizers/valgrind_exclude_test_dhat.txt 23 | DESTINATION ${CMAKE_BINARY_DIR}) 24 | 25 | # Invoked as add_coverage_test("SomeName" sources...) 26 | function(add_coverage_test Name) 27 | add_executable(${Name} ${ARGN}) 28 | target_compile_options(${Name} PRIVATE -g -Og -Wno-deprecated-declarations) 29 | target_link_libraries(${Name} 30 | PUBLIC 31 | up-core-api::up-core-api 32 | up-cpp::up-cpp 33 | spdlog::spdlog 34 | protobuf::protobuf 35 | PRIVATE 36 | GTest::gtest_main 37 | GTest::gmock 38 | ) 39 | target_include_directories(${Name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) 40 | if(CMAKE_SYSTEM_NAME MATCHES "QNX") 41 | # QNX provides pthread inside libc and does not need to link pthread lib. 42 | # 43 | # For QNX we are using cross compilation. 44 | # Thus, we can't call gtest_discover_tests() 45 | # Instead, we call old gtest_add_tests() 46 | gtest_add_tests(TARGET ${Name} SOURCES ${ARGN}) 47 | else() 48 | target_link_libraries(${Name} PRIVATE pthread) 49 | gtest_discover_tests(${Name} XML_OUTPUT_DIR results) 50 | endif() 51 | endfunction() 52 | 53 | # NOTE: This is temporarily just a call to add_coverage_test. When coverage 54 | # reporting is added, this might be changed. 55 | function(add_extra_test Name) 56 | add_coverage_test(${Name} ${ARGN}) 57 | endfunction() 58 | 59 | ########################### COVERAGE ########################################## 60 | # Utils 61 | add_coverage_test("ExpectedTest" coverage/utils/ExpectedTest.cpp) 62 | add_coverage_test("base64Test" coverage/utils/base64Test.cpp) 63 | add_coverage_test("IpAddressTest" coverage/utils/IpAddressTest.cpp) 64 | add_coverage_test("CallbackConnectionTest" coverage/utils/CallbackConnectionTest.cpp) 65 | add_coverage_test("CyclicQueueTest" coverage/utils/CyclicQueueTest.cpp) 66 | add_coverage_test("ThreadPoolTest" coverage/utils/ThreadPoolTest.cpp) 67 | 68 | # Validators 69 | add_coverage_test("UuidValidatorTest" coverage/datamodel/UuidValidatorTest.cpp) 70 | add_coverage_test("UUriValidatorTest" coverage/datamodel/UUriValidatorTest.cpp) 71 | add_coverage_test("UMessageValidatorTest" coverage/datamodel/UMessageValidatorTest.cpp) 72 | 73 | # Serializers 74 | add_coverage_test("UUriSerializerTest" coverage/datamodel/UUriSerializerTest.cpp) 75 | add_coverage_test("UuidSerializerTest" coverage/datamodel/UuidSerializerTest.cpp) 76 | 77 | # Builders 78 | add_coverage_test("PayloadBuilderTest" coverage/datamodel/PayloadBuilderTest.cpp) 79 | add_coverage_test("UMessageBuilderTest" coverage/datamodel/UMessageBuilderTest.cpp) 80 | add_coverage_test("UuidBuilderTest" coverage/datamodel/UuidBuilderTest.cpp) 81 | 82 | # Transport 83 | add_coverage_test("UTransportTest" coverage/transport/UTransportTest.cpp) 84 | 85 | # Communication 86 | add_coverage_test("RpcClientTest" coverage/communication/RpcClientTest.cpp) 87 | add_coverage_test("RpcServerTest" coverage/communication/RpcServerTest.cpp) 88 | add_coverage_test("PublisherTest" coverage/communication/PublisherTest.cpp) 89 | add_coverage_test("SubscriberTest" coverage/communication/SubscriberTest.cpp) 90 | add_coverage_test("NotificationSinkTest" coverage/communication/NotificationSinkTest.cpp) 91 | add_coverage_test("NotificationSourceTest" coverage/communication/NotificationSourceTest.cpp) 92 | 93 | # client 94 | add_coverage_test("ConsumerTest" coverage/client/usubscription/v3/ConsumerTest.cpp) 95 | 96 | ########################## EXTRAS ############################################# 97 | add_extra_test("PublisherSubscriberTest" extra/PublisherSubscriberTest.cpp) 98 | add_extra_test("NotificationTest" extra/NotificationTest.cpp) 99 | add_extra_test("RpcClientServerTest" extra/RpcClientServerTest.cpp) 100 | add_extra_test("UTransportMockTest" extra/UTransportMockTest.cpp) 101 | -------------------------------------------------------------------------------- /test/coverage/communication/PublisherTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "UTransportMock.h" 18 | 19 | namespace uprotocol { 20 | 21 | class TestPublisher : public testing::Test { 22 | private: 23 | std::shared_ptr transportMock_; 24 | v1::UUri source_; 25 | v1::UUri topic_; 26 | v1::UPayloadFormat format_ = v1::UPayloadFormat::UPAYLOAD_FORMAT_TEXT; 27 | std::optional priority_; 28 | std::optional ttl_; 29 | uprotocol::v1::UMessage capture_msg_; 30 | 31 | protected: 32 | std::shared_ptr getTransportMock() const { 33 | return transportMock_; 34 | } 35 | v1::UUri getSource() const { return source_; } 36 | v1::UUri getTopic() const { return topic_; } 37 | v1::UPayloadFormat getFormat() const { return format_; } 38 | std::optional& getPriority() { return priority_; } 39 | std::optional getTTL() const { return ttl_; } 40 | uprotocol::v1::UMessage getCaptureMsg() const { return capture_msg_; } 41 | 42 | // Run once per TEST_F. 43 | // Used to set up clean environments per test. 44 | void SetUp() override { 45 | constexpr uint32_t DEFAULT_UE_ID = 0x00011101; 46 | constexpr uint32_t DEFAULT_RESOURCE_ID = 0x8101; 47 | constexpr uint32_t SOURCE_VERSION_MAJOR = 0xF1; 48 | constexpr uint32_t TOPIC_VERSION_MAJOR = 0xF8; 49 | constexpr uint16_t DEFAULT_TTL_TIME = 1000; 50 | 51 | source_.set_authority_name("10.0.0.1"); 52 | source_.set_ue_id(DEFAULT_UE_ID); 53 | source_.set_ue_version_major(SOURCE_VERSION_MAJOR); 54 | source_.set_resource_id(0x0); 55 | 56 | topic_.set_authority_name("10.0.0.1"); 57 | topic_.set_ue_id(DEFAULT_UE_ID); 58 | topic_.set_ue_version_major(TOPIC_VERSION_MAJOR); 59 | topic_.set_resource_id(DEFAULT_RESOURCE_ID); 60 | transportMock_ = 61 | std::make_shared(source_); 62 | 63 | priority_ = v1::UPriority::UPRIORITY_CS2; 64 | ttl_ = std::chrono::milliseconds(DEFAULT_TTL_TIME); 65 | } 66 | 67 | void TearDown() override {} 68 | 69 | // Run once per execution of the test application. 70 | // Used for setup of all tests. Has access to this instance. 71 | TestPublisher() = default; 72 | 73 | // Run once per execution of the test application. 74 | // Used only for global setup outside of tests. 75 | static void SetUpTestSuite() {} 76 | static void TearDownTestSuite() {} 77 | 78 | public: 79 | ~TestPublisher() override = default; 80 | }; 81 | 82 | TEST_F(TestPublisher, PublisherSuccess) { // NOLINT 83 | std::string test_payload_str = "test_payload"; 84 | communication::Publisher publisher(getTransportMock(), getTopic(), 85 | getFormat(), getPriority(), getTTL()); 86 | 87 | uprotocol::v1::UStatus retval; 88 | retval.set_code(uprotocol::v1::UCode::OK); 89 | getTransportMock()->getSendStatus() = retval; 90 | 91 | datamodel::builder::Payload test_payload(test_payload_str, getFormat()); 92 | auto status = publisher.publish(std::move(test_payload)); 93 | 94 | EXPECT_EQ(status.code(), retval.code()); 95 | 96 | auto [valid, reason] = 97 | uprotocol::datamodel::validator::message::isValidPublish( 98 | getTransportMock()->getMessage()); 99 | EXPECT_EQ(valid, true); 100 | } 101 | 102 | TEST_F(TestPublisher, PublishFailure) { // NOLINT 103 | std::string test_payload_str = "test_payload"; 104 | communication::Publisher publisher(getTransportMock(), getTopic(), 105 | getFormat(), getPriority(), getTTL()); 106 | 107 | uprotocol::v1::UStatus retval; 108 | retval.set_code(uprotocol::v1::UCode::DATA_LOSS); 109 | getTransportMock()->getSendStatus() = retval; 110 | 111 | datamodel::builder::Payload test_payload(test_payload_str, getFormat()); 112 | auto status = publisher.publish(std::move(test_payload)); 113 | 114 | EXPECT_EQ(status.code(), retval.code()); 115 | } 116 | 117 | TEST_F(TestPublisher, PublishSuccessWithoutTTL) { // NOLINT 118 | std::string test_payload_str = "test_payload"; 119 | communication::Publisher publisher(getTransportMock(), getTopic(), 120 | getFormat(), getPriority()); 121 | 122 | uprotocol::v1::UStatus retval; 123 | retval.set_code(uprotocol::v1::UCode::OK); 124 | getTransportMock()->getSendStatus() = retval; 125 | 126 | datamodel::builder::Payload test_payload(test_payload_str, getFormat()); 127 | auto status = publisher.publish(std::move(test_payload)); 128 | 129 | EXPECT_EQ(status.code(), retval.code()); 130 | 131 | auto [valid, reason] = 132 | uprotocol::datamodel::validator::message::isValidPublish( 133 | getTransportMock()->getMessage()); 134 | EXPECT_EQ(valid, true); 135 | 136 | EXPECT_EQ(getTransportMock()->getMessage().attributes().ttl(), 0); 137 | } 138 | 139 | TEST_F(TestPublisher, PublishSuccessWithoutPriority) { // NOLINT 140 | std::string test_payload_str = "test_payload"; 141 | getPriority().reset(); 142 | communication::Publisher publisher(getTransportMock(), getTopic(), 143 | getFormat(), getPriority(), getTTL()); 144 | 145 | uprotocol::v1::UStatus retval; 146 | retval.set_code(uprotocol::v1::UCode::OK); 147 | getTransportMock()->getSendStatus() = retval; 148 | 149 | datamodel::builder::Payload test_payload(test_payload_str, getFormat()); 150 | auto status = publisher.publish(std::move(test_payload)); 151 | 152 | EXPECT_EQ(status.code(), retval.code()); 153 | 154 | auto [valid, reason] = 155 | uprotocol::datamodel::validator::message::isValidPublish( 156 | getTransportMock()->getMessage()); 157 | EXPECT_EQ(valid, true); 158 | 159 | EXPECT_EQ(getTransportMock()->getMessage().attributes().priority(), 160 | v1::UPriority::UPRIORITY_CS1); 161 | } 162 | 163 | // publisher with null transport 164 | TEST_F(TestPublisher, PublisherWithNullTransport) { // NOLINT 165 | auto transport = nullptr; 166 | EXPECT_THROW( // NOLINT 167 | communication::Publisher publisher(transport, getTopic(), getFormat(), 168 | getPriority(), getTTL()), 169 | uprotocol::transport::NullTransport); 170 | } 171 | 172 | } // namespace uprotocol 173 | -------------------------------------------------------------------------------- /test/coverage/datamodel/UuidBuilderTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | 14 | #include "up-cpp/datamodel/builder/Uuid.h" 15 | #include "up-cpp/datamodel/constants/UuidConstants.h" 16 | 17 | namespace uprotocol::datamodel { 18 | 19 | class TestUuidBuilder : public testing::Test { 20 | protected: 21 | void SetUp() override {} 22 | void TearDown() override {} 23 | 24 | TestUuidBuilder() = default; 25 | 26 | static void SetUpTestSuite() {} 27 | static void TearDownTestSuite() {} 28 | 29 | public: 30 | ~TestUuidBuilder() override = default; 31 | }; 32 | 33 | // Test getBuilder 34 | TEST(UuidBuilderTest, GetBuilder) { // NOLINT 35 | auto builder = builder::UuidBuilder::getBuilder(); 36 | auto uuid = builder.build(); 37 | 38 | EXPECT_TRUE(uuid.msb()); 39 | EXPECT_TRUE(uuid.lsb()); 40 | } 41 | 42 | // Test GetTestBuilder 43 | TEST(UuidBuilderTest, GetTestBuilder) { // NOLINT 44 | auto builder = builder::UuidBuilder::getTestBuilder(); 45 | auto uuid = builder.build(); 46 | 47 | EXPECT_TRUE(uuid.msb()); 48 | EXPECT_TRUE(uuid.lsb()); 49 | } 50 | 51 | // Test TestBuilder with time source 52 | TEST(UuidBuilderTest, WithTimeSource) { // NOLINT 53 | constexpr std::time_t FIXED_TIME_T = 1234567890; 54 | auto fixed_time = std::chrono::system_clock::from_time_t(FIXED_TIME_T); 55 | auto fixed_time_ms = 56 | std::chrono::time_point_cast(fixed_time); 57 | auto builder = builder::UuidBuilder::getTestBuilder().withTimeSource( 58 | [fixed_time]() { return fixed_time; }); 59 | auto uuid = builder.build(); 60 | 61 | EXPECT_EQ(uuid.msb() >> UUID_TIMESTAMP_SHIFT, 62 | fixed_time_ms.time_since_epoch().count()); 63 | } 64 | 65 | // Test RandomSource 66 | TEST(UuidBuilderTest, WithRandomSource) { // NOLINT 67 | constexpr uint64_t FIXED_RANDOM_UINT = 0x1234567890ABCDEF; 68 | uint64_t fixed_random = FIXED_RANDOM_UINT; 69 | auto builder = builder::UuidBuilder::getTestBuilder().withRandomSource( 70 | [fixed_random]() { return fixed_random; }); 71 | auto uuid = builder.build(); 72 | 73 | EXPECT_EQ(uuid.msb() & UUID_RANDOM_A_MASK, 74 | fixed_random & UUID_RANDOM_A_MASK); 75 | EXPECT_EQ(uuid.lsb() & UUID_RANDOM_B_MASK, 76 | fixed_random & UUID_RANDOM_B_MASK); 77 | } 78 | 79 | // Test independent state 80 | TEST(UuidBuilderTest, Unguessability) { // NOLINT 81 | auto builder1 = builder::UuidBuilder::getBuilder(); 82 | auto builder2 = builder::UuidBuilder::getBuilder(); 83 | 84 | { 85 | // Check that rand fields generated by different builders are different 86 | auto uuid1 = builder1.build(); 87 | auto uuid2 = builder2.build(); 88 | 89 | EXPECT_NE(uuid1.msb() & UUID_RANDOM_A_MASK, 90 | uuid2.msb() & UUID_RANDOM_A_MASK); 91 | EXPECT_NE(uuid1.lsb() & UUID_RANDOM_B_MASK, 92 | uuid2.lsb() & UUID_RANDOM_B_MASK); 93 | } 94 | 95 | { 96 | // Check that rand fields generated by the same builder are different 97 | auto uuid1 = builder1.build(); 98 | auto uuid2 = builder1.build(); 99 | 100 | EXPECT_NE(uuid1.msb() & UUID_RANDOM_A_MASK, 101 | uuid2.msb() & UUID_RANDOM_A_MASK); 102 | EXPECT_NE(uuid1.lsb() & UUID_RANDOM_B_MASK, 103 | uuid2.lsb() & UUID_RANDOM_B_MASK); 104 | } 105 | } 106 | 107 | // Test exception thrown 108 | TEST(UuidBuilderTest, TestModeOnly) { // NOLINT 109 | auto builder = builder::UuidBuilder::getBuilder(); 110 | 111 | EXPECT_THROW(builder.withTimeSource( // NOLINT 112 | []() { return std::chrono::system_clock::now(); }), 113 | std::domain_error); 114 | EXPECT_THROW(builder.withRandomSource( // NOLINT 115 | []() { return 0x1234567890ABCDEF; }), 116 | std::domain_error); 117 | } 118 | 119 | // Test version and variant 120 | TEST_F(TestUuidBuilder, CheckVersionAndVariant) { // NOLINT 121 | auto builder = builder::UuidBuilder::getBuilder(); 122 | auto uuid = builder.build(); 123 | 124 | EXPECT_EQ((uuid.msb() >> UUID_VERSION_SHIFT) & UUID_VERSION_MASK, 125 | UUID_VERSION_7); 126 | EXPECT_EQ((uuid.lsb() >> UUID_VARIANT_SHIFT) & UUID_VARIANT_MASK, 127 | UUID_VARIANT_RFC4122); 128 | } 129 | 130 | // Test custom time and random source with builder 131 | TEST(UuidBuilderTest, CustomTimeAndRandomSource) { // NOLINT 132 | constexpr std::time_t FIXED_TIME_T = 1623456789; 133 | constexpr uint64_t FIXED_RANDOM_UINT = 0x1234567890ABCDEF; 134 | // Create a custom time source that returns a fixed timestamp 135 | auto fixed_time = std::chrono::system_clock::from_time_t(FIXED_TIME_T); 136 | auto time_source = [fixed_time]() { return fixed_time; }; 137 | 138 | // Create a custom random source that returns a fixed random value 139 | uint64_t fixed_random = FIXED_RANDOM_UINT; 140 | auto random_source = [fixed_random]() { return fixed_random; }; 141 | 142 | // Create a UuidBuilder with the custom time and random sources 143 | auto builder = builder::UuidBuilder::getTestBuilder() 144 | .withTimeSource(time_source) 145 | .withRandomSource(random_source); 146 | 147 | // Generate a UUID using the custom sources 148 | auto uuid = builder.build(); 149 | 150 | // Extract the timestamp and random value from the generated UUID 151 | uint64_t timestamp = uuid.msb() >> UUID_TIMESTAMP_SHIFT; 152 | uint64_t random_value = uuid.lsb() & UUID_RANDOM_B_MASK; 153 | 154 | // Convert the fixed timestamp to milliseconds 155 | auto fixed_time_ms = std::chrono::duration_cast( 156 | fixed_time.time_since_epoch()) 157 | .count(); 158 | 159 | // Check if the generated UUID matches the expected values 160 | EXPECT_EQ(timestamp, fixed_time_ms); 161 | EXPECT_EQ(random_value, fixed_random); 162 | } 163 | 164 | } // namespace uprotocol::datamodel 165 | -------------------------------------------------------------------------------- /test/coverage/utils/CyclicQueueTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | 15 | namespace { 16 | 17 | class TestFixture : public testing::Test { 18 | protected: 19 | // Run once per TEST_F. 20 | // Used to set up clean environments per test. 21 | void SetUp() override {} 22 | void TearDown() override {} 23 | 24 | // Run once per execution of the test application. 25 | // Used for setup of all tests. Has access to this instance. 26 | TestFixture() = default; 27 | 28 | // Run once per execution of the test application. 29 | // Used only for global setup outside of tests. 30 | static void SetUpTestSuite() {} 31 | static void TearDownTestSuite() {} 32 | 33 | public: 34 | ~TestFixture() override = default; 35 | }; 36 | 37 | // TODO(unknown) 38 | TEST_F(TestFixture, SomeTestName2) {} // NOLINT 39 | 40 | } // namespace 41 | -------------------------------------------------------------------------------- /test/coverage/utils/IpAddressTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | 15 | namespace { 16 | 17 | class TestFixture : public testing::Test { 18 | protected: 19 | // Run once per TEST_F. 20 | // Used to set up clean environments per test. 21 | void SetUp() override {} 22 | void TearDown() override {} 23 | 24 | // Run once per execution of the test application. 25 | // Used for setup of all tests. Has access to this instance. 26 | TestFixture() = default; 27 | 28 | // Run once per execution of the test application. 29 | // Used only for global setup outside of tests. 30 | static void SetUpTestSuite() {} 31 | static void TearDownTestSuite() {} 32 | 33 | public: 34 | ~TestFixture() override = default; 35 | }; 36 | 37 | // TODO(unknown) 38 | TEST_F(TestFixture, SomeTestName1) {} // NOLINT 39 | 40 | } // namespace 41 | -------------------------------------------------------------------------------- /test/coverage/utils/ThreadPoolTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | 15 | namespace { 16 | 17 | class TestFixture : public testing::Test { 18 | protected: 19 | // Run once per TEST_F. 20 | // Used to set up clean environments per test. 21 | void SetUp() override {} 22 | void TearDown() override {} 23 | 24 | // Run once per execution of the test application. 25 | // Used for setup of all tests. Has access to this instance. 26 | TestFixture() = default; 27 | 28 | // Run once per execution of the test application. 29 | // Used only for global setup outside of tests. 30 | static void SetUpTestSuite() {} 31 | static void TearDownTestSuite() {} 32 | 33 | public: 34 | ~TestFixture() override = default; 35 | }; 36 | 37 | // TODO(unknown) 38 | TEST_F(TestFixture, SomeTestName3) {} // NOLINT 39 | 40 | } // namespace 41 | -------------------------------------------------------------------------------- /test/coverage/utils/base64Test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | 15 | namespace { 16 | 17 | class TestFixture : public testing::Test { 18 | protected: 19 | // Run once per TEST_F. 20 | // Used to set up clean environments per test. 21 | void SetUp() override {} 22 | void TearDown() override {} 23 | 24 | // Run once per execution of the test application. 25 | // Used for setup of all tests. Has access to this instance. 26 | TestFixture() = default; 27 | 28 | // Run once per execution of the test application. 29 | // Used only for global setup outside of tests. 30 | static void SetUpTestSuite() {} 31 | static void TearDownTestSuite() {} 32 | 33 | public: 34 | ~TestFixture() override = default; 35 | }; 36 | 37 | // TODO(unknown) 38 | TEST_F(TestFixture, SomeTestName0) {} // NOLINT 39 | 40 | } // namespace 41 | -------------------------------------------------------------------------------- /test/extra/NotificationTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "UTransportMock.h" 18 | #include "up-cpp/datamodel/serializer/UUri.h" 19 | 20 | constexpr uint32_t DEFAULT_UE_ID = 0x00011101; 21 | constexpr uint32_t DEFAULT_RESOURCE_ID = 0x8101; 22 | constexpr uint32_t DEFAULT_SOURCE_UE_ID = 0x18000; 23 | constexpr uint16_t DEFAULT_VERSION_MAJOR = 0xF8; 24 | constexpr std::chrono::milliseconds THOUSAND_MILLISECONDS(1000); 25 | 26 | namespace uprotocol::datamodel::serializer::uri { 27 | 28 | class NotificationTest : public testing::Test { 29 | protected: 30 | // Run once per TEST_F. 31 | // Used to set up clean environments per test. 32 | void SetUp() override {} 33 | void TearDown() override {} 34 | 35 | // Run once per execution of the test application. 36 | // Used for setup of all tests. Has access to this instance. 37 | NotificationTest() = default; 38 | 39 | // Run once per execution of the test application. 40 | // Used only for global setup outside of tests. 41 | static void SetUpTestSuite() {} 42 | static void TearDownTestSuite() {} 43 | 44 | [[nodiscard]] static uprotocol::v1::UUri buildValidTestTopic(); 45 | [[nodiscard]] static uprotocol::v1::UUri buildValidDefaultSourceURI(); 46 | 47 | public: 48 | ~NotificationTest() override = default; 49 | }; 50 | 51 | [[nodiscard]] uprotocol::v1::UUri 52 | NotificationTest::buildValidDefaultSourceURI() { 53 | uprotocol::v1::UUri test_default_source_uri; 54 | test_default_source_uri.set_authority_name("10.0.0.1"); 55 | test_default_source_uri.set_ue_id(DEFAULT_SOURCE_UE_ID); 56 | test_default_source_uri.set_ue_version_major(0x1); 57 | test_default_source_uri.set_resource_id(0x0); 58 | return test_default_source_uri; 59 | } 60 | 61 | [[nodiscard]] uprotocol::v1::UUri NotificationTest::buildValidTestTopic() { 62 | uprotocol::v1::UUri test_topic; 63 | test_topic.set_authority_name("10.0.0.2"); 64 | test_topic.set_ue_id(DEFAULT_UE_ID); 65 | test_topic.set_ue_version_major(DEFAULT_VERSION_MAJOR); 66 | test_topic.set_resource_id(DEFAULT_RESOURCE_ID); 67 | return test_topic; 68 | } 69 | 70 | TEST_F(NotificationTest, NotificationSuccess) { // NOLINT 71 | // Initialize 72 | uprotocol::v1::UPayloadFormat format = 73 | uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_TEXT; 74 | std::optional priority = 75 | uprotocol::v1::UPriority::UPRIORITY_CS1; 76 | 77 | std::optional ttl = THOUSAND_MILLISECONDS; 78 | uprotocol::v1::UUri test_default_source_uri = buildValidDefaultSourceURI(); 79 | uprotocol::v1::UUri test_topic = buildValidTestTopic(); 80 | 81 | // Notify Sink 82 | auto transport_mock_notification_sink = 83 | std::make_shared( 84 | test_default_source_uri); 85 | 86 | uprotocol::v1::UMessage capture_msg; 87 | auto callback = [&capture_msg](const auto& message) { 88 | capture_msg = message; 89 | }; 90 | 91 | auto result = uprotocol::communication::NotificationSink::create( 92 | transport_mock_notification_sink, std::move(callback), test_topic); 93 | 94 | // Notify Source 95 | std::string test_payload_str = "test_payload"; 96 | auto transport_mock_notification_source = 97 | std::make_shared( 98 | test_default_source_uri); 99 | 100 | auto movable_topic = test_topic; 101 | 102 | uprotocol::communication::NotificationSource notification_source( 103 | transport_mock_notification_source, std::move(movable_topic), 104 | std::move(test_default_source_uri), format, priority, ttl); 105 | 106 | uprotocol::datamodel::builder::Payload test_payload(test_payload_str, 107 | format); 108 | auto status = notification_source.notify(std::move(test_payload)); 109 | 110 | EXPECT_EQ( 111 | AsString::serialize(transport_mock_notification_source->getMessage() 112 | .attributes() 113 | .source()), 114 | AsString::serialize( 115 | transport_mock_notification_sink->getSourceFilter())); 116 | 117 | EXPECT_EQ( 118 | AsString::serialize(transport_mock_notification_source->getMessage() 119 | .attributes() 120 | .sink()), 121 | AsString::serialize( 122 | transport_mock_notification_sink->getSinkFilter().value())); 123 | 124 | // Manually bridge the two transports 125 | transport_mock_notification_sink->mockMessage( 126 | transport_mock_notification_source->getMessage()); 127 | 128 | // Test 129 | EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals( 130 | transport_mock_notification_source->getMessage(), capture_msg)); 131 | EXPECT_EQ(test_payload_str, capture_msg.payload()); 132 | } 133 | 134 | } // namespace uprotocol::datamodel::serializer::uri 135 | -------------------------------------------------------------------------------- /test/extra/PublisherSubscriberTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include "UTransportMock.h" 18 | #include "up-cpp/communication/Publisher.h" 19 | #include "up-cpp/communication/Subscriber.h" 20 | #include "up-cpp/datamodel/serializer/UUri.h" 21 | 22 | constexpr uint32_t DEFAULT_SOURCE_UE_ID = 0x00011101; 23 | constexpr uint32_t DEFAULT_TOPIC_UE_ID = 0x10010001; 24 | constexpr uint32_t DEFAULT_RESOURCE_ID = 0x8101; 25 | constexpr uint16_t DEFAULT_SOURCE_VERSION_MAJOR = 0xF1; 26 | constexpr uint16_t DEFAULT_TOPIC_VERSION_MAJOR = 0xF8; 27 | constexpr std::chrono::milliseconds THOUSAND_MILLISECONDS(1000); 28 | 29 | namespace uprotocol::v1 { 30 | 31 | class TestPublisherSubscriber : public testing::Test { 32 | private: 33 | std::shared_ptr transportMock_; 34 | UUri source_; 35 | UUri topic_; 36 | UPayloadFormat format_ = UPayloadFormat::UPAYLOAD_FORMAT_TEXT; 37 | std::optional priority_; 38 | std::optional ttl_; 39 | 40 | protected: 41 | std::shared_ptr getTransportMock() const { 42 | return transportMock_; 43 | } 44 | UUri getSource() const { return source_; } 45 | UUri getTopic() const { return topic_; } 46 | UPayloadFormat getFormat() const { return format_; } 47 | std::optional& getPriority() { return priority_; } 48 | std::optional getTTL() const { return ttl_; } 49 | 50 | // Run once per TEST_F. 51 | // Used to set up clean environments per test. 52 | void SetUp() override { 53 | source_.set_authority_name("10.0.0.1"); 54 | source_.set_ue_id(DEFAULT_SOURCE_UE_ID); 55 | source_.set_ue_version_major(DEFAULT_SOURCE_VERSION_MAJOR); 56 | source_.set_resource_id(0x0); 57 | 58 | topic_.set_authority_name("10.0.0.1"); 59 | topic_.set_ue_id(DEFAULT_TOPIC_UE_ID); 60 | topic_.set_ue_version_major(DEFAULT_TOPIC_VERSION_MAJOR); 61 | topic_.set_resource_id(DEFAULT_RESOURCE_ID); 62 | transportMock_ = 63 | std::make_shared(source_); 64 | 65 | format_ = UPayloadFormat::UPAYLOAD_FORMAT_TEXT; 66 | priority_ = UPriority::UPRIORITY_CS2; 67 | ttl_ = THOUSAND_MILLISECONDS; 68 | } 69 | 70 | void TearDown() override {} 71 | 72 | // Run once per execution of the test application. 73 | // Used for setup of all tests. Has access to this instance. 74 | TestPublisherSubscriber() = default; 75 | 76 | // Run once per execution of the test application. 77 | // Used only for global setup outside of tests. 78 | static void SetUpTestSuite() {} 79 | static void TearDownTestSuite() {} 80 | 81 | public: 82 | ~TestPublisherSubscriber() override = default; 83 | }; 84 | 85 | TEST_F(TestPublisherSubscriber, PubSubSuccess) { // NOLINT 86 | // sub 87 | auto transport_sub = 88 | std::make_shared(getSource()); 89 | 90 | uprotocol::v1::UMessage captured_message; 91 | auto callback = [&captured_message](auto message) { 92 | captured_message = std::move(message); 93 | }; 94 | 95 | auto result = uprotocol::communication::Subscriber::subscribe( 96 | transport_sub, getTopic(), std::move(callback)); 97 | 98 | // pub 99 | std::string test_payload_str = "test_payload"; 100 | auto movable_topic = getTopic(); 101 | uprotocol::communication::Publisher publisher( 102 | getTransportMock(), std::move(movable_topic), getFormat(), 103 | getPriority(), getTTL()); 104 | 105 | uprotocol::v1::UStatus retval; 106 | retval.set_code(uprotocol::v1::UCode::OK); 107 | getTransportMock()->getSendStatus() = retval; 108 | 109 | uprotocol::datamodel::builder::Payload test_payload(test_payload_str, 110 | getFormat()); 111 | auto status = publisher.publish(std::move(test_payload)); 112 | 113 | // Test 114 | EXPECT_EQ(uprotocol::datamodel::serializer::uri::AsString::serialize( 115 | getTransportMock()->getMessage().attributes().source()), 116 | uprotocol::datamodel::serializer::uri::AsString::serialize( 117 | transport_sub->getSourceFilter())); 118 | 119 | // Manually bridge the two transports 120 | transport_sub->mockMessage(getTransportMock()->getMessage()); 121 | 122 | // Test 123 | EXPECT_TRUE(google::protobuf::util::MessageDifferencer::Equals( 124 | getTransportMock()->getMessage(), captured_message)); 125 | EXPECT_EQ(test_payload_str, captured_message.payload()); 126 | } 127 | 128 | } // namespace uprotocol::v1 129 | -------------------------------------------------------------------------------- /test/extra/RpcClientServerTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | using namespace std::chrono_literals; 23 | 24 | namespace uprotocol::v1 { 25 | 26 | struct UeDetails { 27 | uint32_t ue_id; 28 | uint32_t ue_version_major; 29 | }; 30 | 31 | struct MyUUri { 32 | static constexpr uint32_t DEFAULT_UE_ID = 0x8000; 33 | 34 | public: 35 | MyUUri(std::string auth_val, UeDetails ue_details, uint32_t resource_id_val) 36 | : auth(std::move(auth_val)), 37 | ue_id(ue_details.ue_id), 38 | ue_version_major(ue_details.ue_version_major), 39 | resource_id(resource_id_val) {} 40 | 41 | [[nodiscard]] const std::string& get_auth() const { return auth; } 42 | 43 | void set_ue_details(UeDetails ue_details) { 44 | ue_id = ue_details.ue_id; 45 | ue_version_major = ue_details.ue_version_major; 46 | } 47 | 48 | [[nodiscard]] uint32_t get_ue_id() const { return ue_id; } 49 | [[nodiscard]] uint32_t get_ue_version_major() const { 50 | return ue_version_major; 51 | } 52 | 53 | void set_resource_id(uint32_t resource) { resource_id = resource; } 54 | [[nodiscard]] uint32_t get_resource_id() const { return resource_id; } 55 | 56 | explicit operator uprotocol::v1::UUri() const { 57 | UUri ret; 58 | ret.set_authority_name(auth); 59 | ret.set_ue_id(ue_id); 60 | ret.set_ue_version_major(ue_version_major); 61 | ret.set_resource_id(resource_id); 62 | return ret; 63 | } 64 | 65 | [[nodiscard]] std::string to_string() const { 66 | return std::string("<< ") + UUri(*this).ShortDebugString() + " >>"; 67 | } 68 | 69 | private: 70 | std::string auth; 71 | uint32_t ue_id = DEFAULT_UE_ID; 72 | uint32_t ue_version_major = 1; 73 | uint32_t resource_id = 1; 74 | }; 75 | 76 | class RpcClientServerTest : public testing::Test { 77 | protected: 78 | // Run once per TEST_F.s 79 | // Used to set up clean environments per test. 80 | void SetUp() override {} 81 | 82 | void TearDown() override {} 83 | 84 | // Run once per execution of the test application. 85 | // Used for setup of all tests. Has access to this instance. 86 | RpcClientServerTest() = default; 87 | 88 | // Run once per execution of the test application. 89 | // Used only for global setup outside of tests. 90 | static void SetUpTestSuite() {} 91 | static void TearDownTestSuite() {} 92 | 93 | public: 94 | ~RpcClientServerTest() override = default; 95 | }; 96 | 97 | TEST_F(RpcClientServerTest, SimpleRoundTrip) { // NOLINT 98 | const MyUUri ident{"me_authority", {65538, 1}, 0}; 99 | const MyUUri rpc_service_uuri{"me_authority", {65538, 1}, 32600}; 100 | auto server_transport = std::make_shared( 101 | static_cast(ident)); 102 | auto client_transport = std::make_shared( 103 | static_cast(ident)); 104 | 105 | // if the client and server try to share the transport handle, this test no 106 | // longer works auto client_transport = server_transport; 107 | 108 | std::string client_request{"RPC Request"}; // NOLINT 109 | uprotocol::datamodel::builder::Payload client_request_payload( 110 | client_request, UPayloadFormat::UPAYLOAD_FORMAT_TEXT); 111 | bool client_called = false; 112 | UMessage client_capture; // NOLINT 113 | 114 | bool server_called = false; 115 | UMessage server_capture; // NOLINT 116 | std::string server_response{"RPC Response"}; // NOLINT 117 | uprotocol::datamodel::builder::Payload server_response_payload( 118 | server_response, UPayloadFormat::UPAYLOAD_FORMAT_TEXT); 119 | 120 | auto server_or_status = uprotocol::communication::RpcServer::create( 121 | server_transport, v1::UUri(rpc_service_uuri), 122 | [&server_called, &server_capture, 123 | &server_response_payload](const UMessage& message) { 124 | server_called = true; 125 | server_capture = message; 126 | return server_response_payload; 127 | }, 128 | UPayloadFormat::UPAYLOAD_FORMAT_TEXT); 129 | ASSERT_TRUE(server_or_status.has_value()); 130 | ASSERT_NE(server_or_status.value(), nullptr); 131 | EXPECT_TRUE(server_transport->getListener()); 132 | 133 | auto client = uprotocol::communication::RpcClient( 134 | client_transport, v1::UUri(rpc_service_uuri), UPriority::UPRIORITY_CS4, 135 | 1000ms); 136 | 137 | uprotocol::communication::RpcClient::InvokeHandle client_handle; 138 | EXPECT_NO_THROW( // NOLINT 139 | client_handle = client.invokeMethod( 140 | std::move(client_request_payload), 141 | [&client_called, &client_capture](auto maybe_response) { 142 | client_called = true; 143 | if (maybe_response.has_value()) { 144 | client_capture = maybe_response.value(); 145 | } 146 | })); 147 | EXPECT_TRUE(client_transport->getSendCount() == 1); 148 | EXPECT_TRUE(client_transport->getListener()); 149 | 150 | (*server_transport->getListener())(client_transport->getMessage()); 151 | EXPECT_TRUE(server_called); 152 | EXPECT_EQ(client_request, server_capture.payload()); 153 | 154 | client_transport->mockMessage(server_transport->getMessage()); 155 | EXPECT_TRUE(client_called); 156 | EXPECT_EQ(server_response, client_capture.payload()); 157 | } 158 | 159 | } // namespace uprotocol::v1 160 | -------------------------------------------------------------------------------- /test/extra/UTransportMockTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | 22 | constexpr uint16_t STR_MAX_LEN = 32; 23 | constexpr uint16_t PAYLOAD_STR_MAX_LEN = 1400; 24 | constexpr uint16_t RANDOM_INT_MAX = 100; 25 | constexpr uint32_t DEFAULT_UE_ID = 0x00010001; 26 | constexpr uint32_t DEFAULT_RESOURCE_ID = 0x8000; 27 | constexpr uint16_t ATTR_TTL = 1000; 28 | 29 | using MsgDiff = google::protobuf::util::MessageDifferencer; 30 | 31 | std::string get_random_string(size_t max_len = STR_MAX_LEN) { 32 | std::random_device random_dev; 33 | std::mt19937 random_gen(random_dev()); 34 | std::uniform_int_distribution char_dist('A', 'z'); 35 | std::uniform_int_distribution len_dist(1, static_cast(max_len)); 36 | auto len = static_cast(len_dist(random_gen)); 37 | std::string retval; 38 | retval.reserve(len); 39 | for (size_t i = 0; i < len; i++) { 40 | retval += static_cast(char_dist(random_gen)); 41 | } 42 | return retval; 43 | } 44 | 45 | int get_random_int(int mn = 0, int mx = RANDOM_INT_MAX) { 46 | std::random_device random_dev; 47 | std::mt19937 random_gen(random_dev()); 48 | std::uniform_int_distribution int_dist(mn, mx); 49 | return int_dist(random_gen); 50 | } 51 | 52 | uprotocol::v1::UUID make_uuid() { 53 | auto id = uprotocol::datamodel::builder::UuidBuilder::getBuilder().build(); 54 | return id; 55 | } 56 | 57 | namespace { 58 | 59 | class TestMockUTransport : public testing::Test { 60 | protected: 61 | // Run once per TEST_F. 62 | // Used to set up clean environments per test. 63 | void SetUp() override {} 64 | 65 | void TearDown() override {} 66 | 67 | // Run once per execution of the test application. 68 | // Used for setup of all tests. Has access to this instance. 69 | TestMockUTransport() = default; 70 | 71 | // Run once per execution of the test application. 72 | // Used only for global setup outside of tests. 73 | static void SetUpTestSuite() {} 74 | static void TearDownTestSuite() {} 75 | 76 | public: 77 | ~TestMockUTransport() override = default; 78 | }; 79 | 80 | TEST_F(TestMockUTransport, Send) { // NOLINT 81 | constexpr uint32_t DEF_SRC_UE_ID = 0x18000; 82 | constexpr uint16_t CODE_MAX = 15; 83 | constexpr uint16_t CODE_MOD = 16; 84 | 85 | uprotocol::v1::UUri def_src_uuri; 86 | def_src_uuri.set_authority_name(get_random_string()); 87 | def_src_uuri.set_ue_id(DEF_SRC_UE_ID); 88 | def_src_uuri.set_ue_version_major(1); 89 | def_src_uuri.set_resource_id(0); 90 | 91 | auto transport = 92 | std::make_shared(def_src_uuri); 93 | EXPECT_NE(nullptr, transport); 94 | EXPECT_TRUE(MsgDiff::Equals(def_src_uuri, transport->getDefaultSource())); 95 | 96 | const size_t max_count = 100000; 97 | for (size_t i = 0; i < max_count; i++) { 98 | auto src = std::make_unique(); 99 | src->set_authority_name("10.0.0.1"); 100 | src->set_ue_id(DEFAULT_UE_ID); 101 | src->set_ue_version_major(1); 102 | src->set_resource_id(DEFAULT_RESOURCE_ID); 103 | 104 | // auto sink = new uprotocol::v1::UUri(); 105 | // sink->set_authority_name("10.0.0.2"); 106 | // sink->set_ue_id(0x00010002); 107 | // sink->set_ue_version_major(2); 108 | // sink->set_resource_id(2); 109 | 110 | auto attr = std::make_unique(); 111 | attr->set_type(uprotocol::v1::UMESSAGE_TYPE_PUBLISH); 112 | *attr->mutable_id() = make_uuid(); 113 | attr->set_allocated_source(src.release()); 114 | // attr->set_allocated_sink(sink); 115 | attr->set_payload_format(uprotocol::v1::UPAYLOAD_FORMAT_PROTOBUF); 116 | attr->set_ttl(ATTR_TTL); 117 | 118 | uprotocol::v1::UMessage msg; 119 | msg.set_allocated_attributes(attr.release()); 120 | msg.set_payload(get_random_string(PAYLOAD_STR_MAX_LEN)); 121 | transport->getSendStatus().set_code( 122 | static_cast(CODE_MAX - (i % CODE_MOD))); 123 | transport->getSendStatus().set_message(get_random_string()); 124 | 125 | auto result = transport->send(msg); 126 | EXPECT_EQ(i + 1, transport->getSendCount()); 127 | EXPECT_TRUE(MsgDiff::Equals(result, transport->getSendStatus())); 128 | EXPECT_TRUE(MsgDiff::Equals(msg, transport->getMessage())); 129 | } 130 | } 131 | 132 | TEST_F(TestMockUTransport, registerListener) { // NOLINT 133 | constexpr uint32_t DEF_SRC_UE_ID = 0x18000; 134 | uprotocol::v1::UUri def_src_uuri; 135 | def_src_uuri.set_authority_name(get_random_string()); 136 | def_src_uuri.set_ue_id(DEF_SRC_UE_ID); 137 | def_src_uuri.set_ue_version_major(1); 138 | def_src_uuri.set_resource_id(0); 139 | 140 | auto transport = 141 | std::make_shared(def_src_uuri); 142 | EXPECT_NE(nullptr, transport); 143 | EXPECT_TRUE(MsgDiff::Equals(def_src_uuri, transport->getDefaultSource())); 144 | 145 | uprotocol::v1::UUri sink_filter; 146 | sink_filter.set_authority_name(get_random_string()); 147 | sink_filter.set_ue_id(DEFAULT_UE_ID); 148 | sink_filter.set_ue_version_major(1); 149 | sink_filter.set_resource_id(DEFAULT_RESOURCE_ID); 150 | 151 | uprotocol::v1::UUri source_filter; 152 | source_filter.set_authority_name(get_random_string()); 153 | source_filter.set_ue_id(DEFAULT_UE_ID); 154 | source_filter.set_ue_version_major(1); 155 | source_filter.set_resource_id(DEFAULT_RESOURCE_ID); 156 | 157 | uprotocol::v1::UMessage capture_msg; 158 | size_t capture_count = 0; 159 | auto action = [&](const uprotocol::v1::UMessage& msg) { 160 | capture_msg = msg; 161 | capture_count++; 162 | }; 163 | auto lhandle = 164 | transport->registerListener(sink_filter, action, source_filter); 165 | EXPECT_TRUE(transport->getListener()); 166 | // EXPECT_EQ(*mock_info.listener, action); // need exposed target_type() to 167 | // make comparable. 168 | EXPECT_TRUE(lhandle.has_value()); 169 | auto handle = std::move(lhandle).value(); 170 | EXPECT_TRUE(handle); 171 | EXPECT_TRUE(transport->getSinkFilter()); 172 | EXPECT_TRUE(MsgDiff::Equals(sink_filter, *transport->getSinkFilter())); 173 | EXPECT_TRUE(MsgDiff::Equals(source_filter, transport->getSourceFilter())); 174 | 175 | const size_t max_count = 100000; 176 | for (size_t i = 0; i < max_count; i++) { 177 | uprotocol::v1::UMessage msg; 178 | auto attr = std::make_unique(); 179 | msg.set_allocated_attributes(attr.release()); 180 | msg.set_payload(get_random_string(PAYLOAD_STR_MAX_LEN)); 181 | transport->mockMessage(msg); 182 | EXPECT_EQ(i + 1, capture_count); 183 | EXPECT_TRUE(MsgDiff::Equals(msg, capture_msg)); 184 | } 185 | } 186 | 187 | } // namespace 188 | -------------------------------------------------------------------------------- /test/include/UTransportMock.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Contributors to the Eclipse Foundation 2 | // 3 | // See the NOTICE file(s) distributed with this work for additional 4 | // information regarding copyright ownership. 5 | // 6 | // This program and the accompanying materials are made available under the 7 | // terms of the Apache License Version 2.0 which is available at 8 | // https://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // SPDX-License-Identifier: Apache-2.0 11 | 12 | #ifndef UTRANSPORTMOCK_H 13 | #define UTRANSPORTMOCK_H 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | namespace uprotocol::test { 22 | 23 | class UTransportMock : public uprotocol::transport::UTransport { 24 | public: 25 | explicit UTransportMock(const v1::UUri& uuri) 26 | : uprotocol::transport::UTransport(uuri), send_count_(0) { 27 | registerListener_status_.set_code(v1::UCode::OK); 28 | } 29 | 30 | void mockMessage(const uprotocol::v1::UMessage& msg) { 31 | ASSERT_TRUE(listener_ && 32 | "registerListener must be set before calling mock_packet"); 33 | (*listener_)(msg); 34 | } 35 | 36 | size_t getSendCount() const { return send_count_.load(); } 37 | uprotocol::v1::UStatus& getSendStatus() { return send_status_; } 38 | uprotocol::v1::UStatus& getRegisterListenerStatus() { 39 | return registerListener_status_; 40 | } 41 | std::optional> 43 | getListener() const { 44 | return listener_; 45 | } 46 | std::optional> 48 | getCleanupListener() const { 49 | return cleanup_listener_; 50 | } 51 | std::optional getSinkFilter() const { 52 | return sink_filter_; 53 | } 54 | v1::UUri getSourceFilter() const { return source_filter_; } 55 | std::mutex& getRegisterMtx() { return register_mtx_; } 56 | v1::UMessage getMessage() const { return message_; } 57 | std::mutex& getMessageMtx() { return message_mtx_; } 58 | 59 | ~UTransportMock() override = default; 60 | 61 | private: 62 | std::atomic send_count_; 63 | 64 | uprotocol::v1::UStatus send_status_; 65 | uprotocol::v1::UStatus registerListener_status_; 66 | 67 | std::optional> 69 | listener_; 70 | std::optional> 72 | cleanup_listener_; 73 | std::optional sink_filter_; 74 | v1::UUri source_filter_; 75 | std::mutex register_mtx_; 76 | 77 | v1::UMessage message_; 78 | std::mutex message_mtx_; 79 | [[nodiscard]] v1::UStatus sendImpl(const v1::UMessage& message) override { 80 | { 81 | std::lock_guard lock(message_mtx_); 82 | message_ = message; 83 | } 84 | send_count_++; 85 | return send_status_; 86 | } 87 | 88 | [[nodiscard]] v1::UStatus registerListenerImpl( 89 | CallableConn&& listener, const v1::UUri& source_filter, 90 | std::optional&& sink_filter) override { 91 | std::lock_guard lock(register_mtx_); 92 | listener_ = listener; 93 | source_filter_ = source_filter; 94 | sink_filter_ = sink_filter; 95 | return registerListener_status_; 96 | } 97 | 98 | void cleanupListener(const CallableConn& listener) override { 99 | cleanup_listener_ = listener; 100 | } 101 | }; 102 | 103 | }; // namespace uprotocol::test 104 | 105 | #endif // UTRANSPORTMOCK_H 106 | -------------------------------------------------------------------------------- /test/sanitizers/valgrind_exclude_test_dhat.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eclipse-uprotocol/up-cpp/94ec934097e4a2781943a3dd5c0071c1d16cbbaa/test/sanitizers/valgrind_exclude_test_dhat.txt -------------------------------------------------------------------------------- /test/sanitizers/valgrind_exclude_test_helgrind.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eclipse-uprotocol/up-cpp/94ec934097e4a2781943a3dd5c0071c1d16cbbaa/test/sanitizers/valgrind_exclude_test_helgrind.txt -------------------------------------------------------------------------------- /test/sanitizers/valgrind_exclude_test_memcheck.txt: -------------------------------------------------------------------------------- 1 | UuidBuilderTest.UuidBuilderTest.WithIndependentState 2 | UuidBuilderTest.UuidBuilderTest.CounterIncrementWithinTimestampTick 3 | UuidBuilderTest.TestUuidBuilder.CounterIncrements 4 | -------------------------------------------------------------------------------- /test/sanitizers/valgrind_exclude_test_threadcheck.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eclipse-uprotocol/up-cpp/94ec934097e4a2781943a3dd5c0071c1d16cbbaa/test/sanitizers/valgrind_exclude_test_threadcheck.txt --------------------------------------------------------------------------------