diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf..000000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 0bb8c1909..000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,22 +0,0 @@ -clone_depth: 5 - -environment: - matrix: - - GENERATOR : "Visual Studio 15 2017 Win64" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 - PLATFORM: x64 - - -configuration: - - Release - -install: - - set PATH=C:\MinGW\bin;C:\MinGW\msys\1.0;%PATH% - -before_build: - - mkdir build - - cd build - - cmake "-G%GENERATOR%" -DCMAKE_IGNORE_PATH="C:/Program Files/Git/usr/bin" .. - -build_script: -- cmake --build . diff --git a/.clang-format b/.clang-format index d61f342e7..80f4f718b 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ --- -BasedOnStyle: Google +BasedOnStyle: Google AccessModifierOffset: -2 ConstructorInitializerIndentWidth: 2 AlignEscapedNewlinesLeft: false @@ -8,14 +8,13 @@ AllowAllParametersOfDeclarationOnNextLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortFunctionsOnASingleLine: None -AllowShortLoopsOnASingleLine: false AlwaysBreakTemplateDeclarations: true AlwaysBreakBeforeMultilineStrings: false BreakBeforeBinaryOperators: false BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: BeforeComma BinPackParameters: true -ColumnLimit: 100 +ColumnLimit: 90 ConstructorInitializerAllOnOneLineOrOnePerLine: true DerivePointerBinding: false PointerBindsToType: true @@ -30,19 +29,20 @@ PenaltyBreakString: 1 PenaltyBreakFirstLessLess: 1000 PenaltyExcessCharacter: 1000 PenaltyReturnTypeOnItsOwnLine: 90 -SpacesBeforeTrailingComments: 3 -Cpp11BracedListStyle: true -Standard: Auto -IndentWidth: 4 -TabWidth: 4 -UseTab: Never +SpacesBeforeTrailingComments: 2 +Cpp11BracedListStyle: false +Standard: Auto +IndentWidth: 2 +TabWidth: 2 +UseTab: Never IndentFunctionDeclarationAfterType: false SpacesInParentheses: false -SpacesInAngles: false +SpacesInAngles: false SpaceInEmptyParentheses: false SpacesInCStyleCastParentheses: false SpaceAfterControlStatementKeyword: true SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: Never ContinuationIndentWidth: 4 SortIncludes: false SpaceAfterCStyleCast: false @@ -53,16 +53,16 @@ BreakBeforeBraces: Custom # Control of individual brace wrapping cases BraceWrapping: { - AfterClass: 'true' - AfterControlStatement: 'true' - AfterEnum : 'true' - AfterFunction : 'true' - AfterNamespace : 'true' - AfterStruct : 'true' - AfterUnion : 'true' - BeforeCatch : 'true' - BeforeElse : 'true' - IndentBraces : 'false' + AfterClass: 'true', + AfterControlStatement: 'true', + AfterEnum : 'true', + AfterFunction : 'true', + AfterNamespace : 'true', + AfterStruct : 'true', + AfterUnion : 'true', + BeforeCatch : 'true', + BeforeElse : 'true', + IndentBraces : 'false', + SplitEmptyFunction: 'false' } ... - diff --git a/.codespell_ignore_words b/.codespell_ignore_words new file mode 100644 index 000000000..ab09b3c2f --- /dev/null +++ b/.codespell_ignore_words @@ -0,0 +1,7 @@ +INOUT +InOut +delimeter +Succesful +worl +valu +Exeption diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..9eb010608 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,19 @@ +--- +name: Bug report +about: Help me help you... +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +If you are experiencing a crash, provide a backtrace (GDB or similar). + +*How to Reproduce** + +Please provide a specific description of how to reproduce the issue or source code that can be compiled and executed. Please attach a file/project that is easy to compile, don't copy and paste code snippets! + +Even better, create a Pull Request with a failing unit test. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..021458556 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..27c6ae66a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ + diff --git a/.github/workflows/cmake_ubuntu.yml b/.github/workflows/cmake_ubuntu.yml new file mode 100644 index 000000000..41ed9a196 --- /dev/null +++ b/.github/workflows/cmake_ubuntu.yml @@ -0,0 +1,59 @@ +name: cmake Ubuntu + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-22.04] + + steps: + - uses: actions/checkout@v2 + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Create default profile + run: conan profile detect + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Install conan dependencies + working-directory: ${{github.workspace}}/build + run: conan install ${{github.workspace}}/conanfile.txt -s build_type=${{env.BUILD_TYPE}} --build=missing + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build + run: cmake ${{github.workspace}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake + + - name: Build + shell: bash + working-directory: ${{github.workspace}}/build + run: cmake --build . --config ${{env.BUILD_TYPE}} + + - name: run test (Linux) + working-directory: ${{github.workspace}}/build/tests + run: ctest + + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 diff --git a/.github/workflows/cmake_windows.yml b/.github/workflows/cmake_windows.yml new file mode 100644 index 000000000..34f4f97ce --- /dev/null +++ b/.github/workflows/cmake_windows.yml @@ -0,0 +1,56 @@ +name: cmake Windows + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally + # well on Windows or Mac. You can convert this to a matrix build if you need + # cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [windows-latest] + + steps: + - uses: actions/checkout@v4 + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + + - name: Create default profile + run: conan profile detect + + - name: Create Build Environment + # Some projects don't allow in-source building, so create a separate build directory + # We'll use this as our working directory for all subsequent commands + run: cmake -E make_directory ${{github.workspace}}/build + + - name: Install conan dependencies + working-directory: ${{github.workspace}}/build + run: conan install ${{github.workspace}}/conanfile.txt -s build_type=${{env.BUILD_TYPE}} --build=missing + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake + + - name: Build + working-directory: ${{github.workspace}}/build + shell: bash + run: cmake --build . --config ${{env.BUILD_TYPE}} + + - name: run test (Windows) + working-directory: ${{github.workspace}}/build + run: $env:PATH+=";${{env.BUILD_TYPE}}"; tests/${{env.BUILD_TYPE}}/behaviortree_cpp_test.exe diff --git a/.github/workflows/codeql.yml.bkp b/.github/workflows/codeql.yml.bkp new file mode 100644 index 000000000..a76a623ca --- /dev/null +++ b/.github/workflows/codeql.yml.bkp @@ -0,0 +1,63 @@ +name: "CodeQL" + +on: + push: + branches: [ 'master' ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ 'master' ] + schedule: + - cron: '36 19 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + queries: +security-and-quality + + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/doxygen-gh-pages.yml b/.github/workflows/doxygen-gh-pages.yml new file mode 100644 index 000000000..b8fb8f46b --- /dev/null +++ b/.github/workflows/doxygen-gh-pages.yml @@ -0,0 +1,18 @@ +name: Doxygen GitHub Pages Deploy Action + +on: + push: + branches: + - main + - master + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: DenverCoder1/doxygen-github-pages-action@v2.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + folder: doc/html diff --git a/.github/workflows/pixi.yaml b/.github/workflows/pixi.yaml new file mode 100644 index 000000000..ddd1cbfb8 --- /dev/null +++ b/.github/workflows/pixi.yaml @@ -0,0 +1,27 @@ +name: Pixi (conda) + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + pixi_conda_build: + strategy: + matrix: + os: + - windows-latest + - ubuntu-latest + runs-on: ${{ matrix.os }} + steps: + # Pixi is the tool used to create/manage conda environment + - uses: actions/checkout@v3 + - uses: prefix-dev/setup-pixi@v0.8.1 + with: + pixi-version: v0.40.3 + - name: Build + run: pixi run build + - name: Run tests + run: pixi run test diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 000000000..ee7fa9229 --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,16 @@ +name: pre-commit + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.1 diff --git a/.github/workflows/ros1.yaml b/.github/workflows/ros1.yaml deleted file mode 100644 index e6d2f9be2..000000000 --- a/.github/workflows/ros1.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: ros1 - -on: [push, pull_request] - -jobs: - industrial_ci: - strategy: - matrix: - env: - - {ROS_DISTRO: melodic, ROS_REPO: main} - - {ROS_DISTRO: kinetic, ROS_REPO: main} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: 'ros-industrial/industrial_ci@master' - env: ${{matrix.env}} - with: - package-name: behaviortree_cpp_v3 diff --git a/.github/workflows/ros2-rolling.yaml b/.github/workflows/ros2-rolling.yaml new file mode 100644 index 000000000..446c49879 --- /dev/null +++ b/.github/workflows/ros2-rolling.yaml @@ -0,0 +1,22 @@ +name: ros2-rolling + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + industrial_ci: + strategy: + matrix: + env: + - {ROS_DISTRO: rolling, ROS_REPO: main} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: 'ros-industrial/industrial_ci@master' + env: ${{matrix.env}} + with: + package-name: plotjuggler diff --git a/.github/workflows/ros2.yaml b/.github/workflows/ros2.yaml index 9f8c5e764..099cc04f2 100644 --- a/.github/workflows/ros2.yaml +++ b/.github/workflows/ros2.yaml @@ -1,17 +1,23 @@ name: ros2 -on: [push, pull_request] +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] jobs: industrial_ci: strategy: matrix: env: - - {ROS_DISTRO: eloquent, ROS_REPO: main} + - {ROS_DISTRO: humble, ROS_REPO: main} + - {ROS_DISTRO: jazzy, ROS_REPO: main} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: 'ros-industrial/industrial_ci@master' env: ${{matrix.env}} with: - package-name: behaviortree_cpp_v3 + package-name: plotjuggler diff --git a/.github/workflows/sonarcube.yml.bkp b/.github/workflows/sonarcube.yml.bkp new file mode 100644 index 000000000..926306125 --- /dev/null +++ b/.github/workflows/sonarcube.yml.bkp @@ -0,0 +1,42 @@ +name: Sonarcube Scan + +on: + push: + branches: + - master + pull_request: + types: [opened, synchronize, reopened] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + env: + BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: Install Build Wrapper + uses: SonarSource/sonarqube-scan-action/install-build-wrapper@v4 + + - name: Install Dependencies + run: | + sudo apt-get update + sudo apt-get install -y libzmq3-dev libsqlite3-dev + + - name: Install googletest + uses: Bacondish2023/setup-googletest@v1 + + - name: Run Build Wrapper + run: | + mkdir build + cmake -S . -B build + build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build build/ --config Release + - name: SonarQube Scan + uses: SonarSource/sonarqube-scan-action@v4 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Put the name of your token here + with: + args: > + --define sonar.cfamily.compile-commands="${{ env.BUILD_WRAPPER_OUT_DIR }}/compile_commands.json" diff --git a/.gitignore b/.gitignore index 4e76fff0b..9d5bd4326 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,19 @@ /CMakeLists.txt.user build* site/* +/.vscode/ +.vs/ + +# clangd cache +/.cache/* +CMakeSettings.json + +# OSX junk +.DS_Store + +# pixi environments +.pixi + +CMakeUserPresets.json + +tags diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..d491f36d9 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,54 @@ + +# To use: +# +# pre-commit run -a +# +# Or: +# +# pre-commit install # (runs every time you commit in git) +# +# To update this file: +# +# pre-commit autoupdate +# +# See https://github.com/pre-commit/pre-commit + +exclude: ^3rdparty/|3rdparty|^include/behaviortree_cpp/contrib/ +repos: + + # Standard hooks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-docstring-first + - id: check-merge-conflict + - id: check-symlinks + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + exclude_types: [svg] + - id: mixed-line-ending + - id: trailing-whitespace + exclude_types: [svg] + - id: fix-byte-order-marker + + # CPP hooks + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v17.0.6 + hooks: + - id: clang-format + args: ['-fallback-style=none', '-i'] + + # Spell check + - repo: https://github.com/codespell-project/codespell + rev: v2.4.1 + hooks: + - id: codespell + additional_dependencies: + - tomli + args: + [--toml=./pyproject.toml] diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c0d1fdb3a..000000000 --- a/.travis.yml +++ /dev/null @@ -1,66 +0,0 @@ -# This config file for Travis CI utilizes ros-industrial/industrial_ci package. -# For more info for the package, see https://github.com/ros-industrial/industrial_ci/blob/master/README.rst - -sudo: required -dist: xenial -language: cpp - -os: - - linux - -compiler: - - gcc - -conan-linux: &conan-linux - os: linux - dist: xenial - language: python - python: "3.7" - services: - - docker - before_install: - - true - install: - - ./conan/travis/install.sh - script: - - ./conan/travis/build.sh - -conan-osx: &conan-osx - os: osx - language: generic - before_install: - - true - install: - - ./conan/travis/install.sh - script: - - ./conan/travis/build.sh - -matrix: - include: - - bare_linux: - env: ROS_DISTRO="none" - fast_finish: false - -before_install: - - sudo apt-get update && sudo apt-get --reinstall install -qq build-essential - - if [ "$ROS_DISTRO" = "none" ]; then sudo apt-get --reinstall install -qq libzmq3-dev libdw-dev; fi - # GTest: see motivation here https://www.eriksmistad.no/getting-started-with-google-test-on-ubuntu/ - - sudo apt-get --reinstall install -qq libgtest-dev cmake - - cd /usr/src/gtest - - sudo cmake CMakeLists.txt - - sudo make - - sudo cp *.a /usr/lib - - cd $TRAVIS_BUILD_DIR - -install: - - if [ "$ROS_DISTRO" != "none" ]; then git clone https://github.com/ros-industrial/industrial_ci.git .ci_config; fi - -before_script: - # Prepare build directory - - mkdir -p build - -script: - - if [ "$ROS_DISTRO" = "none" ]; then (cd build; cmake .. ; sudo cmake --build . --target install; ./bin/behaviortree_cpp_v3_test); fi - - if [ "$ROS_DISTRO" != "none" ]; then (.ci_config/travis.sh); fi - - diff --git a/3rdparty/cpp-sqlite/README.md b/3rdparty/cpp-sqlite/README.md new file mode 100644 index 000000000..913354947 --- /dev/null +++ b/3rdparty/cpp-sqlite/README.md @@ -0,0 +1,37 @@ +## Single file header only sqlite wrapper for C++ + +## Example +```cpp +#include "sqlite.hpp" +#include + +int main() +{ + sqlite::Connection connection("example.db"); + + sqlite::Statement(connection, "CREATE TABLE IF NOT EXISTS exampleTable (" + "textData TEXT, " + "intData INTEGER, " + "floatData REAL)"); + + sqlite::Statement(connection, + "INSERT INTO exampleTable VALUES (?, ?, ?)", + "Hello world", + 1234, + 5.6789); + + sqlite::Result res = sqlite::Query(connection, "SELECT * FROM exampleTable"); + + while(res.Next()) + { + std::string textData = res.Get(0); + int intData = res.Get(1); + float floatData = res.Get(2); + + std::cout << textData << " " << intData << " " << floatData << std::endl; + } + + return 0; +} + +``` diff --git a/3rdparty/cpp-sqlite/sqlite.hpp b/3rdparty/cpp-sqlite/sqlite.hpp new file mode 100644 index 000000000..c512d891c --- /dev/null +++ b/3rdparty/cpp-sqlite/sqlite.hpp @@ -0,0 +1,603 @@ +/** + Copyright (C) 2023 Toni Lipponen + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. + */ + +#pragma once +#include +#include +#include +#include +#include +#include + +#if __cplusplus > 201402L + #define CPP_SQLITE_NODISCARD [[nodiscard]] +#else + #define CPP_SQLITE_NODISCARD +#endif + +#if defined(CPP_SQLITE_NOTHROW) + #define CPP_SQLITE_THROW(...) return false +#else + #define CPP_SQLITE_THROW(...) throw sqlite::Error(__VA_ARGS__) +#endif + +namespace sqlite +{ + class Error : public std::runtime_error + { + public: + explicit Error(const char* message, int errorCode = SQLITE_ERROR) + : std::runtime_error(message), m_errorCode(errorCode) + { + + } + + explicit Error(const std::string& message, int errorCode = SQLITE_ERROR) + : std::runtime_error(message), m_errorCode(errorCode) + { + + } + + CPP_SQLITE_NODISCARD + int GetCode() const + { + return m_errorCode; + } + + private: + int m_errorCode; + }; + + namespace Priv + { + inline bool CheckError(sqlite3* db, int code) + { + if(code != SQLITE_OK && code != SQLITE_DONE) + { + const int extendedCode = sqlite3_extended_errcode(db); + std::string errstr = sqlite3_errstr(extendedCode); + std::string errmsg = sqlite3_errmsg(db); + + CPP_SQLITE_THROW(errstr + ": " + errmsg, extendedCode); + } + + return true; + } + + inline bool CheckError(int code) + { + if(code != SQLITE_OK && code != SQLITE_DONE) + { + std::string errstr = std::string("SQL error: ") + sqlite3_errstr(code); + CPP_SQLITE_THROW(errstr, code); + } + + return true; + } + } + + class Connection + { + public: + Connection() : m_connection(nullptr) {} + + explicit Connection(const std::string& filename) + { + this->Open(filename); + } + + Connection(const Connection&) = delete; + + Connection(Connection&& other) noexcept + { + this->m_connection = other.m_connection; + other.m_connection = nullptr; + } + + virtual ~Connection() noexcept + { + try + { + this->Close(); + } + catch(...) + { + + } + } + + Connection& operator=(const Connection&) = delete; + + Connection& operator=(Connection&& other) noexcept + { + if(&other != this) + { + this->m_connection = other.m_connection; + other.m_connection = nullptr; + } + + return *this; + } + + bool Open(const std::string& filename) + { + return sqlite::Priv::CheckError(sqlite3_open(filename.data(), &m_connection)); + } + + bool Close() + { + const auto result = Priv::CheckError(sqlite3_close(m_connection)); + m_connection = nullptr; + + return result; + } + + CPP_SQLITE_NODISCARD + int GetExtendedResult() const + { + return sqlite3_extended_errcode(m_connection); + } + + CPP_SQLITE_NODISCARD + sqlite3* GetPtr() + { + return m_connection; + } + + private: + sqlite3* m_connection = nullptr; + }; + + class Blob + { + public: + Blob(const void* data, int32_t bytes) + { + m_data.resize(bytes); + std::memcpy(&m_data.at(0), data, bytes); + } + + explicit Blob(std::vector data) + : m_data(std::move(data)) + { + + } + + CPP_SQLITE_NODISCARD + uint32_t GetSize() const + { + return m_data.size(); + } + + CPP_SQLITE_NODISCARD + unsigned char* GetData() + { + return m_data.data(); + } + + CPP_SQLITE_NODISCARD + const unsigned char* GetData() const + { + return m_data.data(); + } + + private: + std::vector m_data; + }; + + /** Non-owning blob*/ + class NOBlob + { + public: + NOBlob(const void* ptr, uint32_t bytes) + : m_ptr(ptr), m_bytes(bytes) + { + + } + + CPP_SQLITE_NODISCARD + uint32_t GetSize() const + { + return m_bytes; + } + + const void* GetData() + { + return m_ptr; + } + + CPP_SQLITE_NODISCARD + const void* GetData() const + { + return m_ptr; + } + + private: + const void* m_ptr; + uint32_t m_bytes; + }; + + namespace Priv + { + inline void Append(sqlite3_stmt* statement, int index, const int32_t& data) + { + sqlite::Priv::CheckError(sqlite3_bind_int(statement, index, data)); + } + + inline void Append(sqlite3_stmt* statement, int index, const int64_t& data) + { + sqlite::Priv::CheckError(sqlite3_bind_int64(statement, index, data)); + } + + inline void Append(sqlite3_stmt* statement, int index, const float& data) + { + sqlite::Priv::CheckError(sqlite3_bind_double(statement, index, static_cast(data))); + } + + inline void Append(sqlite3_stmt* statement, int index, const double& data) + { + sqlite::Priv::CheckError(sqlite3_bind_double(statement, index, data)); + } + + inline void Append(sqlite3_stmt* statement, int index, const std::string& data) + { + sqlite::Priv::CheckError(sqlite3_bind_text(statement, index, data.data(), static_cast(data.size()), nullptr)); + } + + inline void Append(sqlite3_stmt* statement, int index, const char* data) + { + sqlite::Priv::CheckError(sqlite3_bind_text(statement, index, data, static_cast(std::strlen(data)), nullptr)); + } + + inline void Append(sqlite3_stmt* statement, int index, const sqlite::Blob& blob) + { + sqlite::Priv::CheckError(sqlite3_bind_blob(statement, index, blob.GetData(), static_cast(blob.GetSize()), nullptr)); + } + + inline void Append(sqlite3_stmt* statement, int index, const sqlite::NOBlob& blob) + { + sqlite::Priv::CheckError(sqlite3_bind_blob(statement, index, blob.GetData(), static_cast(blob.GetSize()), nullptr)); + } + + template + inline void AppendToQuery(sqlite3_stmt* statement, int index, const Arg& arg) + { + sqlite::Priv::Append(statement, index, arg); + } + + template + inline void AppendToQuery(sqlite3_stmt* statement, int index, const First& first, const Args&... args) + { + sqlite::Priv::Append(statement, index, first); + sqlite::Priv::AppendToQuery(statement, ++index, args...); + } + + struct Statement + { + Statement() : handle(nullptr) {} + + Statement(sqlite::Connection& connection, const std::string& command) + { + auto* db = connection.GetPtr(); + + const int code = sqlite3_prepare_v2( + db, + command.data(), + static_cast(command.size()), + &handle, + nullptr); + + Priv::CheckError(db, code); + } + + Statement(Statement&& other) noexcept + { + std::swap(handle, other.handle); + } + + ~Statement() + { + sqlite::Priv::CheckError(sqlite3_finalize(handle)); + } + + Statement& operator=(Statement&& other) noexcept + { + handle = other.handle; + other.handle = nullptr; + + return *this; + } + + CPP_SQLITE_NODISCARD + bool Advance() const + { + const int code = sqlite3_step(handle); + + if(code == SQLITE_ROW) + { + return true; + } + + sqlite::Priv::CheckError(code); + Reset(); + + return false; + } + + bool Reset() const + { + return sqlite::Priv::CheckError(sqlite3_reset(handle)); + } + + CPP_SQLITE_NODISCARD + int ColumnCount() const + { + Reset(); + + if(!Advance()) + { + return 0; + } + + const int count = sqlite3_column_count(handle); + Reset(); + + return count; + } + + CPP_SQLITE_NODISCARD + std::string GetColumnName(int columnIndex) const + { + Reset(); + + if(!Advance()) + { +#ifndef CPP_SQLITE_NOTHROW + throw sqlite::Error("SQL error: invalid column index"); +#endif + } + + std::string name = sqlite3_column_name(handle, columnIndex); + + if(name.empty()) + { +#ifndef CPP_SQLITE_NOTHROW + throw sqlite::Error("SQL error: failed to get column name at index " + std::to_string(columnIndex)); +#endif + } + + Reset(); + + return name; + } + + template + CPP_SQLITE_NODISCARD + T Get(int) const + { + static_assert(sizeof(T) == -1, "SQL error: invalid column data type"); + } + + sqlite3_stmt* handle = nullptr; + }; + + template<> + inline float Statement::Get(int col) const + { + return static_cast(sqlite3_column_double(handle, col)); + } + + template<> + inline double Statement::Get(int col) const + { + return sqlite3_column_double(handle, col); + } + + template<> + inline int32_t Statement::Get(int col) const + { + return sqlite3_column_int(handle, col); + } + + template<> + inline int64_t Statement::Get(int col) const + { + return sqlite3_column_int64(handle, col); + } + + template<> + inline std::string Statement::Get(int col) const + { + const unsigned char* bytes = sqlite3_column_text(handle, col); + const int size = sqlite3_column_bytes(handle, col); + + if(size == 0) + { + return ""; + } + + return {reinterpret_cast(bytes), static_cast(size)}; + } + + template<> + inline sqlite::Blob Statement::Get(int col) const + { + const void* bytes = sqlite3_column_blob(handle, col); + const int size = sqlite3_column_bytes(handle, col); + + return {bytes, size}; + } + } + + class Type + { + private: + Type(const sqlite::Priv::Statement& statement, int col) + : m_statement(statement), m_columnIndex(col) + { + + } + public: + template + operator T() const + { + return m_statement.Get(m_columnIndex); + } + + friend class Result; + + private: + const sqlite::Priv::Statement& m_statement; + const int m_columnIndex; + }; + + class Result + { + explicit Result(sqlite::Priv::Statement&& statement) + : m_statement(std::move(statement)) + { + + } + + public: + Result() = default; + + Result(Result&& other) noexcept + { + m_statement = std::move(other.m_statement); + } + + Result& operator=(Result&& other) noexcept + { + m_statement = std::move(other.m_statement); + + return *this; + } + + CPP_SQLITE_NODISCARD + bool HasData() const + { + return ColumnCount() > 0; + } + + CPP_SQLITE_NODISCARD + int ColumnCount() const + { + return m_statement.ColumnCount(); + } + + bool Reset() const + { + return m_statement.Reset(); + } + + CPP_SQLITE_NODISCARD + bool Next() const + { + return m_statement.Advance(); + } + + CPP_SQLITE_NODISCARD + Type Get(int columnIndex) const + { + return {m_statement, columnIndex}; + } + + CPP_SQLITE_NODISCARD + std::string GetColumnName(int columnIndex) const + { + return m_statement.GetColumnName(columnIndex); + } + + friend void Statement(sqlite::Connection&, const std::string&); + + template + friend Result Query(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args); + friend Result Query(sqlite::Connection& connection, const std::string& command); + + private: + sqlite::Priv::Statement m_statement; + }; + + template + inline void Statement(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args) + { + sqlite::Priv::Statement statement(connection, command); + sqlite::Priv::AppendToQuery(statement.handle, 1, first, args...); + + (void)statement.Advance(); + } + + inline void Statement(sqlite::Connection& connection, const std::string& command) + { + sqlite::Priv::Statement statement(connection, command); + + (void)statement.Advance(); + } + + template + CPP_SQLITE_NODISCARD + inline Result Query(sqlite::Connection& connection, const std::string& command, const First& first, const Args... args) + { + sqlite::Priv::Statement statement(connection, command); + sqlite::Priv::AppendToQuery(statement.handle, 1, first, args...); + + return Result(std::move(statement)); + } + + CPP_SQLITE_NODISCARD + inline Result Query(sqlite::Connection& connection, const std::string& command) + { + sqlite::Priv::Statement statement(connection, command); + + return Result(std::move(statement)); + } + + inline bool Backup(sqlite::Connection& from, sqlite::Connection& to) + { + sqlite3_backup* backup = sqlite3_backup_init(to.GetPtr(), "main", from.GetPtr(), "main"); + + if(!backup) + { + CPP_SQLITE_THROW("SQL error: failed to initialize backup"); + } + + if(!Priv::CheckError(sqlite3_backup_step(backup, -1))) + { + return false; + } + + if(!Priv::CheckError(sqlite3_backup_finish(backup))) + { + return false; + } + + return true; + } + + inline bool Backup(sqlite::Connection& from, const std::string& filename) + { + sqlite::Connection to(filename); + + return sqlite::Backup(from, to); + } +} diff --git a/3rdparty/cppzmq/LICENSE b/3rdparty/cppzmq/LICENSE new file mode 100644 index 000000000..ae98bd859 --- /dev/null +++ b/3rdparty/cppzmq/LICENSE @@ -0,0 +1,17 @@ + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. diff --git a/3rdparty/cppzmq/README.md b/3rdparty/cppzmq/README.md new file mode 100644 index 000000000..e2bea0b63 --- /dev/null +++ b/3rdparty/cppzmq/README.md @@ -0,0 +1,196 @@ +[![CI](https://github.com/zeromq/cppzmq/actions/workflows/ci.yml/badge.svg)](https://github.com/zeromq/cppzmq/actions) +[![Coverage Status](https://coveralls.io/repos/github/zeromq/cppzmq/badge.svg?branch=master)](https://coveralls.io/github/zeromq/cppzmq?branch=master) +[![License](https://img.shields.io/github/license/zeromq/cppzmq.svg)](https://github.com/zeromq/cppzmq/blob/master/LICENSE) + +Introduction & Design Goals +=========================== + +cppzmq is a C++ binding for libzmq. It has the following design goals: + - cppzmq maps the libzmq C API to C++ concepts. In particular: + - it is type-safe (the libzmq C API exposes various class-like concepts as void*) + - it provides exception-based error handling (the libzmq C API provides errno-based error handling) + - it provides RAII-style classes that automate resource management (the libzmq C API requires the user to take care to free resources explicitly) + - cppzmq is a light-weight, header-only binding. You only need to include the header file zmq.hpp (and maybe zmq_addon.hpp) to use it. + - zmq.hpp is meant to contain direct mappings of the abstractions provided by the libzmq C API, while zmq_addon.hpp provides additional higher-level abstractions. + +There are other C++ bindings for ZeroMQ with different design goals. In particular, none of the following bindings are header-only: + - [zmqpp](https://github.com/zeromq/zmqpp) is a high-level binding to libzmq. + - [czmqpp](https://github.com/zeromq/czmqpp) is a binding based on the high-level czmq API. + - [fbzmq](https://github.com/facebook/fbzmq) is a binding that integrates with Apache Thrift and provides higher-level abstractions in addition. It requires C++14. + +Supported platforms +=================== + + - Only a subset of the platforms that are supported by libzmq itself are supported. Some features already require a compiler supporting C++11. In the future, probably all features will require C++11. To build and run the tests, CMake and Catch are required. + - Any libzmq 4.x version is expected to work. DRAFT features may only work for the most recent tested version. Currently explicitly tested libzmq versions are + - 4.2.0 (without DRAFT API) + - 4.3.4 (with and without DRAFT API) + - Platforms with full support (i.e. CI executing build and tests) + - Ubuntu 18.04 x64 (with gcc 4.8.5, 5.5.0, 7.5.0) + - Ubuntu 20.04 x64 (with gcc 9.3.0, 10.3.0 and clang 12) + - Visual Studio 2017 x64 + - Visual Studio 2019 x64 + - macOS 10.15 (with clang 12, without DRAFT API) + - Additional platforms that are known to work: + - We have no current reports on additional platforms that are known to work yet. Please add your platform here. If CI can be provided for them with a cloud-based CI service working with GitHub, you are invited to add CI, and make it possible to be included in the list above. + - Additional platforms that probably work: + - Any platform supported by libzmq that provides a sufficiently recent gcc (4.8.1 or newer) or clang (3.4.1 or newer) + - Visual Studio 2012+ x86/x64 + +Examples +======== +These examples require at least C++11. +```c++ +#include + +int main() +{ + zmq::context_t ctx; + zmq::socket_t sock(ctx, zmq::socket_type::push); + sock.bind("inproc://test"); + sock.send(zmq::str_buffer("Hello, world"), zmq::send_flags::dontwait); +} +``` +This a more complex example where we send and receive multi-part messages over TCP with a wildcard port. +```c++ +#include +#include + +int main() +{ + zmq::context_t ctx; + zmq::socket_t sock1(ctx, zmq::socket_type::push); + zmq::socket_t sock2(ctx, zmq::socket_type::pull); + sock1.bind("tcp://127.0.0.1:*"); + const std::string last_endpoint = + sock1.get(zmq::sockopt::last_endpoint); + std::cout << "Connecting to " + << last_endpoint << std::endl; + sock2.connect(last_endpoint); + + std::array send_msgs = { + zmq::str_buffer("foo"), + zmq::str_buffer("bar!") + }; + if (!zmq::send_multipart(sock1, send_msgs)) + return 1; + + std::vector recv_msgs; + const auto ret = zmq::recv_multipart( + sock2, std::back_inserter(recv_msgs)); + if (!ret) + return 1; + std::cout << "Got " << *ret + << " messages" << std::endl; + return 0; +} +``` + +See the `examples` directory for more examples. When the project is compiled with tests enabled, each example gets compiled to an executable. + + +API Overview +============ + +For an extensive overview of the `zmq.hpp` API in use, see this [Tour of CPPZMQ by @brettviren](https://brettviren.github.io/cppzmq-tour/index.html). + +Bindings for libzmq in `zmq.hpp`: + +Types: +* class `zmq::context_t` +* enum `zmq::ctxopt` +* class `zmq::socket_t` +* class `zmq::socket_ref` +* enum `zmq::socket_type` +* enum `zmq::sockopt` +* enum `zmq::send_flags` +* enum `zmq::recv_flags` +* class `zmq::message_t` +* class `zmq::const_buffer` +* class `zmq::mutable_buffer` +* struct `zmq::recv_buffer_size` +* alias `zmq::send_result_t` +* alias `zmq::recv_result_t` +* alias `zmq::recv_buffer_result_t` +* class `zmq::error_t` +* class `zmq::monitor_t` +* struct `zmq_event_t`, +* alias `zmq::free_fn`, +* alias `zmq::pollitem_t`, +* alias `zmq::fd_t` +* class `zmq::poller_t` DRAFT +* enum `zmq::event_flags` DRAFT +* enum `zmq::poller_event` DRAFT + +Functions: +* `zmq::version` +* `zmq::poll` +* `zmq::proxy` +* `zmq::proxy_steerable` +* `zmq::buffer` +* `zmq::str_buffer` + +Extra high-level types and functions `zmq_addon.hpp`: + +Types: +* class `zmq::multipart_t` +* class `zmq::active_poller_t` DRAFT + +Functions: +* `zmq::recv_multipart` +* `zmq::send_multipart` +* `zmq::send_multipart_n` +* `zmq::encode` +* `zmq::decode` + +Compatibility Guidelines +======================== + +The users of cppzmq are expected to follow the guidelines below to ensure not to break when upgrading cppzmq to newer versions (non-exhaustive list): + +* Do not depend on any macros defined in cppzmq unless explicitly declared public here. + +The following macros may be used by consumers of cppzmq: `CPPZMQ_VERSION`, `CPPZMQ_VERSION_MAJOR`, `CPPZMQ_VERSION_MINOR`, `CPPZMQ_VERSION_PATCH`. + +Contribution policy +=================== + +The contribution policy is at: http://rfc.zeromq.org/spec:22 + +Build instructions +================== + +Build steps: + +1. Build [libzmq](https://github.com/zeromq/libzmq) via cmake. This does an out of source build and installs the build files + - download and unzip the lib, cd to directory + - mkdir build + - cd build + - cmake .. + - sudo make -j4 install + +2. Build cppzmq via cmake. This does an out of source build and installs the build files + - download and unzip the lib, cd to directory + - mkdir build + - cd build + - cmake .. + - sudo make -j4 install + +3. Build cppzmq via [vcpkg](https://github.com/Microsoft/vcpkg/). This does an out of source build and installs the build files + - git clone https://github.com/Microsoft/vcpkg.git + - cd vcpkg + - ./bootstrap-vcpkg.sh # bootstrap-vcpkg.bat for Powershell + - ./vcpkg integrate install + - ./vcpkg install cppzmq + +Using this: + +A cmake find package scripts is provided for you to easily include this library. +Add these lines in your CMakeLists.txt to include the headers and library files of +cpp zmq (which will also include libzmq for you). + +``` +#find cppzmq wrapper, installed by make of cppzmq +find_package(cppzmq) +target_link_libraries(*Your Project Name* cppzmq) +``` diff --git a/3rdparty/cppzmq/zmq.hpp b/3rdparty/cppzmq/zmq.hpp new file mode 100644 index 000000000..3fa484c6c --- /dev/null +++ b/3rdparty/cppzmq/zmq.hpp @@ -0,0 +1,2762 @@ +/* + Copyright (c) 2016-2017 ZeroMQ community + Copyright (c) 2009-2011 250bpm s.r.o. + Copyright (c) 2011 Botond Ballo + Copyright (c) 2007-2009 iMatix Corporation + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef __ZMQ_HPP_INCLUDED__ +#define __ZMQ_HPP_INCLUDED__ + +#ifdef _WIN32 +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif + +// included here for _HAS_CXX* macros +#include + +#if defined(_MSVC_LANG) +#define CPPZMQ_LANG _MSVC_LANG +#else +#define CPPZMQ_LANG __cplusplus +#endif +// overwrite if specific language macros indicate higher version +#if defined(_HAS_CXX14) && _HAS_CXX14 && CPPZMQ_LANG < 201402L +#undef CPPZMQ_LANG +#define CPPZMQ_LANG 201402L +#endif +#if defined(_HAS_CXX17) && _HAS_CXX17 && CPPZMQ_LANG < 201703L +#undef CPPZMQ_LANG +#define CPPZMQ_LANG 201703L +#endif + +// macros defined if has a specific standard or greater +#if CPPZMQ_LANG >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900) +#define ZMQ_CPP11 +#endif +#if CPPZMQ_LANG >= 201402L +#define ZMQ_CPP14 +#endif +#if CPPZMQ_LANG >= 201703L +#define ZMQ_CPP17 +#endif + +#if defined(ZMQ_CPP14) && !defined(_MSC_VER) +#define ZMQ_DEPRECATED(msg) [[deprecated(msg)]] +#elif defined(_MSC_VER) +#define ZMQ_DEPRECATED(msg) __declspec(deprecated(msg)) +#elif defined(__GNUC__) +#define ZMQ_DEPRECATED(msg) __attribute__((deprecated(msg))) +#else +#define ZMQ_DEPRECATED(msg) +#endif + +#if defined(ZMQ_CPP17) +#define ZMQ_NODISCARD [[nodiscard]] +#else +#define ZMQ_NODISCARD +#endif + +#if defined(ZMQ_CPP11) +#define ZMQ_NOTHROW noexcept +#define ZMQ_EXPLICIT explicit +#define ZMQ_OVERRIDE override +#define ZMQ_NULLPTR nullptr +#define ZMQ_CONSTEXPR_FN constexpr +#define ZMQ_CONSTEXPR_VAR constexpr +#define ZMQ_CPP11_DEPRECATED(msg) ZMQ_DEPRECATED(msg) +#else +#define ZMQ_NOTHROW throw() +#define ZMQ_EXPLICIT +#define ZMQ_OVERRIDE +#define ZMQ_NULLPTR 0 +#define ZMQ_CONSTEXPR_FN +#define ZMQ_CONSTEXPR_VAR const +#define ZMQ_CPP11_DEPRECATED(msg) +#endif +#if defined(ZMQ_CPP14) && (!defined(_MSC_VER) || _MSC_VER > 1900) && (!defined(__GNUC__) || __GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ > 3)) +#define ZMQ_EXTENDED_CONSTEXPR +#endif +#if defined(ZMQ_CPP17) +#define ZMQ_INLINE_VAR inline +#define ZMQ_CONSTEXPR_IF constexpr +#else +#define ZMQ_INLINE_VAR +#define ZMQ_CONSTEXPR_IF +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef ZMQ_CPP11 +#include +#include +#include +#include +#endif + +#if defined(__has_include) && defined(ZMQ_CPP17) +#define CPPZMQ_HAS_INCLUDE_CPP17(X) __has_include(X) +#else +#define CPPZMQ_HAS_INCLUDE_CPP17(X) 0 +#endif + +#if CPPZMQ_HAS_INCLUDE_CPP17() && !defined(CPPZMQ_HAS_OPTIONAL) +#define CPPZMQ_HAS_OPTIONAL 1 +#endif +#ifndef CPPZMQ_HAS_OPTIONAL +#define CPPZMQ_HAS_OPTIONAL 0 +#elif CPPZMQ_HAS_OPTIONAL +#include +#endif + +#if CPPZMQ_HAS_INCLUDE_CPP17() && !defined(CPPZMQ_HAS_STRING_VIEW) +#define CPPZMQ_HAS_STRING_VIEW 1 +#endif +#ifndef CPPZMQ_HAS_STRING_VIEW +#define CPPZMQ_HAS_STRING_VIEW 0 +#elif CPPZMQ_HAS_STRING_VIEW +#include +#endif + +/* Version macros for compile-time API version detection */ +#define CPPZMQ_VERSION_MAJOR 4 +#define CPPZMQ_VERSION_MINOR 10 +#define CPPZMQ_VERSION_PATCH 0 + +#define CPPZMQ_VERSION \ + ZMQ_MAKE_VERSION(CPPZMQ_VERSION_MAJOR, CPPZMQ_VERSION_MINOR, \ + CPPZMQ_VERSION_PATCH) + +// Detect whether the compiler supports C++11 rvalue references. +#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 2)) \ + && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define ZMQ_HAS_RVALUE_REFS +#define ZMQ_DELETED_FUNCTION = delete +#elif defined(__clang__) +#if __has_feature(cxx_rvalue_references) +#define ZMQ_HAS_RVALUE_REFS +#endif + +#if __has_feature(cxx_deleted_functions) +#define ZMQ_DELETED_FUNCTION = delete +#else +#define ZMQ_DELETED_FUNCTION +#endif +#elif defined(_MSC_VER) && (_MSC_VER >= 1900) +#define ZMQ_HAS_RVALUE_REFS +#define ZMQ_DELETED_FUNCTION = delete +#elif defined(_MSC_VER) && (_MSC_VER >= 1600) +#define ZMQ_HAS_RVALUE_REFS +#define ZMQ_DELETED_FUNCTION +#else +#define ZMQ_DELETED_FUNCTION +#endif + +#if defined(ZMQ_CPP11) && !defined(__llvm__) && !defined(__INTEL_COMPILER) \ + && defined(__GNUC__) && __GNUC__ < 5 +#define ZMQ_CPP11_PARTIAL +#elif defined(__GLIBCXX__) && __GLIBCXX__ < 20160805 +//the date here is the last date of gcc 4.9.4, which +// effectively means libstdc++ from gcc 5.5 and higher won't trigger this branch +#define ZMQ_CPP11_PARTIAL +#endif + +#ifdef ZMQ_CPP11 +#ifdef ZMQ_CPP11_PARTIAL +#define ZMQ_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T) +#else +#include +#define ZMQ_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable::value +#endif +#endif + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 3, 0) +#define ZMQ_NEW_MONITOR_EVENT_LAYOUT +#endif + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) +#define ZMQ_HAS_PROXY_STEERABLE +/* Socket event data */ +typedef struct +{ + uint16_t event; // id of the event as bitfield + int32_t value; // value is either error code, fd or reconnect interval +} zmq_event_t; +#endif + +// Avoid using deprecated message receive function when possible +#if ZMQ_VERSION < ZMQ_MAKE_VERSION(3, 2, 0) +#define zmq_msg_recv(msg, socket, flags) zmq_recvmsg(socket, msg, flags) +#endif + + +// In order to prevent unused variable warnings when building in non-debug +// mode use this macro to make assertions. +#ifndef NDEBUG +#define ZMQ_ASSERT(expression) assert(expression) +#else +#define ZMQ_ASSERT(expression) (void) (expression) +#endif + +namespace zmq +{ +#ifdef ZMQ_CPP11 +namespace detail +{ +namespace ranges +{ +using std::begin; +using std::end; +template auto begin(T &&r) -> decltype(begin(std::forward(r))) +{ + return begin(std::forward(r)); +} +template auto end(T &&r) -> decltype(end(std::forward(r))) +{ + return end(std::forward(r)); +} +} // namespace ranges + +template using void_t = void; + +template +using iter_value_t = typename std::iterator_traits::value_type; + +template +using range_iter_t = decltype( + ranges::begin(std::declval::type &>())); + +template using range_value_t = iter_value_t>; + +template struct is_range : std::false_type +{ +}; + +template +struct is_range< + T, + void_t::type &>()) + == ranges::end(std::declval::type &>()))>> + : std::true_type +{ +}; + +} // namespace detail +#endif + +typedef zmq_free_fn free_fn; +typedef zmq_pollitem_t pollitem_t; + +// duplicate definition from libzmq 4.3.3 +#if defined _WIN32 +#if defined _WIN64 +typedef unsigned __int64 fd_t; +#else +typedef unsigned int fd_t; +#endif +#else +typedef int fd_t; +#endif + +class error_t : public std::exception +{ + public: + error_t() ZMQ_NOTHROW : errnum(zmq_errno()) {} + explicit error_t(int err) ZMQ_NOTHROW : errnum(err) {} + virtual const char *what() const ZMQ_NOTHROW ZMQ_OVERRIDE + { + return zmq_strerror(errnum); + } + int num() const ZMQ_NOTHROW { return errnum; } + + private: + int errnum; +}; + +namespace detail { +inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_) +{ + int rc = zmq_poll(items_, static_cast(nitems_), timeout_); + if (rc < 0) + throw error_t(); + return rc; +} +} + +#ifdef ZMQ_CPP11 +ZMQ_DEPRECATED("from 4.8.0, use poll taking std::chrono::duration instead of long") +inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_) +#else +inline int poll(zmq_pollitem_t *items_, size_t nitems_, long timeout_ = -1) +#endif +{ + return detail::poll(items_, nitems_, timeout_); +} + +ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") +inline int poll(zmq_pollitem_t const *items_, size_t nitems_, long timeout_ = -1) +{ + return detail::poll(const_cast(items_), nitems_, timeout_); +} + +#ifdef ZMQ_CPP11 +ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") +inline int +poll(zmq_pollitem_t const *items, size_t nitems, std::chrono::milliseconds timeout) +{ + return detail::poll(const_cast(items), nitems, + static_cast(timeout.count())); +} + +ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") +inline int poll(std::vector const &items, + std::chrono::milliseconds timeout) +{ + return detail::poll(const_cast(items.data()), items.size(), + static_cast(timeout.count())); +} + +ZMQ_DEPRECATED("from 4.3.1, use poll taking non-const items") +inline int poll(std::vector const &items, long timeout_ = -1) +{ + return detail::poll(const_cast(items.data()), items.size(), timeout_); +} + +inline int +poll(zmq_pollitem_t *items, size_t nitems, std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) +{ + return detail::poll(items, nitems, static_cast(timeout.count())); +} + +inline int poll(std::vector &items, + std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) +{ + return detail::poll(items.data(), items.size(), static_cast(timeout.count())); +} + +ZMQ_DEPRECATED("from 4.3.1, use poll taking std::chrono::duration instead of long") +inline int poll(std::vector &items, long timeout_) +{ + return detail::poll(items.data(), items.size(), timeout_); +} + +template +inline int poll(std::array &items, + std::chrono::milliseconds timeout = std::chrono::milliseconds{-1}) +{ + return detail::poll(items.data(), items.size(), static_cast(timeout.count())); +} +#endif + + +inline void version(int *major_, int *minor_, int *patch_) +{ + zmq_version(major_, minor_, patch_); +} + +#ifdef ZMQ_CPP11 +inline std::tuple version() +{ + std::tuple v; + zmq_version(&std::get<0>(v), &std::get<1>(v), &std::get<2>(v)); + return v; +} + +#if !defined(ZMQ_CPP11_PARTIAL) +namespace detail +{ +template struct is_char_type +{ + // true if character type for string literals in C++11 + static constexpr bool value = + std::is_same::value || std::is_same::value + || std::is_same::value || std::is_same::value; +}; +} +#endif + +#endif + +class message_t +{ + public: + message_t() ZMQ_NOTHROW + { + int rc = zmq_msg_init(&msg); + ZMQ_ASSERT(rc == 0); + } + + explicit message_t(size_t size_) + { + int rc = zmq_msg_init_size(&msg, size_); + if (rc != 0) + throw error_t(); + } + + template message_t(ForwardIter first, ForwardIter last) + { + typedef typename std::iterator_traits::value_type value_t; + + assert(std::distance(first, last) >= 0); + size_t const size_ = + static_cast(std::distance(first, last)) * sizeof(value_t); + int const rc = zmq_msg_init_size(&msg, size_); + if (rc != 0) + throw error_t(); + std::copy(first, last, data()); + } + + message_t(const void *data_, size_t size_) + { + int rc = zmq_msg_init_size(&msg, size_); + if (rc != 0) + throw error_t(); + if (size_) { + // this constructor allows (nullptr, 0), + // memcpy with a null pointer is UB + memcpy(data(), data_, size_); + } + } + + message_t(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR) + { + int rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_); + if (rc != 0) + throw error_t(); + } + + // overload set of string-like types and generic containers +#if defined(ZMQ_CPP11) && !defined(ZMQ_CPP11_PARTIAL) + // NOTE this constructor will include the null terminator + // when called with a string literal. + // An overload taking const char* can not be added because + // it would be preferred over this function and break compatiblity. + template< + class Char, + size_t N, + typename = typename std::enable_if::value>::type> + ZMQ_DEPRECATED("from 4.7.0, use constructors taking iterators, (pointer, size) " + "or strings instead") + explicit message_t(const Char (&data)[N]) : + message_t(detail::ranges::begin(data), detail::ranges::end(data)) + { + } + + template::value + && ZMQ_IS_TRIVIALLY_COPYABLE(detail::range_value_t) + && !detail::is_char_type>::value + && !std::is_same::value>::type> + explicit message_t(const Range &rng) : + message_t(detail::ranges::begin(rng), detail::ranges::end(rng)) + { + } + + explicit message_t(const std::string &str) : message_t(str.data(), str.size()) {} + +#if CPPZMQ_HAS_STRING_VIEW + explicit message_t(std::string_view str) : message_t(str.data(), str.size()) {} +#endif + +#endif + +#ifdef ZMQ_HAS_RVALUE_REFS + message_t(message_t &&rhs) ZMQ_NOTHROW : msg(rhs.msg) + { + int rc = zmq_msg_init(&rhs.msg); + ZMQ_ASSERT(rc == 0); + } + + message_t &operator=(message_t &&rhs) ZMQ_NOTHROW + { + std::swap(msg, rhs.msg); + return *this; + } +#endif + + ~message_t() ZMQ_NOTHROW + { + int rc = zmq_msg_close(&msg); + ZMQ_ASSERT(rc == 0); + } + + void rebuild() + { + int rc = zmq_msg_close(&msg); + if (rc != 0) + throw error_t(); + rc = zmq_msg_init(&msg); + ZMQ_ASSERT(rc == 0); + } + + void rebuild(size_t size_) + { + int rc = zmq_msg_close(&msg); + if (rc != 0) + throw error_t(); + rc = zmq_msg_init_size(&msg, size_); + if (rc != 0) + throw error_t(); + } + + void rebuild(const void *data_, size_t size_) + { + int rc = zmq_msg_close(&msg); + if (rc != 0) + throw error_t(); + rc = zmq_msg_init_size(&msg, size_); + if (rc != 0) + throw error_t(); + memcpy(data(), data_, size_); + } + + void rebuild(const std::string &str) + { + rebuild(str.data(), str.size()); + } + + void rebuild(void *data_, size_t size_, free_fn *ffn_, void *hint_ = ZMQ_NULLPTR) + { + int rc = zmq_msg_close(&msg); + if (rc != 0) + throw error_t(); + rc = zmq_msg_init_data(&msg, data_, size_, ffn_, hint_); + if (rc != 0) + throw error_t(); + } + + ZMQ_DEPRECATED("from 4.3.1, use move taking non-const reference instead") + void move(message_t const *msg_) + { + int rc = zmq_msg_move(&msg, const_cast(msg_->handle())); + if (rc != 0) + throw error_t(); + } + + void move(message_t &msg_) + { + int rc = zmq_msg_move(&msg, msg_.handle()); + if (rc != 0) + throw error_t(); + } + + ZMQ_DEPRECATED("from 4.3.1, use copy taking non-const reference instead") + void copy(message_t const *msg_) + { + int rc = zmq_msg_copy(&msg, const_cast(msg_->handle())); + if (rc != 0) + throw error_t(); + } + + void copy(message_t &msg_) + { + int rc = zmq_msg_copy(&msg, msg_.handle()); + if (rc != 0) + throw error_t(); + } + + bool more() const ZMQ_NOTHROW + { + int rc = zmq_msg_more(const_cast(&msg)); + return rc != 0; + } + + void *data() ZMQ_NOTHROW { return zmq_msg_data(&msg); } + + const void *data() const ZMQ_NOTHROW + { + return zmq_msg_data(const_cast(&msg)); + } + + size_t size() const ZMQ_NOTHROW + { + return zmq_msg_size(const_cast(&msg)); + } + + ZMQ_NODISCARD bool empty() const ZMQ_NOTHROW { return size() == 0u; } + + template T *data() ZMQ_NOTHROW { return static_cast(data()); } + + template T const *data() const ZMQ_NOTHROW + { + return static_cast(data()); + } + + ZMQ_DEPRECATED("from 4.3.0, use operator== instead") + bool equal(const message_t *other) const ZMQ_NOTHROW { return *this == *other; } + + bool operator==(const message_t &other) const ZMQ_NOTHROW + { + const size_t my_size = size(); + return my_size == other.size() && 0 == memcmp(data(), other.data(), my_size); + } + + bool operator!=(const message_t &other) const ZMQ_NOTHROW + { + return !(*this == other); + } + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(3, 2, 0) + int get(int property_) + { + int value = zmq_msg_get(&msg, property_); + if (value == -1) + throw error_t(); + return value; + } +#endif + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 1, 0) + const char *gets(const char *property_) + { + const char *value = zmq_msg_gets(&msg, property_); + if (value == ZMQ_NULLPTR) + throw error_t(); + return value; + } +#endif + +#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) + uint32_t routing_id() const + { + return zmq_msg_routing_id(const_cast(&msg)); + } + + void set_routing_id(uint32_t routing_id) + { + int rc = zmq_msg_set_routing_id(&msg, routing_id); + if (rc != 0) + throw error_t(); + } + + const char *group() const + { + return zmq_msg_group(const_cast(&msg)); + } + + void set_group(const char *group) + { + int rc = zmq_msg_set_group(&msg, group); + if (rc != 0) + throw error_t(); + } +#endif + + // interpret message content as a string + std::string to_string() const + { + return std::string(static_cast(data()), size()); + } +#if CPPZMQ_HAS_STRING_VIEW + // interpret message content as a string + std::string_view to_string_view() const noexcept + { + return std::string_view(static_cast(data()), size()); + } +#endif + + /** Dump content to string for debugging. + * Ascii chars are readable, the rest is printed as hex. + * Probably ridiculously slow. + * Use to_string() or to_string_view() for + * interpreting the message as a string. + */ + std::string str() const + { + // Partly mutuated from the same method in zmq::multipart_t + std::stringstream os; + + const unsigned char *msg_data = this->data(); + unsigned char byte; + size_t size = this->size(); + int is_ascii[2] = {0, 0}; + + os << "zmq::message_t [size " << std::dec << std::setw(3) + << std::setfill('0') << size << "] ("; + // Totally arbitrary + if (size >= 1000) { + os << "... too big to print)"; + } else { + while (size--) { + byte = *msg_data++; + + is_ascii[1] = (byte >= 32 && byte < 127); + if (is_ascii[1] != is_ascii[0]) + os << " "; // Separate text/non text + + if (is_ascii[1]) { + os << byte; + } else { + os << std::hex << std::uppercase << std::setw(2) + << std::setfill('0') << static_cast(byte); + } + is_ascii[0] = is_ascii[1]; + } + os << ")"; + } + return os.str(); + } + + void swap(message_t &other) ZMQ_NOTHROW + { + // this assumes zmq::msg_t from libzmq is trivially relocatable + std::swap(msg, other.msg); + } + + ZMQ_NODISCARD zmq_msg_t *handle() ZMQ_NOTHROW { return &msg; } + ZMQ_NODISCARD const zmq_msg_t *handle() const ZMQ_NOTHROW { return &msg; } + + private: + // The underlying message + zmq_msg_t msg; + + // Disable implicit message copying, so that users won't use shared + // messages (less efficient) without being aware of the fact. + message_t(const message_t &) ZMQ_DELETED_FUNCTION; + void operator=(const message_t &) ZMQ_DELETED_FUNCTION; +}; + +inline void swap(message_t &a, message_t &b) ZMQ_NOTHROW +{ + a.swap(b); +} + +#ifdef ZMQ_CPP11 +enum class ctxopt +{ +#ifdef ZMQ_BLOCKY + blocky = ZMQ_BLOCKY, +#endif +#ifdef ZMQ_IO_THREADS + io_threads = ZMQ_IO_THREADS, +#endif +#ifdef ZMQ_THREAD_SCHED_POLICY + thread_sched_policy = ZMQ_THREAD_SCHED_POLICY, +#endif +#ifdef ZMQ_THREAD_PRIORITY + thread_priority = ZMQ_THREAD_PRIORITY, +#endif +#ifdef ZMQ_THREAD_AFFINITY_CPU_ADD + thread_affinity_cpu_add = ZMQ_THREAD_AFFINITY_CPU_ADD, +#endif +#ifdef ZMQ_THREAD_AFFINITY_CPU_REMOVE + thread_affinity_cpu_remove = ZMQ_THREAD_AFFINITY_CPU_REMOVE, +#endif +#ifdef ZMQ_THREAD_NAME_PREFIX + thread_name_prefix = ZMQ_THREAD_NAME_PREFIX, +#endif +#ifdef ZMQ_MAX_MSGSZ + max_msgsz = ZMQ_MAX_MSGSZ, +#endif +#ifdef ZMQ_ZERO_COPY_RECV + zero_copy_recv = ZMQ_ZERO_COPY_RECV, +#endif +#ifdef ZMQ_MAX_SOCKETS + max_sockets = ZMQ_MAX_SOCKETS, +#endif +#ifdef ZMQ_SOCKET_LIMIT + socket_limit = ZMQ_SOCKET_LIMIT, +#endif +#ifdef ZMQ_IPV6 + ipv6 = ZMQ_IPV6, +#endif +#ifdef ZMQ_MSG_T_SIZE + msg_t_size = ZMQ_MSG_T_SIZE +#endif +}; +#endif + +class context_t +{ + public: + context_t() + { + ptr = zmq_ctx_new(); + if (ptr == ZMQ_NULLPTR) + throw error_t(); + } + + + explicit context_t(int io_threads_, int max_sockets_ = ZMQ_MAX_SOCKETS_DFLT) + { + ptr = zmq_ctx_new(); + if (ptr == ZMQ_NULLPTR) + throw error_t(); + + int rc = zmq_ctx_set(ptr, ZMQ_IO_THREADS, io_threads_); + ZMQ_ASSERT(rc == 0); + + rc = zmq_ctx_set(ptr, ZMQ_MAX_SOCKETS, max_sockets_); + ZMQ_ASSERT(rc == 0); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + context_t(context_t &&rhs) ZMQ_NOTHROW : ptr(rhs.ptr) { rhs.ptr = ZMQ_NULLPTR; } + context_t &operator=(context_t &&rhs) ZMQ_NOTHROW + { + close(); + std::swap(ptr, rhs.ptr); + return *this; + } +#endif + + ~context_t() ZMQ_NOTHROW { close(); } + + ZMQ_CPP11_DEPRECATED("from 4.7.0, use set taking zmq::ctxopt instead") + int setctxopt(int option_, int optval_) + { + int rc = zmq_ctx_set(ptr, option_, optval_); + ZMQ_ASSERT(rc == 0); + return rc; + } + + ZMQ_CPP11_DEPRECATED("from 4.7.0, use get taking zmq::ctxopt instead") + int getctxopt(int option_) { return zmq_ctx_get(ptr, option_); } + +#ifdef ZMQ_CPP11 + void set(ctxopt option, int optval) + { + int rc = zmq_ctx_set(ptr, static_cast(option), optval); + if (rc == -1) + throw error_t(); + } + + ZMQ_NODISCARD int get(ctxopt option) + { + int rc = zmq_ctx_get(ptr, static_cast(option)); + // some options have a default value of -1 + // which is unfortunate, and may result in errors + // that don't make sense + if (rc == -1) + throw error_t(); + return rc; + } +#endif + + // Terminates context (see also shutdown()). + void close() ZMQ_NOTHROW + { + if (ptr == ZMQ_NULLPTR) + return; + + int rc; + do { + rc = zmq_ctx_term(ptr); + } while (rc == -1 && errno == EINTR); + + ZMQ_ASSERT(rc == 0); + ptr = ZMQ_NULLPTR; + } + + // Shutdown context in preparation for termination (close()). + // Causes all blocking socket operations and any further + // socket operations to return with ETERM. + void shutdown() ZMQ_NOTHROW + { + if (ptr == ZMQ_NULLPTR) + return; + int rc = zmq_ctx_shutdown(ptr); + ZMQ_ASSERT(rc == 0); + } + + // Be careful with this, it's probably only useful for + // using the C api together with an existing C++ api. + // Normally you should never need to use this. + ZMQ_EXPLICIT operator void *() ZMQ_NOTHROW { return ptr; } + + ZMQ_EXPLICIT operator void const *() const ZMQ_NOTHROW { return ptr; } + + ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return ptr; } + + ZMQ_DEPRECATED("from 4.7.0, use handle() != nullptr instead") + operator bool() const ZMQ_NOTHROW { return ptr != ZMQ_NULLPTR; } + + void swap(context_t &other) ZMQ_NOTHROW { std::swap(ptr, other.ptr); } + + private: + void *ptr; + + context_t(const context_t &) ZMQ_DELETED_FUNCTION; + void operator=(const context_t &) ZMQ_DELETED_FUNCTION; +}; + +inline void swap(context_t &a, context_t &b) ZMQ_NOTHROW +{ + a.swap(b); +} + +#ifdef ZMQ_CPP11 + +struct recv_buffer_size +{ + size_t size; // number of bytes written to buffer + size_t untruncated_size; // untruncated message size in bytes + + ZMQ_NODISCARD bool truncated() const noexcept + { + return size != untruncated_size; + } +}; + +#if CPPZMQ_HAS_OPTIONAL + +using send_result_t = std::optional; +using recv_result_t = std::optional; +using recv_buffer_result_t = std::optional; + +#else + +namespace detail +{ +// A C++11 type emulating the most basic +// operations of std::optional for trivial types +template class trivial_optional +{ + public: + static_assert(std::is_trivial::value, "T must be trivial"); + using value_type = T; + + trivial_optional() = default; + trivial_optional(T value) noexcept : _value(value), _has_value(true) {} + + const T *operator->() const noexcept + { + assert(_has_value); + return &_value; + } + T *operator->() noexcept + { + assert(_has_value); + return &_value; + } + + const T &operator*() const noexcept + { + assert(_has_value); + return _value; + } + T &operator*() noexcept + { + assert(_has_value); + return _value; + } + + T &value() + { + if (!_has_value) + throw std::exception(); + return _value; + } + const T &value() const + { + if (!_has_value) + throw std::exception(); + return _value; + } + + explicit operator bool() const noexcept { return _has_value; } + bool has_value() const noexcept { return _has_value; } + + private: + T _value{}; + bool _has_value{false}; +}; +} // namespace detail + +using send_result_t = detail::trivial_optional; +using recv_result_t = detail::trivial_optional; +using recv_buffer_result_t = detail::trivial_optional; + +#endif + +namespace detail +{ +template constexpr T enum_bit_or(T a, T b) noexcept +{ + static_assert(std::is_enum::value, "must be enum"); + using U = typename std::underlying_type::type; + return static_cast(static_cast(a) | static_cast(b)); +} +template constexpr T enum_bit_and(T a, T b) noexcept +{ + static_assert(std::is_enum::value, "must be enum"); + using U = typename std::underlying_type::type; + return static_cast(static_cast(a) & static_cast(b)); +} +template constexpr T enum_bit_xor(T a, T b) noexcept +{ + static_assert(std::is_enum::value, "must be enum"); + using U = typename std::underlying_type::type; + return static_cast(static_cast(a) ^ static_cast(b)); +} +template constexpr T enum_bit_not(T a) noexcept +{ + static_assert(std::is_enum::value, "must be enum"); + using U = typename std::underlying_type::type; + return static_cast(~static_cast(a)); +} +} // namespace detail + +// partially satisfies named requirement BitmaskType +enum class send_flags : int +{ + none = 0, + dontwait = ZMQ_DONTWAIT, + sndmore = ZMQ_SNDMORE +}; + +constexpr send_flags operator|(send_flags a, send_flags b) noexcept +{ + return detail::enum_bit_or(a, b); +} +constexpr send_flags operator&(send_flags a, send_flags b) noexcept +{ + return detail::enum_bit_and(a, b); +} +constexpr send_flags operator^(send_flags a, send_flags b) noexcept +{ + return detail::enum_bit_xor(a, b); +} +constexpr send_flags operator~(send_flags a) noexcept +{ + return detail::enum_bit_not(a); +} + +// partially satisfies named requirement BitmaskType +enum class recv_flags : int +{ + none = 0, + dontwait = ZMQ_DONTWAIT +}; + +constexpr recv_flags operator|(recv_flags a, recv_flags b) noexcept +{ + return detail::enum_bit_or(a, b); +} +constexpr recv_flags operator&(recv_flags a, recv_flags b) noexcept +{ + return detail::enum_bit_and(a, b); +} +constexpr recv_flags operator^(recv_flags a, recv_flags b) noexcept +{ + return detail::enum_bit_xor(a, b); +} +constexpr recv_flags operator~(recv_flags a) noexcept +{ + return detail::enum_bit_not(a); +} + + +// mutable_buffer, const_buffer and buffer are based on +// the Networking TS specification, draft: +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4771.pdf + +class mutable_buffer +{ + public: + constexpr mutable_buffer() noexcept : _data(nullptr), _size(0) {} + constexpr mutable_buffer(void *p, size_t n) noexcept : _data(p), _size(n) + { +#ifdef ZMQ_EXTENDED_CONSTEXPR + assert(p != nullptr || n == 0); +#endif + } + + constexpr void *data() const noexcept { return _data; } + constexpr size_t size() const noexcept { return _size; } + mutable_buffer &operator+=(size_t n) noexcept + { + // (std::min) is a workaround for when a min macro is defined + const auto shift = (std::min)(n, _size); + _data = static_cast(_data) + shift; + _size -= shift; + return *this; + } + + private: + void *_data; + size_t _size; +}; + +inline mutable_buffer operator+(const mutable_buffer &mb, size_t n) noexcept +{ + return mutable_buffer(static_cast(mb.data()) + (std::min)(n, mb.size()), + mb.size() - (std::min)(n, mb.size())); +} +inline mutable_buffer operator+(size_t n, const mutable_buffer &mb) noexcept +{ + return mb + n; +} + +class const_buffer +{ + public: + constexpr const_buffer() noexcept : _data(nullptr), _size(0) {} + constexpr const_buffer(const void *p, size_t n) noexcept : _data(p), _size(n) + { +#ifdef ZMQ_EXTENDED_CONSTEXPR + assert(p != nullptr || n == 0); +#endif + } + constexpr const_buffer(const mutable_buffer &mb) noexcept : + _data(mb.data()), _size(mb.size()) + { + } + + constexpr const void *data() const noexcept { return _data; } + constexpr size_t size() const noexcept { return _size; } + const_buffer &operator+=(size_t n) noexcept + { + const auto shift = (std::min)(n, _size); + _data = static_cast(_data) + shift; + _size -= shift; + return *this; + } + + private: + const void *_data; + size_t _size; +}; + +inline const_buffer operator+(const const_buffer &cb, size_t n) noexcept +{ + return const_buffer(static_cast(cb.data()) + + (std::min)(n, cb.size()), + cb.size() - (std::min)(n, cb.size())); +} +inline const_buffer operator+(size_t n, const const_buffer &cb) noexcept +{ + return cb + n; +} + +// buffer creation + +constexpr mutable_buffer buffer(void *p, size_t n) noexcept +{ + return mutable_buffer(p, n); +} +constexpr const_buffer buffer(const void *p, size_t n) noexcept +{ + return const_buffer(p, n); +} +constexpr mutable_buffer buffer(const mutable_buffer &mb) noexcept +{ + return mb; +} +inline mutable_buffer buffer(const mutable_buffer &mb, size_t n) noexcept +{ + return mutable_buffer(mb.data(), (std::min)(mb.size(), n)); +} +constexpr const_buffer buffer(const const_buffer &cb) noexcept +{ + return cb; +} +inline const_buffer buffer(const const_buffer &cb, size_t n) noexcept +{ + return const_buffer(cb.data(), (std::min)(cb.size(), n)); +} + +namespace detail +{ +template struct is_buffer +{ + static constexpr bool value = + std::is_same::value || std::is_same::value; +}; + +template struct is_pod_like +{ + // NOTE: The networking draft N4771 section 16.11 requires + // T in the buffer functions below to be + // trivially copyable OR standard layout. + // Here we decide to be conservative and require both. + static constexpr bool value = + ZMQ_IS_TRIVIALLY_COPYABLE(T) && std::is_standard_layout::value; +}; + +template constexpr auto seq_size(const C &c) noexcept -> decltype(c.size()) +{ + return c.size(); +} +template +constexpr size_t seq_size(const T (&/*array*/)[N]) noexcept +{ + return N; +} + +template +auto buffer_contiguous_sequence(Seq &&seq) noexcept + -> decltype(buffer(std::addressof(*std::begin(seq)), size_t{})) +{ + using T = typename std::remove_cv< + typename std::remove_reference::type>::type; + static_assert(detail::is_pod_like::value, "T must be POD"); + + const auto size = seq_size(seq); + return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr, + size * sizeof(T)); +} +template +auto buffer_contiguous_sequence(Seq &&seq, size_t n_bytes) noexcept + -> decltype(buffer_contiguous_sequence(seq)) +{ + using T = typename std::remove_cv< + typename std::remove_reference::type>::type; + static_assert(detail::is_pod_like::value, "T must be POD"); + + const auto size = seq_size(seq); + return buffer(size != 0u ? std::addressof(*std::begin(seq)) : nullptr, + (std::min)(size * sizeof(T), n_bytes)); +} + +} // namespace detail + +// C array +template mutable_buffer buffer(T (&data)[N]) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +mutable_buffer buffer(T (&data)[N], size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +template const_buffer buffer(const T (&data)[N]) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +const_buffer buffer(const T (&data)[N], size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +// std::array +template mutable_buffer buffer(std::array &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +mutable_buffer buffer(std::array &data, size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +template +const_buffer buffer(std::array &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +const_buffer buffer(std::array &data, size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +template +const_buffer buffer(const std::array &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +const_buffer buffer(const std::array &data, size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +// std::vector +template +mutable_buffer buffer(std::vector &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +mutable_buffer buffer(std::vector &data, size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +template +const_buffer buffer(const std::vector &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +const_buffer buffer(const std::vector &data, size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +// std::basic_string +template +mutable_buffer buffer(std::basic_string &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +mutable_buffer buffer(std::basic_string &data, + size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +template +const_buffer buffer(const std::basic_string &data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +const_buffer buffer(const std::basic_string &data, + size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} + +#if CPPZMQ_HAS_STRING_VIEW +// std::basic_string_view +template +const_buffer buffer(std::basic_string_view data) noexcept +{ + return detail::buffer_contiguous_sequence(data); +} +template +const_buffer buffer(std::basic_string_view data, size_t n_bytes) noexcept +{ + return detail::buffer_contiguous_sequence(data, n_bytes); +} +#endif + +// Buffer for a string literal (null terminated) +// where the buffer size excludes the terminating character. +// Equivalent to zmq::buffer(std::string_view("...")). +template +constexpr const_buffer str_buffer(const Char (&data)[N]) noexcept +{ + static_assert(detail::is_pod_like::value, "Char must be POD"); +#ifdef ZMQ_EXTENDED_CONSTEXPR + assert(data[N - 1] == Char{0}); +#endif + return const_buffer(static_cast(data), (N - 1) * sizeof(Char)); +} + +namespace literals +{ +constexpr const_buffer operator"" _zbuf(const char *str, size_t len) noexcept +{ + return const_buffer(str, len * sizeof(char)); +} +constexpr const_buffer operator"" _zbuf(const wchar_t *str, size_t len) noexcept +{ + return const_buffer(str, len * sizeof(wchar_t)); +} +constexpr const_buffer operator"" _zbuf(const char16_t *str, size_t len) noexcept +{ + return const_buffer(str, len * sizeof(char16_t)); +} +constexpr const_buffer operator"" _zbuf(const char32_t *str, size_t len) noexcept +{ + return const_buffer(str, len * sizeof(char32_t)); +} +} + +#ifdef ZMQ_CPP11 +enum class socket_type : int +{ + req = ZMQ_REQ, + rep = ZMQ_REP, + dealer = ZMQ_DEALER, + router = ZMQ_ROUTER, + pub = ZMQ_PUB, + sub = ZMQ_SUB, + xpub = ZMQ_XPUB, + xsub = ZMQ_XSUB, + push = ZMQ_PUSH, + pull = ZMQ_PULL, +#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) + server = ZMQ_SERVER, + client = ZMQ_CLIENT, + radio = ZMQ_RADIO, + dish = ZMQ_DISH, + gather = ZMQ_GATHER, + scatter = ZMQ_SCATTER, + dgram = ZMQ_DGRAM, +#endif +#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3) + peer = ZMQ_PEER, + channel = ZMQ_CHANNEL, +#endif +#if ZMQ_VERSION_MAJOR >= 4 + stream = ZMQ_STREAM, +#endif + pair = ZMQ_PAIR +}; +#endif + +namespace sockopt +{ +// There are two types of options, +// integral type with known compiler time size (int, bool, int64_t, uint64_t) +// and arrays with dynamic size (strings, binary data). + +// BoolUnit: if true accepts values of type bool (but passed as T into libzmq) +template struct integral_option +{ +}; + +// NullTerm: +// 0: binary data +// 1: null-terminated string (`getsockopt` size includes null) +// 2: binary (size 32) or Z85 encoder string of size 41 (null included) +template struct array_option +{ +}; + +#define ZMQ_DEFINE_INTEGRAL_OPT(OPT, NAME, TYPE) \ + using NAME##_t = integral_option; \ + ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {} +#define ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(OPT, NAME, TYPE) \ + using NAME##_t = integral_option; \ + ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {} +#define ZMQ_DEFINE_ARRAY_OPT(OPT, NAME) \ + using NAME##_t = array_option; \ + ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {} +#define ZMQ_DEFINE_ARRAY_OPT_BINARY(OPT, NAME) \ + using NAME##_t = array_option; \ + ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {} +#define ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(OPT, NAME) \ + using NAME##_t = array_option; \ + ZMQ_INLINE_VAR ZMQ_CONSTEXPR_VAR NAME##_t NAME {} + +// deprecated, use zmq::fd_t +using cppzmq_fd_t = ::zmq::fd_t; + +#ifdef ZMQ_AFFINITY +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_AFFINITY, affinity, uint64_t); +#endif +#ifdef ZMQ_BACKLOG +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_BACKLOG, backlog, int); +#endif +#ifdef ZMQ_BINDTODEVICE +ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_BINDTODEVICE, bindtodevice); +#endif +#ifdef ZMQ_CONFLATE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CONFLATE, conflate, int); +#endif +#ifdef ZMQ_CONNECT_ROUTING_ID +ZMQ_DEFINE_ARRAY_OPT(ZMQ_CONNECT_ROUTING_ID, connect_routing_id); +#endif +#ifdef ZMQ_CONNECT_TIMEOUT +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_CONNECT_TIMEOUT, connect_timeout, int); +#endif +#ifdef ZMQ_CURVE_PUBLICKEY +ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_PUBLICKEY, curve_publickey); +#endif +#ifdef ZMQ_CURVE_SECRETKEY +ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SECRETKEY, curve_secretkey); +#endif +#ifdef ZMQ_CURVE_SERVER +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_CURVE_SERVER, curve_server, int); +#endif +#ifdef ZMQ_CURVE_SERVERKEY +ZMQ_DEFINE_ARRAY_OPT_BIN_OR_Z85(ZMQ_CURVE_SERVERKEY, curve_serverkey); +#endif +#ifdef ZMQ_DISCONNECT_MSG +ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_DISCONNECT_MSG, disconnect_msg); +#endif +#ifdef ZMQ_EVENTS +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_EVENTS, events, int); +#endif +#ifdef ZMQ_FD +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_FD, fd, ::zmq::fd_t); +#endif +#ifdef ZMQ_GSSAPI_PLAINTEXT +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_PLAINTEXT, gssapi_plaintext, int); +#endif +#ifdef ZMQ_GSSAPI_SERVER +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_GSSAPI_SERVER, gssapi_server, int); +#endif +#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL +ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL, gssapi_service_principal); +#endif +#ifdef ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE, + gssapi_service_principal_nametype, + int); +#endif +#ifdef ZMQ_GSSAPI_PRINCIPAL +ZMQ_DEFINE_ARRAY_OPT(ZMQ_GSSAPI_PRINCIPAL, gssapi_principal); +#endif +#ifdef ZMQ_GSSAPI_PRINCIPAL_NAMETYPE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_GSSAPI_PRINCIPAL_NAMETYPE, + gssapi_principal_nametype, + int); +#endif +#ifdef ZMQ_HANDSHAKE_IVL +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HANDSHAKE_IVL, handshake_ivl, int); +#endif +#ifdef ZMQ_HEARTBEAT_IVL +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_IVL, heartbeat_ivl, int); +#endif +#ifdef ZMQ_HEARTBEAT_TIMEOUT +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TIMEOUT, heartbeat_timeout, int); +#endif +#ifdef ZMQ_HEARTBEAT_TTL +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_HEARTBEAT_TTL, heartbeat_ttl, int); +#endif +#ifdef ZMQ_HELLO_MSG +ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_HELLO_MSG, hello_msg); +#endif +#ifdef ZMQ_IMMEDIATE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IMMEDIATE, immediate, int); +#endif +#ifdef ZMQ_INVERT_MATCHING +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_INVERT_MATCHING, invert_matching, int); +#endif +#ifdef ZMQ_IPV6 +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_IPV6, ipv6, int); +#endif +#ifdef ZMQ_LAST_ENDPOINT +ZMQ_DEFINE_ARRAY_OPT(ZMQ_LAST_ENDPOINT, last_endpoint); +#endif +#ifdef ZMQ_LINGER +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_LINGER, linger, int); +#endif +#ifdef ZMQ_MAXMSGSIZE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MAXMSGSIZE, maxmsgsize, int64_t); +#endif +#ifdef ZMQ_MECHANISM +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MECHANISM, mechanism, int); +#endif +#ifdef ZMQ_METADATA +ZMQ_DEFINE_ARRAY_OPT(ZMQ_METADATA, metadata); +#endif +#ifdef ZMQ_MULTICAST_HOPS +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_HOPS, multicast_hops, int); +#endif +#ifdef ZMQ_MULTICAST_LOOP +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_MULTICAST_LOOP, multicast_loop, int); +#endif +#ifdef ZMQ_MULTICAST_MAXTPDU +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_MULTICAST_MAXTPDU, multicast_maxtpdu, int); +#endif +#ifdef ZMQ_ONLY_FIRST_SUBSCRIBE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ONLY_FIRST_SUBSCRIBE, only_first_subscribe, int); +#endif +#ifdef ZMQ_PLAIN_SERVER +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PLAIN_SERVER, plain_server, int); +#endif +#ifdef ZMQ_PLAIN_PASSWORD +ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_PASSWORD, plain_password); +#endif +#ifdef ZMQ_PLAIN_USERNAME +ZMQ_DEFINE_ARRAY_OPT(ZMQ_PLAIN_USERNAME, plain_username); +#endif +#ifdef ZMQ_PRIORITY +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_PRIORITY, priority, int); +#endif +#ifdef ZMQ_USE_FD +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_USE_FD, use_fd, int); +#endif +#ifdef ZMQ_PROBE_ROUTER +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_PROBE_ROUTER, probe_router, int); +#endif +#ifdef ZMQ_RATE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RATE, rate, int); +#endif +#ifdef ZMQ_RCVBUF +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVBUF, rcvbuf, int); +#endif +#ifdef ZMQ_RCVHWM +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVHWM, rcvhwm, int); +#endif +#ifdef ZMQ_RCVMORE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_RCVMORE, rcvmore, int); +#endif +#ifdef ZMQ_RCVTIMEO +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RCVTIMEO, rcvtimeo, int); +#endif +#ifdef ZMQ_RECONNECT_IVL +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL, reconnect_ivl, int); +#endif +#ifdef ZMQ_RECONNECT_IVL_MAX +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_IVL_MAX, reconnect_ivl_max, int); +#endif +#ifdef ZMQ_RECONNECT_STOP +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECONNECT_STOP, reconnect_stop, int); +#endif +#ifdef ZMQ_RECOVERY_IVL +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_RECOVERY_IVL, recovery_ivl, int); +#endif +#ifdef ZMQ_REQ_CORRELATE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_CORRELATE, req_correlate, int); +#endif +#ifdef ZMQ_REQ_RELAXED +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_REQ_RELAXED, req_relaxed, int); +#endif +#ifdef ZMQ_ROUTER_HANDOVER +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_HANDOVER, router_handover, int); +#endif +#ifdef ZMQ_ROUTER_MANDATORY +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ROUTER_MANDATORY, router_mandatory, int); +#endif +#ifdef ZMQ_ROUTER_NOTIFY +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_ROUTER_NOTIFY, router_notify, int); +#endif +#ifdef ZMQ_ROUTING_ID +ZMQ_DEFINE_ARRAY_OPT_BINARY(ZMQ_ROUTING_ID, routing_id); +#endif +#ifdef ZMQ_SNDBUF +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDBUF, sndbuf, int); +#endif +#ifdef ZMQ_SNDHWM +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDHWM, sndhwm, int); +#endif +#ifdef ZMQ_SNDTIMEO +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_SNDTIMEO, sndtimeo, int); +#endif +#ifdef ZMQ_SOCKS_PASSWORD +ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PASSWORD, socks_password); +#endif +#ifdef ZMQ_SOCKS_PROXY +ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_PROXY, socks_proxy); +#endif +#ifdef ZMQ_SOCKS_USERNAME +ZMQ_DEFINE_ARRAY_OPT(ZMQ_SOCKS_USERNAME, socks_username); +#endif +#ifdef ZMQ_STREAM_NOTIFY +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_STREAM_NOTIFY, stream_notify, int); +#endif +#ifdef ZMQ_SUBSCRIBE +ZMQ_DEFINE_ARRAY_OPT(ZMQ_SUBSCRIBE, subscribe); +#endif +#ifdef ZMQ_TCP_KEEPALIVE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE, tcp_keepalive, int); +#endif +#ifdef ZMQ_TCP_KEEPALIVE_CNT +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_CNT, tcp_keepalive_cnt, int); +#endif +#ifdef ZMQ_TCP_KEEPALIVE_IDLE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_IDLE, tcp_keepalive_idle, int); +#endif +#ifdef ZMQ_TCP_KEEPALIVE_INTVL +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_KEEPALIVE_INTVL, tcp_keepalive_intvl, int); +#endif +#ifdef ZMQ_TCP_MAXRT +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TCP_MAXRT, tcp_maxrt, int); +#endif +#ifdef ZMQ_THREAD_SAFE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_THREAD_SAFE, thread_safe, int); +#endif +#ifdef ZMQ_TOS +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TOS, tos, int); +#endif +#ifdef ZMQ_TYPE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, type, int); +#ifdef ZMQ_CPP11 +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_TYPE, socket_type, socket_type); +#endif // ZMQ_CPP11 +#endif // ZMQ_TYPE +#ifdef ZMQ_UNSUBSCRIBE +ZMQ_DEFINE_ARRAY_OPT(ZMQ_UNSUBSCRIBE, unsubscribe); +#endif +#ifdef ZMQ_VMCI_BUFFER_SIZE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_SIZE, vmci_buffer_size, uint64_t); +#endif +#ifdef ZMQ_VMCI_BUFFER_MIN_SIZE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MIN_SIZE, vmci_buffer_min_size, uint64_t); +#endif +#ifdef ZMQ_VMCI_BUFFER_MAX_SIZE +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_BUFFER_MAX_SIZE, vmci_buffer_max_size, uint64_t); +#endif +#ifdef ZMQ_VMCI_CONNECT_TIMEOUT +ZMQ_DEFINE_INTEGRAL_OPT(ZMQ_VMCI_CONNECT_TIMEOUT, vmci_connect_timeout, int); +#endif +#ifdef ZMQ_XPUB_VERBOSE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSE, xpub_verbose, int); +#endif +#ifdef ZMQ_XPUB_VERBOSER +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_VERBOSER, xpub_verboser, int); +#endif +#ifdef ZMQ_XPUB_MANUAL +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL, xpub_manual, int); +#endif +#ifdef ZMQ_XPUB_MANUAL_LAST_VALUE +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_MANUAL_LAST_VALUE, xpub_manual_last_value, int); +#endif +#ifdef ZMQ_XPUB_NODROP +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_XPUB_NODROP, xpub_nodrop, int); +#endif +#ifdef ZMQ_XPUB_WELCOME_MSG +ZMQ_DEFINE_ARRAY_OPT(ZMQ_XPUB_WELCOME_MSG, xpub_welcome_msg); +#endif +#ifdef ZMQ_ZAP_ENFORCE_DOMAIN +ZMQ_DEFINE_INTEGRAL_BOOL_UNIT_OPT(ZMQ_ZAP_ENFORCE_DOMAIN, zap_enforce_domain, int); +#endif +#ifdef ZMQ_ZAP_DOMAIN +ZMQ_DEFINE_ARRAY_OPT(ZMQ_ZAP_DOMAIN, zap_domain); +#endif + +} // namespace sockopt +#endif // ZMQ_CPP11 + + +namespace detail +{ +class socket_base +{ + public: + socket_base() ZMQ_NOTHROW : _handle(ZMQ_NULLPTR) {} + ZMQ_EXPLICIT socket_base(void *handle) ZMQ_NOTHROW : _handle(handle) {} + + template + ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt") + void setsockopt(int option_, T const &optval) + { + setsockopt(option_, &optval, sizeof(T)); + } + + ZMQ_CPP11_DEPRECATED("from 4.7.0, use `set` taking option from zmq::sockopt") + void setsockopt(int option_, const void *optval_, size_t optvallen_) + { + int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_); + if (rc != 0) + throw error_t(); + } + + ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt") + void getsockopt(int option_, void *optval_, size_t *optvallen_) const + { + int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_); + if (rc != 0) + throw error_t(); + } + + template + ZMQ_CPP11_DEPRECATED("from 4.7.0, use `get` taking option from zmq::sockopt") + T getsockopt(int option_) const + { + T optval; + size_t optlen = sizeof(T); + getsockopt(option_, &optval, &optlen); + return optval; + } + +#ifdef ZMQ_CPP11 + // Set integral socket option, e.g. + // `socket.set(zmq::sockopt::linger, 0)` + template + void set(sockopt::integral_option, const T &val) + { + static_assert(std::is_integral::value, "T must be integral"); + set_option(Opt, &val, sizeof val); + } + + // Set integral socket option from boolean, e.g. + // `socket.set(zmq::sockopt::immediate, false)` + template + void set(sockopt::integral_option, bool val) + { + static_assert(std::is_integral::value, "T must be integral"); + T rep_val = val; + set_option(Opt, &rep_val, sizeof rep_val); + } + + // Set array socket option, e.g. + // `socket.set(zmq::sockopt::plain_username, "foo123")` + template + void set(sockopt::array_option, const char *buf) + { + set_option(Opt, buf, std::strlen(buf)); + } + + // Set array socket option, e.g. + // `socket.set(zmq::sockopt::routing_id, zmq::buffer(id))` + template + void set(sockopt::array_option, const_buffer buf) + { + set_option(Opt, buf.data(), buf.size()); + } + + // Set array socket option, e.g. + // `socket.set(zmq::sockopt::routing_id, id_str)` + template + void set(sockopt::array_option, const std::string &buf) + { + set_option(Opt, buf.data(), buf.size()); + } + +#if CPPZMQ_HAS_STRING_VIEW + // Set array socket option, e.g. + // `socket.set(zmq::sockopt::routing_id, id_str)` + template + void set(sockopt::array_option, std::string_view buf) + { + set_option(Opt, buf.data(), buf.size()); + } +#endif + + // Get scalar socket option, e.g. + // `auto opt = socket.get(zmq::sockopt::linger)` + template + ZMQ_NODISCARD T get(sockopt::integral_option) const + { + static_assert(std::is_scalar::value, "T must be scalar"); + T val; + size_t size = sizeof val; + get_option(Opt, &val, &size); + assert(size == sizeof val); + return val; + } + + // Get array socket option, writes to buf, returns option size in bytes, e.g. + // `size_t optsize = socket.get(zmq::sockopt::routing_id, zmq::buffer(id))` + template + ZMQ_NODISCARD size_t get(sockopt::array_option, + mutable_buffer buf) const + { + size_t size = buf.size(); + get_option(Opt, buf.data(), &size); + return size; + } + + // Get array socket option as string (initializes the string buffer size to init_size) e.g. + // `auto s = socket.get(zmq::sockopt::routing_id)` + // Note: removes the null character from null-terminated string options, + // i.e. the string size excludes the null character. + template + ZMQ_NODISCARD std::string get(sockopt::array_option, + size_t init_size = 1024) const + { + if ZMQ_CONSTEXPR_IF (NullTerm == 2) { + if (init_size == 1024) { + init_size = 41; // get as Z85 string + } + } + std::string str(init_size, '\0'); + size_t size = get(sockopt::array_option{}, buffer(str)); + if ZMQ_CONSTEXPR_IF (NullTerm == 1) { + if (size > 0) { + assert(str[size - 1] == '\0'); + --size; + } + } else if ZMQ_CONSTEXPR_IF (NullTerm == 2) { + assert(size == 32 || size == 41); + if (size == 41) { + assert(str[size - 1] == '\0'); + --size; + } + } + str.resize(size); + return str; + } +#endif + + void bind(std::string const &addr) { bind(addr.c_str()); } + + void bind(const char *addr_) + { + int rc = zmq_bind(_handle, addr_); + if (rc != 0) + throw error_t(); + } + + void unbind(std::string const &addr) { unbind(addr.c_str()); } + + void unbind(const char *addr_) + { + int rc = zmq_unbind(_handle, addr_); + if (rc != 0) + throw error_t(); + } + + void connect(std::string const &addr) { connect(addr.c_str()); } + + void connect(const char *addr_) + { + int rc = zmq_connect(_handle, addr_); + if (rc != 0) + throw error_t(); + } + + void disconnect(std::string const &addr) { disconnect(addr.c_str()); } + + void disconnect(const char *addr_) + { + int rc = zmq_disconnect(_handle, addr_); + if (rc != 0) + throw error_t(); + } + + ZMQ_DEPRECATED("from 4.7.1, use handle() != nullptr or operator bool") + bool connected() const ZMQ_NOTHROW { return (_handle != ZMQ_NULLPTR); } + + ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking a const_buffer and send_flags") + size_t send(const void *buf_, size_t len_, int flags_ = 0) + { + int nbytes = zmq_send(_handle, buf_, len_, flags_); + if (nbytes >= 0) + return static_cast(nbytes); + if (zmq_errno() == EAGAIN) + return 0; + throw error_t(); + } + + ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags") + bool send(message_t &msg_, + int flags_ = 0) // default until removed + { + int nbytes = zmq_msg_send(msg_.handle(), _handle, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno() == EAGAIN) + return false; + throw error_t(); + } + + template + ZMQ_CPP11_DEPRECATED( + "from 4.4.1, use send taking message_t or buffer (for contiguous " + "ranges), and send_flags") + bool send(T first, T last, int flags_ = 0) + { + zmq::message_t msg(first, last); + int nbytes = zmq_msg_send(msg.handle(), _handle, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno() == EAGAIN) + return false; + throw error_t(); + } + +#ifdef ZMQ_HAS_RVALUE_REFS + ZMQ_CPP11_DEPRECATED("from 4.3.1, use send taking message_t and send_flags") + bool send(message_t &&msg_, + int flags_ = 0) // default until removed + { +#ifdef ZMQ_CPP11 + return send(msg_, static_cast(flags_)).has_value(); +#else + return send(msg_, flags_); +#endif + } +#endif + +#ifdef ZMQ_CPP11 + send_result_t send(const_buffer buf, send_flags flags = send_flags::none) + { + const int nbytes = + zmq_send(_handle, buf.data(), buf.size(), static_cast(flags)); + if (nbytes >= 0) + return static_cast(nbytes); + if (zmq_errno() == EAGAIN) + return {}; + throw error_t(); + } + + send_result_t send(message_t &msg, send_flags flags) + { + int nbytes = zmq_msg_send(msg.handle(), _handle, static_cast(flags)); + if (nbytes >= 0) + return static_cast(nbytes); + if (zmq_errno() == EAGAIN) + return {}; + throw error_t(); + } + + send_result_t send(message_t &&msg, send_flags flags) + { + return send(msg, flags); + } +#endif + + ZMQ_CPP11_DEPRECATED( + "from 4.3.1, use recv taking a mutable_buffer and recv_flags") + size_t recv(void *buf_, size_t len_, int flags_ = 0) + { + int nbytes = zmq_recv(_handle, buf_, len_, flags_); + if (nbytes >= 0) + return static_cast(nbytes); + if (zmq_errno() == EAGAIN) + return 0; + throw error_t(); + } + + ZMQ_CPP11_DEPRECATED( + "from 4.3.1, use recv taking a reference to message_t and recv_flags") + bool recv(message_t *msg_, int flags_ = 0) + { + int nbytes = zmq_msg_recv(msg_->handle(), _handle, flags_); + if (nbytes >= 0) + return true; + if (zmq_errno() == EAGAIN) + return false; + throw error_t(); + } + +#ifdef ZMQ_CPP11 + ZMQ_NODISCARD + recv_buffer_result_t recv(mutable_buffer buf, + recv_flags flags = recv_flags::none) + { + const int nbytes = + zmq_recv(_handle, buf.data(), buf.size(), static_cast(flags)); + if (nbytes >= 0) { + return recv_buffer_size{ + (std::min)(static_cast(nbytes), buf.size()), + static_cast(nbytes)}; + } + if (zmq_errno() == EAGAIN) + return {}; + throw error_t(); + } + + ZMQ_NODISCARD + recv_result_t recv(message_t &msg, recv_flags flags = recv_flags::none) + { + const int nbytes = + zmq_msg_recv(msg.handle(), _handle, static_cast(flags)); + if (nbytes >= 0) { + assert(msg.size() == static_cast(nbytes)); + return static_cast(nbytes); + } + if (zmq_errno() == EAGAIN) + return {}; + throw error_t(); + } +#endif + +#if defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 0) + void join(const char *group) + { + int rc = zmq_join(_handle, group); + if (rc != 0) + throw error_t(); + } + + void leave(const char *group) + { + int rc = zmq_leave(_handle, group); + if (rc != 0) + throw error_t(); + } +#endif + + ZMQ_NODISCARD void *handle() ZMQ_NOTHROW { return _handle; } + ZMQ_NODISCARD const void *handle() const ZMQ_NOTHROW { return _handle; } + + ZMQ_EXPLICIT operator bool() const ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; } + // note: non-const operator bool can be removed once + // operator void* is removed from socket_t + ZMQ_EXPLICIT operator bool() ZMQ_NOTHROW { return _handle != ZMQ_NULLPTR; } + + protected: + void *_handle; + + private: + void set_option(int option_, const void *optval_, size_t optvallen_) + { + int rc = zmq_setsockopt(_handle, option_, optval_, optvallen_); + if (rc != 0) + throw error_t(); + } + + void get_option(int option_, void *optval_, size_t *optvallen_) const + { + int rc = zmq_getsockopt(_handle, option_, optval_, optvallen_); + if (rc != 0) + throw error_t(); + } +}; +} // namespace detail + +struct from_handle_t +{ + struct _private + { + }; // disabling use other than with from_handle + ZMQ_CONSTEXPR_FN ZMQ_EXPLICIT from_handle_t(_private /*p*/) ZMQ_NOTHROW {} +}; + +ZMQ_CONSTEXPR_VAR from_handle_t from_handle = + from_handle_t(from_handle_t::_private()); + +// A non-owning nullable reference to a socket. +// The reference is invalidated on socket close or destruction. +class socket_ref : public detail::socket_base +{ + public: + socket_ref() ZMQ_NOTHROW : detail::socket_base() {} +#ifdef ZMQ_CPP11 + socket_ref(std::nullptr_t) ZMQ_NOTHROW : detail::socket_base() {} +#endif + socket_ref(from_handle_t /*fh*/, void *handle) ZMQ_NOTHROW + : detail::socket_base(handle) + { + } +}; + +#ifdef ZMQ_CPP11 +inline bool operator==(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW +{ + return sr.handle() == nullptr; +} +inline bool operator==(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW +{ + return sr.handle() == nullptr; +} +inline bool operator!=(socket_ref sr, std::nullptr_t /*p*/) ZMQ_NOTHROW +{ + return !(sr == nullptr); +} +inline bool operator!=(std::nullptr_t /*p*/, socket_ref sr) ZMQ_NOTHROW +{ + return !(sr == nullptr); +} +#endif + +inline bool operator==(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW +{ + return std::equal_to()(a.handle(), b.handle()); +} +inline bool operator!=(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW +{ + return !(a == b); +} +inline bool operator<(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW +{ + return std::less()(a.handle(), b.handle()); +} +inline bool operator>(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW +{ + return b < a; +} +inline bool operator<=(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW +{ + return !(a > b); +} +inline bool operator>=(const detail::socket_base& a, const detail::socket_base& b) ZMQ_NOTHROW +{ + return !(a < b); +} + +} // namespace zmq + +#ifdef ZMQ_CPP11 +namespace std +{ +template<> struct hash +{ + size_t operator()(zmq::socket_ref sr) const ZMQ_NOTHROW + { + return hash()(sr.handle()); + } +}; +} // namespace std +#endif + +namespace zmq +{ +class socket_t : public detail::socket_base +{ + friend class monitor_t; + + public: + socket_t() ZMQ_NOTHROW : detail::socket_base(ZMQ_NULLPTR), ctxptr(ZMQ_NULLPTR) {} + + socket_t(context_t &context_, int type_) : + detail::socket_base(zmq_socket(context_.handle(), type_)), + ctxptr(context_.handle()) + { + if (_handle == ZMQ_NULLPTR) + throw error_t(); + } + +#ifdef ZMQ_CPP11 + socket_t(context_t &context_, socket_type type_) : + socket_t(context_, static_cast(type_)) + { + } +#endif + +#ifdef ZMQ_HAS_RVALUE_REFS + socket_t(socket_t &&rhs) ZMQ_NOTHROW : detail::socket_base(rhs._handle), + ctxptr(rhs.ctxptr) + { + rhs._handle = ZMQ_NULLPTR; + rhs.ctxptr = ZMQ_NULLPTR; + } + socket_t &operator=(socket_t &&rhs) ZMQ_NOTHROW + { + close(); + std::swap(_handle, rhs._handle); + std::swap(ctxptr, rhs.ctxptr); + return *this; + } +#endif + + ~socket_t() ZMQ_NOTHROW { close(); } + + operator void *() ZMQ_NOTHROW { return _handle; } + + operator void const *() const ZMQ_NOTHROW { return _handle; } + + void close() ZMQ_NOTHROW + { + if (_handle == ZMQ_NULLPTR) + // already closed + return; + int rc = zmq_close(_handle); + ZMQ_ASSERT(rc == 0); + _handle = ZMQ_NULLPTR; + ctxptr = ZMQ_NULLPTR; + } + + void swap(socket_t &other) ZMQ_NOTHROW + { + std::swap(_handle, other._handle); + std::swap(ctxptr, other.ctxptr); + } + + operator socket_ref() ZMQ_NOTHROW { return socket_ref(from_handle, _handle); } + + private: + void *ctxptr; + + socket_t(const socket_t &) ZMQ_DELETED_FUNCTION; + void operator=(const socket_t &) ZMQ_DELETED_FUNCTION; + + // used by monitor_t + socket_t(void *context_, int type_) : + detail::socket_base(zmq_socket(context_, type_)), ctxptr(context_) + { + if (_handle == ZMQ_NULLPTR) + throw error_t(); + if (ctxptr == ZMQ_NULLPTR) + throw error_t(); + } +}; + +inline void swap(socket_t &a, socket_t &b) ZMQ_NOTHROW +{ + a.swap(b); +} + +ZMQ_DEPRECATED("from 4.3.1, use proxy taking socket_t objects") +inline void proxy(void *frontend, void *backend, void *capture) +{ + int rc = zmq_proxy(frontend, backend, capture); + if (rc != 0) + throw error_t(); +} + +inline void +proxy(socket_ref frontend, socket_ref backend, socket_ref capture = socket_ref()) +{ + int rc = zmq_proxy(frontend.handle(), backend.handle(), capture.handle()); + if (rc != 0) + throw error_t(); +} + +#ifdef ZMQ_HAS_PROXY_STEERABLE +ZMQ_DEPRECATED("from 4.3.1, use proxy_steerable taking socket_t objects") +inline void +proxy_steerable(void *frontend, void *backend, void *capture, void *control) +{ + int rc = zmq_proxy_steerable(frontend, backend, capture, control); + if (rc != 0) + throw error_t(); +} + +inline void proxy_steerable(socket_ref frontend, + socket_ref backend, + socket_ref capture, + socket_ref control) +{ + int rc = zmq_proxy_steerable(frontend.handle(), backend.handle(), + capture.handle(), control.handle()); + if (rc != 0) + throw error_t(); +} +#endif + +class monitor_t +{ + public: + monitor_t() : _socket(), _monitor_socket() {} + + virtual ~monitor_t() { close(); } + +#ifdef ZMQ_HAS_RVALUE_REFS + monitor_t(monitor_t &&rhs) ZMQ_NOTHROW : _socket(), _monitor_socket() + { + std::swap(_socket, rhs._socket); + std::swap(_monitor_socket, rhs._monitor_socket); + } + + monitor_t &operator=(monitor_t &&rhs) ZMQ_NOTHROW + { + close(); + _socket = socket_ref(); + std::swap(_socket, rhs._socket); + std::swap(_monitor_socket, rhs._monitor_socket); + return *this; + } +#endif + + + void + monitor(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL) + { + monitor(socket, addr.c_str(), events); + } + + void monitor(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL) + { + init(socket, addr_, events); + while (true) { + check_event(-1); + } + } + + void init(socket_t &socket, std::string const &addr, int events = ZMQ_EVENT_ALL) + { + init(socket, addr.c_str(), events); + } + + void init(socket_t &socket, const char *addr_, int events = ZMQ_EVENT_ALL) + { + int rc = zmq_socket_monitor(socket.handle(), addr_, events); + if (rc != 0) + throw error_t(); + + _socket = socket; + _monitor_socket = socket_t(socket.ctxptr, ZMQ_PAIR); + _monitor_socket.connect(addr_); + + on_monitor_started(); + } + + bool check_event(int timeout = 0) + { + assert(_monitor_socket); + + zmq::message_t eventMsg; + + zmq::pollitem_t items[] = { + {_monitor_socket.handle(), 0, ZMQ_POLLIN, 0}, + }; + + #ifdef ZMQ_CPP11 + zmq::poll(&items[0], 1, std::chrono::milliseconds(timeout)); + #else + zmq::poll(&items[0], 1, timeout); + #endif + + if (items[0].revents & ZMQ_POLLIN) { + int rc = zmq_msg_recv(eventMsg.handle(), _monitor_socket.handle(), 0); + if (rc == -1 && zmq_errno() == ETERM) + return false; + assert(rc != -1); + + } else { + return false; + } + +#if ZMQ_VERSION_MAJOR >= 4 + const char *data = static_cast(eventMsg.data()); + zmq_event_t msgEvent; + memcpy(&msgEvent.event, data, sizeof(uint16_t)); + data += sizeof(uint16_t); + memcpy(&msgEvent.value, data, sizeof(int32_t)); + zmq_event_t *event = &msgEvent; +#else + zmq_event_t *event = static_cast(eventMsg.data()); +#endif + +#ifdef ZMQ_NEW_MONITOR_EVENT_LAYOUT + zmq::message_t addrMsg; + int rc = zmq_msg_recv(addrMsg.handle(), _monitor_socket.handle(), 0); + if (rc == -1 && zmq_errno() == ETERM) { + return false; + } + + assert(rc != -1); + std::string address = addrMsg.to_string(); +#else + // Bit of a hack, but all events in the zmq_event_t union have the same layout so this will work for all event types. + std::string address = event->data.connected.addr; +#endif + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + if (event->event == ZMQ_EVENT_MONITOR_STOPPED) { + return false; + } + +#endif + + switch (event->event) { + case ZMQ_EVENT_CONNECTED: + on_event_connected(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_DELAYED: + on_event_connect_delayed(*event, address.c_str()); + break; + case ZMQ_EVENT_CONNECT_RETRIED: + on_event_connect_retried(*event, address.c_str()); + break; + case ZMQ_EVENT_LISTENING: + on_event_listening(*event, address.c_str()); + break; + case ZMQ_EVENT_BIND_FAILED: + on_event_bind_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPTED: + on_event_accepted(*event, address.c_str()); + break; + case ZMQ_EVENT_ACCEPT_FAILED: + on_event_accept_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSED: + on_event_closed(*event, address.c_str()); + break; + case ZMQ_EVENT_CLOSE_FAILED: + on_event_close_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_DISCONNECTED: + on_event_disconnected(*event, address.c_str()); + break; +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 0) || (defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3)) + case ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL: + on_event_handshake_failed_no_detail(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL: + on_event_handshake_failed_protocol(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_FAILED_AUTH: + on_event_handshake_failed_auth(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_SUCCEEDED: + on_event_handshake_succeeded(*event, address.c_str()); + break; +#elif defined(ZMQ_BUILD_DRAFT_API) && ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) + case ZMQ_EVENT_HANDSHAKE_FAILED: + on_event_handshake_failed(*event, address.c_str()); + break; + case ZMQ_EVENT_HANDSHAKE_SUCCEED: + on_event_handshake_succeed(*event, address.c_str()); + break; +#endif + default: + on_event_unknown(*event, address.c_str()); + break; + } + + return true; + } + +#ifdef ZMQ_EVENT_MONITOR_STOPPED + void abort() + { + if (_socket) + zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0); + + _socket = socket_ref(); + } +#endif + virtual void on_monitor_started() {} + virtual void on_event_connected(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_connect_delayed(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_connect_retried(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_listening(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_bind_failed(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_accepted(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_accept_failed(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_closed(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_close_failed(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_disconnected(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3) + virtual void on_event_handshake_failed_no_detail(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_handshake_failed_protocol(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_handshake_failed_auth(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_handshake_succeeded(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } +#elif ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 1) + virtual void on_event_handshake_failed(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } + virtual void on_event_handshake_succeed(const zmq_event_t &event_, + const char *addr_) + { + (void) event_; + (void) addr_; + } +#endif + virtual void on_event_unknown(const zmq_event_t &event_, const char *addr_) + { + (void) event_; + (void) addr_; + } + + private: + monitor_t(const monitor_t &) ZMQ_DELETED_FUNCTION; + void operator=(const monitor_t &) ZMQ_DELETED_FUNCTION; + + socket_ref _socket; + socket_t _monitor_socket; + + void close() ZMQ_NOTHROW + { + if (_socket) + zmq_socket_monitor(_socket.handle(), ZMQ_NULLPTR, 0); + _monitor_socket.close(); + } +}; + +#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) + +// polling events +enum class event_flags : short +{ + none = 0, + pollin = ZMQ_POLLIN, + pollout = ZMQ_POLLOUT, + pollerr = ZMQ_POLLERR, + pollpri = ZMQ_POLLPRI +}; + +constexpr event_flags operator|(event_flags a, event_flags b) noexcept +{ + return detail::enum_bit_or(a, b); +} +constexpr event_flags operator&(event_flags a, event_flags b) noexcept +{ + return detail::enum_bit_and(a, b); +} +constexpr event_flags operator^(event_flags a, event_flags b) noexcept +{ + return detail::enum_bit_xor(a, b); +} +constexpr event_flags operator~(event_flags a) noexcept +{ + return detail::enum_bit_not(a); +} + +struct no_user_data; + +// layout compatible with zmq_poller_event_t +template struct poller_event +{ + socket_ref socket; + ::zmq::fd_t fd; + T *user_data; + event_flags events; +}; + +template class poller_t +{ + public: + using event_type = poller_event; + + poller_t() : poller_ptr(zmq_poller_new()) + { + if (!poller_ptr) + throw error_t(); + } + + template< + typename Dummy = void, + typename = + typename std::enable_if::value, Dummy>::type> + void add(zmq::socket_ref socket, event_flags events, T *user_data) + { + add_impl(socket, events, user_data); + } + + void add(zmq::socket_ref socket, event_flags events) + { + add_impl(socket, events, nullptr); + } + + template< + typename Dummy = void, + typename = + typename std::enable_if::value, Dummy>::type> + void add(fd_t fd, event_flags events, T *user_data) + { + add_impl(fd, events, user_data); + } + + void add(fd_t fd, event_flags events) { add_impl(fd, events, nullptr); } + + void remove(zmq::socket_ref socket) + { + if (0 != zmq_poller_remove(poller_ptr.get(), socket.handle())) { + throw error_t(); + } + } + + void modify(zmq::socket_ref socket, event_flags events) + { + if (0 + != zmq_poller_modify(poller_ptr.get(), socket.handle(), + static_cast(events))) { + throw error_t(); + } + } + + size_t wait_all(std::vector &poller_events, + const std::chrono::milliseconds timeout) + { + int rc = zmq_poller_wait_all( + poller_ptr.get(), + reinterpret_cast(poller_events.data()), + static_cast(poller_events.size()), + static_cast(timeout.count())); + if (rc > 0) + return static_cast(rc); + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 2, 3) + if (zmq_errno() == EAGAIN) +#else + if (zmq_errno() == ETIMEDOUT) +#endif + return 0; + + throw error_t(); + } + +#if ZMQ_VERSION >= ZMQ_MAKE_VERSION(4, 3, 3) + size_t size() const noexcept + { + int rc = zmq_poller_size(const_cast(poller_ptr.get())); + ZMQ_ASSERT(rc >= 0); + return static_cast(std::max(rc, 0)); + } +#endif + + private: + struct destroy_poller_t + { + void operator()(void *ptr) noexcept + { + int rc = zmq_poller_destroy(&ptr); + ZMQ_ASSERT(rc == 0); + } + }; + + std::unique_ptr poller_ptr; + + void add_impl(zmq::socket_ref socket, event_flags events, T *user_data) + { + if (0 + != zmq_poller_add(poller_ptr.get(), socket.handle(), user_data, + static_cast(events))) { + throw error_t(); + } + } + + void add_impl(fd_t fd, event_flags events, T *user_data) + { + if (0 + != zmq_poller_add_fd(poller_ptr.get(), fd, user_data, + static_cast(events))) { + throw error_t(); + } + } +}; +#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) + +inline std::ostream &operator<<(std::ostream &os, const message_t &msg) +{ + return os << msg.str(); +} + +} // namespace zmq + +#endif // __ZMQ_HPP_INCLUDED__ diff --git a/3rdparty/cppzmq/zmq_addon.hpp b/3rdparty/cppzmq/zmq_addon.hpp new file mode 100644 index 000000000..958eec56d --- /dev/null +++ b/3rdparty/cppzmq/zmq_addon.hpp @@ -0,0 +1,753 @@ +/* + Copyright (c) 2016-2017 ZeroMQ community + Copyright (c) 2016 VOCA AS / Harald Nøkland + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. +*/ + +#ifndef __ZMQ_ADDON_HPP_INCLUDED__ +#define __ZMQ_ADDON_HPP_INCLUDED__ + +#include "zmq.hpp" + +#include +#include +#include +#include +#ifdef ZMQ_CPP11 +#include +#include +#include +#endif + +namespace zmq +{ +#ifdef ZMQ_CPP11 + +namespace detail +{ +template +recv_result_t +recv_multipart_n(socket_ref s, OutputIt out, size_t n, recv_flags flags) +{ + size_t msg_count = 0; + message_t msg; + while (true) { + if ZMQ_CONSTEXPR_IF (CheckN) { + if (msg_count >= n) + throw std::runtime_error( + "Too many message parts in recv_multipart_n"); + } + if (!s.recv(msg, flags)) { + // zmq ensures atomic delivery of messages + assert(msg_count == 0); + return {}; + } + ++msg_count; + const bool more = msg.more(); + *out++ = std::move(msg); + if (!more) + break; + } + return msg_count; +} + +inline bool is_little_endian() +{ + const uint16_t i = 0x01; + return *reinterpret_cast(&i) == 0x01; +} + +inline void write_network_order(unsigned char *buf, const uint32_t value) +{ + if (is_little_endian()) { + ZMQ_CONSTEXPR_VAR uint32_t mask = (std::numeric_limits::max)(); + *buf++ = static_cast((value >> 24) & mask); + *buf++ = static_cast((value >> 16) & mask); + *buf++ = static_cast((value >> 8) & mask); + *buf++ = static_cast(value & mask); + } else { + std::memcpy(buf, &value, sizeof(value)); + } +} + +inline uint32_t read_u32_network_order(const unsigned char *buf) +{ + if (is_little_endian()) { + return (static_cast(buf[0]) << 24) + + (static_cast(buf[1]) << 16) + + (static_cast(buf[2]) << 8) + + static_cast(buf[3]); + } else { + uint32_t value; + std::memcpy(&value, buf, sizeof(value)); + return value; + } +} +} // namespace detail + +/* Receive a multipart message. + + Writes the zmq::message_t objects to OutputIterator out. + The out iterator must handle an unspecified number of writes, + e.g. by using std::back_inserter. + + Returns: the number of messages received or nullopt (on EAGAIN). + Throws: if recv throws. Any exceptions thrown + by the out iterator will be propagated and the message + may have been only partially received with pending + message parts. It is adviced to close this socket in that event. +*/ +template +ZMQ_NODISCARD recv_result_t recv_multipart(socket_ref s, + OutputIt out, + recv_flags flags = recv_flags::none) +{ + return detail::recv_multipart_n(s, std::move(out), 0, flags); +} + +/* Receive a multipart message. + + Writes at most n zmq::message_t objects to OutputIterator out. + If the number of message parts of the incoming message exceeds n + then an exception will be thrown. + + Returns: the number of messages received or nullopt (on EAGAIN). + Throws: if recv throws. Throws std::runtime_error if the number + of message parts exceeds n (exactly n messages will have been written + to out). Any exceptions thrown + by the out iterator will be propagated and the message + may have been only partially received with pending + message parts. It is adviced to close this socket in that event. +*/ +template +ZMQ_NODISCARD recv_result_t recv_multipart_n(socket_ref s, + OutputIt out, + size_t n, + recv_flags flags = recv_flags::none) +{ + return detail::recv_multipart_n(s, std::move(out), n, flags); +} + +/* Send a multipart message. + + The range must be a ForwardRange of zmq::message_t, + zmq::const_buffer or zmq::mutable_buffer. + The flags may be zmq::send_flags::sndmore if there are + more message parts to be sent after the call to this function. + + Returns: the number of messages sent (exactly msgs.size()) or nullopt (on EAGAIN). + Throws: if send throws. Any exceptions thrown + by the msgs range will be propagated and the message + may have been only partially sent. It is adviced to close this socket in that event. +*/ +template::value + && (std::is_same, message_t>::value + || detail::is_buffer>::value)>::type +#endif + > +send_result_t +send_multipart(socket_ref s, Range &&msgs, send_flags flags = send_flags::none) +{ + using std::begin; + using std::end; + auto it = begin(msgs); + const auto end_it = end(msgs); + size_t msg_count = 0; + while (it != end_it) { + const auto next = std::next(it); + const auto msg_flags = + flags | (next == end_it ? send_flags::none : send_flags::sndmore); + if (!s.send(*it, msg_flags)) { + // zmq ensures atomic delivery of messages + assert(it == begin(msgs)); + return {}; + } + ++msg_count; + it = next; + } + return msg_count; +} + +/* Encode a multipart message. + + The range must be a ForwardRange of zmq::message_t. A + zmq::multipart_t or STL container may be passed for encoding. + + Returns: a zmq::message_t holding the encoded multipart data. + + Throws: std::range_error is thrown if the size of any single part + can not fit in an unsigned 32 bit integer. + + The encoding is compatible with that used by the CZMQ function + zmsg_encode(), see https://rfc.zeromq.org/spec/50/. + Each part consists of a size followed by the data. + These are placed contiguously into the output message. A part of + size less than 255 bytes will have a single byte size value. + Larger parts will have a five byte size value with the first byte + set to 0xFF and the remaining four bytes holding the size of the + part's data. +*/ +template::value + && (std::is_same, message_t>::value + || detail::is_buffer>::value)>::type +#endif + > +message_t encode(const Range &parts) +{ + size_t mmsg_size = 0; + + // First pass check sizes + for (const auto &part : parts) { + const size_t part_size = part.size(); + if (part_size > (std::numeric_limits::max)()) { + // Size value must fit into uint32_t. + throw std::range_error("Invalid size, message part too large"); + } + const size_t count_size = + part_size < (std::numeric_limits::max)() ? 1 : 5; + mmsg_size += part_size + count_size; + } + + message_t encoded(mmsg_size); + unsigned char *buf = encoded.data(); + for (const auto &part : parts) { + const uint32_t part_size = static_cast(part.size()); + const unsigned char *part_data = + static_cast(part.data()); + + if (part_size < (std::numeric_limits::max)()) { + // small part + *buf++ = (unsigned char) part_size; + } else { + // big part + *buf++ = (std::numeric_limits::max)(); + detail::write_network_order(buf, part_size); + buf += sizeof(part_size); + } + std::memcpy(buf, part_data, part_size); + buf += part_size; + } + + assert(static_cast(buf - encoded.data()) == mmsg_size); + return encoded; +} + +/* Decode an encoded message to multiple parts. + + The given output iterator must be a ForwardIterator to a container + holding zmq::message_t such as a zmq::multipart_t or various STL + containers. + + Returns the ForwardIterator advanced once past the last decoded + part. + + Throws: a std::out_of_range is thrown if the encoded part sizes + lead to exceeding the message data bounds. + + The decoding assumes the message is encoded in the manner + performed by zmq::encode(), see https://rfc.zeromq.org/spec/50/. + */ +template OutputIt decode(const message_t &encoded, OutputIt out) +{ + const unsigned char *source = encoded.data(); + const unsigned char *const limit = source + encoded.size(); + + while (source < limit) { + size_t part_size = *source++; + if (part_size == (std::numeric_limits::max)()) { + if (static_cast(limit - source) < sizeof(uint32_t)) { + throw std::out_of_range( + "Malformed encoding, overflow in reading size"); + } + part_size = detail::read_u32_network_order(source); + // the part size is allowed to be less than 0xFF + source += sizeof(uint32_t); + } + + if (static_cast(limit - source) < part_size) { + throw std::out_of_range("Malformed encoding, overflow in reading part"); + } + *out = message_t(source, part_size); + ++out; + source += part_size; + } + + assert(source == limit); + return out; +} + +#endif + + +#ifdef ZMQ_HAS_RVALUE_REFS + +/* + This class handles multipart messaging. It is the C++ equivalent of zmsg.h, + which is part of CZMQ (the high-level C binding). Furthermore, it is a major + improvement compared to zmsg.hpp, which is part of the examples in the ØMQ + Guide. Unnecessary copying is avoided by using move semantics to efficiently + add/remove parts. +*/ +class multipart_t +{ + private: + std::deque m_parts; + + public: + typedef std::deque::value_type value_type; + + typedef std::deque::iterator iterator; + typedef std::deque::const_iterator const_iterator; + + typedef std::deque::reverse_iterator reverse_iterator; + typedef std::deque::const_reverse_iterator const_reverse_iterator; + + // Default constructor + multipart_t() {} + + // Construct from socket receive + multipart_t(socket_ref socket) { recv(socket); } + + // Construct from memory block + multipart_t(const void *src, size_t size) { addmem(src, size); } + + // Construct from string + multipart_t(const std::string &string) { addstr(string); } + + // Construct from message part + multipart_t(message_t &&message) { add(std::move(message)); } + + // Move constructor + multipart_t(multipart_t &&other) ZMQ_NOTHROW { m_parts = std::move(other.m_parts); } + + // Move assignment operator + multipart_t &operator=(multipart_t &&other) ZMQ_NOTHROW + { + m_parts = std::move(other.m_parts); + return *this; + } + + // Destructor + virtual ~multipart_t() { clear(); } + + message_t &operator[](size_t n) { return m_parts[n]; } + + const message_t &operator[](size_t n) const { return m_parts[n]; } + + message_t &at(size_t n) { return m_parts.at(n); } + + const message_t &at(size_t n) const { return m_parts.at(n); } + + iterator begin() { return m_parts.begin(); } + + const_iterator begin() const { return m_parts.begin(); } + + const_iterator cbegin() const { return m_parts.cbegin(); } + + reverse_iterator rbegin() { return m_parts.rbegin(); } + + const_reverse_iterator rbegin() const { return m_parts.rbegin(); } + + iterator end() { return m_parts.end(); } + + const_iterator end() const { return m_parts.end(); } + + const_iterator cend() const { return m_parts.cend(); } + + reverse_iterator rend() { return m_parts.rend(); } + + const_reverse_iterator rend() const { return m_parts.rend(); } + + // Delete all parts + void clear() { m_parts.clear(); } + + // Get number of parts + size_t size() const { return m_parts.size(); } + + // Check if number of parts is zero + bool empty() const { return m_parts.empty(); } + + // Receive multipart message from socket + bool recv(socket_ref socket, int flags = 0) + { + clear(); + bool more = true; + while (more) { + message_t message; +#ifdef ZMQ_CPP11 + if (!socket.recv(message, static_cast(flags))) + return false; +#else + if (!socket.recv(&message, flags)) + return false; +#endif + more = message.more(); + add(std::move(message)); + } + return true; + } + + // Send multipart message to socket + bool send(socket_ref socket, int flags = 0) + { + flags &= ~(ZMQ_SNDMORE); + bool more = size() > 0; + while (more) { + message_t message = pop(); + more = size() > 0; +#ifdef ZMQ_CPP11 + if (!socket.send(message, static_cast( + (more ? ZMQ_SNDMORE : 0) | flags))) + return false; +#else + if (!socket.send(message, (more ? ZMQ_SNDMORE : 0) | flags)) + return false; +#endif + } + clear(); + return true; + } + + // Concatenate other multipart to front + void prepend(multipart_t &&other) + { + while (!other.empty()) + push(other.remove()); + } + + // Concatenate other multipart to back + void append(multipart_t &&other) + { + while (!other.empty()) + add(other.pop()); + } + + // Push memory block to front + void pushmem(const void *src, size_t size) + { + m_parts.push_front(message_t(src, size)); + } + + // Push memory block to back + void addmem(const void *src, size_t size) + { + m_parts.push_back(message_t(src, size)); + } + + // Push string to front + void pushstr(const std::string &string) + { + m_parts.push_front(message_t(string.data(), string.size())); + } + + // Push string to back + void addstr(const std::string &string) + { + m_parts.push_back(message_t(string.data(), string.size())); + } + + // Push type (fixed-size) to front + template void pushtyp(const T &type) + { + static_assert(!std::is_same::value, + "Use pushstr() instead of pushtyp()"); + m_parts.push_front(message_t(&type, sizeof(type))); + } + + // Push type (fixed-size) to back + template void addtyp(const T &type) + { + static_assert(!std::is_same::value, + "Use addstr() instead of addtyp()"); + m_parts.push_back(message_t(&type, sizeof(type))); + } + + // Push message part to front + void push(message_t &&message) { m_parts.push_front(std::move(message)); } + + // Push message part to back + void add(message_t &&message) { m_parts.push_back(std::move(message)); } + + // Alias to allow std::back_inserter() + void push_back(message_t &&message) { m_parts.push_back(std::move(message)); } + + // Pop string from front + std::string popstr() + { + std::string string(m_parts.front().data(), m_parts.front().size()); + m_parts.pop_front(); + return string; + } + + // Pop type (fixed-size) from front + template T poptyp() + { + static_assert(!std::is_same::value, + "Use popstr() instead of poptyp()"); + if (sizeof(T) != m_parts.front().size()) + throw std::runtime_error( + "Invalid type, size does not match the message size"); + T type = *m_parts.front().data(); + m_parts.pop_front(); + return type; + } + + // Pop message part from front + message_t pop() + { + message_t message = std::move(m_parts.front()); + m_parts.pop_front(); + return message; + } + + // Pop message part from back + message_t remove() + { + message_t message = std::move(m_parts.back()); + m_parts.pop_back(); + return message; + } + + // get message part from front + const message_t &front() { return m_parts.front(); } + + // get message part from back + const message_t &back() { return m_parts.back(); } + + // Get pointer to a specific message part + const message_t *peek(size_t index) const { return &m_parts[index]; } + + // Get a string copy of a specific message part + std::string peekstr(size_t index) const + { + std::string string(m_parts[index].data(), m_parts[index].size()); + return string; + } + + // Peek type (fixed-size) from front + template T peektyp(size_t index) const + { + static_assert(!std::is_same::value, + "Use peekstr() instead of peektyp()"); + if (sizeof(T) != m_parts[index].size()) + throw std::runtime_error( + "Invalid type, size does not match the message size"); + T type = *m_parts[index].data(); + return type; + } + + // Create multipart from type (fixed-size) + template static multipart_t create(const T &type) + { + multipart_t multipart; + multipart.addtyp(type); + return multipart; + } + + // Copy multipart + multipart_t clone() const + { + multipart_t multipart; + for (size_t i = 0; i < size(); i++) + multipart.addmem(m_parts[i].data(), m_parts[i].size()); + return multipart; + } + + // Dump content to string + std::string str() const + { + std::stringstream ss; + for (size_t i = 0; i < m_parts.size(); i++) { + const unsigned char *data = m_parts[i].data(); + size_t size = m_parts[i].size(); + + // Dump the message as text or binary + bool isText = true; + for (size_t j = 0; j < size; j++) { + if (data[j] < 32 || data[j] > 127) { + isText = false; + break; + } + } + ss << "\n[" << std::dec << std::setw(3) << std::setfill('0') << size + << "] "; + if (size >= 1000) { + ss << "... (too big to print)"; + continue; + } + for (size_t j = 0; j < size; j++) { + if (isText) + ss << static_cast(data[j]); + else + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(data[j]); + } + } + return ss.str(); + } + + // Check if equal to other multipart + bool equal(const multipart_t *other) const ZMQ_NOTHROW + { + return *this == *other; + } + + bool operator==(const multipart_t &other) const ZMQ_NOTHROW + { + if (size() != other.size()) + return false; + for (size_t i = 0; i < size(); i++) + if (at(i) != other.at(i)) + return false; + return true; + } + + bool operator!=(const multipart_t &other) const ZMQ_NOTHROW + { + return !(*this == other); + } + +#ifdef ZMQ_CPP11 + + // Return single part message_t encoded from this multipart_t. + message_t encode() const { return zmq::encode(*this); } + + // Decode encoded message into multiple parts and append to self. + void decode_append(const message_t &encoded) + { + zmq::decode(encoded, std::back_inserter(*this)); + } + + // Return a new multipart_t containing the decoded message_t. + static multipart_t decode(const message_t &encoded) + { + multipart_t tmp; + zmq::decode(encoded, std::back_inserter(tmp)); + return tmp; + } + +#endif + + private: + // Disable implicit copying (moving is more efficient) + multipart_t(const multipart_t &other) ZMQ_DELETED_FUNCTION; + void operator=(const multipart_t &other) ZMQ_DELETED_FUNCTION; +}; // class multipart_t + +inline std::ostream &operator<<(std::ostream &os, const multipart_t &msg) +{ + return os << msg.str(); +} + +#endif // ZMQ_HAS_RVALUE_REFS + +#if defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) +class active_poller_t +{ + public: + active_poller_t() = default; + ~active_poller_t() = default; + + active_poller_t(const active_poller_t &) = delete; + active_poller_t &operator=(const active_poller_t &) = delete; + + active_poller_t(active_poller_t &&src) = default; + active_poller_t &operator=(active_poller_t &&src) = default; + + using handler_type = std::function; + + void add(zmq::socket_ref socket, event_flags events, handler_type handler) + { + if (!handler) + throw std::invalid_argument("null handler in active_poller_t::add"); + auto ret = handlers.emplace( + socket, std::make_shared(std::move(handler))); + if (!ret.second) + throw error_t(EINVAL); // already added + try { + base_poller.add(socket, events, ret.first->second.get()); + need_rebuild = true; + } + catch (...) { + // rollback + handlers.erase(socket); + throw; + } + } + + void remove(zmq::socket_ref socket) + { + base_poller.remove(socket); + handlers.erase(socket); + need_rebuild = true; + } + + void modify(zmq::socket_ref socket, event_flags events) + { + base_poller.modify(socket, events); + } + + size_t wait(std::chrono::milliseconds timeout) + { + if (need_rebuild) { + poller_events.resize(handlers.size()); + poller_handlers.clear(); + poller_handlers.reserve(handlers.size()); + for (const auto &handler : handlers) { + poller_handlers.push_back(handler.second); + } + need_rebuild = false; + } + const auto count = base_poller.wait_all(poller_events, timeout); + std::for_each(poller_events.begin(), + poller_events.begin() + static_cast(count), + [](decltype(base_poller)::event_type &event) { + assert(event.user_data != nullptr); + (*event.user_data)(event.events); + }); + return count; + } + + ZMQ_NODISCARD bool empty() const noexcept { return handlers.empty(); } + + size_t size() const noexcept { return handlers.size(); } + + private: + bool need_rebuild{false}; + + poller_t base_poller{}; + std::unordered_map> handlers{}; + std::vector poller_events{}; + std::vector> poller_handlers{}; +}; // class active_poller_t +#endif // defined(ZMQ_BUILD_DRAFT_API) && defined(ZMQ_CPP11) && defined(ZMQ_HAVE_POLLER) + + +} // namespace zmq + +#endif // __ZMQ_ADDON_HPP_INCLUDED__ diff --git a/3rdparty/filesystem/fwd.h b/3rdparty/filesystem/fwd.h deleted file mode 100644 index 3552199e2..000000000 --- a/3rdparty/filesystem/fwd.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - fwd.h -- Forward declarations for path.h and resolver.h - - Copyright (c) 2015 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#if !defined(NAMESPACE_BEGIN) -#define NAMESPACE_BEGIN(name) namespace name { -#endif -#if !defined(NAMESPACE_END) -#define NAMESPACE_END(name) } -#endif - -NAMESPACE_BEGIN(filesystem) - -class path; -class resolver; - -NAMESPACE_END(filesystem) diff --git a/3rdparty/filesystem/path.h b/3rdparty/filesystem/path.h deleted file mode 100644 index a5b582c5f..000000000 --- a/3rdparty/filesystem/path.h +++ /dev/null @@ -1,339 +0,0 @@ -/* - path.h -- A simple class for manipulating paths on Linux/Windows/Mac OS - - Copyright (c) 2015 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "fwd.h" -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) -# include -#else -# include -#endif -#include - -#if defined(__linux) -# include -#endif - -NAMESPACE_BEGIN(filesystem) - -/** - * \brief Simple class for manipulating paths on Linux/Windows/Mac OS - * - * This class is just a temporary workaround to avoid the heavy boost - * dependency until boost::filesystem is integrated into the standard template - * library at some point in the future. - */ -class path { -public: - enum path_type { - windows_path = 0, - posix_path = 1, -#if defined(_WIN32) - native_path = windows_path -#else - native_path = posix_path -#endif - }; - - path() : m_type(native_path), m_absolute(false) { } - - path(const path &path) - : m_type(path.m_type), m_path(path.m_path), m_absolute(path.m_absolute) {} - - path(path &&path) - : m_type(path.m_type), m_path(std::move(path.m_path)), - m_absolute(path.m_absolute) {} - - path(const char *string) { set(string); } - - path(const std::string &string) { set(string); } - -#if defined(_WIN32) - path(const std::wstring &wstring) { set(wstring); } - path(const wchar_t *wstring) { set(wstring); } -#endif - - size_t length() const { return m_path.size(); } - - bool empty() const { return m_path.empty(); } - - bool is_absolute() const { return m_absolute; } - - path make_absolute() const { -#if !defined(_WIN32) - char temp[PATH_MAX]; - if (realpath(str().c_str(), temp) == NULL) - throw std::runtime_error("Internal error in realpath(): " + std::string(strerror(errno))); - return path(temp); -#else - std::wstring value = wstr(), out(MAX_PATH, '\0'); - DWORD length = GetFullPathNameW(value.c_str(), MAX_PATH, &out[0], NULL); - if (length == 0) - throw std::runtime_error("Internal error in realpath(): " + std::to_string(GetLastError())); - return path(out.substr(0, length)); -#endif - } - - bool exists() const { -#if defined(_WIN32) - return GetFileAttributesW(wstr().c_str()) != INVALID_FILE_ATTRIBUTES; -#else - struct stat sb; - return stat(str().c_str(), &sb) == 0; -#endif - } - - size_t file_size() const { -#if defined(_WIN32) - struct _stati64 sb; - if (_wstati64(wstr().c_str(), &sb) != 0) - throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!"); -#else - struct stat sb; - if (stat(str().c_str(), &sb) != 0) - throw std::runtime_error("path::file_size(): cannot stat file \"" + str() + "\"!"); -#endif - return (size_t) sb.st_size; - } - - bool is_directory() const { -#if defined(_WIN32) - DWORD result = GetFileAttributesW(wstr().c_str()); - if (result == INVALID_FILE_ATTRIBUTES) - return false; - return (result & FILE_ATTRIBUTE_DIRECTORY) != 0; -#else - struct stat sb; - if (stat(str().c_str(), &sb)) - return false; - return S_ISDIR(sb.st_mode); -#endif - } - - bool is_file() const { -#if defined(_WIN32) - DWORD attr = GetFileAttributesW(wstr().c_str()); - return (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) == 0); -#else - struct stat sb; - if (stat(str().c_str(), &sb)) - return false; - return S_ISREG(sb.st_mode); -#endif - } - - std::string extension() const { - const std::string &name = filename(); - size_t pos = name.find_last_of("."); - if (pos == std::string::npos) - return ""; - return name.substr(pos+1); - } - - std::string filename() const { - if (empty()) - return ""; - const std::string &last = m_path[m_path.size()-1]; - return last; - } - - path parent_path() const { - path result; - result.m_absolute = m_absolute; - - if (m_path.empty()) { - if (!m_absolute) - result.m_path.push_back(".."); - } else { - size_t until = m_path.size() - 1; - for (size_t i = 0; i < until; ++i) - result.m_path.push_back(m_path[i]); - } - return result; - } - - path operator/(const path &other) const { - if (other.m_absolute) - throw std::runtime_error("path::operator/(): expected a relative path!"); - if (m_type != other.m_type) - throw std::runtime_error("path::operator/(): expected a path of the same type!"); - - path result(*this); - - for (size_t i=0; i= 2 && std::isalpha(str[0]) && str[1] == ':'; - } else { - m_path = tokenize(str, "/"); - m_absolute = !str.empty() && str[0] == '/'; - } - } - - path &operator=(const path &path) { - m_type = path.m_type; - m_path = path.m_path; - m_absolute = path.m_absolute; - return *this; - } - - path &operator=(path &&path) { - if (this != &path) { - m_type = path.m_type; - m_path = std::move(path.m_path); - m_absolute = path.m_absolute; - } - return *this; - } - - friend std::ostream &operator<<(std::ostream &os, const path &path) { - os << path.str(); - return os; - } - - bool remove_file() { -#if !defined(_WIN32) - return std::remove(str().c_str()) == 0; -#else - return DeleteFileW(wstr().c_str()) != 0; -#endif - } - - bool resize_file(size_t target_length) { -#if !defined(_WIN32) - return ::truncate(str().c_str(), (off_t) target_length) == 0; -#else - HANDLE handle = CreateFileW(wstr().c_str(), GENERIC_WRITE, 0, nullptr, 0, FILE_ATTRIBUTE_NORMAL, nullptr); - if (handle == INVALID_HANDLE_VALUE) - return false; - LARGE_INTEGER size; - size.QuadPart = (LONGLONG) target_length; - if (SetFilePointerEx(handle, size, NULL, FILE_BEGIN) == 0) { - CloseHandle(handle); - return false; - } - if (SetEndOfFile(handle) == 0) { - CloseHandle(handle); - return false; - } - CloseHandle(handle); - return true; -#endif - } - - static path getcwd() { -#if !defined(_WIN32) - char temp[PATH_MAX]; - if (::getcwd(temp, PATH_MAX) == NULL) - throw std::runtime_error("Internal error in getcwd(): " + std::string(strerror(errno))); - return path(temp); -#else - std::wstring temp(MAX_PATH, '\0'); - if (!_wgetcwd(&temp[0], MAX_PATH)) - throw std::runtime_error("Internal error in getcwd(): " + std::to_string(GetLastError())); - return path(temp.c_str()); -#endif - } - -#if defined(_WIN32) - std::wstring wstr(path_type type = native_path) const { - std::string temp = str(type); - int size = MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), NULL, 0); - std::wstring result(size, 0); - MultiByteToWideChar(CP_UTF8, 0, &temp[0], (int)temp.size(), &result[0], size); - return result; - } - - - void set(const std::wstring &wstring, path_type type = native_path) { - std::string string; - if (!wstring.empty()) { - int size = WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(), - NULL, 0, NULL, NULL); - string.resize(size, 0); - WideCharToMultiByte(CP_UTF8, 0, &wstring[0], (int)wstring.size(), - &string[0], size, NULL, NULL); - } - set(string, type); - } - - path &operator=(const std::wstring &str) { set(str); return *this; } -#endif - - bool operator==(const path &p) const { return p.m_path == m_path; } - bool operator!=(const path &p) const { return p.m_path != m_path; } - -protected: - static std::vector tokenize(const std::string &string, const std::string &delim) { - std::string::size_type lastPos = 0, pos = string.find_first_of(delim, lastPos); - std::vector tokens; - - while (lastPos != std::string::npos) { - if (pos != lastPos) - tokens.push_back(string.substr(lastPos, pos - lastPos)); - lastPos = pos; - if (lastPos == std::string::npos || lastPos + 1 == string.length()) - break; - pos = string.find_first_of(delim, ++lastPos); - } - - return tokens; - } - -protected: - path_type m_type; - std::vector m_path; - bool m_absolute; -}; - -inline bool create_directory(const path& p) { -#if defined(_WIN32) - return CreateDirectoryW(p.wstr().c_str(), NULL) != 0; -#else - return mkdir(p.str().c_str(), S_IRUSR | S_IWUSR | S_IXUSR) == 0; -#endif -} - -NAMESPACE_END(filesystem) diff --git a/3rdparty/filesystem/resolver.h b/3rdparty/filesystem/resolver.h deleted file mode 100644 index 0c7576841..000000000 --- a/3rdparty/filesystem/resolver.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - resolver.h -- A simple class for cross-platform path resolution - - Copyright (c) 2015 Wenzel Jakob - - All rights reserved. Use of this source code is governed by a - BSD-style license that can be found in the LICENSE file. -*/ - -#pragma once - -#include "path.h" - -NAMESPACE_BEGIN(filesystem) - -/** - * \brief Simple class for resolving paths on Linux/Windows/Mac OS - * - * This convenience class looks for a file or directory given its name - * and a set of search paths. The implementation walks through the - * search paths in order and stops once the file is found. - */ -class resolver { -public: - typedef std::vector::iterator iterator; - typedef std::vector::const_iterator const_iterator; - - resolver() { - m_paths.push_back(path::getcwd()); - } - - size_t size() const { return m_paths.size(); } - - iterator begin() { return m_paths.begin(); } - iterator end() { return m_paths.end(); } - - const_iterator begin() const { return m_paths.begin(); } - const_iterator end() const { return m_paths.end(); } - - void erase(iterator it) { m_paths.erase(it); } - - void prepend(const path &path) { m_paths.insert(m_paths.begin(), path); } - void append(const path &path) { m_paths.push_back(path); } - const path &operator[](size_t index) const { return m_paths[index]; } - path &operator[](size_t index) { return m_paths[index]; } - - path resolve(const path &value) const { - for (const_iterator it = m_paths.begin(); it != m_paths.end(); ++it) { - path combined = *it / value; - if (combined.exists()) - return combined; - } - return value; - } - - friend std::ostream &operator<<(std::ostream &os, const resolver &r) { - os << "resolver[" << std::endl; - for (size_t i = 0; i < r.m_paths.size(); ++i) { - os << " \"" << r.m_paths[i] << "\""; - if (i + 1 < r.m_paths.size()) - os << ","; - os << std::endl; - } - os << "]"; - return os; - } - -private: - std::vector m_paths; -}; - -NAMESPACE_END(filesystem) diff --git a/include/behaviortree_cpp_v3/flatbuffers/base.h b/3rdparty/flatbuffers/base.h similarity index 66% rename from include/behaviortree_cpp_v3/flatbuffers/base.h rename to 3rdparty/flatbuffers/base.h index 1686dc680..1c19dde98 100644 --- a/include/behaviortree_cpp_v3/flatbuffers/base.h +++ b/3rdparty/flatbuffers/base.h @@ -32,7 +32,7 @@ #include #include -#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H) +#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H) && defined(__AVR__) #include #else #include @@ -43,17 +43,17 @@ #include #include #include +#include #include #include -#ifdef _STLPORT_VERSION - #define FLATBUFFERS_CPP98_STL -#endif -#ifndef FLATBUFFERS_CPP98_STL - #include +#if defined(__unix__) && !defined(FLATBUFFERS_LOCALE_INDEPENDENT) + #include #endif -#include "behaviortree_cpp_v3/flatbuffers/stl_emulation.h" +#ifdef __ANDROID__ + #include +#endif #if defined(__ICCARM__) #include @@ -139,9 +139,9 @@ #endif #endif // !defined(FLATBUFFERS_LITTLEENDIAN) -#define FLATBUFFERS_VERSION_MAJOR 1 -#define FLATBUFFERS_VERSION_MINOR 11 -#define FLATBUFFERS_VERSION_REVISION 0 +#define FLATBUFFERS_VERSION_MAJOR 24 +#define FLATBUFFERS_VERSION_MINOR 3 +#define FLATBUFFERS_VERSION_REVISION 25 #define FLATBUFFERS_STRING_EXPAND(X) #X #define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) namespace flatbuffers { @@ -154,10 +154,12 @@ namespace flatbuffers { defined(__clang__) #define FLATBUFFERS_FINAL_CLASS final #define FLATBUFFERS_OVERRIDE override - #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : flatbuffers::voffset_t + #define FLATBUFFERS_EXPLICIT_CPP11 explicit + #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE : ::flatbuffers::voffset_t #else #define FLATBUFFERS_FINAL_CLASS #define FLATBUFFERS_OVERRIDE + #define FLATBUFFERS_EXPLICIT_CPP11 #define FLATBUFFERS_VTABLE_UNDERLYING_TYPE #endif @@ -165,13 +167,16 @@ namespace flatbuffers { (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) || \ (defined(__cpp_constexpr) && __cpp_constexpr >= 200704) #define FLATBUFFERS_CONSTEXPR constexpr + #define FLATBUFFERS_CONSTEXPR_CPP11 constexpr + #define FLATBUFFERS_CONSTEXPR_DEFINED #else #define FLATBUFFERS_CONSTEXPR const + #define FLATBUFFERS_CONSTEXPR_CPP11 #endif #if (defined(__cplusplus) && __cplusplus >= 201402L) || \ (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) - #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR + #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR_CPP11 #else #define FLATBUFFERS_CONSTEXPR_CPP14 #endif @@ -189,9 +194,25 @@ namespace flatbuffers { #if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \ (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) || \ defined(__clang__) - #define FLATBUFFERS_DELETE_FUNC(func) func = delete; + #define FLATBUFFERS_DELETE_FUNC(func) func = delete #else - #define FLATBUFFERS_DELETE_FUNC(func) private: func; + #define FLATBUFFERS_DELETE_FUNC(func) private: func +#endif + +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 409)) || \ + defined(__clang__) + #define FLATBUFFERS_DEFAULT_DECLARATION +#endif + +// Check if we can use template aliases +// Not possible if Microsoft Compiler before 2012 +// Possible is the language feature __cpp_alias_templates is defined well +// Or possible if the C++ std is C+11 or newer +#if (defined(_MSC_VER) && _MSC_VER > 1700 /* MSVC2012 */) \ + || (defined(__cpp_alias_templates) && __cpp_alias_templates >= 200704) \ + || (defined(__cplusplus) && __cplusplus >= 201103L) + #define FLATBUFFERS_TEMPLATES_ALIASES #endif #ifndef FLATBUFFERS_HAS_STRING_VIEW @@ -212,10 +233,27 @@ namespace flatbuffers { typedef std::experimental::string_view string_view; } #define FLATBUFFERS_HAS_STRING_VIEW 1 + // Check for absl::string_view + #elif __has_include("absl/strings/string_view.h") && \ + __has_include("absl/base/config.h") && \ + (__cplusplus >= 201411) + #include "absl/base/config.h" + #if !defined(ABSL_USES_STD_STRING_VIEW) + #include "absl/strings/string_view.h" + namespace flatbuffers { + typedef absl::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + #endif #endif #endif // __has_include #endif // !FLATBUFFERS_HAS_STRING_VIEW +#ifndef FLATBUFFERS_GENERAL_HEAP_ALLOC_OK + // Allow heap allocations to be used + #define FLATBUFFERS_GENERAL_HEAP_ALLOC_OK 1 +#endif // !FLATBUFFERS_GENERAL_HEAP_ALLOC_OK + #ifndef FLATBUFFERS_HAS_NEW_STRTOD // Modern (C++11) strtod and strtof functions are available for use. // 1) nan/inf strings as argument of strtod; @@ -228,11 +266,12 @@ namespace flatbuffers { #endif // !FLATBUFFERS_HAS_NEW_STRTOD #ifndef FLATBUFFERS_LOCALE_INDEPENDENT - // Enable locale independent functions {strtof_l, strtod_l,strtoll_l, strtoull_l}. - // They are part of the POSIX-2008 but not part of the C/C++ standard. - // GCC/Clang have definition (_XOPEN_SOURCE>=700) if POSIX-2008. - #if ((defined(_MSC_VER) && _MSC_VER >= 1800) || \ - (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE>=700))) + // Enable locale independent functions {strtof_l, strtod_l,strtoll_l, + // strtoull_l}. + #if (defined(_MSC_VER) && _MSC_VER >= 1800) || \ + (defined(__ANDROID_API__) && __ANDROID_API__>= 21) || \ + (defined(_XOPEN_VERSION) && (_XOPEN_VERSION >= 700)) && \ + (!defined(__Fuchsia__) && !defined(__ANDROID_API__)) #define FLATBUFFERS_LOCALE_INDEPENDENT 1 #else #define FLATBUFFERS_LOCALE_INDEPENDENT 0 @@ -240,27 +279,29 @@ namespace flatbuffers { #endif // !FLATBUFFERS_LOCALE_INDEPENDENT // Suppress Undefined Behavior Sanitizer (recoverable only). Usage: -// - __supress_ubsan__("undefined") -// - __supress_ubsan__("signed-integer-overflow") +// - FLATBUFFERS_SUPPRESS_UBSAN("undefined") +// - FLATBUFFERS_SUPPRESS_UBSAN("signed-integer-overflow") #if defined(__clang__) && (__clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >=7)) - #define __supress_ubsan__(type) __attribute__((no_sanitize(type))) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) __attribute__((no_sanitize(type))) #elif defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 409) - #define __supress_ubsan__(type) __attribute__((no_sanitize_undefined)) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) __attribute__((no_sanitize_undefined)) #else - #define __supress_ubsan__(type) + #define FLATBUFFERS_SUPPRESS_UBSAN(type) #endif -// This is constexpr function used for checking compile-time constants. -// Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. -template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { - return !!t; +namespace flatbuffers { + // This is constexpr function used for checking compile-time constants. + // Avoid `#pragma warning(disable: 4127) // C4127: expression is constant`. + template FLATBUFFERS_CONSTEXPR inline bool IsConstTrue(T t) { + return !!t; + } } // Enable C++ attribute [[]] if std:c++17 or higher. #if ((__cplusplus >= 201703L) \ || (defined(_MSVC_LANG) && (_MSVC_LANG >= 201703L))) // All attributes unknown to an implementation are ignored without causing an error. - #define FLATBUFFERS_ATTRIBUTE(attr) [[attr]] + #define FLATBUFFERS_ATTRIBUTE(attr) attr #define FLATBUFFERS_FALLTHROUGH() [[fallthrough]] #else @@ -285,9 +326,11 @@ namespace flatbuffers { // Also, using a consistent offset type maintains compatibility of serialized // offset values between 32bit and 64bit systems. typedef uint32_t uoffset_t; +typedef uint64_t uoffset64_t; // Signed offsets for references that can go in both directions. typedef int32_t soffset_t; +typedef int64_t soffset64_t; // Offset/index used in v-tables, can be changed to uint8_t in // format forks to save a bit of space if desired. @@ -296,10 +339,28 @@ typedef uint16_t voffset_t; typedef uintmax_t largest_scalar_t; // In 32bits, this evaluates to 2GB - 1 -#define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(::flatbuffers::soffset_t) * 8 - 1)) - 1) +#define FLATBUFFERS_MAX_BUFFER_SIZE std::numeric_limits<::flatbuffers::soffset_t>::max() +#define FLATBUFFERS_MAX_64_BUFFER_SIZE std::numeric_limits<::flatbuffers::soffset64_t>::max() + +// The minimum size buffer that can be a valid flatbuffer. +// Includes the offset to the root table (uoffset_t), the offset to the vtable +// of the root table (soffset_t), the size of the vtable (uint16_t), and the +// size of the referring table (uint16_t). +#define FLATBUFFERS_MIN_BUFFER_SIZE sizeof(uoffset_t) + sizeof(soffset_t) + \ + sizeof(uint16_t) + sizeof(uint16_t) // We support aligning the contents of buffers up to this size. -#define FLATBUFFERS_MAX_ALIGNMENT 16 +#ifndef FLATBUFFERS_MAX_ALIGNMENT + #define FLATBUFFERS_MAX_ALIGNMENT 32 +#endif + +/// @brief The length of a FlatBuffer file header. +static const size_t kFileIdentifierLength = 4; + +inline bool VerifyAlignmentRequirements(size_t align, size_t min_align = 1) { + return (min_align <= align) && (align <= (FLATBUFFERS_MAX_ALIGNMENT)) && + (align & (align - 1)) == 0; // must be power of 2 +} #if defined(_MSC_VER) #pragma warning(push) @@ -362,29 +423,74 @@ template T EndianScalar(T t) { template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. -__supress_ubsan__("alignment") +FLATBUFFERS_SUPPRESS_UBSAN("alignment") T ReadScalar(const void *p) { return EndianScalar(*reinterpret_cast(p)); } +// See https://github.com/google/flatbuffers/issues/5950 + +#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overflow" +#endif + template // UBSAN: C++ aliasing type rules, see std::bit_cast<> for details. -__supress_ubsan__("alignment") +FLATBUFFERS_SUPPRESS_UBSAN("alignment") void WriteScalar(void *p, T t) { *reinterpret_cast(p) = EndianScalar(t); } template struct Offset; -template __supress_ubsan__("alignment") void WriteScalar(void *p, Offset t) { +template FLATBUFFERS_SUPPRESS_UBSAN("alignment") void WriteScalar(void *p, Offset t) { *reinterpret_cast(p) = EndianScalar(t.o); } +#if (FLATBUFFERS_GCC >= 100000) && (FLATBUFFERS_GCC < 110000) + #pragma GCC diagnostic pop +#endif + // Computes how many bytes you'd have to pad to be able to write an // "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in // memory). +FLATBUFFERS_SUPPRESS_UBSAN("unsigned-integer-overflow") inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { return ((~buf_size) + 1) & (scalar_size - 1); } +// Generic 'operator==' with conditional specialisations. +// T e - new value of a scalar field. +// T def - default of scalar (is known at compile-time). +template inline bool IsTheSameAs(T e, T def) { return e == def; } + +#if defined(FLATBUFFERS_NAN_DEFAULTS) && \ + defined(FLATBUFFERS_HAS_NEW_STRTOD) && (FLATBUFFERS_HAS_NEW_STRTOD > 0) +// Like `operator==(e, def)` with weak NaN if T=(float|double). +template inline bool IsFloatTheSameAs(T e, T def) { + return (e == def) || ((def != def) && (e != e)); +} +template<> inline bool IsTheSameAs(float e, float def) { + return IsFloatTheSameAs(e, def); +} +template<> inline bool IsTheSameAs(double e, double def) { + return IsFloatTheSameAs(e, def); +} +#endif + +// Check 'v' is out of closed range [low; high]. +// Workaround for GCC warning [-Werror=type-limits]: +// comparison is always true due to limited range of data type. +template +inline bool IsOutRange(const T &v, const T &low, const T &high) { + return (v < low) || (high < v); +} + +// Check 'v' is in closed range [low; high]. +template +inline bool IsInRange(const T &v, const T &low, const T &high) { + return !IsOutRange(v, low, high); +} + } // namespace flatbuffers #endif // FLATBUFFERS_BASE_H_ diff --git a/3rdparty/lexy/CMakeLists.txt b/3rdparty/lexy/CMakeLists.txt new file mode 100644 index 000000000..a76693a9e --- /dev/null +++ b/3rdparty/lexy/CMakeLists.txt @@ -0,0 +1,79 @@ +# Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +# SPDX-License-Identifier: BSL-1.0 + +cmake_minimum_required(VERSION 3.8) +project(lexy VERSION 2022.12.1 LANGUAGES CXX) + +set(LEXY_USER_CONFIG_HEADER "" CACHE FILEPATH "The user config header for lexy.") +option(LEXY_FORCE_CPP17 "Whether or not lexy should use C++17 even if compiler supports C++20." OFF) + +add_subdirectory(src) + +option(LEXY_ENABLE_INSTALL "whether or not to enable the install rule" ON) +if(LEXY_ENABLE_INSTALL) + include(CMakePackageConfigHelpers) + include(GNUInstallDirs) + + install(TARGETS lexy lexy_core lexy_file lexy_unicode lexy_ext _lexy_base lexy_dev + EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + + install(EXPORT ${PROJECT_NAME}Targets + NAMESPACE foonathan:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + configure_package_config_file( + cmake/lexyConfig.cmake.in + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + # YYYY.MM.N1 is compatible with YYYY.MM.N2. + write_basic_package_version_file( + "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + COMPATIBILITY SameMinorVersion) + + install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + + install(DIRECTORY include/lexy include/lexy_ext + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.hpp") +endif() + +if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + cmake_minimum_required(VERSION 3.18) + option(LEXY_BUILD_BENCHMARKS "whether or not benchmarks should be built" OFF) + option(LEXY_BUILD_EXAMPLES "whether or not examples should be built" ON) + option(LEXY_BUILD_TESTS "whether or not tests should be built" ON) + option(LEXY_BUILD_DOCS "whether or not docs should be built" OFF) + option(LEXY_BUILD_PACKAGE "whether or not the package should be built" ON) + + if(LEXY_BUILD_PACKAGE) + set(package_files include/ src/ cmake/ CMakeLists.txt LICENSE) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip + COMMAND ${CMAKE_COMMAND} -E tar c ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip --format=zip -- ${package_files} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${package_files}) + add_custom_target(lexy_package DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/lexy-src.zip) + endif() + + if(LEXY_BUILD_EXAMPLES) + add_subdirectory(examples) + endif() + if(LEXY_BUILD_BENCHMARKS) + add_subdirectory(benchmarks) + endif() + if(LEXY_BUILD_TESTS) + set(DOCTEST_NO_INSTALL ON) + enable_testing() + add_subdirectory(tests) + endif() + if(LEXY_BUILD_DOCS) + add_subdirectory(docs EXCLUDE_FROM_ALL) + endif() +endif() diff --git a/3rdparty/lexy/LICENSE b/3rdparty/lexy/LICENSE new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/3rdparty/lexy/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/lexy/README.adoc b/3rdparty/lexy/README.adoc new file mode 100644 index 000000000..6bc88487f --- /dev/null +++ b/3rdparty/lexy/README.adoc @@ -0,0 +1,175 @@ += lexy + +ifdef::env-github[] +image:https://img.shields.io/endpoint?url=https%3A%2F%2Fwww.jonathanmueller.dev%2Fproject%2Flexy%2Findex.json[Project Status,link=https://www.jonathanmueller.dev/project/] +image:https://github.com/foonathan/lexy/workflows/Main%20CI/badge.svg[Build Status] +image:https://img.shields.io/badge/try_it_online-blue[Playground,link=https://lexy.foonathan.net/playground] +endif::[] + +lexy is a parser combinator library for {cpp}17 and onwards. +It allows you to write a parser by specifying it in a convenient {cpp} DSL, +which gives you all the flexibility and control of a handwritten parser without any of the manual work. + +ifdef::env-github[] +*Documentation*: https://lexy.foonathan.net/[lexy.foonathan.net] +endif::[] + +.IPv4 address parser +-- +ifndef::env-github[] +[.godbolt-example] +.+++{{< svg "icons/play.svg" >}}+++ +endif::[] +[source,cpp] +---- +namespace dsl = lexy::dsl; + +// Parse an IPv4 address into a `std::uint32_t`. +struct ipv4_address +{ + // What is being matched. + static constexpr auto rule = []{ + // Match a sequence of (decimal) digits and convert it into a std::uint8_t. + auto octet = dsl::integer; + + // Match four of them separated by periods. + return dsl::times<4>(octet, dsl::sep(dsl::period)) + dsl::eof; + }(); + + // How the matched output is being stored. + static constexpr auto value + = lexy::callback([](std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) { + return (a << 24) | (b << 16) | (c << 8) | d; + }); +}; +---- +-- + +== Features + +Full control:: + * *Describe the parser, not some abstract grammar*: + Unlike parser generators that use some table driven magic for parsing, lexy's grammar is just syntax sugar for a hand-written recursive descent parser. + The parsing algorithm does exactly what you've instructed it to do -- no more ambiguities or weird shift/reduce errors! + * *No implicit backtracking or lookahead*: + It will only backtrack when you say it should, and only lookahead when and how far you want it. + Don't worry about rules that have side-effects, they won't be executed unnecessarily thanks to the user-specified lookahead conditions. + https://lexy.foonathan.net/playground?example=peek[Try it online]. + * *Escape hatch for manual parsing*: + Sometimes you want to parse something that can't be expressed easily with lexy's facilities. + Don't worry, you can integrate a hand-written parser into the grammar at any point. + https://lexy.foonathan.net/playground/?example=scan[Try it online]. + * *Tracing*: + Figure out why the grammar isn't working the way you want it to. + https://lexy.foonathan.net/playground/?example=trace&mode=trace[Try it online]. + +Easily integrated:: + * *A pure {cpp} DSL*: + No need to use an external grammar file; embed the grammar directly in your {cpp} project using operator overloading and functions. + * *Bring your own data structures*: + You can directly store results into your own types and have full control over all heap allocations. + * *Fully `constexpr` parsing*: + You want to parse a string literal at compile-time? You can do so. + * *Minimal standard library dependencies*: + The core parsing library only depends on fundamental headers such as `` or ``; no big includes like `` or ``. + * *Header-only core library* (by necessity, not by choice -- it's `constexpr` after all). + +ifdef::env-github[Designed for text::] +ifndef::env-github[Designed for text (e.g. {{< github-example json >}}, {{< github-example xml >}}, {{< github-example email >}}) ::] + * *Unicode support*: parse UTF-8, UTF-16, or UTF-32, and access the Unicode character database to query char classes or perform case folding. + https://lexy.foonathan.net/playground?example=identifier-unicode[Try it online]. + * *Convenience*: + Built-in rules for parsing nested structures, quotes and escape sequences. + https://lexy.foonathan.net/playground?example=parenthesized[Try it online]. + * *Automatic whitespace skipping*: + No need to manually handle whitespace or comments. + https://lexy.foonathan.net/playground/?example=whitespace_comment[Try it online]. + +ifdef::env-github[Designed for programming languages::] +ifndef::env-github[Designed for programming languages (e.g. {{< github-example calculator >}}, {{< github-example shell >}})::] + * *Keyword and identifier parsing*: + Reserve a set of keywords that won't be matched as regular identifiers. + https://lexy.foonathan.net/playground/?example=reserved_identifier[Try it online]. + * *Operator parsing*: + Parse unary/binary operators with different precedences and associativity, including chained comparisons `a < b < c`. + https://lexy.foonathan.net/playground/?example=expr[Try it online]. + * *Automatic error recovery*: + Log an error, recover, and continue parsing! + https://lexy.foonathan.net/playground/?example=recover[Try it online]. + +ifdef::env-github[Designed for binary input::] +ifndef::env-github[Designed for binary input (e.g. {{< github-example protobuf >}})::] + * *Bytes*: Rules for parsing `N` bytes or Nbit big/little endian integer. + * *Bits*: Rules for parsing individual bit patterns. + * *Blobs*: Rules for parsing TLV formats. + +== FAQ + +Why should I use lexy over XYZ?:: + lexy is closest to other PEG parsers. + However, they usually do more implicit backtracking, which can hurt performance and you need to be very careful with rules that have side-effects. + This is not the case for lexy, where backtracking is controlled using branch conditions. + lexy also gives you a lot of control over error reporting, supports error recovery, special support for operator precedence parsing, and other advanced features. + + http://boost-spirit.com/home/[Boost.Spirit]::: + The main difference: it is not a Boost library. + In addition, Boost.Spirit is quite old and doesn't support e.g. non-common ranges as input. + Boost.Spirit also eagerly creates attributes from the rules, which can lead to nested tuples/variants while lexy uses callbacks which enables zero-copy parsing directly into your own data structure. + However, lexy's grammar is more verbose and designed to parser bigger grammars instead of the small one-off rules that Boost.Spirit is good at. + https://github.com/taocpp/PEGTL[PEGTL]::: + PEGTL is very similar and was a big inspiration. + The biggest difference is that lexy uses an operator based DSL instead of inheriting from templated classes as PEGTL does; + depending on your preference this can be an advantage or disadvantage. + Hand-written Parsers::: + Writing a handwritten parser is more manual work and error prone. + lexy automates that away without having to sacrifice control. + You can use it to quickly prototype a parser and then slowly replace more and more with a handwritten parser over time; + mixing a hand-written parser and a lexy grammar works seamlessly. + +How bad are the compilation times?:: +They're not as bad as you might expect (in debug mode, that is). ++ +The example JSON parser compiles in about 2s on my machine. +If we remove all the lexy specific parts and just benchmark the time it takes for the compiler to process the datastructure (and stdlib includes), +that takes about 700ms. +If we validate JSON only instead of parsing it, so remove the data structures and keep only the lexy specific parts, we're looking at about 840ms. ++ +Keep in mind, that you can fully isolate lexy in a single translation unit that only needs to be touched when you change the parser. +You can also split a lexy grammar into multiple translation units using the `dsl::subgrammar` rule. + +How bad are the {cpp} error messages if you mess something up?:: + They're certainly worse than the error message lexy gives you. + The big problem here is that the first line gives you the error, followed by dozens of template instantiations, which end at your `lexy::parse` call. + Besides providing an external tool to filter those error messages, there is nothing I can do about that. + +How fast is it?:: + Benchmarks are available in the `benchmarks/` directory. + A sample result of the JSON validator benchmark which compares the example JSON parser with various other implementations is available https://lexy.foonathan.net/benchmark_json/[here]. + +Why is it called lexy?:: + I previously had a tokenizer library called foonathan/lex. + I've tried adding a parser to it, but found that the line between pure tokenization and parsing has become increasingly blurred. + lexy is a re-imagination on of the parser I've added to foonathan/lex, and I've simply kept a similar name. + +ifdef::env-github[] +== Documentation + +The documentation, including tutorials, reference documentation, and an interactive playground can be found at https://lexy.foonathan.net/[lexy.foonathan.net]. + +A minimal `CMakeLists.txt` that uses lexy can look like this: + +.`CMakeLists.txt` +```cmake +project(lexy-example) + +include(FetchContent) +FetchContent_Declare(lexy URL https://lexy.foonathan.net/download/lexy-src.zip) +FetchContent_MakeAvailable(lexy) + +add_executable(lexy_example) +target_sources(lexy_example PRIVATE main.cpp) +target_link_libraries(lexy_example PRIVATE foonathan::lexy) +``` + +endif::[] + diff --git a/3rdparty/lexy/cmake/lexyConfig.cmake.in b/3rdparty/lexy/cmake/lexyConfig.cmake.in new file mode 100644 index 000000000..e6dc89d30 --- /dev/null +++ b/3rdparty/lexy/cmake/lexyConfig.cmake.in @@ -0,0 +1,8 @@ +# Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +# SPDX-License-Identifier: BSL-1.0 + +# lexy CMake configuration file. + +@PACKAGE_INIT@ + +include ("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") diff --git a/3rdparty/lexy/include/lexy/_detail/any_ref.hpp b/3rdparty/lexy/include/lexy/_detail/any_ref.hpp new file mode 100644 index 000000000..9eca714b2 --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/any_ref.hpp @@ -0,0 +1,68 @@ +// Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_ANY_REF_HPP_INCLUDED +#define LEXY_DETAIL_ANY_REF_HPP_INCLUDED + +#include + +// Essentially a void*, but we can cast it in a constexpr context. +// The cost is an extra layer of indirection. + +namespace lexy::_detail +{ +template +class any_holder; + +// Store a pointer to this instead of a void*. +class any_base +{ +public: + any_base(const any_base&) = delete; + any_base& operator=(const any_base&) = delete; + + template + constexpr T& get() noexcept + { + return static_cast*>(this)->get(); + } + template + constexpr const T& get() const noexcept + { + return static_cast*>(this)->get(); + } + +private: + constexpr any_base() = default; + ~any_base() = default; + + template + friend class any_holder; +}; + +using any_ref = any_base*; +using any_cref = const any_base*; + +// Need to store the object in here. +template +class any_holder : public any_base +{ +public: + constexpr explicit any_holder(T&& obj) : _obj(LEXY_MOV(obj)) {} + + constexpr T& get() noexcept + { + return _obj; + } + constexpr const T& get() const noexcept + { + return _obj; + } + +private: + T _obj; +}; +} // namespace lexy::_detail + +#endif // LEXY_DETAIL_ANY_REF_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/assert.hpp b/3rdparty/lexy/include/lexy/_detail/assert.hpp new file mode 100644 index 000000000..52aa115de --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/assert.hpp @@ -0,0 +1,51 @@ +// Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_ASSERT_HPP_INCLUDED +#define LEXY_DETAIL_ASSERT_HPP_INCLUDED + +#include + +#ifndef LEXY_ENABLE_ASSERT + +// By default, enable assertions if NDEBUG is not defined. + +# if NDEBUG +# define LEXY_ENABLE_ASSERT 0 +# else +# define LEXY_ENABLE_ASSERT 1 +# endif + +#endif + +#if LEXY_ENABLE_ASSERT + +// We want assertions: use assert() if that's available, otherwise abort. +// We don't use assert() directly as that's not constexpr. + +# if NDEBUG + +# include +# define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : std::abort()) +# define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : std::abort()) + +# else + +# include + +# define LEXY_PRECONDITION(Expr) ((Expr) ? void(0) : assert(Expr)) +# define LEXY_ASSERT(Expr, Msg) ((Expr) ? void(0) : assert((Expr) && (Msg))) + +# endif + +#else + +// We don't want assertions. + +# define LEXY_PRECONDITION(Expr) static_cast(sizeof(Expr)) +# define LEXY_ASSERT(Expr, Msg) static_cast(sizeof(Expr)) + +#endif + +#endif // LEXY_DETAIL_ASSERT_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp b/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp new file mode 100644 index 000000000..94ba1fd27 --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/buffer_builder.hpp @@ -0,0 +1,160 @@ +// Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED +#define LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED + +#include +#include +#include +#include +#include + +namespace lexy::_detail +{ +// Builds a buffer: it has a read are and a write area. +// The characters in the read area are already valid and can be read. +// The characters in the write area are not valid, but can be written too. +template +class buffer_builder +{ + static_assert(std::is_trivial_v); + + static constexpr std::size_t total_size_bytes = 1024; + static constexpr std::size_t stack_buffer_size + = (total_size_bytes - 3 * sizeof(T*)) / sizeof(T); + static constexpr auto growth_factor = 2; + +public: + buffer_builder() noexcept : _data(_stack_buffer), _read_size(0), _write_size(stack_buffer_size) + { + static_assert(sizeof(*this) == total_size_bytes, "invalid buffer size calculation"); + } + + ~buffer_builder() noexcept + { + // Free memory if we allocated any. + if (_data != _stack_buffer) + ::operator delete(_data); + } + + buffer_builder(const buffer_builder&) = delete; + buffer_builder& operator=(const buffer_builder&) = delete; + + // The total capacity: read + write. + std::size_t capacity() const noexcept + { + return _read_size + _write_size; + } + + // The read area. + const T* read_data() const noexcept + { + return _data; + } + std::size_t read_size() const noexcept + { + return _read_size; + } + + // The write area. + T* write_data() noexcept + { + return _data + _read_size; + } + std::size_t write_size() const noexcept + { + return _write_size; + } + + // Clears the read area. + void clear() noexcept + { + _write_size += _read_size; + _read_size = 0; + } + + // Takes the first n characters of the write area and appends them to the read area. + void commit(std::size_t n) noexcept + { + LEXY_PRECONDITION(n <= _write_size); + _read_size += n; + _write_size -= n; + } + + // Increases the write area, invalidates all pointers. + void grow() + { + const auto cur_cap = capacity(); + const auto new_cap = growth_factor * cur_cap; + + // Allocate new memory. + auto memory = static_cast(::operator new(new_cap * sizeof(T))); + // Copy the read area into the new memory. + std::memcpy(memory, _data, _read_size); + + // Release the old memory, if there was any. + if (_data != _stack_buffer) + ::operator delete(_data); + + // Update for the new area. + _data = memory; + // _read_size hasn't been changed + _write_size = new_cap - _read_size; + } + + //=== iterator ===// + // Stable iterator over the memory. + class stable_iterator : public forward_iterator_base + { + public: + constexpr stable_iterator() = default; + + explicit constexpr stable_iterator(const _detail::buffer_builder& buffer, + std::size_t idx) noexcept + : _buffer(&buffer), _idx(idx) + {} + + constexpr const T& deref() const noexcept + { + LEXY_PRECONDITION(_idx != _buffer->read_size()); + return _buffer->read_data()[_idx]; + } + + constexpr void increment() noexcept + { + LEXY_PRECONDITION(_idx != _buffer->read_size()); + ++_idx; + } + + constexpr bool equal(stable_iterator rhs) const noexcept + { + if (!_buffer || !rhs._buffer) + return !_buffer && !rhs._buffer; + else + { + LEXY_PRECONDITION(_buffer == rhs._buffer); + return _idx == rhs._idx; + } + } + + constexpr std::size_t index() const noexcept + { + return _idx; + } + + private: + const _detail::buffer_builder* _buffer = nullptr; + std::size_t _idx = 0; + }; + +private: + T* _data; + std::size_t _read_size; + std::size_t _write_size; + T _stack_buffer[stack_buffer_size]; +}; +} // namespace lexy::_detail + +#endif // LEXY_DETAIL_BUFFER_BUILDER_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/code_point.hpp b/3rdparty/lexy/include/lexy/_detail/code_point.hpp new file mode 100644 index 000000000..bc805b11e --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/code_point.hpp @@ -0,0 +1,368 @@ +// Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_CODE_POINT_HPP_INCLUDED +#define LEXY_DETAIL_CODE_POINT_HPP_INCLUDED + +#include + +//=== encoding ===// +namespace lexy::_detail +{ +template +constexpr std::size_t encode_code_point(char32_t cp, typename Encoding::char_type* buffer, + std::size_t size) +{ + if constexpr (std::is_same_v) + { + LEXY_PRECONDITION(size >= 1); + + *buffer = char(cp); + return 1; + } + else if constexpr (std::is_same_v // + || std::is_same_v) + { + using char_type = typename Encoding::char_type; + // Taken from http://www.herongyang.com/Unicode/UTF-8-UTF-8-Encoding-Algorithm.html. + if (cp <= 0x7F) + { + LEXY_PRECONDITION(size >= 1); + + buffer[0] = char_type(cp); + return 1; + } + else if (cp <= 0x07'FF) + { + LEXY_PRECONDITION(size >= 2); + + auto first = (cp >> 6) & 0x1F; + auto second = (cp >> 0) & 0x3F; + + buffer[0] = char_type(0xC0 | first); + buffer[1] = char_type(0x80 | second); + return 2; + } + else if (cp <= 0xFF'FF) + { + LEXY_PRECONDITION(size >= 3); + + auto first = (cp >> 12) & 0x0F; + auto second = (cp >> 6) & 0x3F; + auto third = (cp >> 0) & 0x3F; + + buffer[0] = char_type(0xE0 | first); + buffer[1] = char_type(0x80 | second); + buffer[2] = char_type(0x80 | third); + return 3; + } + else + { + LEXY_PRECONDITION(size >= 4); + + auto first = (cp >> 18) & 0x07; + auto second = (cp >> 12) & 0x3F; + auto third = (cp >> 6) & 0x3F; + auto fourth = (cp >> 0) & 0x3F; + + buffer[0] = char_type(0xF0 | first); + buffer[1] = char_type(0x80 | second); + buffer[2] = char_type(0x80 | third); + buffer[3] = char_type(0x80 | fourth); + return 4; + } + } + else if constexpr (std::is_same_v) + { + if (cp <= 0xFF'FF) + { + LEXY_PRECONDITION(size >= 1); + + buffer[0] = char16_t(cp); + return 1; + } + else + { + // Algorithm implemented from + // https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF. + LEXY_PRECONDITION(size >= 2); + + auto u_prime = cp - 0x1'0000; + auto high_ten_bits = u_prime >> 10; + auto low_ten_bits = u_prime & 0b0000'0011'1111'1111; + + buffer[0] = char16_t(0xD800 + high_ten_bits); + buffer[1] = char16_t(0xDC00 + low_ten_bits); + return 2; + } + } + else if constexpr (std::is_same_v) + { + LEXY_PRECONDITION(size >= 1); + + *buffer = char32_t(cp); + return 1; + } + else + { + static_assert(lexy::_detail::error, + "cannot encode a code point in this encoding"); + (void)cp; + (void)buffer; + (void)size; + return 0; + } +} +} // namespace lexy::_detail + +//=== parsing ===// +namespace lexy::_detail +{ +enum class cp_error +{ + success, + eof, + leads_with_trailing, + missing_trailing, + surrogate, + overlong_sequence, + out_of_range, +}; + +template +struct cp_result +{ + char32_t cp; + cp_error error; + typename Reader::marker end; +}; + +template +constexpr cp_result parse_code_point(Reader reader) +{ + if constexpr (std::is_same_v) + { + if (reader.peek() == Reader::encoding::eof()) + return {{}, cp_error::eof, reader.current()}; + + auto cur = reader.peek(); + reader.bump(); + + auto cp = static_cast(cur); + if (cp <= 0x7F) + return {cp, cp_error::success, reader.current()}; + else + return {cp, cp_error::out_of_range, reader.current()}; + } + else if constexpr (std::is_same_v // + || std::is_same_v) + { + using uchar_t = unsigned char; + constexpr auto payload_lead1 = 0b0111'1111; + constexpr auto payload_lead2 = 0b0001'1111; + constexpr auto payload_lead3 = 0b0000'1111; + constexpr auto payload_lead4 = 0b0000'0111; + constexpr auto payload_cont = 0b0011'1111; + + constexpr auto pattern_lead1 = 0b0 << 7; + constexpr auto pattern_lead2 = 0b110 << 5; + constexpr auto pattern_lead3 = 0b1110 << 4; + constexpr auto pattern_lead4 = 0b11110 << 3; + constexpr auto pattern_cont = 0b10 << 6; + + auto first = uchar_t(reader.peek()); + if ((first & ~payload_lead1) == pattern_lead1) + { + // ASCII character. + reader.bump(); + return {first, cp_error::success, reader.current()}; + } + else if ((first & ~payload_cont) == pattern_cont) + { + return {{}, cp_error::leads_with_trailing, reader.current()}; + } + else if ((first & ~payload_lead2) == pattern_lead2) + { + reader.bump(); + + auto second = uchar_t(reader.peek()); + if ((second & ~payload_cont) != pattern_cont) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + auto result = char32_t(first & payload_lead2); + result <<= 6; + result |= char32_t(second & payload_cont); + + // C0 and C1 are overlong ASCII. + if (first == 0xC0 || first == 0xC1) + return {result, cp_error::overlong_sequence, reader.current()}; + else + return {result, cp_error::success, reader.current()}; + } + else if ((first & ~payload_lead3) == pattern_lead3) + { + reader.bump(); + + auto second = uchar_t(reader.peek()); + if ((second & ~payload_cont) != pattern_cont) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + auto third = uchar_t(reader.peek()); + if ((third & ~payload_cont) != pattern_cont) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + auto result = char32_t(first & payload_lead3); + result <<= 6; + result |= char32_t(second & payload_cont); + result <<= 6; + result |= char32_t(third & payload_cont); + + auto cp = result; + if (0xD800 <= cp && cp <= 0xDFFF) + return {cp, cp_error::surrogate, reader.current()}; + else if (first == 0xE0 && second < 0xA0) + return {cp, cp_error::overlong_sequence, reader.current()}; + else + return {cp, cp_error::success, reader.current()}; + } + else if ((first & ~payload_lead4) == pattern_lead4) + { + reader.bump(); + + auto second = uchar_t(reader.peek()); + if ((second & ~payload_cont) != pattern_cont) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + auto third = uchar_t(reader.peek()); + if ((third & ~payload_cont) != pattern_cont) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + auto fourth = uchar_t(reader.peek()); + if ((fourth & ~payload_cont) != pattern_cont) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + auto result = char32_t(first & payload_lead4); + result <<= 6; + result |= char32_t(second & payload_cont); + result <<= 6; + result |= char32_t(third & payload_cont); + result <<= 6; + result |= char32_t(fourth & payload_cont); + + auto cp = result; + if (cp > 0x10'FFFF) + return {cp, cp_error::out_of_range, reader.current()}; + else if (first == 0xF0 && second < 0x90) + return {cp, cp_error::overlong_sequence, reader.current()}; + else + return {cp, cp_error::success, reader.current()}; + } + else // FE or FF + { + return {{}, cp_error::eof, reader.current()}; + } + } + else if constexpr (std::is_same_v) + { + constexpr auto payload1 = 0b0000'0011'1111'1111; + constexpr auto payload2 = payload1; + + constexpr auto pattern1 = 0b110110 << 10; + constexpr auto pattern2 = 0b110111 << 10; + + if (reader.peek() == Reader::encoding::eof()) + return {{}, cp_error::eof, reader.current()}; + + auto first = char16_t(reader.peek()); + if ((first & ~payload1) == pattern1) + { + reader.bump(); + if (reader.peek() == Reader::encoding::eof()) + return {{}, cp_error::missing_trailing, reader.current()}; + + auto second = char16_t(reader.peek()); + if ((second & ~payload2) != pattern2) + return {{}, cp_error::missing_trailing, reader.current()}; + reader.bump(); + + // We've got a valid code point. + auto result = char32_t(first & payload1); + result <<= 10; + result |= char32_t(second & payload2); + result |= 0x10000; + return {result, cp_error::success, reader.current()}; + } + else if ((first & ~payload2) == pattern2) + { + return {{}, cp_error::leads_with_trailing, reader.current()}; + } + else + { + // Single code unit code point; always valid. + reader.bump(); + return {first, cp_error::success, reader.current()}; + } + } + else if constexpr (std::is_same_v) + { + if (reader.peek() == Reader::encoding::eof()) + return {{}, cp_error::eof, reader.current()}; + + auto cur = reader.peek(); + reader.bump(); + + auto cp = cur; + if (cp > 0x10'FFFF) + return {cp, cp_error::out_of_range, reader.current()}; + else if (0xD800 <= cp && cp <= 0xDFFF) + return {cp, cp_error::surrogate, reader.current()}; + else + return {cp, cp_error::success, reader.current()}; + } + else + { + static_assert(lexy::_detail::error, + "no known code point for this encoding"); + return {}; + } +} + +template +constexpr void recover_code_point(Reader& reader, cp_result result) +{ + switch (result.error) + { + case cp_error::success: + // Consume the entire code point. + reader.reset(result.end); + break; + case cp_error::eof: + // We don't need to do anything to "recover" from EOF. + break; + + case cp_error::leads_with_trailing: + // Invalid code unit, consume to recover. + LEXY_PRECONDITION(result.end.position() == reader.position()); + reader.bump(); + break; + + case cp_error::missing_trailing: + case cp_error::surrogate: + case cp_error::out_of_range: + case cp_error::overlong_sequence: + // Consume all the invalid code units to recover. + reader.reset(result.end); + break; + } +} +} // namespace lexy::_detail + +#endif // LEXY_DETAIL_CODE_POINT_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/config.hpp b/3rdparty/lexy/include/lexy/_detail/config.hpp new file mode 100644 index 000000000..4aa40135b --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/config.hpp @@ -0,0 +1,199 @@ +// Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_CONFIG_HPP_INCLUDED +#define LEXY_DETAIL_CONFIG_HPP_INCLUDED + +#include +#include + +#if defined(LEXY_USER_CONFIG_HEADER) +# include LEXY_USER_CONFIG_HEADER +#elif defined(__has_include) +# if __has_include() +# include +# elif __has_include("lexy_user_config.hpp") +# include "lexy_user_config.hpp" +# endif +#endif + +#ifndef LEXY_HAS_UNICODE_DATABASE +# define LEXY_HAS_UNICODE_DATABASE 0 +#endif + +#ifndef LEXY_EXPERIMENTAL +# define LEXY_EXPERIMENTAL 0 +#endif + +//=== utility traits===// +#define LEXY_MOV(...) static_cast&&>(__VA_ARGS__) +#define LEXY_FWD(...) static_cast(__VA_ARGS__) + +#define LEXY_DECLVAL(...) lexy::_detail::declval<__VA_ARGS__>() + +#define LEXY_DECAY_DECLTYPE(...) std::decay_t + +/// Creates a new type from the instantiation of a template. +/// This is used to shorten type names. +#define LEXY_INSTANTIATION_NEWTYPE(Name, Templ, ...) \ + struct Name : Templ<__VA_ARGS__> \ + { \ + using Templ<__VA_ARGS__>::Templ; \ + } + +namespace lexy::_detail +{ +template +constexpr bool error = false; + +template +std::add_rvalue_reference_t declval(); + +template +constexpr void swap(T& lhs, T& rhs) +{ + T tmp = LEXY_MOV(lhs); + lhs = LEXY_MOV(rhs); + rhs = LEXY_MOV(tmp); +} + +template +constexpr bool is_decayed_same = std::is_same_v, std::decay_t>; + +template +using type_or = std::conditional_t, Fallback, T>; +} // namespace lexy::_detail + +//=== NTTP ===// +#ifndef LEXY_HAS_NTTP +// See https://github.com/foonathan/lexy/issues/15. +# if __cpp_nontype_template_parameter_class >= 201806 || __cpp_nontype_template_args >= 201911 +# define LEXY_HAS_NTTP 1 +# else +# define LEXY_HAS_NTTP 0 +# endif +#endif + +#if LEXY_HAS_NTTP +# define LEXY_NTTP_PARAM auto +#else +# define LEXY_NTTP_PARAM const auto& +#endif + +//=== consteval ===// +#ifndef LEXY_HAS_CONSTEVAL +# if defined(_MSC_VER) && !defined(__clang__) +// Currently can't handle returning strings from consteval, check back later. +# define LEXY_HAS_CONSTEVAL 0 +# elif __cpp_consteval +# define LEXY_HAS_CONSTEVAL 1 +# else +# define LEXY_HAS_CONSTEVAL 0 +# endif +#endif + +#if LEXY_HAS_CONSTEVAL +# define LEXY_CONSTEVAL consteval +#else +# define LEXY_CONSTEVAL constexpr +#endif + +//=== constexpr ===// +#ifndef LEXY_HAS_CONSTEXPR_DTOR +# if __cpp_constexpr_dynamic_alloc +# define LEXY_HAS_CONSTEXPR_DTOR 1 +# else +# define LEXY_HAS_CONSTEXPR_DTOR 0 +# endif +#endif + +#if LEXY_HAS_CONSTEXPR_DTOR +# define LEXY_CONSTEXPR_DTOR constexpr +#else +# define LEXY_CONSTEXPR_DTOR +#endif + +//=== char8_t ===// +#ifndef LEXY_HAS_CHAR8_T +# if __cpp_char8_t +# define LEXY_HAS_CHAR8_T 1 +# else +# define LEXY_HAS_CHAR8_T 0 +# endif +#endif + +#if LEXY_HAS_CHAR8_T + +# define LEXY_CHAR_OF_u8 char8_t +# define LEXY_CHAR8_T char8_t +# define LEXY_CHAR8_STR(Str) u8##Str + +#else + +namespace lexy +{ +using _char8_t = unsigned char; +} // namespace lexy + +# define LEXY_CHAR_OF_u8 char +# define LEXY_CHAR8_T ::lexy::_char8_t +# define LEXY_CHAR8_STR(Str) \ + LEXY_NTTP_STRING(::lexy::_detail::type_string, u8##Str)::c_str + +#endif + +//=== endianness ===// +#ifndef LEXY_IS_LITTLE_ENDIAN +# if defined(__BYTE_ORDER__) +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define LEXY_IS_LITTLE_ENDIAN 1 +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define LEXY_IS_LITTLE_ENDIAN 0 +# else +# error "unsupported byte order" +# endif +# elif defined(_MSC_VER) +# define LEXY_IS_LITTLE_ENDIAN 1 +# else +# error "unknown endianness" +# endif +#endif + +//=== force inline ===// +#ifndef LEXY_FORCE_INLINE +# if defined(__has_cpp_attribute) +# if __has_cpp_attribute(gnu::always_inline) +# define LEXY_FORCE_INLINE [[gnu::always_inline]] +# endif +# endif +# +# ifndef LEXY_FORCE_INLINE +# define LEXY_FORCE_INLINE inline +# endif +#endif + +//=== empty_member ===// +#ifndef LEXY_EMPTY_MEMBER + +# if defined(__has_cpp_attribute) +# if defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 11 +// GCC <= 11 has buggy support, see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101040 +# define LEXY_HAS_EMPTY_MEMBER 0 +# elif __has_cpp_attribute(no_unique_address) +# define LEXY_HAS_EMPTY_MEMBER 1 +# endif +# endif +# ifndef LEXY_HAS_EMPTY_MEMBER +# define LEXY_HAS_EMPTY_MEMBER 0 +# endif + +# if LEXY_HAS_EMPTY_MEMBER +# define LEXY_EMPTY_MEMBER [[no_unique_address]] +# else +# define LEXY_EMPTY_MEMBER +# endif + +#endif + +#endif // LEXY_DETAIL_CONFIG_HPP_INCLUDED + diff --git a/3rdparty/lexy/include/lexy/_detail/detect.hpp b/3rdparty/lexy/include/lexy/_detail/detect.hpp new file mode 100644 index 000000000..7534c44c4 --- /dev/null +++ b/3rdparty/lexy/include/lexy/_detail/detect.hpp @@ -0,0 +1,35 @@ +// Copyright (C) 2020-2024 Jonathan MÃŧller and lexy contributors +// SPDX-License-Identifier: BSL-1.0 + +#ifndef LEXY_DETAIL_DETECT_HPP_INCLUDED +#define LEXY_DETAIL_DETECT_HPP_INCLUDED + +#include + +namespace lexy::_detail +{ +template +using void_t = void; + +template