diff --git a/.clang-tidy b/.clang-tidy index 1ba914c7c..b7f31d939 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,14 +1,43 @@ --- -Checks: '*,-cert-dcl21-cpp,-cert-err60-cpp,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-reinterpret-cast,-fuchsia-*,-google-runtime-references,-hicpp-no-array-decay' +Checks: '*,-altera-*,-bugprone-easily-swappable-parameters,-bugprone-signed-char-misuse,-cert-dcl21-cpp,-cert-err58-cpp,-cert-err60-cpp,-cppcoreguidelines-avoid-c-arrays,-cppcoreguidelines-avoid-non-const-global-variables,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-macro-usage,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-type-reinterpret-cast,-fuchsia-*,-google-runtime-references,-hicpp-avoid-c-arrays,-hicpp-no-array-decay,-hicpp-vararg,-llvmlibc-*,-misc-no-recursion,-modernize-avoid-c-arrays,-modernize-use-trailing-return-type,-readability-function-cognitive-complexity,-readability-identifier-length,-readability-implicit-bool-conversion,-readability-magic-numbers' # # Disabled checks: # +# altera-* +# Doesn't apply. +# +# bugprone-easily-swappable-parameters +# Can't change this any more, because these functions are part of our public +# interface. +# +# bugprone-signed-char-misuse +# Lots of warnings in varint.hpp otherwise. +# # cert-dcl21-cpp # It is unclear whether this is still a good recommendation in modern C++. # +# cert-err58-cpp +# Due to the Catch2 test framework. +# # cert-err60-cpp # Reports std::runtime_error as broken which we can't do anything about. # +# cppcoreguidelines-avoid-c-arrays +# hicpp-avoid-c-arrays +# modernize-avoid-c-arrays +# Makes sense for some array, but especially for char arrays using +# std::array isn't a good solution. +# +# cppcoreguidelines-avoid-magic-numbers +# readability-magic-numbers +# Good idea, but it goes too far to force this everywhere. +# +# cppcoreguidelines-avoid-non-const-global-variables +# Getting these from Catch2 test framework, not from the code itself. +# +# cppcoreguidelines-macro-usage +# There are cases where macros are simply needed. +# # cppcoreguidelines-pro-bounds-array-to-pointer-decay # Limited use and many false positives including for all asserts. # @@ -28,6 +57,24 @@ Checks: '*,-cert-dcl21-cpp,-cert-err60-cpp,-cppcoreguidelines-pro-bounds-pointer # hicpp-no-array-decay # Limited use and many false positives including for all asserts. # +# llvmlibc-* +# Doesn't apply. +# +# misc-no-recursion +# Nothing wrong with recursion. +# +# modernize-use-trailing-return-type +# We are not quite that modern. +# +# readability-function-cognitive-complexity +# Getting these mostly from Catch2 test framework. +# +# readability-identifier-length +# Short identifiers do make sense sometimes. +# +# readability-implicit-bool-conversion +# Not necessarily more readable. +# WarningsAsErrors: '*' HeaderFilterRegex: '\/include\/' AnalyzeTemporaryDtors: false diff --git a/.github/actions/build-windows/action.yml b/.github/actions/build-windows/action.yml new file mode 100644 index 000000000..6f764c94e --- /dev/null +++ b/.github/actions/build-windows/action.yml @@ -0,0 +1,10 @@ +name: Build on Windows + +runs: + using: composite + steps: + - name: Build + run: cmake --build . --config Release --verbose + shell: bash + working-directory: build + diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml new file mode 100644 index 000000000..9d1e9acf5 --- /dev/null +++ b/.github/actions/build/action.yml @@ -0,0 +1,10 @@ +name: Build + +runs: + using: composite + steps: + - name: Build + run: make VERBOSE=1 + shell: bash + working-directory: build + diff --git a/.github/actions/cmake-windows/action.yml b/.github/actions/cmake-windows/action.yml new file mode 100644 index 000000000..5ff0c6754 --- /dev/null +++ b/.github/actions/cmake-windows/action.yml @@ -0,0 +1,12 @@ +name: CMake on Windows + +runs: + using: composite + steps: + - name: Create build directory + run: mkdir build + shell: bash + - name: Configure + run: cmake -LA .. -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake + shell: bash + working-directory: build diff --git a/.github/actions/cmake/action.yml b/.github/actions/cmake/action.yml new file mode 100644 index 000000000..3bb00ea16 --- /dev/null +++ b/.github/actions/cmake/action.yml @@ -0,0 +1,16 @@ +name: CMake + +runs: + using: composite + steps: + - name: Create build directory + run: mkdir build + shell: bash + - name: Configure + run: | + cmake -LA .. \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DPROTOZERO_DATA_VIEW=${PROTOZERO_DATA_VIEW} \ + -DCMAKE_CXX_STANDARD=${CPP_VERSION} + shell: bash + working-directory: build diff --git a/.github/actions/ctest-windows/action.yml b/.github/actions/ctest-windows/action.yml new file mode 100644 index 000000000..64bc52dca --- /dev/null +++ b/.github/actions/ctest-windows/action.yml @@ -0,0 +1,10 @@ +name: Test on Windows + +runs: + using: composite + steps: + - name: Test + run: ctest --output-on-failure -C Release + shell: bash + working-directory: build + diff --git a/.github/actions/ctest/action.yml b/.github/actions/ctest/action.yml new file mode 100644 index 000000000..95bc1e6ce --- /dev/null +++ b/.github/actions/ctest/action.yml @@ -0,0 +1,10 @@ +name: Test + +runs: + using: composite + steps: + - name: Test + run: ctest --output-on-failure + shell: bash + working-directory: build + diff --git a/.github/actions/install-ubuntu/action.yml b/.github/actions/install-ubuntu/action.yml new file mode 100644 index 000000000..4904badb5 --- /dev/null +++ b/.github/actions/install-ubuntu/action.yml @@ -0,0 +1,14 @@ +name: Install Prerequisites on Ubuntu + +runs: + using: composite + + steps: + - name: Install packages + run: | + sudo apt-get update -q + sudo apt-get install -yq \ + libprotobuf-dev \ + protobuf-compiler + if [ "$CC" = clang-13 ]; then sudo apt-get install -yq --no-install-suggests --no-install-recommends clang-13; fi + shell: bash diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..b02b4caa8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,176 @@ +name: CI + +on: [ push, pull_request ] + +jobs: + linux: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + image: + - "ubuntu:18.04" # gcc 7.5.0, clang 6.0.0, cmake 3.10.2 + - "ubuntu:20.04" # gcc 9.3.0, clang 10.0.0, cmake 3.16.3 + - "ubuntu:21.04" # gcc 10.3.0, clang 12.0.0, cmake 3.18.4 + - "ubuntu:21.10" # gcc 11.2.0, clang 13.0.0, cmake 3.18.4 + - "debian:stretch" # gcc 6.3.0, clang 3.8.1, cmake 3.7.2 + - "debian:buster" # gcc 8.3.0, clang 7.0.1, cmake 3.13.4 + - "debian:bullseye" # gcc 10.2.1, clang 11.0.1, cmake 3.18.4 + - "debian:testing" # gcc 10.3.0, clang 11.1.0, cmake 3.21.3 + - "debian:experimental" # gcc 11.0.0, clang 14.0.0, cmake 3.21.3 + - "fedora:34" # gcc 11.2.1, clang 12.0.1, cmake 3.20.5 + - "fedora:35" # gcc 11.2.1, clang 13.0.0, cmake 3.22.0 + build_type: [Debug] + cpp_compiler: [g++] + cpp_version: [11] + include: + - image: "debian:bullseye" + cpp_version: 14 + - image: "debian:bullseye" + cpp_version: 17 + - image: "debian:bullseye" + cpp_version: 20 + - image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: 14 + - image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: 17 + - image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + cpp_version: 20 + - image: "debian:bullseye" + build_type: Release + - image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + - image: "debian:bullseye" + c_compiler: clang + cpp_compiler: clang++ + data_view: std::string_view +# Disabled because it fails due to a bug in the protobuf library +# See https://github.com/protocolbuffers/protobuf/issues/7224 +# - image: "debian:bullseye" +# c_compiler: clang +# cpp_compiler: clang++ +# CXXFLAGS: "-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" +# LDFLAGS: "-fsanitize=address,undefined,integer" + - image: "debian:testing" + c_compiler: clang + cpp_compiler: clang++ + - image: "debian:experimental" + c_compiler: clang-14 + cpp_compiler: clang++-14 + container: + image: ${{ matrix.image }} + env: + BUILD_TYPE: ${{ matrix.build_type }} + CC: ${{ matrix.c_compiler }} + CXX: ${{ matrix.cpp_compiler }} + CXXFLAGS: ${{ matrix.CXXFLAGS }} + LDFLAGS: ${{ matrix.LDFLAGS }} + CPP_VERSION: ${{ matrix.cpp_version }} + PROTOZERO_DATA_VIEW: ${{ matrix.data_view }} + APT_LISTCHANGES_FRONTEND: none + DEBIAN_FRONTEND: noninteractive + steps: + - name: Prepare container (apt) + shell: bash + if: startsWith(matrix.image, 'debian:') || startsWith(matrix.image, 'ubuntu:') + run: | + apt-get update -qq + apt-get install -y \ + clang \ + cmake \ + doxygen \ + g++ \ + graphviz \ + libprotobuf-dev \ + make \ + protobuf-compiler + - name: Install compiler + shell: bash + if: matrix.cpp_compiler == 'clang++-14' + run: apt-get install -y --no-install-suggests --no-install-recommends clang-14 + - name: Prepare container (dnf) + shell: bash + if: startsWith(matrix.image, 'fedora:') + run: | + dnf install --quiet --assumeyes \ + cmake \ + doxygen \ + gcc-c++ \ + graphviz \ + make \ + protobuf-devel \ + protobuf-lite-static + - uses: actions/checkout@v2 + - uses: ./.github/actions/cmake + - uses: ./.github/actions/build + - uses: ./.github/actions/ctest + + ubuntu-latest: + runs-on: ubuntu-20.04 + env: + CC: clang-13 + CXX: clang++-13 + BUILD_TYPE: Debug + steps: + - name: Install new clang + run: | + wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - + sudo add-apt-repository 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-13 main' + shell: bash + - uses: actions/checkout@v2 + - uses: ./.github/actions/install-ubuntu + - uses: ./.github/actions/cmake + - uses: ./.github/actions/build + - uses: ./.github/actions/ctest + + macos: + strategy: + fail-fast: false + matrix: + os: + - "macos-10.15" + - "macos-11.0" + build_type: [Debug] + include: + - os: "macos-11.0" + build_type: Release + runs-on: ${{ matrix.os }} + env: + CC: clang + CXX: clang++ + BUILD_TYPE: ${{ matrix.build_type }} + steps: + - run: brew install protobuf + - uses: actions/checkout@v2 + - uses: ./.github/actions/cmake + - uses: ./.github/actions/build + - uses: ./.github/actions/ctest + + windows: + strategy: + fail-fast: false + matrix: + os: + - windows-2019 + - windows-2022 + runs-on: ${{ matrix.os }} + steps: + - run: | + vcpkg install \ + protobuf:x64-windows \ + protobuf-c:x64-windows + shell: bash + - uses: actions/checkout@v2 + with: + submodules: true + - uses: ./.github/actions/cmake-windows + - uses: ./.github/actions/build-windows + - uses: ./.github/actions/ctest-windows + diff --git a/.travis.yml b/.travis.yml index 891e59bba..e0e94d8e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,6 @@ language: generic -sudo: false - -dist: trusty - #----------------------------------------------------------------------------- # Save common build configurations as shortcuts, so we can reference them later. @@ -17,147 +13,265 @@ addons_shortcuts: addons_clang35: &clang35 apt: sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-3.5' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'clang-3.5' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-3.5' ] addons_clang38: &clang38 apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-3.8' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'clang-3.8' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-3.8' ] addons_clang39: &clang39 apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-3.9' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'clang-3.9' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-3.9' ] addons_clang40: &clang40 apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-4.0' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'clang-4.0' ] + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-4.0' ] addons_clang50: &clang50 apt: - sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-5.0' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'clang-5.0', 'clang-tidy-5.0' ] - addons_gcc47: &gcc47 + sources: [ 'ubuntu-toolchain-r-test' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-5.0' ] + addons_clang60: &clang60 apt: sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'g++-4.7', 'gcc-4.7' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-6.0' ] + addons_clang7: &clang7 + apt: + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-7' ] + addons_clang8: &clang8 + apt: + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-8' ] + addons_clang9: &clang9 + apt: + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'clang-9', 'clang-tidy-9' ] addons_gcc48: &gcc48 apt: sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'g++-4.8', 'gcc-4.8' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'g++-4.8', 'gcc-4.8' ] addons_gcc49: &gcc49 apt: sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'g++-4.9', 'gcc-4.9' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'g++-4.9', 'gcc-4.9' ] addons_gcc5: &gcc5 apt: sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'g++-5', 'gcc-5' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'g++-5', 'gcc-5' ] addons_gcc6: &gcc6 apt: sources: [ 'ubuntu-toolchain-r-test' ] - packages: [ 'libprotobuf-dev','protobuf-compiler', 'g++-6', 'gcc-6' ] + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'g++-6', 'gcc-6' ] + addons_gcc7: &gcc7 + apt: + packages: [ 'libprotobuf-dev', 'protobuf-compiler' ] + addons_gcc8: &gcc8 + apt: + packages: [ 'libprotobuf-dev', 'protobuf-compiler', 'g++-8', 'gcc-8' ] #----------------------------------------------------------------------------- -matrix: +jobs: include: - os: linux + dist: trusty compiler: "clang-3.5" - env: BUILD='Debug' CC=clang-3.5 CXX=clang++-3.5 + env: BUILD=Debug CC=clang-3.5 CXX=clang++-3.5 addons: *clang35 + - os: linux + dist: xenial compiler: "clang-3.8" - env: BUILD='Debug' CC=clang-3.8 CXX=clang++-3.8 + env: BUILD=Debug CC=clang-3.8 CXX=clang++-3.8 addons: *clang38 + - os: linux + dist: xenial compiler: "clang-3.9" - env: BUILD='Debug' CC=clang-3.9 CXX=clang++-3.9 + env: BUILD=Debug CC=clang-3.9 CXX=clang++-3.9 addons: *clang39 + - os: linux + dist: xenial compiler: "clang-4.0" - env: BUILD='Debug' CC=clang-4.0 CXX=clang++-4.0 + env: BUILD=Debug CC=clang-4.0 CXX=clang++-4.0 addons: *clang40 + - os: linux + dist: xenial compiler: "clang-5.0" - env: BUILD='Debug' CC=clang-5.0 CXX=clang++-5.0 - CLANG_TIDY=clang-tidy-5.0 + env: BUILD=Debug CC=clang-5.0 CXX=clang++-5.0 addons: *clang50 + - os: linux - compiler: "clang-5.0" - env: BUILD='Release' CC=clang-5.0 CXX=clang++-5.0 - addons: *clang50 + dist: xenial + compiler: "clang-6.0" + env: BUILD=Debug CC=clang-6.0 CXX=clang++-6.0 + addons: *clang60 + - os: linux - compiler: "clang-5.0" - env: BUILD='Debug' CC=clang-5.0 CXX=clang++-5.0 + dist: bionic + compiler: "clang-7" + env: BUILD=Debug CC=clang-7 CXX=clang++-7 + addons: *clang7 + + - os: linux + dist: bionic + compiler: "clang-8" + env: BUILD=Debug CC=clang-8 CXX=clang++-8 + addons: *clang8 + + - os: linux + dist: bionic + compiler: "clang-9" + env: BUILD=Debug CC=clang-9 CXX=clang++-9 + CLANG_TIDY=clang-tidy-9 + addons: *clang9 + + - os: linux + dist: bionic + compiler: "clang-9" + env: BUILD=Debug CC=clang-9 CXX=clang++-9 CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address,undefined,integer" - # LSAN doesn't work on container-based system - sudo: required - addons: *clang50 + addons: *clang9 + - os: linux - compiler: "gcc-4.7" - env: BUILD='Debug' CC=gcc-4.7 CXX=g++-4.7 - addons: *gcc47 + dist: bionic + compiler: "clang-9" + env: BUILD=Release CC=clang-9 CXX=clang++-9 + addons: *clang9 + - os: linux + arch: arm64 + dist: bionic + compiler: "clang-9" + env: BUILD=Debug CC=clang-9 CXX=clang++-9 + CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" + LDFLAGS="-fsanitize=address,undefined,integer" + addons: *clang9 + + - os: linux + arch: ppc64le + dist: bionic + compiler: "clang-9" + env: BUILD=Debug CC=clang-9 CXX=clang++-9 + CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" + LDFLAGS="-fsanitize=address,undefined,integer" + addons: *clang9 + + - os: linux + arch: s390x + dist: bionic + compiler: "clang-9" + env: BUILD=Debug CC=clang-9 CXX=clang++-9 + CXXFLAGS="-fsanitize=address,undefined,integer -fno-sanitize-recover=all -fno-omit-frame-pointer" + LDFLAGS="-fsanitize=address,undefined,integer" + addons: *clang9 + + - os: linux + dist: trusty compiler: "gcc-4.8" - env: BUILD='Debug' CC=gcc-4.8 CXX=g++-4.8 + env: BUILD=Debug CC=gcc-4.8 CXX=g++-4.8 addons: *gcc48 + - os: linux + dist: trusty compiler: "gcc-4.9" - env: BUILD='Debug' CC=gcc-4.9 CXX=g++-4.9 - COVERAGE=gcov-4.9 - CXXFLAGS="--coverage" LDFLAGS="--coverage" + env: BUILD=Debug CC=gcc-4.9 CXX=g++-4.9 addons: *gcc49 + - os: linux + dist: trusty compiler: "gcc-5" - env: BUILD='Debug' CC=gcc-5 CXX=g++-5 + env: BUILD=Debug CC=gcc-5 CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0" addons: *gcc5 + - os: linux + dist: xenial compiler: "gcc-5" - env: BUILD='Debug' CC=gcc-5 CXX=g++-5 + env: BUILD=Debug CC=gcc-5 CXX=g++-5 CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1" addons: *gcc5 + - os: linux + dist: xenial compiler: "gcc-6" - env: BUILD='Debug' CC=gcc-6 CXX=g++-6 + env: BUILD=Debug CC=gcc-6 CXX=g++-6 addons: *gcc6 + - os: linux - compiler: "gcc-6" - env: BUILD='Debug' CC=gcc-6 CXX=g++-6 - PROTOZERO_DATA_VIEW=std::experimental::string_view - addons: *gcc6 + dist: bionic + compiler: "gcc-7" + env: BUILD=Debug CC=gcc-7 CXX=g++-7 + addons: *gcc7 + - os: linux - compiler: "gcc-6" - env: BUILD='Release' CC=gcc-6 CXX=g++-6 - addons: *gcc6 + dist: bionic + compiler: "gcc-8" + env: BUILD=Debug CC=gcc-8 CXX=g++-8 + addons: *gcc8 + + - os: linux + dist: bionic + compiler: "gcc-8" + env: BUILD=Debug CC=gcc-8 CXX=g++-8 + COVERAGE=gcov-8 + CXXFLAGS="--coverage" LDFLAGS="--coverage" + addons: *gcc8 + + - os: linux + dist: bionic + compiler: "gcc-8" + env: BUILD=Debug CC=gcc-8 CXX=g++-8 + PROTOZERO_DATA_VIEW=std::string_view + addons: *gcc8 + + - os: linux + dist: bionic + compiler: "gcc-8" + env: BUILD=Release CC=gcc-8 CXX=g++-8 + addons: *gcc8 + + - os: linux + arch: arm64 + dist: bionic + compiler: "gcc-8" + env: BUILD=Debug CC=gcc-8 CXX=g++-8 + addons: *gcc8 + + - os: linux + arch: ppc64le + dist: bionic + compiler: "gcc-8" + env: BUILD=Debug CC=gcc-8 CXX=g++-8 + addons: *gcc8 + + - os: linux + arch: s390x + dist: bionic + compiler: "gcc-8" + env: BUILD=Debug CC=gcc-8 CXX=g++-8 + addons: *gcc8 + - os: osx - osx_image: xcode6.4 + osx_image: xcode9.4 compiler: clang - env: BUILD='Debug' + env: BUILD=Debug + - os: osx - osx_image: xcode7.3 + osx_image: xcode10.3 compiler: clang - env: BUILD='Debug' + env: BUILD=Debug + - os: osx - osx_image: xcode8.3 + osx_image: xcode11.4 compiler: clang - env: BUILD='Debug' + env: BUILD=Debug + - os: osx - osx_image: xcode9.1 + osx_image: xcode11.4 compiler: clang - env: BUILD='Debug' - - os: osx - osx_image: xcode9.1 - compiler: clang - env: BUILD='Release' + env: BUILD=Release #----------------------------------------------------------------------------- -install: - - if [[ $(uname -s) == 'Darwin' ]]; then - brew update; - brew install protobuf; - fi - script: - mkdir build - cd build diff --git a/CHANGELOG.md b/CHANGELOG.md index d43f422b6..35a793c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,8 @@ # Changelog All notable changes to this project will be documented in this file. -The format is based on [Keep a Changelog](http://keepachangelog.com/) -This project adheres to [Semantic Versioning](http://semver.org/). - +The format is based on [Keep a Changelog](https://keepachangelog.com/) +This project adheres to [Semantic Versioning](https://semver.org/). ## [unreleased] - @@ -15,6 +14,105 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [1.7.1] - 2022-01-10 + +### Changed + +- Don't build tests if the standard CMake `BUILD_TESTING` variable is set to + off. +- Now needs CMake 3.5.0 or greater. +- Update included catch2 framework to current version v2.13.8. +- Only enable clang-tidy make target if protobuf was found. +- Allow setting C++ version to compile with in CMake config. + +### Fixed + +- Fixes undefined behaviour in `float` and `double` byteswap. +- Add missing includes of "config.hpp". +- Avoid narrowing conversion by doing an explicit `static_cast`. + + +## [1.7.0] - 2020-06-08 + +### Added + +- Support for buffer types other that `std::string`. `pbf_writer` is now + just a typedef for `basic_pbf_writer`. Other buffer types + can be used with `basic_pbf_writer`. See `doc/advanced.md` for details. + +### Changed + +- Switched to *catch2* for testing. +- Some minor tweaks. + +### Fixed + +- Removed some undefined behaviour. + + +## [1.6.8] - 2019-08-15 + +### Changed + +- Various code cleanups due to clang-tidy warnings. + +### Fixed + +- Made `data_view::compare` noexcept. + + +## [1.6.7] - 2018-02-21 + +### Fixed + +- Signed-unsigned comparison on 32 bit systems. + + +## [1.6.6] - 2018-02-20 + +### Fixed + +- Fixed several place with possible undefined behaviour. + + +## [1.6.5] - 2018-02-05 + +### Fixed + +- Avoid UB: Do not calculate pointer outside array bounds. +- Specify proto2 syntax in .proto files to appease protoc. + + +## [1.6.4] - 2018-11-08 + +### Added + +- Add function `data()` to get the not yet read data from a `pbf_reader`. +- New `add_packed_fixed()` template function for `pbf_writer`. +- New `length_of_varint()` helper function calculates how long a varint + would be for a specified value. + +### Changed + +- More consistent implementation of operators as free friend functions. + +### Fixed + +- Fixed some zigzag encoding tests on MSVC. +- Add extra cast so we do an xor with unsigned ints. +- No more bitwise operations on signed integers in varint decoder. +- No more bitwise operations on signed integers in zigzag encoder/decoder. + + +## [1.6.3] - 2018-07-17 + +### Changed + +- Moved `byteswap_inplace` functions from detail into protozero namespace. + They can be useful outsize protozero. +- More asserts and unit tests and small cleanups. + + ## [1.6.2] - 2018-03-09 ### Changed @@ -301,7 +399,15 @@ This project adheres to [Semantic Versioning](http://semver.org/). - Make pbf reader and writer code endianess-aware. -[unreleased]: https://github.com/osmcode/libosmium/compare/v1.6.2...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v1.7.1...HEAD +[1.7.1]: https://github.com/osmcode/libosmium/compare/v1.7.0...v1.7.1 +[1.7.0]: https://github.com/osmcode/libosmium/compare/v1.6.8...v1.7.0 +[1.6.8]: https://github.com/osmcode/libosmium/compare/v1.6.7...v1.6.8 +[1.6.7]: https://github.com/osmcode/libosmium/compare/v1.6.6...v1.6.7 +[1.6.6]: https://github.com/osmcode/libosmium/compare/v1.6.5...v1.6.6 +[1.6.5]: https://github.com/osmcode/libosmium/compare/v1.6.4...v1.6.5 +[1.6.4]: https://github.com/osmcode/libosmium/compare/v1.6.3...v1.6.4 +[1.6.3]: https://github.com/osmcode/libosmium/compare/v1.6.2...v1.6.3 [1.6.2]: https://github.com/osmcode/libosmium/compare/v1.6.1...v1.6.2 [1.6.1]: https://github.com/osmcode/libosmium/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/osmcode/libosmium/compare/v1.5.3...v1.6.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 24e293d73..b975d4901 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,32 +6,35 @@ # #----------------------------------------------------------------------------- -cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR) #----------------------------------------------------------------------------- -project(protozero) - -set(PROTOZERO_VERSION_MAJOR 1) -set(PROTOZERO_VERSION_MINOR 6) -set(PROTOZERO_VERSION_PATCH 2) - -set(PROTOZERO_VERSION - "${PROTOZERO_VERSION_MAJOR}.${PROTOZERO_VERSION_MINOR}.${PROTOZERO_VERSION_PATCH}") +project(protozero VERSION 1.7.1 LANGUAGES CXX C) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) #----------------------------------------------------------------------------- +if (NOT "${CMAKE_CXX_STANDARD}") + set(CMAKE_CXX_STANDARD 11) +endif() +message(STATUS "Building in C++${CMAKE_CXX_STANDARD} mode") +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +#----------------------------------------------------------------------------- + +option(BUILD_TESTING "Build tests" ON) option(WERROR "Add -Werror flag to build (turns warnings into errors)" ON) if(MSVC) - add_definitions(-std=c++11 /W3) + add_compile_options(/W3) add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) else() - add_definitions(-std=c++11 -Wall -Wextra -pedantic -Wsign-compare -Wunused-parameter -Wno-float-equal -Wno-covered-switch-default) + add_compile_options(-Wall -Wextra -pedantic -Wsign-compare -Wunused-parameter -Wno-float-equal -Wno-covered-switch-default) if(WERROR) - add_definitions(-Werror) + add_compile_options(-Werror) endif() endif() @@ -58,15 +61,16 @@ find_package(Protobuf) # #----------------------------------------------------------------------------- message(STATUS "Looking for clang-tidy") -find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-6.0 clang-tidy-5.0) +find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-14 clang-tidy-13 clang-tidy-12 clang-tidy-11) -if(CLANG_TIDY) +if(CLANG_TIDY AND PROTOBUF_FOUND) message(STATUS "Looking for clang-tidy - found ${CLANG_TIDY}") add_custom_target(clang-tidy ${CLANG_TIDY} -p ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/test/*.cpp - ${CMAKE_SOURCE_DIR}/test/t/*/*.cpp + ${CMAKE_SOURCE_DIR}/test/t/*/reader_test_cases.cpp + ${CMAKE_SOURCE_DIR}/test/t/*/writer_test_cases.cpp ${CMAKE_SOURCE_DIR}/test/unit/*.cpp ${CMAKE_SOURCE_DIR}/tools/*.cpp ) @@ -133,13 +137,13 @@ install(DIRECTORY include/protozero DESTINATION include) #----------------------------------------------------------------------------- -enable_testing() - add_subdirectory(doc) +if(BUILD_TESTING) + enable_testing() + add_subdirectory(test) +endif() + add_subdirectory(tools) -add_subdirectory(test) - - #----------------------------------------------------------------------------- diff --git a/FUZZING.md b/FUZZING.md index 44ae0ef16..54547bf89 100644 --- a/FUZZING.md +++ b/FUZZING.md @@ -1,5 +1,5 @@ -To do fuzz testing using [AFL](http://lcamtuf.coredump.cx/afl/) compile with +To do fuzz testing using [AFL](https://lcamtuf.coredump.cx/afl/) compile with the AFL compiler wrappers: mkdir build diff --git a/README.md b/README.md index 63972e0a6..f1c10cc7c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ changing frequently or lazy decoding is not critical for your application then this approach offers no value: just use the C++ API that can be generated with the Google Protobufs `protoc` program. -[![Travis Build Status](https://travis-ci.org/mapbox/protozero.svg?branch=master)](https://travis-ci.org/mapbox/protozero) +[![Travis Build Status](https://travis-ci.com/mapbox/protozero.svg?branch=master)](https://travis-ci.com/mapbox/protozero) [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/mapbox/protozero?svg=true)](https://ci.appveyor.com/project/Mapbox/protozero) [![Coverage Status](https://codecov.io/gh/mapbox/protozero/branch/master/graph/badge.svg)](https://codecov.io/gh/mapbox/protozero) [![Packaging status](https://repology.org/badge/tiny-repos/protozero.svg)](https://repology.org/metapackage/protozero) @@ -56,9 +56,9 @@ You have to have a working knowledge of how * Read the [upgrading instructions](UPGRADING.md) if you are upgrading from an older version of Protozero. -The build process will also build the Doxygen-based reference documentation -if you have [Doxygen](http://www.stack.nl/~dimitri/doxygen/) installed. Then -open `doc/html/index.html` in your browser to read it. +The build process will also build the Doxygen-based reference documentation if +you have Doxygen installed. Then open `doc/html/index.html` in your browser to +read it. ## Endianness @@ -123,7 +123,7 @@ You might have to set `CLANG_TIDY` in CMake config. ## Cppcheck -For extra checks with [Cppcheck](http://cppcheck.sourceforge.net/) you can, +For extra checks with [Cppcheck](https://cppcheck.sourceforge.io/) you can, after the CMake step, call make cppcheck diff --git a/UPGRADING.md b/UPGRADING.md index 9a5db8ee9..bf9040809 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -13,6 +13,11 @@ macro `PROTOZERO_STRICT_API` in which case Protozero will compile without the code used for backwards compatibilty. You will then get compile errors for older API usages. +## Upgrading from *v1.6* to *v1.7* + +* The `pbf_writer` class is now a typedef for `basic_pbf_writer` + If you have forward declared it in your code, it might have to change. + ## Upgrading from *v1.5* to *v1.6.0* * The `data_view` class moved from `types.hpp` into its own header file diff --git a/appveyor.yml b/appveyor.yml index 3c3dc7bce..236a7b72c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,16 +14,20 @@ clone_depth: 1 environment: matrix: + - config: Debug + autocrlf: true + - config: RelWithDebInfo + autocrlf: true + - config: Debug + autocrlf: false + - config: RelWithDebInfo + autocrlf: false + - config: Debug + autocrlf: false + platform: x86 - config: MSYS2 autocrlf: true - - config: Debug - autocrlf: true - - config: RelWithDebInfo - autocrlf: true - - config: Debug - autocrlf: false - - config: RelWithDebInfo - autocrlf: false + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 #----------------------------------------------------------------------------- @@ -36,22 +40,32 @@ init: # halts: "msys2-runtime and catgets are in conflict. Remove catgets?" # See also: https://github.com/Alexpux/MSYS2-packages/issues/1141 install: - - if [%config%]==[MSYS2] ( - C:\msys64\usr\bin\pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade --ask=20 - && C:\msys64\usr\bin\pacman -Rc --noconfirm mingw-w64-x86_64-gcc-libs + - if "%config%"=="MSYS2" ( + set "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%" && + pacman --noconfirm --sync --refresh --refresh --sysupgrade --sysupgrade --ask=20 && + pacman -Rc --noconfirm mingw-w64-x86_64-gcc-libs && + pacman -S --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-doxygen mingw-w64-x86_64-protobuf ) build_script: - - if [%config%]==[MSYS2] ( - build-msys2.bat + - cd c:\projects\protozero + - mkdir build + - cd build + - if "%platform%"=="x64" ( + set vcvarsall_arg=amd64 ) else ( - build-appveyor.bat + set vcvarsall_arg=x86 + ) + - if "%config%"=="MSYS2" ( + cmake .. -LA -G "MSYS Makefiles" && + make VERBOSE=1 + ) else ( + "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall" %vcvarsall_arg% && + cmake .. -LA -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=%config% && + nmake VERBOSE=1 ) -# remove garbage VS messages -# http://help.appveyor.com/discussions/problems/4569-the-target-_convertpdbfiles-listed-in-a-beforetargets-attribute-at-c-does-not-exist-in-the-project-and-will-be-ignored -before_build: - - del "C:\Program Files (x86)\MSBuild\14.0\Microsoft.Common.targets\ImportAfter\Xamarin.Common.targets" - +test_script: + - ctest --output-on-failure #----------------------------------------------------------------------------- diff --git a/bench/data/README.md b/bench/data/README.md index 202f94db1..79e1b5fed 100644 --- a/bench/data/README.md +++ b/bench/data/README.md @@ -2,7 +2,7 @@ mapbox-streets-v6/14/8714/8017.vector.pbf - - http://c.tile.openstreetmap.org/14/8714/8017.png + - https://c.tile.openstreetmap.org/14/8714/8017.png - https://a.tiles.mapbox.com/v4/mapbox.mapbox-streets-v6/14/8714/8017.vector.pbf - https://www.mapbox.com/developers/vector-tiles/mapbox-streets/ @@ -10,4 +10,4 @@ enf-14-4824-6157.vector.pbf - enf.8k273nmi - https://b.tiles.mapbox.com/v4/enf.c3a2de35/14/4824/6157@2x.png - - https://www.mapbox.com/blog/twitter-map-every-tweet/ \ No newline at end of file + - https://www.mapbox.com/blog/twitter-map-every-tweet/ diff --git a/build-appveyor.bat b/build-appveyor.bat deleted file mode 100644 index 76e848c29..000000000 --- a/build-appveyor.bat +++ /dev/null @@ -1,65 +0,0 @@ -@ECHO OFF -SETLOCAL -SET EL=0 - -ECHO ~~~~~~ %~f0 ~~~~~~ - -::show all available env vars -SET -ECHO cmake on AppVeyor -cmake -version - -ECHO activating VS cmd prompt && CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -SET protobuf_sdk=protozero-dep-protobuf-2.6.1.7z -IF EXIST %protobuf_sdk% (ECHO protobuf already downloaded) ELSE (ECHO downloading protobuf ... && powershell Invoke-WebRequest https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/$env:protobuf_sdk -OutFile $pwd\$env:protobuf_sdk) -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -IF EXIST deps\protobuf (ECHO protobuf already extracted) ELSE (CALL 7z x -y %protobuf_sdk% | %windir%\system32\FIND "ing archive") -IF %ERRORLEVEL% NEQ 0 GOTO ERROR -SET PATH=%~dp0deps\protobuf;%PATH% - -IF EXIST build ECHO deleting build dir... && RD /Q /S build -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -MKDIR build -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -CD build -ECHO config^: %config% - -::This will produce lots of LNK4099 warnings which can be ignored. -::Unfortunately they can't be disabled, see -::http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings -SET CMAKE_CMD=cmake .. ^ --LA -G "Visual Studio 14 Win64" - -ECHO calling^: %CMAKE_CMD% -%CMAKE_CMD% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -SET avlogger= -IF /I "%APPVEYOR%"=="True" SET avlogger=/logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - -msbuild protozero.sln ^ -/p:Configuration=%config% ^ -/toolsversion:14.0 ^ -/p:Platform=x64 ^ -/p:PlatformToolset=v140 %avlogger% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -ctest --output-on-failure ^ --C %config% ^ -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -GOTO DONE - -:ERROR -ECHO ~~~~~~ ERROR %~f0 ~~~~~~ -SET EL=%ERRORLEVEL% - -:DONE -IF %EL% NEQ 0 ECHO. && ECHO !!! ERRORLEVEL^: %EL% !!! && ECHO. -ECHO ~~~~~~ DONE %~f0 ~~~~~~ - -EXIT /b %EL% diff --git a/build-local.bat b/build-local.bat deleted file mode 100644 index 62db254ac..000000000 --- a/build-local.bat +++ /dev/null @@ -1,29 +0,0 @@ -@ECHO OFF -SETLOCAL -SET EL=0 - -ECHO =========== %~f0 =========== - -SET VERBOSITY_MSBUILD=diagnostic -IF NOT "%1"=="" SET VERBOSITY_MSBUILD=%1 -SET platform=x64 -SET configuration=Release -CALL build-appveyor.bat %VERBOSITY_MSBUILD% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -SET platform=x86 -SET configuration=Debug -CALL build-appveyor.bat %VERBOSITY_MSBUILD% -IF %ERRORLEVEL% NEQ 0 GOTO ERROR - -GOTO DONE - -:ERROR -ECHO =========== ERROR %~f0 =========== -ECHO ERRORLEVEL^: %ERRORLEVEL% -SET EL=%ERRORLEVEL% - -:DONE -ECHO =========== DONE %~f0 =========== - -EXIT /b %EL% diff --git a/build-msys2.bat b/build-msys2.bat deleted file mode 100644 index 1a02156b5..000000000 --- a/build-msys2.bat +++ /dev/null @@ -1,18 +0,0 @@ -echo "Adding MSYS2 to path..." -SET "PATH=C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH%" -echo %PATH% - -echo "Installing MSYS2 packages..." -bash -lc "pacman -S --needed --noconfirm mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-doxygen mingw-w64-x86_64-protobuf" - -echo "Generating makefiles" -mkdir build -cd build -cmake .. -LA -G "MSYS Makefiles" - -echo "Building" -make VERBOSE=1 - -echo "Testing" -ctest --output-on-failure - diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 148190551..358f1d353 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -1,4 +1,4 @@ -# Doxyfile 1.8.8 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -17,11 +17,11 @@ # Project related configuration options #--------------------------------------------------------------------------- -# This tag specifies the encoding used for all characters in the config file -# that follow. The default is UTF-8 which is also the encoding used for all text -# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv -# for the list of possible encodings. +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,13 +32,13 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "protozero" +PROJECT_NAME = protozero # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @PROTOZERO_VERSION@ +PROJECT_NUMBER = @PROJECT_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -46,10 +46,10 @@ PROJECT_NUMBER = @PROTOZERO_VERSION@ PROJECT_BRIEF = "Minimalistic protocol buffer decoder and encoder in C++." -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. PROJECT_LOGO = @@ -58,9 +58,9 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = "@PROJECT_BINARY_DIR@/doc" +OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/doc -# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where @@ -93,14 +93,22 @@ ALLOW_UNICODE_NAMES = NO OUTPUT_LANGUAGE = English -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the @@ -135,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. @@ -179,6 +187,16 @@ SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus @@ -199,15 +217,23 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO @@ -226,16 +252,15 @@ TAB_SIZE = 4 # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -264,28 +289,40 @@ OPTIMIZE_FOR_FORTRAN = NO OPTIMIZE_OUTPUT_VHDL = NO +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: -# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: -# Fortran. In the later case the parser tries to guess whether the code is fixed -# or free formatted code, this is the default for Fortran type files), VHDL. For -# instance to make doxygen treat .inc files as Fortran files (default is PHP), -# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. # -# Note For files without extension you can use no_extension as a placeholder. +# Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. +# documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. @@ -293,10 +330,19 @@ EXTENSION_MAPPING = MARKDOWN_SUPPORT = YES +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES @@ -318,7 +364,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -336,13 +382,20 @@ SIP_SUPPORT = NO IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first +# tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent @@ -397,11 +450,24 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. @@ -411,35 +477,41 @@ LOOKUP_CACHE_SIZE = 0 EXTRACT_ALL = NO -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES -# This flag is only useful for Objective-C code. When set to YES local methods, +# This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are +# included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. @@ -454,6 +526,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation @@ -464,21 +543,21 @@ HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. +# declarations. If set to NO, these declarations will be included in the +# documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these +# documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. @@ -491,22 +570,36 @@ HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the +# their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. @@ -534,14 +627,14 @@ INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. +# name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that +# name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. @@ -586,27 +679,25 @@ SORT_BY_SCOPE_NAME = NO STRICT_PROTO_MATCHING = NO -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. @@ -631,8 +722,8 @@ ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES @@ -677,7 +768,7 @@ LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. @@ -696,7 +787,7 @@ CITE_BIB_FILES = QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. @@ -704,7 +795,7 @@ QUIET = YES WARNINGS = YES -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. @@ -721,12 +812,22 @@ WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated @@ -750,7 +851,7 @@ WARN_LOGFILE = # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with -# spaces. +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = @PROJECT_SOURCE_DIR@/README.md \ @@ -761,20 +862,29 @@ INPUT = @PROJECT_SOURCE_DIR@/README.md \ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. FILE_PATTERNS = @@ -818,7 +928,10 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = protozero::detail protozero_assert PROTOZERO_*_ENDIAN PROTOZERO_BYTE_ORDER +EXCLUDE_SYMBOLS = protozero::detail \ + protozero_assert \ + PROTOZERO_*_ENDIAN \ + PROTOZERO_BYTE_ORDER # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -860,6 +973,10 @@ IMAGE_PATH = # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. INPUT_FILTER = @@ -869,11 +986,15 @@ INPUT_FILTER = # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for +# INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. @@ -921,7 +1042,7 @@ INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. +# entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO @@ -933,7 +1054,7 @@ REFERENCED_BY_RELATION = NO REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. @@ -953,12 +1074,12 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version +# (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # @@ -980,17 +1101,23 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES -# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the -# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the -# cost of reduced performance. This can be particularly helpful with template -# rich C++ code for which doxygen's built-in parser lacks the necessary type -# information. +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. # Note: The availability of this option depends on whether or not doxygen was -# compiled with the --with-libclang option. +# generated with the -Duse_libclang=ON option for CMake. # The default value is: NO. CLANG_ASSISTED_PARSING = NO +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + # If clang assisted parsing is enabled you can provide the compiler with command # line options that you would normally use when invoking the compiler. Note that # the include paths will already be set by doxygen for the files and directories @@ -999,6 +1126,19 @@ CLANG_ASSISTED_PARSING = NO CLANG_OPTIONS = +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1010,13 +1150,6 @@ CLANG_OPTIONS = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored @@ -1029,7 +1162,7 @@ IGNORE_PREFIX = # Configuration options related to the HTML output #--------------------------------------------------------------------------- -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES @@ -1095,10 +1228,10 @@ HTML_STYLESHEET = # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. +# standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. -# Note: The order of the extra stylesheet files is of importance (e.g. the last -# stylesheet in the list overrules the setting of the previous ones in the +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1115,9 +1248,9 @@ HTML_EXTRA_STYLESHEET = HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to +# will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1146,12 +1279,24 @@ HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1175,13 +1320,14 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1220,8 +1366,8 @@ DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML @@ -1243,28 +1389,28 @@ GENERATE_HTMLHELP = NO CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1296,7 +1442,8 @@ QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1304,8 +1451,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1313,30 +1460,30 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = @@ -1378,7 +1525,7 @@ DISABLE_INDEX = NO # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has @@ -1406,13 +1553,24 @@ ENUM_VALUES_PER_LINE = 4 TREEVIEW_WIDTH = 250 -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML @@ -1422,7 +1580,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1433,9 +1591,15 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. @@ -1446,7 +1610,7 @@ USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. @@ -1461,8 +1625,8 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest @@ -1476,7 +1640,8 @@ MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1504,7 +1669,7 @@ MATHJAX_CODEFILE = SEARCHENGINE = YES # When the SERVER_BASED_SEARCH tag is enabled the search engine will be -# implemented using a web server instead of a web client using Javascript. There +# implemented using a web server instead of a web client using JavaScript. There # are two flavors of web server based searching depending on the EXTERNAL_SEARCH # setting. When disabled, doxygen will generate a PHP script for searching and # an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing @@ -1521,9 +1686,10 @@ SERVER_BASED_SEARCH = NO # external search engine pointed to by the SEARCHENGINE_URL option to obtain the # search results. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1534,10 +1700,11 @@ EXTERNAL_SEARCH = NO # The SEARCHENGINE_URL should point to a search engine hosted by a web server # which will return the search results when EXTERNAL_SEARCH is enabled. # -# Doxygen ships with an example indexer ( doxyindexer) and search engine +# Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: http://xapian.org/). See the section "External Indexing and -# Searching" for details. +# Xapian (see: +# https://xapian.org/). See the section "External Indexing and Searching" for +# details. # This tag requires that the tag SEARCHENGINE is set to YES. SEARCHENGINE_URL = @@ -1572,7 +1739,7 @@ EXTRA_SEARCH_MAPPINGS = # Configuration options related to the LaTeX output #--------------------------------------------------------------------------- -# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO @@ -1588,22 +1755,36 @@ LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. # -# Note that when enabling USE_PDFLATEX this option is only used for generating -# bitmaps for formulas in the HTML output, but not in the Makefile that is -# written to the output directory. -# The default file is: latex. +# Note that when not enabling USE_PDFLATEX the default is latex when enabling +# USE_PDFLATEX the default is pdflatex and when in the later case latex is +# chosen this is overwritten by pdflatex. For specific output languages the +# default can have been set differently, this depends on the implementation of +# the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. +# Note: This tag is used in the Makefile / make.bat. +# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file +# (.tex). # The default file is: makeindex. # This tag requires that the tag GENERATE_LATEX is set to YES. MAKEINDEX_CMD_NAME = makeindex -# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to +# generate index for LaTeX. In case there is no backslash (\) as first character +# it will be automatically added in the LaTeX code. +# Note: This tag is used in the generated output file (.tex). +# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat. +# The default value is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_MAKEINDEX_CMD = makeindex + +# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1621,9 +1802,12 @@ COMPACT_LATEX = NO PAPER_TYPE = a4 # The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names -# that should be included in the LaTeX output. To get the times font for -# instance you can specify -# EXTRA_PACKAGES=times +# that should be included in the LaTeX output. The package can be specified just +# by its name or with the correct syntax as to be used with the LaTeX +# \usepackage command. To get the times font for instance you can specify : +# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times} +# To use the option intlimits with the amsmath package you can specify: +# EXTRA_PACKAGES=[intlimits]{amsmath} # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1638,9 +1822,9 @@ EXTRA_PACKAGES = # Note: Only use a user-defined header if you know what you are doing! The # following commands have a special meaning inside the header: $title, # $datetime, $date, $doxygenversion, $projectname, $projectnumber, -# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string, -# for the replacement values of the other commands the user is refered to -# HTML_HEADER. +# $projectbrief, $projectlogo. Doxygen will replace $title with the empty +# string, for the replacement values of the other commands the user is referred +# to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_HEADER = @@ -1656,6 +1840,17 @@ LATEX_HEADER = LATEX_FOOTER = +# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# LaTeX style sheets that are included after the standard style sheets created +# by doxygen. Using this option one can overrule certain style aspects. Doxygen +# will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_STYLESHEET = + # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output # directory. Note that the files will be copied as-is; there are no commands or @@ -1673,9 +1868,11 @@ LATEX_EXTRA_FILES = PDF_HYPERLINKS = YES -# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate -# the PDF file directly from the LaTeX files. Set this option to YES to get a -# higher quality PDF documentation. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1709,17 +1906,33 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. LATEX_BIB_STYLE = plain +# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_TIMESTAMP = NO + +# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute) +# path from which the emoji images will be read. If a relative path is entered, +# it will be relative to the LATEX_OUTPUT directory. If left blank the +# LATEX_OUTPUT directory will be used. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EMOJI_DIRECTORY = + #--------------------------------------------------------------------------- # Configuration options related to the RTF output #--------------------------------------------------------------------------- -# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The # RTF output is optimized for Word 97 and may not look too pretty with other RTF # readers/editors. # The default value is: NO. @@ -1734,7 +1947,7 @@ GENERATE_RTF = NO RTF_OUTPUT = rtf -# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF # documents. This may be useful for small projects and may help to save some # trees in general. # The default value is: NO. @@ -1754,9 +1967,9 @@ COMPACT_RTF = NO RTF_HYPERLINKS = NO -# Load stylesheet definitions from file. Syntax is similar to doxygen's config -# file, i.e. a series of assignments. You only have to provide replacements, -# missing definitions are set to their default value. +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# configuration file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. # # See also section "Doxygen usage" for information on how to generate the # default style sheet that doxygen normally uses. @@ -1765,17 +1978,27 @@ RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is -# similar to doxygen's config file. A template extensions file can be generated -# using doxygen -e rtf extensionFile. +# similar to doxygen's configuration file. A template extensions file can be +# generated using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. RTF_EXTENSIONS_FILE = +# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code +# with syntax highlighting in the RTF output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_SOURCE_CODE = NO + #--------------------------------------------------------------------------- # Configuration options related to the man page output #--------------------------------------------------------------------------- -# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for # classes and files. # The default value is: NO. @@ -1819,7 +2042,7 @@ MAN_LINKS = NO # Configuration options related to the XML output #--------------------------------------------------------------------------- -# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that # captures the structure of the code including all documentation. # The default value is: NO. @@ -1833,7 +2056,7 @@ GENERATE_XML = NO XML_OUTPUT = xml -# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program # listings (including syntax highlighting and cross-referencing information) to # the XML output. Note that enabling this will significantly increase the size # of the XML output. @@ -1842,11 +2065,18 @@ XML_OUTPUT = xml XML_PROGRAMLISTING = YES +# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include +# namespace members in file scope as well, matching the HTML output. +# The default value is: NO. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_NS_MEMB_FILE_SCOPE = NO + #--------------------------------------------------------------------------- # Configuration options related to the DOCBOOK output #--------------------------------------------------------------------------- -# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files # that can be used to generate PDF. # The default value is: NO. @@ -1860,7 +2090,7 @@ GENERATE_DOCBOOK = NO DOCBOOK_OUTPUT = docbook -# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the +# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the # program listings (including syntax highlighting and cross-referencing # information) to the DOCBOOK output. Note that enabling this will significantly # increase the size of the DOCBOOK output. @@ -1873,10 +2103,10 @@ DOCBOOK_PROGRAMLISTING = NO # Configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- -# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen -# Definitions (see http://autogen.sf.net) file that captures the structure of -# the code including all documentation. Note that this feature is still -# experimental and incomplete at the moment. +# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an +# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures +# the structure of the code including all documentation. Note that this feature +# is still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -1885,7 +2115,7 @@ GENERATE_AUTOGEN_DEF = NO # Configuration options related to the Perl module output #--------------------------------------------------------------------------- -# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module # file that captures the structure of the code including all documentation. # # Note that this feature is still experimental and incomplete at the moment. @@ -1893,7 +2123,7 @@ GENERATE_AUTOGEN_DEF = NO GENERATE_PERLMOD = NO -# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary # Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI # output from the Perl module output. # The default value is: NO. @@ -1901,9 +2131,9 @@ GENERATE_PERLMOD = NO PERLMOD_LATEX = NO -# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely # formatted so it can be parsed by a human reader. This is useful if you want to -# understand what is going on. On the other hand, if this tag is set to NO the +# understand what is going on. On the other hand, if this tag is set to NO, the # size of the Perl module output will be much smaller and Perl will parse it # just the same. # The default value is: YES. @@ -1923,14 +2153,14 @@ PERLMOD_MAKEVAR_PREFIX = # Configuration options related to the preprocessor #--------------------------------------------------------------------------- -# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all # C-preprocessor directives found in the sources and include files. # The default value is: YES. ENABLE_PREPROCESSING = YES -# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names -# in the source code. If set to NO only conditional compilation will be +# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names +# in the source code. If set to NO, only conditional compilation will be # performed. Macro expansion can be done in a controlled way by setting # EXPAND_ONLY_PREDEF to YES. # The default value is: NO. @@ -1946,7 +2176,7 @@ MACRO_EXPANSION = NO EXPAND_ONLY_PREDEF = NO -# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# If the SEARCH_INCLUDES tag is set to YES, the include files in the # INCLUDE_PATH will be searched if a #include is found. # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. @@ -2022,37 +2252,32 @@ TAGFILES = GENERATE_TAGFILE = -# If the ALLEXTERNALS tag is set to YES all external class will be listed in the -# class index. If set to NO only the inherited external classes will be listed. +# If the ALLEXTERNALS tag is set to YES, all external class will be listed in +# the class index. If set to NO, only the inherited external classes will be +# listed. # The default value is: NO. ALLEXTERNALS = NO -# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in -# the modules index. If set to NO, only the current project's groups will be +# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will be # listed. # The default value is: YES. EXTERNAL_GROUPS = YES -# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in # the related pages index. If set to NO, only the current project's pages will # be listed. # The default value is: YES. EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- -# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram # (in HTML and LaTeX) for classes with base or super classes. Setting the tag to # NO turns the diagrams off. Note that this option also works with HAVE_DOT # disabled, but it is recommended to install and use dot, since it yields more @@ -2061,15 +2286,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. @@ -2077,7 +2293,7 @@ MSCGEN_PATH = DIA_PATH = -# If set to YES, the inheritance and collaboration graphs will hide inheritance +# If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. # The default value is: YES. @@ -2150,7 +2366,7 @@ COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES -# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. # The default value is: NO. @@ -2167,10 +2383,32 @@ UML_LOOK = NO # but if the number exceeds 15, the total amount of fields shown is limited to # 10. # Minimum value: 0, maximum value: 100, default value: 10. -# This tag requires that the tag HAVE_DOT is set to YES. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2202,7 +2440,8 @@ INCLUDED_BY_GRAPH = YES # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable call graphs for selected -# functions only using the \callgraph command. +# functions only using the \callgraph command. Disabling a call graph can be +# accomplished by means of the command \hidecallgraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2213,7 +2452,8 @@ CALL_GRAPH = NO # # Note that enabling this option will significantly increase the time of a run. # So in most cases it will be better to enable caller graphs for selected -# functions only using the \callergraph command. +# functions only using the \callergraph command. Disabling a caller graph can be +# accomplished by means of the command \hidecallergraph. # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2236,13 +2476,17 @@ GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images -# generated by dot. +# generated by dot. For an explanation of the image formats see the section +# output formats in the documentation of the dot tool (Graphviz (see: +# http://www.graphviz.org/)). # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). # Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, # png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, -# gif:cairo:gd, gif:gd, gif:gd:gd and svg. +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, +# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and +# png:gdiplus:gdiplus. # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. @@ -2290,10 +2534,19 @@ DIAFILE_DIRS = # PlantUML is not used or called during a preprocessing step. Doxygen will # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -# This tag requires that the tag HAVE_DOT is set to YES. PLANTUML_JAR_PATH = +# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a +# configuration file for plantuml. + +PLANTUML_CFG_FILE = + +# When using plantuml, the specified paths are searched for files specified by +# the !include statement in a plantuml block. + +PLANTUML_INCLUDE_PATH = + # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes # larger than this value, doxygen will truncate the graph, which is visualized @@ -2330,7 +2583,7 @@ MAX_DOT_GRAPH_DEPTH = 0 DOT_TRANSPARENT = NO -# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) support # this, this feature is disabled by default. @@ -2347,9 +2600,11 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/doc/advanced.md b/doc/advanced.md index f5a0e1120..d579538d0 100644 --- a/doc/advanced.md +++ b/doc/advanced.md @@ -269,3 +269,58 @@ still considerably cheaper than decoding the varints. You have to benchmark your use case to see whether the `reserve()` (or whatever you are using the `size()` for) is worth it. + +## Using a different buffer class than std::string + +Normally you are using the `pbf_writer` or `pbf_builder` classes which use a +`std::string` that you supply as their buffer for building the actual protocol +buffers message into. But you can use a different buffer implementation +instead. This might be useful if you want to use a fixed-size buffer for +instance. + +The `pbf_writer` and `pbf_builder` classes are actually only aliases for the +`basic_pbf_writer` and `basic_pbf_builder` template classes: + +```cpp +using pbf_writer = basic_pbf_writer; + +template +using pbf_builder = basic_pbf_builder; +``` + +If you want to use a different buffer type, use the `basic_*` form of the +class and use the buffer class as template parameter. When instantiating the +`basic_pbf_writer` or `basic_pbf_builder`, the only parameter to the +constructor must always be a reference to an object of the buffer class. + +```cpp +some_buffer_class buffer; +basic_pbf_writer writer{buffer}; +``` + +For this to work you must supply template specializations for some static +functions in the `protozero::buffer_customization` struct, see +`buffer_tmpl.hpp` for details. + +Protozero already supports two buffer types: +* `std::string` (to use include `protozero/buffer_string.hpp`) +* `std::vector` (to use include `protozero/buffer_vector.hpp`) + +There is a class `protozero::fixed_size_buffer_adaptor` you can use as adaptor +for any fixed-sized buffer you might have. Include `protozero/buffer_fixed.hpp` +to use it: + +```cpp +#include + +your_buffer_class some_buffer; +protozero::fixed_size_buffer_adaptor buffer_adaptor{some_buffer.data(), some_buffer.size()}; +basic_pbf_writer writer{buffer_adaptor}; +``` + +The buffer adaptor can be initialized with any container if it supports the +`data()` and `size()` member functions: + +```cpp +protozero::fixed_size_buffer_adaptor buffer_adaptor{some_buffer}; +``` diff --git a/include/protozero/basic_pbf_builder.hpp b/include/protozero/basic_pbf_builder.hpp new file mode 100644 index 000000000..0ede726fa --- /dev/null +++ b/include/protozero/basic_pbf_builder.hpp @@ -0,0 +1,266 @@ +#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP +#define PROTOZERO_BASIC_PBF_BUILDER_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file basic_pbf_builder.hpp + * + * @brief Contains the basic_pbf_builder template class. + */ + +#include "basic_pbf_writer.hpp" +#include "types.hpp" + +#include + +namespace protozero { + +/** + * The basic_pbf_builder is used to write PBF formatted messages into a buffer. + * It is based on the basic_pbf_writer class and has all the same methods. The + * difference is that while the pbf_writer class takes an integer tag, + * this template class takes a tag of the template type T. The idea is that + * T will be an enumeration value and this helps reduce the possibility of + * programming errors. + * + * Almost all methods in this class can throw an std::bad_alloc exception if + * the underlying buffer class wants to resize. + * + * Read the tutorial to understand how this class is used. In most cases you + * want to use the pbf_builder class which uses a std::string as buffer type. + */ +template +class basic_pbf_builder : public basic_pbf_writer { + + static_assert(std::is_same::type>::value, + "T must be enum with underlying type protozero::pbf_tag_type"); + +public: + + /// The type of messages this class will build. + using enum_type = T; + + basic_pbf_builder() = default; + + /** + * Create a builder using the given string as a data store. The object + * stores a reference to that string and adds all data to it. The string + * doesn't have to be empty. The pbf_message object will just append data. + */ + explicit basic_pbf_builder(TBuffer& data) noexcept : + basic_pbf_writer{data} { + } + + /** + * Construct a pbf_builder for a submessage from the pbf_message or + * pbf_writer of the parent message. + * + * @param parent_writer The parent pbf_message or pbf_writer + * @param tag Tag of the field that will be written + */ + template + basic_pbf_builder(basic_pbf_writer& parent_writer, P tag) noexcept : + basic_pbf_writer{parent_writer, pbf_tag_type(tag)} { + } + +/// @cond INTERNAL +#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ + void add_##name(T tag, type value) { \ + basic_pbf_writer::add_##name(pbf_tag_type(tag), value); \ + } + + PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float) + PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double) + +#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR +/// @endcond + + /** + * Add "bytes" field to data. + * + * @param tag Tag of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_bytes(T tag, const char* value, std::size_t size) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value, size); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_bytes(T tag, const data_view& value) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_bytes(T tag, const std::string& value) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + + /** + * Add "bytes" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag of the field + * @param value Pointer to zero-delimited value to be written + */ + void add_bytes(T tag, const char* value) { + basic_pbf_writer::add_bytes(pbf_tag_type(tag), value); + } + + /** + * Add "bytes" field to data using vectored input. All the data in the + * 2nd and further arguments is "concatenated" with only a single copy + * into the final buffer. + * + * This will work with objects of any type supporting the data() and + * size() methods like std::string or protozero::data_view. + * + * Example: + * @code + * std::string data1 = "abc"; + * std::string data2 = "xyz"; + * builder.add_bytes_vectored(1, data1, data2); + * @endcode + * + * @tparam Ts List of types supporting data() and size() methods. + * @param tag Tag of the field + * @param values List of objects of types Ts with data to be appended. + */ + template + void add_bytes_vectored(T tag, Ts&&... values) { + basic_pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward(values)...); + } + + /** + * Add "string" field to data. + * + * @param tag Tag of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_string(T tag, const char* value, std::size_t size) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value, size); + } + + /** + * Add "string" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_string(T tag, const data_view& value) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value); + } + + /** + * Add "string" field to data. + * + * @param tag Tag of the field + * @param value Value to be written + */ + void add_string(T tag, const std::string& value) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value); + } + + /** + * Add "string" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag of the field + * @param value Pointer to value to be written + */ + void add_string(T tag, const char* value) { + basic_pbf_writer::add_string(pbf_tag_type(tag), value); + } + + /** + * Add "message" field to data. + * + * @param tag Tag of the field + * @param value Pointer to message to be written + * @param size Length of the message + */ + void add_message(T tag, const char* value, std::size_t size) { + basic_pbf_writer::add_message(pbf_tag_type(tag), value, size); + } + + /** + * Add "message" field to data. + * + * @param tag Tag of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(T tag, const data_view& value) { + basic_pbf_writer::add_message(pbf_tag_type(tag), value); + } + + /** + * Add "message" field to data. + * + * @param tag Tag of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(T tag, const std::string& value) { + basic_pbf_writer::add_message(pbf_tag_type(tag), value); + } + +/// @cond INTERNAL +#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ + template \ + void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ + basic_pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ + } + + PROTOZERO_WRITER_WRAP_ADD_PACKED(bool) + PROTOZERO_WRITER_WRAP_ADD_PACKED(enum) + PROTOZERO_WRITER_WRAP_ADD_PACKED(int32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(int64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32) + PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64) + PROTOZERO_WRITER_WRAP_ADD_PACKED(float) + PROTOZERO_WRITER_WRAP_ADD_PACKED(double) + +#undef PROTOZERO_WRITER_WRAP_ADD_PACKED +/// @endcond + +}; // class basic_pbf_builder + +} // end namespace protozero + +#endif // PROTOZERO_BASIC_PBF_BUILDER_HPP diff --git a/include/protozero/basic_pbf_writer.hpp b/include/protozero/basic_pbf_writer.hpp new file mode 100644 index 000000000..f167c4d1d --- /dev/null +++ b/include/protozero/basic_pbf_writer.hpp @@ -0,0 +1,1054 @@ +#ifndef PROTOZERO_BASIC_PBF_WRITER_HPP +#define PROTOZERO_BASIC_PBF_WRITER_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file basic_pbf_writer.hpp + * + * @brief Contains the basic_pbf_writer template class. + */ + +#include "buffer_tmpl.hpp" +#include "config.hpp" +#include "data_view.hpp" +#include "types.hpp" +#include "varint.hpp" + +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace protozero { + +namespace detail { + + template class packed_field_varint; + template class packed_field_svarint; + template class packed_field_fixed; + +} // end namespace detail + +/** + * The basic_pbf_writer is used to write PBF formatted messages into a buffer. + * + * This uses TBuffer as the type for the underlaying buffer. In typical uses + * this is std::string, but you can use a different type that must support + * the right interface. Please see the documentation for details. + * + * Almost all methods in this class can throw an std::bad_alloc exception if + * the underlying buffer class wants to resize. + */ +template +class basic_pbf_writer { + + // A pointer to a buffer holding the data already written to the PBF + // message. For default constructed writers or writers that have been + // rolled back, this is a nullptr. + TBuffer* m_data = nullptr; + + // A pointer to a parent writer object if this is a submessage. If this + // is a top-level writer, it is a nullptr. + basic_pbf_writer* m_parent_writer = nullptr; + + // This is usually 0. If there is an open submessage, this is set in the + // parent to the rollback position, ie. the last position before the + // submessage was started. This is the position where the header of the + // submessage starts. + std::size_t m_rollback_pos = 0; + + // This is usually 0. If there is an open submessage, this is set in the + // parent to the position where the data of the submessage is written to. + std::size_t m_pos = 0; + + void add_varint(uint64_t value) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + add_varint_to_buffer(m_data, value); + } + + void add_field(pbf_tag_type tag, pbf_wire_type type) { + protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1U << 29U) - 1))) && "tag out of range"); + const uint32_t b = (tag << 3U) | uint32_t(type); + add_varint(b); + } + + void add_tagged_varint(pbf_tag_type tag, uint64_t value) { + add_field(tag, pbf_wire_type::varint); + add_varint(value); + } + + template + void add_fixed(T value) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); +#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN + byteswap_inplace(&value); +#endif + buffer_customization::append(m_data, reinterpret_cast(&value), sizeof(T)); + } + + template + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag /*unused*/) { + if (first == last) { + return; + } + + basic_pbf_writer sw{*this, tag}; + + while (first != last) { + sw.add_fixed(*first++); + } + } + + template + void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag /*unused*/) { + if (first == last) { + return; + } + + const auto length = std::distance(first, last); + add_length_varint(tag, sizeof(T) * pbf_length_type(length)); + reserve(sizeof(T) * std::size_t(length)); + + while (first != last) { + add_fixed(*first++); + } + } + + template + void add_packed_varint(pbf_tag_type tag, It first, It last) { + if (first == last) { + return; + } + + basic_pbf_writer sw{*this, tag}; + + while (first != last) { + sw.add_varint(uint64_t(*first++)); + } + } + + template + void add_packed_svarint(pbf_tag_type tag, It first, It last) { + if (first == last) { + return; + } + + basic_pbf_writer sw{*this, tag}; + + while (first != last) { + sw.add_varint(encode_zigzag64(*first++)); + } + } + + // The number of bytes to reserve for the varint holding the length of + // a length-delimited field. The length has to fit into pbf_length_type, + // and a varint needs 8 bit for every 7 bit. + enum : int { + reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1 + }; + + // If m_rollpack_pos is set to this special value, it means that when + // the submessage is closed, nothing needs to be done, because the length + // of the submessage has already been written correctly. + enum : std::size_t { + size_is_known = std::numeric_limits::max() + }; + + void open_submessage(pbf_tag_type tag, std::size_t size) { + protozero_assert(m_pos == 0); + protozero_assert(m_data); + if (size == 0) { + m_rollback_pos = buffer_customization::size(m_data); + add_field(tag, pbf_wire_type::length_delimited); + buffer_customization::append_zeros(m_data, std::size_t(reserve_bytes)); + } else { + m_rollback_pos = size_is_known; + add_length_varint(tag, pbf_length_type(size)); + reserve(size); + } + m_pos = buffer_customization::size(m_data); + } + + void rollback_submessage() { + protozero_assert(m_pos != 0); + protozero_assert(m_rollback_pos != size_is_known); + protozero_assert(m_data); + buffer_customization::resize(m_data, m_rollback_pos); + m_pos = 0; + } + + void commit_submessage() { + protozero_assert(m_pos != 0); + protozero_assert(m_rollback_pos != size_is_known); + protozero_assert(m_data); + const auto length = pbf_length_type(buffer_customization::size(m_data) - m_pos); + + protozero_assert(buffer_customization::size(m_data) >= m_pos - reserve_bytes); + const auto n = add_varint_to_buffer(buffer_customization::at_pos(m_data, m_pos - reserve_bytes), length); + + buffer_customization::erase_range(m_data, m_pos - reserve_bytes + n, m_pos); + m_pos = 0; + } + + void close_submessage() { + protozero_assert(m_data); + if (m_pos == 0 || m_rollback_pos == size_is_known) { + return; + } + if (buffer_customization::size(m_data) - m_pos == 0) { + rollback_submessage(); + } else { + commit_submessage(); + } + } + + void add_length_varint(pbf_tag_type tag, pbf_length_type length) { + add_field(tag, pbf_wire_type::length_delimited); + add_varint(length); + } + +public: + + /** + * Create a writer using the specified buffer as a data store. The + * basic_pbf_writer stores a pointer to that buffer and adds all data to + * it. The buffer doesn't have to be empty. The basic_pbf_writer will just + * append data. + */ + explicit basic_pbf_writer(TBuffer& buffer) noexcept : + m_data{&buffer} { + } + + /** + * Create a writer without a data store. In this form the writer can not + * be used! + */ + basic_pbf_writer() noexcept = default; + + /** + * Construct a basic_pbf_writer for a submessage from the basic_pbf_writer + * of the parent message. + * + * @param parent_writer The basic_pbf_writer + * @param tag Tag (field number) of the field that will be written + * @param size Optional size of the submessage in bytes (use 0 for unknown). + * Setting this allows some optimizations but is only possible in + * a few very specific cases. + */ + basic_pbf_writer(basic_pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size = 0) : + m_data{parent_writer.m_data}, + m_parent_writer{&parent_writer} { + m_parent_writer->open_submessage(tag, size); + } + + /// A basic_pbf_writer object can not be copied + basic_pbf_writer(const basic_pbf_writer&) = delete; + + /// A basic_pbf_writer object can not be copied + basic_pbf_writer& operator=(const basic_pbf_writer&) = delete; + + /** + * A basic_pbf_writer object can be moved. After this the other + * basic_pbf_writer will be invalid. + */ + basic_pbf_writer(basic_pbf_writer&& other) noexcept : + m_data{other.m_data}, + m_parent_writer{other.m_parent_writer}, + m_rollback_pos{other.m_rollback_pos}, + m_pos{other.m_pos} { + other.m_data = nullptr; + other.m_parent_writer = nullptr; + other.m_rollback_pos = 0; + other.m_pos = 0; + } + + /** + * A basic_pbf_writer object can be moved. After this the other + * basic_pbf_writer will be invalid. + */ + basic_pbf_writer& operator=(basic_pbf_writer&& other) noexcept { + m_data = other.m_data; + m_parent_writer = other.m_parent_writer; + m_rollback_pos = other.m_rollback_pos; + m_pos = other.m_pos; + other.m_data = nullptr; + other.m_parent_writer = nullptr; + other.m_rollback_pos = 0; + other.m_pos = 0; + return *this; + } + + ~basic_pbf_writer() noexcept { + try { + if (m_parent_writer != nullptr) { + m_parent_writer->close_submessage(); + } + } catch (...) { + // This try/catch is used to make the destructor formally noexcept. + // close_submessage() is not noexcept, but will not throw the way + // it is called here, so we are good. But to be paranoid, call... + std::terminate(); + } + } + + /** + * Check if this writer is valid. A writer is invalid if it was default + * constructed, moved from, or if commit() has been called on it. + * Otherwise it is valid. + */ + bool valid() const noexcept { + return m_data != nullptr; + } + + /** + * Swap the contents of this object with the other. + * + * @param other Other object to swap data with. + */ + void swap(basic_pbf_writer& other) noexcept { + using std::swap; + swap(m_data, other.m_data); + swap(m_parent_writer, other.m_parent_writer); + swap(m_rollback_pos, other.m_rollback_pos); + swap(m_pos, other.m_pos); + } + + /** + * Reserve size bytes in the underlying message store in addition to + * whatever the message store already holds. So unlike + * the `std::string::reserve()` method this is not an absolute size, + * but additional memory that should be reserved. + * + * @param size Number of bytes to reserve in underlying message store. + */ + void reserve(std::size_t size) { + protozero_assert(m_data); + buffer_customization::reserve_additional(m_data, size); + } + + /** + * Commit this submessage. This does the same as when the basic_pbf_writer + * goes out of scope and is destructed. + * + * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the + * basic_pbf_writer constructor taking a parent message. + * @post The basic_pbf_writer is invalid and can't be used any more. + */ + void commit() { + protozero_assert(m_parent_writer && "you can't call commit() on a basic_pbf_writer without a parent"); + protozero_assert(m_pos == 0 && "you can't call commit() on a basic_pbf_writer that has an open nested submessage"); + m_parent_writer->close_submessage(); + m_parent_writer = nullptr; + m_data = nullptr; + } + + /** + * Cancel writing of this submessage. The complete submessage will be + * removed as if it was never created and no fields were added. + * + * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the + * basic_pbf_writer constructor taking a parent message. + * @post The basic_pbf_writer is invalid and can't be used any more. + */ + void rollback() { + protozero_assert(m_parent_writer && "you can't call rollback() on a basic_pbf_writer without a parent"); + protozero_assert(m_pos == 0 && "you can't call rollback() on a basic_pbf_writer that has an open nested submessage"); + m_parent_writer->rollback_submessage(); + m_parent_writer = nullptr; + m_data = nullptr; + } + + ///@{ + /** + * @name Scalar field writer functions + */ + + /** + * Add "bool" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_bool(pbf_tag_type tag, bool value) { + add_field(tag, pbf_wire_type::varint); + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + m_data->push_back(char(value)); + } + + /** + * Add "enum" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_enum(pbf_tag_type tag, int32_t value) { + add_tagged_varint(tag, uint64_t(value)); + } + + /** + * Add "int32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_int32(pbf_tag_type tag, int32_t value) { + add_tagged_varint(tag, uint64_t(value)); + } + + /** + * Add "sint32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sint32(pbf_tag_type tag, int32_t value) { + add_tagged_varint(tag, encode_zigzag32(value)); + } + + /** + * Add "uint32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_uint32(pbf_tag_type tag, uint32_t value) { + add_tagged_varint(tag, value); + } + + /** + * Add "int64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_int64(pbf_tag_type tag, int64_t value) { + add_tagged_varint(tag, uint64_t(value)); + } + + /** + * Add "sint64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sint64(pbf_tag_type tag, int64_t value) { + add_tagged_varint(tag, encode_zigzag64(value)); + } + + /** + * Add "uint64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_uint64(pbf_tag_type tag, uint64_t value) { + add_tagged_varint(tag, value); + } + + /** + * Add "fixed32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_fixed32(pbf_tag_type tag, uint32_t value) { + add_field(tag, pbf_wire_type::fixed32); + add_fixed(value); + } + + /** + * Add "sfixed32" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sfixed32(pbf_tag_type tag, int32_t value) { + add_field(tag, pbf_wire_type::fixed32); + add_fixed(value); + } + + /** + * Add "fixed64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_fixed64(pbf_tag_type tag, uint64_t value) { + add_field(tag, pbf_wire_type::fixed64); + add_fixed(value); + } + + /** + * Add "sfixed64" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_sfixed64(pbf_tag_type tag, int64_t value) { + add_field(tag, pbf_wire_type::fixed64); + add_fixed(value); + } + + /** + * Add "float" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_float(pbf_tag_type tag, float value) { + add_field(tag, pbf_wire_type::fixed32); + add_fixed(value); + } + + /** + * Add "double" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_double(pbf_tag_type tag, double value) { + add_field(tag, pbf_wire_type::fixed64); + add_fixed(value); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag (field number) of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + protozero_assert(size <= std::numeric_limits::max()); + add_length_varint(tag, pbf_length_type(size)); + buffer_customization::append(m_data, value, size); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_bytes(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "bytes" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_bytes(pbf_tag_type tag, const std::string& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "bytes" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag (field number) of the field + * @param value Pointer to zero-delimited value to be written + */ + void add_bytes(pbf_tag_type tag, const char* value) { + add_bytes(tag, value, std::strlen(value)); + } + + /** + * Add "bytes" field to data using vectored input. All the data in the + * 2nd and further arguments is "concatenated" with only a single copy + * into the final buffer. + * + * This will work with objects of any type supporting the data() and + * size() methods like std::string or protozero::data_view. + * + * Example: + * @code + * std::string data1 = "abc"; + * std::string data2 = "xyz"; + * writer.add_bytes_vectored(1, data1, data2); + * @endcode + * + * @tparam Ts List of types supporting data() and size() methods. + * @param tag Tag (field number) of the field + * @param values List of objects of types Ts with data to be appended. + */ + template + void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) { + protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage"); + protozero_assert(m_data); + size_t sum_size = 0; + (void)std::initializer_list{sum_size += values.size()...}; + protozero_assert(sum_size <= std::numeric_limits::max()); + add_length_varint(tag, pbf_length_type(sum_size)); + buffer_customization::reserve_additional(m_data, sum_size); + (void)std::initializer_list{(buffer_customization::append(m_data, values.data(), values.size()), 0)...}; + } + + /** + * Add "string" field to data. + * + * @param tag Tag (field number) of the field + * @param value Pointer to value to be written + * @param size Number of bytes to be written + */ + void add_string(pbf_tag_type tag, const char* value, std::size_t size) { + add_bytes(tag, value, size); + } + + /** + * Add "string" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_string(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "string" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written + */ + void add_string(pbf_tag_type tag, const std::string& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "string" field to data. Bytes from the value are written until + * a null byte is encountered. The null byte is not added. + * + * @param tag Tag (field number) of the field + * @param value Pointer to value to be written + */ + void add_string(pbf_tag_type tag, const char* value) { + add_bytes(tag, value, std::strlen(value)); + } + + /** + * Add "message" field to data. + * + * @param tag Tag (field number) of the field + * @param value Pointer to message to be written + * @param size Length of the message + */ + void add_message(pbf_tag_type tag, const char* value, std::size_t size) { + add_bytes(tag, value, size); + } + + /** + * Add "message" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(pbf_tag_type tag, const data_view& value) { + add_bytes(tag, value.data(), value.size()); + } + + /** + * Add "message" field to data. + * + * @param tag Tag (field number) of the field + * @param value Value to be written. The value must be a complete message. + */ + void add_message(pbf_tag_type tag, const std::string& value) { + add_bytes(tag, value.data(), value.size()); + } + + ///@} + + ///@{ + /** + * @name Repeated packed field writer functions + */ + + /** + * Add "repeated packed bool" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to bool. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add "repeated packed enum" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int32_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add "repeated packed int32" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int32_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add "repeated packed sint32" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int32_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_svarint(tag, first, last); + } + + /** + * Add "repeated packed uint32" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to uint32_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add "repeated packed int64" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int64_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add "repeated packed sint64" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int64_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_svarint(tag, first, last); + } + + /** + * Add "repeated packed uint64" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to uint64_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_varint(tag, first, last); + } + + /** + * Add a "repeated packed" fixed-size field to data. The following + * fixed-size fields are available: + * + * uint32_t -> repeated packed fixed32 + * int32_t -> repeated packed sfixed32 + * uint64_t -> repeated packed fixed64 + * int64_t -> repeated packed sfixed64 + * double -> repeated packed double + * float -> repeated packed float + * + * @tparam ValueType One of the following types: (u)int32/64_t, double, float. + * @tparam InputIterator A type satisfying the InputIterator concept. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_fixed(pbf_tag_type tag, InputIterator first, InputIterator last) { + static_assert(std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, "Only some types are allowed"); + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed fixed32" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to uint32_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed sfixed32" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int32_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed fixed64" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to uint64_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed sfixed64" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to int64_t. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed float" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to float. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + /** + * Add "repeated packed double" field to data. + * + * @tparam InputIterator A type satisfying the InputIterator concept. + * Dereferencing the iterator must yield a type assignable to double. + * @param tag Tag (field number) of the field + * @param first Iterator pointing to the beginning of the data + * @param last Iterator pointing one past the end of data + */ + template + void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { + add_packed_fixed(tag, first, last, + typename std::iterator_traits::iterator_category{}); + } + + ///@} + + template friend class detail::packed_field_varint; + template friend class detail::packed_field_svarint; + template friend class detail::packed_field_fixed; + +}; // class basic_pbf_writer + +/** + * Swap two basic_pbf_writer objects. + * + * @param lhs First object. + * @param rhs Second object. + */ +template +inline void swap(basic_pbf_writer& lhs, basic_pbf_writer& rhs) noexcept { + lhs.swap(rhs); +} + +namespace detail { + + template + class packed_field { + + basic_pbf_writer m_writer{}; + + public: + + packed_field(const packed_field&) = delete; + packed_field& operator=(const packed_field&) = delete; + + packed_field(packed_field&&) noexcept = default; + packed_field& operator=(packed_field&&) noexcept = default; + + packed_field() = default; + + packed_field(basic_pbf_writer& parent_writer, pbf_tag_type tag) : + m_writer{parent_writer, tag} { + } + + packed_field(basic_pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : + m_writer{parent_writer, tag, size} { + } + + ~packed_field() noexcept = default; + + bool valid() const noexcept { + return m_writer.valid(); + } + + void commit() { + m_writer.commit(); + } + + void rollback() { + m_writer.rollback(); + } + + basic_pbf_writer& writer() noexcept { + return m_writer; + } + + }; // class packed_field + + template + class packed_field_fixed : public packed_field { + + public: + + packed_field_fixed() : + packed_field{} { + } + + template + packed_field_fixed(basic_pbf_writer& parent_writer, P tag) : + packed_field{parent_writer, static_cast(tag)} { + } + + template + packed_field_fixed(basic_pbf_writer& parent_writer, P tag, std::size_t size) : + packed_field{parent_writer, static_cast(tag), size * sizeof(T)} { + } + + void add_element(T value) { + this->writer().template add_fixed(value); + } + + }; // class packed_field_fixed + + template + class packed_field_varint : public packed_field { + + public: + + packed_field_varint() : + packed_field{} { + } + + template + packed_field_varint(basic_pbf_writer& parent_writer, P tag) : + packed_field{parent_writer, static_cast(tag)} { + } + + void add_element(T value) { + this->writer().add_varint(uint64_t(value)); + } + + }; // class packed_field_varint + + template + class packed_field_svarint : public packed_field { + + public: + + packed_field_svarint() : + packed_field{} { + } + + template + packed_field_svarint(basic_pbf_writer& parent_writer, P tag) : + packed_field{parent_writer, static_cast(tag)} { + } + + void add_element(T value) { + this->writer().add_varint(encode_zigzag64(value)); + } + + }; // class packed_field_svarint + +} // end namespace detail + +} // end namespace protozero + +#endif // PROTOZERO_BASIC_PBF_WRITER_HPP diff --git a/include/protozero/buffer_fixed.hpp b/include/protozero/buffer_fixed.hpp new file mode 100644 index 000000000..b2e6d1d27 --- /dev/null +++ b/include/protozero/buffer_fixed.hpp @@ -0,0 +1,222 @@ +#ifndef PROTOZERO_BUFFER_FIXED_HPP +#define PROTOZERO_BUFFER_FIXED_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file buffer_fixed.hpp + * + * @brief Contains the fixed_size_buffer_adaptor class. + */ + +#include "buffer_tmpl.hpp" +#include "config.hpp" + +#include +#include +#include +#include + +namespace protozero { + +/** + * This class can be used instead of std::string if you want to create a + * vector tile in a fixed-size buffer. Any operation that needs more space + * than is available will fail with a std::length_error exception. + */ +class fixed_size_buffer_adaptor { + + char* m_data; + std::size_t m_capacity; + std::size_t m_size = 0; + +public: + + /// @cond usual container typedefs not documented + + using size_type = std::size_t; + + using value_type = char; + using reference = value_type&; + using const_reference = const value_type&; + using pointer = value_type*; + using const_pointer = const value_type*; + + using iterator = pointer; + using const_iterator = const_pointer; + + /// @endcond + + /** + * Constructor. + * + * @param data Pointer to some memory allocated for the buffer. + * @param capacity Number of bytes available. + */ + fixed_size_buffer_adaptor(char* data, std::size_t capacity) noexcept : + m_data(data), + m_capacity(capacity) { + } + + /** + * Constructor. + * + * @param container Some container class supporting the member functions + * data() and size(). + */ + template + explicit fixed_size_buffer_adaptor(T& container) : + m_data(container.data()), + m_capacity(container.size()) { + } + + /// Returns a pointer to the data in the buffer. + const char* data() const noexcept { + return m_data; + } + + /// Returns a pointer to the data in the buffer. + char* data() noexcept { + return m_data; + } + + /// The capacity this buffer was created with. + std::size_t capacity() const noexcept { + return m_capacity; + } + + /// The number of bytes used in the buffer. Always <= capacity(). + std::size_t size() const noexcept { + return m_size; + } + + /// Return iterator to beginning of data. + char* begin() noexcept { + return m_data; + } + + /// Return iterator to beginning of data. + const char* begin() const noexcept { + return m_data; + } + + /// Return iterator to beginning of data. + const char* cbegin() const noexcept { + return m_data; + } + + /// Return iterator to end of data. + char* end() noexcept { + return m_data + m_size; + } + + /// Return iterator to end of data. + const char* end() const noexcept { + return m_data + m_size; + } + + /// Return iterator to end of data. + const char* cend() const noexcept { + return m_data + m_size; + } + +/// @cond INTERNAL + + // Do not rely on anything beyond this point + + void append(const char* data, std::size_t count) { + if (m_size + count > m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + std::copy_n(data, count, m_data + m_size); + m_size += count; + } + + void append_zeros(std::size_t count) { + if (m_size + count > m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + std::fill_n(m_data + m_size, count, '\0'); + m_size += count; + } + + void resize(std::size_t size) { + protozero_assert(size < m_size); + if (size > m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + m_size = size; + } + + void erase_range(std::size_t from, std::size_t to) { + protozero_assert(from <= m_size); + protozero_assert(to <= m_size); + protozero_assert(from < to); + std::copy(m_data + to, m_data + m_size, m_data + from); + m_size -= (to - from); + } + + char* at_pos(std::size_t pos) { + protozero_assert(pos <= m_size); + return m_data + pos; + } + + void push_back(char ch) { + if (m_size >= m_capacity) { + throw std::length_error{"fixed size data store exhausted"}; + } + m_data[m_size++] = ch; + } +/// @endcond + +}; // class fixed_size_buffer_adaptor + +/// @cond INTERNAL +template <> +struct buffer_customization { + + static std::size_t size(const fixed_size_buffer_adaptor* buffer) noexcept { + return buffer->size(); + } + + static void append(fixed_size_buffer_adaptor* buffer, const char* data, std::size_t count) { + buffer->append(data, count); + } + + static void append_zeros(fixed_size_buffer_adaptor* buffer, std::size_t count) { + buffer->append_zeros(count); + } + + static void resize(fixed_size_buffer_adaptor* buffer, std::size_t size) { + buffer->resize(size); + } + + static void reserve_additional(fixed_size_buffer_adaptor* /*buffer*/, std::size_t /*size*/) { + /* nothing to be done for fixed-size buffers */ + } + + static void erase_range(fixed_size_buffer_adaptor* buffer, std::size_t from, std::size_t to) { + buffer->erase_range(from, to); + } + + static char* at_pos(fixed_size_buffer_adaptor* buffer, std::size_t pos) { + return buffer->at_pos(pos); + } + + static void push_back(fixed_size_buffer_adaptor* buffer, char ch) { + buffer->push_back(ch); + } + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_FIXED_HPP diff --git a/include/protozero/buffer_string.hpp b/include/protozero/buffer_string.hpp new file mode 100644 index 000000000..02e8ad25b --- /dev/null +++ b/include/protozero/buffer_string.hpp @@ -0,0 +1,78 @@ +#ifndef PROTOZERO_BUFFER_STRING_HPP +#define PROTOZERO_BUFFER_STRING_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file buffer_string.hpp + * + * @brief Contains the customization points for buffer implementation based + * on std::string + */ + +#include "buffer_tmpl.hpp" +#include "config.hpp" + +#include +#include +#include + +namespace protozero { + +// Implementation of buffer customizations points for std::string + +/// @cond INTERNAL +template <> +struct buffer_customization { + + static std::size_t size(const std::string* buffer) noexcept { + return buffer->size(); + } + + static void append(std::string* buffer, const char* data, std::size_t count) { + buffer->append(data, count); + } + + static void append_zeros(std::string* buffer, std::size_t count) { + buffer->append(count, '\0'); + } + + static void resize(std::string* buffer, std::size_t size) { + protozero_assert(size < buffer->size()); + buffer->resize(size); + } + + static void reserve_additional(std::string* buffer, std::size_t size) { + buffer->reserve(buffer->size() + size); + } + + static void erase_range(std::string* buffer, std::size_t from, std::size_t to) { + protozero_assert(from <= buffer->size()); + protozero_assert(to <= buffer->size()); + protozero_assert(from <= to); + buffer->erase(std::next(buffer->begin(), static_cast(from)), + std::next(buffer->begin(), static_cast(to))); + } + + static char* at_pos(std::string* buffer, std::size_t pos) { + protozero_assert(pos <= buffer->size()); + return (&*buffer->begin()) + pos; + } + + static void push_back(std::string* buffer, char ch) { + buffer->push_back(ch); + } + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_STRING_HPP diff --git a/include/protozero/buffer_tmpl.hpp b/include/protozero/buffer_tmpl.hpp new file mode 100644 index 000000000..ac223996d --- /dev/null +++ b/include/protozero/buffer_tmpl.hpp @@ -0,0 +1,113 @@ +#ifndef PROTOZERO_BUFFER_TMPL_HPP +#define PROTOZERO_BUFFER_TMPL_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file buffer_tmpl.hpp + * + * @brief Contains the customization points for buffer implementations. + */ + +#include +#include +#include + +namespace protozero { + +// Implementation of buffer customizations points for std::string + +/// @cond INTERNAL +template +struct buffer_customization { + + /** + * Get the number of bytes currently used in the buffer. + * + * @param buffer Pointer to the buffer. + * @returns number of bytes used in the buffer. + */ + static std::size_t size(const std::string* buffer); + + /** + * Append count bytes from data to the buffer. + * + * @param buffer Pointer to the buffer. + * @param data Pointer to the data. + * @param count Number of bytes to be added to the buffer. + */ + static void append(std::string* buffer, const char* data, std::size_t count); + + /** + * Append count zero bytes to the buffer. + * + * @param buffer Pointer to the buffer. + * @param count Number of bytes to be added to the buffer. + */ + static void append_zeros(std::string* buffer, std::size_t count); + + /** + * Shrink the buffer to the specified size. The new size will always be + * smaller than the current size. + * + * @param buffer Pointer to the buffer. + * @param size New size of the buffer. + * + * @pre size < current size of buffer + */ + static void resize(std::string* buffer, std::size_t size); + + /** + * Reserve an additional size bytes for use in the buffer. This is used for + * variable-sized buffers to tell the buffer implementation that soon more + * memory will be used. The implementation can ignore this. + * + * @param buffer Pointer to the buffer. + * @param size Number of bytes to reserve. + */ + static void reserve_additional(std::string* buffer, std::size_t size); + + /** + * Delete data from the buffer. This must move back the data after the + * part being deleted and resize the buffer accordingly. + * + * @param buffer Pointer to the buffer. + * @param from Offset into the buffer where we want to erase from. + * @param to Offset into the buffer one past the last byte we want to erase. + * + * @pre from, to <= size of the buffer, from < to + */ + static void erase_range(std::string* buffer, std::size_t from, std::size_t to); + + /** + * Return a pointer to the memory at the specified position in the buffer. + * + * @param buffer Pointer to the buffer. + * @param pos The position in the buffer. + * @returns pointer to the memory in the buffer at the specified position. + * + * @pre pos <= size of the buffer + */ + static char* at_pos(std::string* buffer, std::size_t pos); + + /** + * Add a char to the buffer incrementing the number of chars in the buffer. + * + * @param buffer Pointer to the buffer. + * @param ch The character to add. + */ + static void push_back(std::string* buffer, char ch); + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_TMPL_HPP diff --git a/include/protozero/buffer_vector.hpp b/include/protozero/buffer_vector.hpp new file mode 100644 index 000000000..c163300c5 --- /dev/null +++ b/include/protozero/buffer_vector.hpp @@ -0,0 +1,78 @@ +#ifndef PROTOZERO_BUFFER_VECTOR_HPP +#define PROTOZERO_BUFFER_VECTOR_HPP + +/***************************************************************************** + +protozero - Minimalistic protocol buffer decoder and encoder in C++. + +This file is from https://github.com/mapbox/protozero where you can find more +documentation. + +*****************************************************************************/ + +/** + * @file buffer_vector.hpp + * + * @brief Contains the customization points for buffer implementation based + * on std::vector + */ + +#include "buffer_tmpl.hpp" +#include "config.hpp" + +#include +#include +#include + +namespace protozero { + +// Implementation of buffer customizations points for std::vector + +/// @cond INTERNAL +template <> +struct buffer_customization> { + + static std::size_t size(const std::vector* buffer) noexcept { + return buffer->size(); + } + + static void append(std::vector* buffer, const char* data, std::size_t count) { + buffer->insert(buffer->end(), data, data + count); + } + + static void append_zeros(std::vector* buffer, std::size_t count) { + buffer->insert(buffer->end(), count, '\0'); + } + + static void resize(std::vector* buffer, std::size_t size) { + protozero_assert(size < buffer->size()); + buffer->resize(size); + } + + static void reserve_additional(std::vector* buffer, std::size_t size) { + buffer->reserve(buffer->size() + size); + } + + static void erase_range(std::vector* buffer, std::size_t from, std::size_t to) { + protozero_assert(from <= buffer->size()); + protozero_assert(to <= buffer->size()); + protozero_assert(from <= to); + buffer->erase(std::next(buffer->begin(), static_cast(from)), + std::next(buffer->begin(), static_cast(to))); + } + + static char* at_pos(std::vector* buffer, std::size_t pos) { + protozero_assert(pos <= buffer->size()); + return (&*buffer->begin()) + pos; + } + + static void push_back(std::vector* buffer, char ch) { + buffer->push_back(ch); + } + +}; +/// @endcond + +} // namespace protozero + +#endif // PROTOZERO_BUFFER_VECTOR_HPP diff --git a/include/protozero/byteswap.hpp b/include/protozero/byteswap.hpp index fd8a83a68..75cae6910 100644 --- a/include/protozero/byteswap.hpp +++ b/include/protozero/byteswap.hpp @@ -16,9 +16,10 @@ documentation. * @brief Contains functions to swap bytes in values (for different endianness). */ -#include +#include "config.hpp" #include +#include namespace protozero { namespace detail { @@ -27,10 +28,10 @@ inline uint32_t byteswap_impl(uint32_t value) noexcept { #ifdef PROTOZERO_USE_BUILTIN_BSWAP return __builtin_bswap32(value); #else - return ((value & 0xff000000) >> 24) | - ((value & 0x00ff0000) >> 8) | - ((value & 0x0000ff00) << 8) | - ((value & 0x000000ff) << 24); + return ((value & 0xff000000U) >> 24U) | + ((value & 0x00ff0000U) >> 8U) | + ((value & 0x0000ff00U) << 8U) | + ((value & 0x000000ffU) << 24U); #endif } @@ -38,46 +39,70 @@ inline uint64_t byteswap_impl(uint64_t value) noexcept { #ifdef PROTOZERO_USE_BUILTIN_BSWAP return __builtin_bswap64(value); #else - return ((value & 0xff00000000000000ULL) >> 56) | - ((value & 0x00ff000000000000ULL) >> 40) | - ((value & 0x0000ff0000000000ULL) >> 24) | - ((value & 0x000000ff00000000ULL) >> 8) | - ((value & 0x00000000ff000000ULL) << 8) | - ((value & 0x0000000000ff0000ULL) << 24) | - ((value & 0x000000000000ff00ULL) << 40) | - ((value & 0x00000000000000ffULL) << 56); + return ((value & 0xff00000000000000ULL) >> 56U) | + ((value & 0x00ff000000000000ULL) >> 40U) | + ((value & 0x0000ff0000000000ULL) >> 24U) | + ((value & 0x000000ff00000000ULL) >> 8U) | + ((value & 0x00000000ff000000ULL) << 8U) | + ((value & 0x0000000000ff0000ULL) << 24U) | + ((value & 0x000000000000ff00ULL) << 40U) | + ((value & 0x00000000000000ffULL) << 56U); #endif } +} // end namespace detail + +/// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(uint32_t* ptr) noexcept { - *ptr = byteswap_impl(*ptr); + *ptr = detail::byteswap_impl(*ptr); } +/// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(uint64_t* ptr) noexcept { - *ptr = byteswap_impl(*ptr); + *ptr = detail::byteswap_impl(*ptr); } +/// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(int32_t* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); - *bptr = byteswap_impl(*bptr); + auto* bptr = reinterpret_cast(ptr); + *bptr = detail::byteswap_impl(*bptr); } +/// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(int64_t* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); - *bptr = byteswap_impl(*bptr); + auto* bptr = reinterpret_cast(ptr); + *bptr = detail::byteswap_impl(*bptr); } +/// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(float* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); - *bptr = byteswap_impl(*bptr); + static_assert(sizeof(float) == 4, "Expecting four byte float"); + + uint32_t tmp = 0; + std::memcpy(&tmp, ptr, 4); + tmp = detail::byteswap_impl(tmp); // uint32 overload + std::memcpy(ptr, &tmp, 4); } +/// byteswap the data pointed to by ptr in-place. inline void byteswap_inplace(double* ptr) noexcept { - auto bptr = reinterpret_cast(ptr); - *bptr = byteswap_impl(*bptr); + static_assert(sizeof(double) == 8, "Expecting eight byte double"); + + uint64_t tmp = 0; + std::memcpy(&tmp, ptr, 8); + tmp = detail::byteswap_impl(tmp); // uint64 overload + std::memcpy(ptr, &tmp, 8); } +namespace detail { + + // Added for backwards compatibility with any code that might use this + // function (even if it shouldn't have). Will be removed in a later + // version of protozero. + using ::protozero::byteswap_inplace; + } // end namespace detail + } // end namespace protozero #endif // PROTOZERO_BYTESWAP_HPP diff --git a/include/protozero/data_view.hpp b/include/protozero/data_view.hpp index 952c91203..3ec87af34 100644 --- a/include/protozero/data_view.hpp +++ b/include/protozero/data_view.hpp @@ -16,7 +16,7 @@ documentation. * @brief Contains the implementation of the data_view class. */ -#include +#include "config.hpp" #include #include @@ -55,8 +55,8 @@ public: * @param length Length of the data. */ constexpr data_view(const char* ptr, std::size_t length) noexcept - : m_data(ptr), - m_size(length) { + : m_data{ptr}, + m_size{length} { } /** @@ -65,8 +65,8 @@ public: * @param str String with the data. */ data_view(const std::string& str) noexcept // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - : m_data(str.data()), - m_size(str.size()) { + : m_data{str.data()}, + m_size{str.size()} { } /** @@ -75,8 +75,8 @@ public: * @param ptr Pointer to the data. */ data_view(const char* ptr) noexcept // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - : m_data(ptr), - m_size(std::strlen(ptr)) { + : m_data{ptr}, + m_size{std::strlen(ptr)} { } /** @@ -141,8 +141,8 @@ public: * * @pre Must not be default constructed data_view. */ - int compare(data_view other) const { - protozero_assert(m_data && other.m_data); + int compare(data_view other) const noexcept { + assert(m_data && other.m_data); const int cmp = std::memcmp(data(), other.data(), std::min(size(), other.size())); if (cmp == 0) { diff --git a/include/protozero/iterators.hpp b/include/protozero/iterators.hpp index c1d7d8d69..ee8ef8ecf 100644 --- a/include/protozero/iterators.hpp +++ b/include/protozero/iterators.hpp @@ -16,8 +16,8 @@ documentation. * @brief Contains the iterators for access to packed repeated fields. */ -#include -#include +#include "config.hpp" +#include "varint.hpp" #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include @@ -56,7 +56,7 @@ public: * Default constructor. Create empty iterator_range. */ constexpr iterator_range() : - P(iterator{}, iterator{}) { + P{iterator{}, iterator{}} { } /** @@ -66,8 +66,8 @@ public: * @param last_iterator Iterator to end of range. */ constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) : - P(std::forward(first_iterator), - std::forward(last_iterator)) { + P{std::forward(first_iterator), + std::forward(last_iterator)} { } /// Return iterator to beginning of range. @@ -164,6 +164,8 @@ class const_fixed_iterator { public: + /// @cond usual iterator functions not documented + using iterator_category = std::random_access_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; @@ -173,7 +175,7 @@ public: const_fixed_iterator() noexcept = default; explicit const_fixed_iterator(const char* data) noexcept : - m_data(data) { + m_data{data} { } const_fixed_iterator(const const_fixed_iterator&) noexcept = default; @@ -184,11 +186,11 @@ public: ~const_fixed_iterator() noexcept = default; - value_type operator*() const { + value_type operator*() const noexcept { value_type result; std::memcpy(&result, m_data, sizeof(value_type)); #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN - detail::byteswap_inplace(&result); + byteswap_inplace(&result); #endif return result; } @@ -204,14 +206,6 @@ public: return tmp; } - bool operator==(const_fixed_iterator rhs) const noexcept { - return m_data == rhs.m_data; - } - - bool operator!=(const_fixed_iterator rhs) const noexcept { - return !(*this == rhs); - } - const_fixed_iterator& operator--() noexcept { m_data -= sizeof(value_type); return *this; @@ -223,6 +217,14 @@ public: return tmp; } + friend bool operator==(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return lhs.m_data == rhs.m_data; + } + + friend bool operator!=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { + return !(lhs == rhs); + } + friend bool operator<(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return lhs.m_data < rhs.m_data; } @@ -237,7 +239,6 @@ public: friend bool operator>=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept { return !(lhs < rhs); - } const_fixed_iterator& operator+=(difference_type val) noexcept { @@ -276,6 +277,8 @@ public: return *(*this + n); } + /// @endcond + }; // class const_fixed_iterator /** @@ -288,13 +291,15 @@ class const_varint_iterator { protected: /// Pointer to current iterator position - const char* m_data = nullptr; + const char* m_data = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors) /// Pointer to end iterator position - const char* m_end = nullptr; + const char* m_end = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors) public: + /// @cond usual iterator functions not documented + using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; @@ -302,19 +307,24 @@ public: using reference = value_type&; static difference_type distance(const_varint_iterator begin, const_varint_iterator end) noexcept { + // The "distance" between default initialized const_varint_iterator's + // is always 0. + if (!begin.m_data) { + return 0; + } // We know that each varint contains exactly one byte with the most // significant bit not set. We can use this to quickly figure out // how many varints there are without actually decoding the varints. return std::count_if(begin.m_data, end.m_data, [](char c) noexcept { - return (static_cast(c) & 0x80u) == 0; + return (static_cast(c) & 0x80U) == 0; }); } const_varint_iterator() noexcept = default; const_varint_iterator(const char* data, const char* end) noexcept : - m_data(data), - m_end(end) { + m_data{data}, + m_end{end} { } const_varint_iterator(const const_varint_iterator&) noexcept = default; @@ -326,16 +336,19 @@ public: ~const_varint_iterator() noexcept = default; value_type operator*() const { + protozero_assert(m_data); const char* d = m_data; // will be thrown away return static_cast(decode_varint(&d, m_end)); } const_varint_iterator& operator++() { + protozero_assert(m_data); skip_varint(&m_data, m_end); return *this; } const_varint_iterator operator++(int) { + protozero_assert(m_data); const const_varint_iterator tmp{*this}; ++(*this); return tmp; @@ -349,6 +362,8 @@ public: return !(*this == rhs); } + /// @endcond + }; // class const_varint_iterator /** @@ -360,6 +375,8 @@ class const_svarint_iterator : public const_varint_iterator { public: + /// @cond usual iterator functions not documented + using iterator_category = std::forward_iterator_tag; using value_type = T; using difference_type = std::ptrdiff_t; @@ -367,11 +384,11 @@ public: using reference = value_type&; const_svarint_iterator() noexcept : - const_varint_iterator() { + const_varint_iterator{} { } const_svarint_iterator(const char* data, const char* end) noexcept : - const_varint_iterator(data, end) { + const_varint_iterator{data, end} { } const_svarint_iterator(const const_svarint_iterator&) = default; @@ -383,21 +400,26 @@ public: ~const_svarint_iterator() = default; value_type operator*() const { + protozero_assert(this->m_data); const char* d = this->m_data; // will be thrown away return static_cast(decode_zigzag64(decode_varint(&d, this->m_end))); } const_svarint_iterator& operator++() { + protozero_assert(this->m_data); skip_varint(&this->m_data, this->m_end); return *this; } const_svarint_iterator operator++(int) { + protozero_assert(this->m_data); const const_svarint_iterator tmp{*this}; ++(*this); return tmp; } + /// @endcond + }; // class const_svarint_iterator } // end namespace protozero @@ -408,6 +430,8 @@ namespace std { // functions can't be partially specialized, we have to do this for // every value_type we are using. + /// @cond individual overloads do not need to be documented + template <> inline typename protozero::const_varint_iterator::difference_type distance>(protozero::const_varint_iterator first, // NOLINT(readability-inconsistent-declaration-parameter-name) @@ -450,6 +474,8 @@ namespace std { return protozero::const_svarint_iterator::distance(first, last); } + /// @endcond + } // end namespace std #endif // PROTOZERO_ITERATORS_HPP diff --git a/include/protozero/pbf_builder.hpp b/include/protozero/pbf_builder.hpp index 2e74b2fd9..71a2dec2b 100644 --- a/include/protozero/pbf_builder.hpp +++ b/include/protozero/pbf_builder.hpp @@ -16,249 +16,16 @@ documentation. * @brief Contains the pbf_builder template class. */ -#include -#include +#include "basic_pbf_builder.hpp" +#include "pbf_writer.hpp" -#include +#include namespace protozero { -/** - * The pbf_builder is used to write PBF formatted messages into a buffer. It - * is based on the pbf_writer class and has all the same methods. The - * difference is that while the pbf_writer class takes an integer tag, - * this template class takes a tag of the template type T. The idea is that - * T will be an enumeration value and this helps reduce the possibility of - * programming errors. - * - * Almost all methods in this class can throw an std::bad_alloc exception if - * the std::string used as a buffer wants to resize. - * - * Read the tutorial to understand how this class is used. - */ +/// Specialization of basic_pbf_builder using std::string as buffer type. template -class pbf_builder : public pbf_writer { - - static_assert(std::is_same::type>::value, - "T must be enum with underlying type protozero::pbf_tag_type"); - -public: - - /// The type of messages this class will build. - using enum_type = T; - - pbf_builder() = default; - - /** - * Create a builder using the given string as a data store. The object - * stores a reference to that string and adds all data to it. The string - * doesn't have to be empty. The pbf_message object will just append data. - */ - explicit pbf_builder(std::string& data) noexcept : - pbf_writer(data) { - } - - /** - * Construct a pbf_builder for a submessage from the pbf_message or - * pbf_writer of the parent message. - * - * @param parent_writer The parent pbf_message or pbf_writer - * @param tag Tag of the field that will be written - */ - template - pbf_builder(pbf_writer& parent_writer, P tag) noexcept : - pbf_writer(parent_writer, pbf_tag_type(tag)) { - } - -/// @cond INTERNAL -#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ - void add_##name(T tag, type value) { \ - pbf_writer::add_##name(pbf_tag_type(tag), value); \ - } - - PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float) - PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double) - -#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR -/// @endcond - - /** - * Add "bytes" field to data. - * - * @param tag Tag of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_bytes(T tag, const char* value, std::size_t size) { - pbf_writer::add_bytes(pbf_tag_type(tag), value, size); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_bytes(T tag, const data_view& value) { - pbf_writer::add_bytes(pbf_tag_type(tag), value); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_bytes(T tag, const std::string& value) { - pbf_writer::add_bytes(pbf_tag_type(tag), value); - } - - /** - * Add "bytes" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag of the field - * @param value Pointer to zero-delimited value to be written - */ - void add_bytes(T tag, const char* value) { - pbf_writer::add_bytes(pbf_tag_type(tag), value); - } - - /** - * Add "bytes" field to data using vectored input. All the data in the - * 2nd and further arguments is "concatenated" with only a single copy - * into the final buffer. - * - * This will work with objects of any type supporting the data() and - * size() methods like std::string or protozero::data_view. - * - * Example: - * @code - * std::string data1 = "abc"; - * std::string data2 = "xyz"; - * builder.add_bytes_vectored(1, data1, data2); - * @endcode - * - * @tparam Ts List of types supporting data() and size() methods. - * @param tag Tag of the field - * @param values List of objects of types Ts with data to be appended. - */ - template - void add_bytes_vectored(T tag, Ts&&... values) { - pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward(values)...); - } - - /** - * Add "string" field to data. - * - * @param tag Tag of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_string(T tag, const char* value, std::size_t size) { - pbf_writer::add_string(pbf_tag_type(tag), value, size); - } - - /** - * Add "string" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_string(T tag, const data_view& value) { - pbf_writer::add_string(pbf_tag_type(tag), value); - } - - /** - * Add "string" field to data. - * - * @param tag Tag of the field - * @param value Value to be written - */ - void add_string(T tag, const std::string& value) { - pbf_writer::add_string(pbf_tag_type(tag), value); - } - - /** - * Add "string" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag of the field - * @param value Pointer to value to be written - */ - void add_string(T tag, const char* value) { - pbf_writer::add_string(pbf_tag_type(tag), value); - } - - /** - * Add "message" field to data. - * - * @param tag Tag of the field - * @param value Pointer to message to be written - * @param size Length of the message - */ - void add_message(T tag, const char* value, std::size_t size) { - pbf_writer::add_message(pbf_tag_type(tag), value, size); - } - - /** - * Add "message" field to data. - * - * @param tag Tag of the field - * @param value Value to be written. The value must be a complete message. - */ - void add_message(T tag, const data_view& value) { - pbf_writer::add_message(pbf_tag_type(tag), value); - } - - /** - * Add "message" field to data. - * - * @param tag Tag of the field - * @param value Value to be written. The value must be a complete message. - */ - void add_message(T tag, const std::string& value) { - pbf_writer::add_message(pbf_tag_type(tag), value); - } - -/// @cond INTERNAL -#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ - template \ - void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ - pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ - } - - PROTOZERO_WRITER_WRAP_ADD_PACKED(bool) - PROTOZERO_WRITER_WRAP_ADD_PACKED(enum) - PROTOZERO_WRITER_WRAP_ADD_PACKED(int32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(int64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32) - PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64) - PROTOZERO_WRITER_WRAP_ADD_PACKED(float) - PROTOZERO_WRITER_WRAP_ADD_PACKED(double) - -#undef PROTOZERO_WRITER_WRAP_ADD_PACKED -/// @endcond - -}; // class pbf_builder +using pbf_builder = basic_pbf_builder; } // end namespace protozero diff --git a/include/protozero/pbf_message.hpp b/include/protozero/pbf_message.hpp index 10040d5b4..d7fd8b5d0 100644 --- a/include/protozero/pbf_message.hpp +++ b/include/protozero/pbf_message.hpp @@ -16,8 +16,8 @@ documentation. * @brief Contains the pbf_message template class. */ -#include -#include +#include "pbf_reader.hpp" +#include "types.hpp" #include @@ -78,7 +78,7 @@ public: */ template pbf_message(Args&&... args) noexcept : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - pbf_reader(std::forward(args)...) { + pbf_reader{std::forward(args)...} { } /** diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp index 5f8ea0eca..92bfdee5e 100644 --- a/include/protozero/pbf_reader.hpp +++ b/include/protozero/pbf_reader.hpp @@ -16,12 +16,12 @@ documentation. * @brief Contains the pbf_reader class. */ -#include -#include -#include -#include -#include -#include +#include "config.hpp" +#include "data_view.hpp" +#include "exception.hpp" +#include "iterators.hpp" +#include "types.hpp" +#include "varint.hpp" #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN # include @@ -80,7 +80,7 @@ class pbf_reader { skip_bytes(sizeof(T)); std::memcpy(&result, data, sizeof(T)); #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN - detail::byteswap_inplace(&result); + byteswap_inplace(&result); #endif return result; } @@ -98,7 +98,8 @@ class pbf_reader { template T get_varint() { - return static_cast(decode_varint(&m_data, m_end)); + const auto val = static_cast(decode_varint(&m_data, m_end)); + return val; } template @@ -112,7 +113,7 @@ class pbf_reader { } void skip_bytes(pbf_length_type len) { - if (m_data + len > m_end) { + if (m_end - m_data < static_cast(len)) { throw end_of_buffer_exception{}; } m_data += len; @@ -151,8 +152,8 @@ public: * @post There is no current field. */ explicit pbf_reader(const data_view& view) noexcept - : m_data(view.data()), - m_end(view.data() + view.size()) { + : m_data{view.data()}, + m_end{view.data() + view.size()} { } /** @@ -166,8 +167,8 @@ public: * @post There is no current field. */ pbf_reader(const char* data, std::size_t size) noexcept - : m_data(data), - m_end(data + size) { + : m_data{data}, + m_end{data + size} { } #ifndef PROTOZERO_STRICT_API @@ -183,8 +184,8 @@ public: * @deprecated Use one of the other constructors. */ explicit pbf_reader(const std::pair& data) noexcept - : m_data(data.first), - m_end(data.first + data.second) { + : m_data{data.first}, + m_end{data.first + data.second} { } #endif @@ -199,8 +200,8 @@ public: * @post There is no current field. */ explicit pbf_reader(const std::string& data) noexcept - : m_data(data.data()), - m_end(data.data() + data.size()) { + : m_data{data.data()}, + m_end{data.data() + data.size()} { } /** @@ -242,7 +243,14 @@ public: * read. */ operator bool() const noexcept { // NOLINT(google-explicit-constructor, hicpp-explicit-conversions) - return m_data < m_end; + return m_data != m_end; + } + + /** + * Get a view of the not yet read data. + */ + data_view data() const noexcept { + return {m_data, static_cast(m_end - m_data)}; } /** @@ -279,7 +287,7 @@ public: } const auto value = get_varint(); - m_tag = pbf_tag_type(value >> 3u); + m_tag = pbf_tag_type(value >> 3U); // tags 0 and 19000 to 19999 are not allowed as per // https://developers.google.com/protocol-buffers/docs/proto#assigning-tags @@ -287,7 +295,7 @@ public: throw invalid_tag_exception{}; } - m_wire_type = pbf_wire_type(value & 0x07u); + m_wire_type = pbf_wire_type(value & 0x07U); switch (m_wire_type) { case pbf_wire_type::varint: case pbf_wire_type::fixed64: @@ -486,9 +494,9 @@ public: bool get_bool() { protozero_assert(tag() != 0 && "call next() before accessing field value"); protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint"); - const auto data = m_data; + const bool result = m_data[0] != 0; skip_varint(&m_data, m_end); - return data[0] != 0; + return result; } /** diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp index 1abe9a38a..9a07bd5b9 100644 --- a/include/protozero/pbf_writer.hpp +++ b/include/protozero/pbf_writer.hpp @@ -16,1033 +16,60 @@ documentation. * @brief Contains the pbf_writer class. */ -#include -#include -#include -#include +#include "basic_pbf_writer.hpp" +#include "buffer_string.hpp" -#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN -# include -#endif - -#include #include -#include -#include -#include -#include #include -#include namespace protozero { -namespace detail { - - template class packed_field_varint; - template class packed_field_svarint; - template class packed_field_fixed; - -} // end namespace detail - /** - * The pbf_writer is used to write PBF formatted messages into a buffer. - * - * Almost all methods in this class can throw an std::bad_alloc exception if - * the std::string used as a buffer wants to resize. + * Specialization of basic_pbf_writer using std::string as buffer type. */ -class pbf_writer { - - // A pointer to a string buffer holding the data already written to the - // PBF message. For default constructed writers or writers that have been - // rolled back, this is a nullptr. - std::string* m_data = nullptr; - - // A pointer to a parent writer object if this is a submessage. If this - // is a top-level writer, it is a nullptr. - pbf_writer* m_parent_writer = nullptr; - - // This is usually 0. If there is an open submessage, this is set in the - // parent to the rollback position, ie. the last position before the - // submessage was started. This is the position where the header of the - // submessage starts. - std::size_t m_rollback_pos = 0; - - // This is usually 0. If there is an open submessage, this is set in the - // parent to the position where the data of the submessage is written to. - std::size_t m_pos = 0; - - void add_varint(uint64_t value) { - protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); - protozero_assert(m_data); - write_varint(std::back_inserter(*m_data), value); - } - - void add_field(pbf_tag_type tag, pbf_wire_type type) { - protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1u << 29u) - 1))) && "tag out of range"); - const uint32_t b = (tag << 3u) | uint32_t(type); - add_varint(b); - } - - void add_tagged_varint(pbf_tag_type tag, uint64_t value) { - add_field(tag, pbf_wire_type::varint); - add_varint(value); - } - - template - void add_fixed(T value) { - protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); - protozero_assert(m_data); -#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN - detail::byteswap_inplace(&value); -#endif - m_data->append(reinterpret_cast(&value), sizeof(T)); - } - - template - void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag /*unused*/) { - if (first == last) { - return; - } - - pbf_writer sw{*this, tag}; - - while (first != last) { - sw.add_fixed(*first++); - } - } - - template - void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag /*unused*/) { - if (first == last) { - return; - } - - const auto length = std::distance(first, last); - add_length_varint(tag, sizeof(T) * pbf_length_type(length)); - reserve(sizeof(T) * std::size_t(length)); - - while (first != last) { - add_fixed(*first++); - } - } - - template - void add_packed_varint(pbf_tag_type tag, It first, It last) { - if (first == last) { - return; - } - - pbf_writer sw{*this, tag}; - - while (first != last) { - sw.add_varint(uint64_t(*first++)); - } - } - - template - void add_packed_svarint(pbf_tag_type tag, It first, It last) { - if (first == last) { - return; - } - - pbf_writer sw{*this, tag}; - - while (first != last) { - sw.add_varint(encode_zigzag64(*first++)); - } - } - - // The number of bytes to reserve for the varint holding the length of - // a length-delimited field. The length has to fit into pbf_length_type, - // and a varint needs 8 bit for every 7 bit. - enum constant_reserve_bytes : int { - reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1 - }; - - // If m_rollpack_pos is set to this special value, it means that when - // the submessage is closed, nothing needs to be done, because the length - // of the submessage has already been written correctly. - enum constant_size_is_known : std::size_t { - size_is_known = std::numeric_limits::max() - }; - - void open_submessage(pbf_tag_type tag, std::size_t size) { - protozero_assert(m_pos == 0); - protozero_assert(m_data); - if (size == 0) { - m_rollback_pos = m_data->size(); - add_field(tag, pbf_wire_type::length_delimited); - m_data->append(std::size_t(reserve_bytes), '\0'); - } else { - m_rollback_pos = size_is_known; - add_length_varint(tag, pbf_length_type(size)); - reserve(size); - } - m_pos = m_data->size(); - } - - void rollback_submessage() { - protozero_assert(m_pos != 0); - protozero_assert(m_rollback_pos != size_is_known); - protozero_assert(m_data); - m_data->resize(m_rollback_pos); - m_pos = 0; - } - - void commit_submessage() { - protozero_assert(m_pos != 0); - protozero_assert(m_rollback_pos != size_is_known); - protozero_assert(m_data); - const auto length = pbf_length_type(m_data->size() - m_pos); - - protozero_assert(m_data->size() >= m_pos - reserve_bytes); - const auto n = write_varint(m_data->begin() + int64_t(m_pos) - reserve_bytes, length); - - m_data->erase(m_data->begin() + int64_t(m_pos) - reserve_bytes + n, m_data->begin() + int64_t(m_pos)); - m_pos = 0; - } - - void close_submessage() { - protozero_assert(m_data); - if (m_pos == 0 || m_rollback_pos == size_is_known) { - return; - } - if (m_data->size() - m_pos == 0) { - rollback_submessage(); - } else { - commit_submessage(); - } - } - - void add_length_varint(pbf_tag_type tag, pbf_length_type length) { - add_field(tag, pbf_wire_type::length_delimited); - add_varint(length); - } - -public: - - /** - * Create a writer using the given string as a data store. The pbf_writer - * stores a reference to that string and adds all data to it. The string - * doesn't have to be empty. The pbf_writer will just append data. - */ - explicit pbf_writer(std::string& data) noexcept : - m_data(&data) { - } - - /** - * Create a writer without a data store. In this form the writer can not - * be used! - */ - pbf_writer() noexcept = default; - - /** - * Construct a pbf_writer for a submessage from the pbf_writer of the - * parent message. - * - * @param parent_writer The pbf_writer - * @param tag Tag (field number) of the field that will be written - * @param size Optional size of the submessage in bytes (use 0 for unknown). - * Setting this allows some optimizations but is only possible in - * a few very specific cases. - */ - pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : - m_data(parent_writer.m_data), - m_parent_writer(&parent_writer) { - m_parent_writer->open_submessage(tag, size); - } - - /// A pbf_writer object can not be copied - pbf_writer(const pbf_writer&) = delete; - - /// A pbf_writer object can not be copied - pbf_writer& operator=(const pbf_writer&) = delete; - - /** - * A pbf_writer object can be moved. After this the other pbf_writer will - * be invalid. - */ - pbf_writer(pbf_writer&& other) noexcept : - m_data(other.m_data), - m_parent_writer(other.m_parent_writer), - m_rollback_pos(other.m_rollback_pos), - m_pos(other.m_pos) { - other.m_data = nullptr; - other.m_parent_writer = nullptr; - other.m_rollback_pos = 0; - other.m_pos = 0; - } - - /** - * A pbf_writer object can be moved. After this the other pbf_writer will - * be invalid. - */ - pbf_writer& operator=(pbf_writer&& other) noexcept { - m_data = other.m_data; - m_parent_writer = other.m_parent_writer; - m_rollback_pos = other.m_rollback_pos; - m_pos = other.m_pos; - other.m_data = nullptr; - other.m_parent_writer = nullptr; - other.m_rollback_pos = 0; - other.m_pos = 0; - return *this; - } - - ~pbf_writer() { - if (m_parent_writer != nullptr) { - m_parent_writer->close_submessage(); - } - } - - /** - * Check if this writer is valid. A writer is invalid if it was default - * constructed, moved from, or if commit() has been called on it. - * Otherwise it is valid. - */ - bool valid() const noexcept { - return m_data != nullptr; - } - - /** - * Swap the contents of this object with the other. - * - * @param other Other object to swap data with. - */ - void swap(pbf_writer& other) noexcept { - using std::swap; - swap(m_data, other.m_data); - swap(m_parent_writer, other.m_parent_writer); - swap(m_rollback_pos, other.m_rollback_pos); - swap(m_pos, other.m_pos); - } - - /** - * Reserve size bytes in the underlying message store in addition to - * whatever the message store already holds. So unlike - * the `std::string::reserve()` method this is not an absolute size, - * but additional memory that should be reserved. - * - * @param size Number of bytes to reserve in underlying message store. - */ - void reserve(std::size_t size) { - protozero_assert(m_data); - m_data->reserve(m_data->size() + size); - } - - /** - * Commit this submessage. This does the same as when the pbf_writer - * goes out of scope and is destructed. - * - * @pre Must be a pbf_writer of a submessage, ie one opened with the - * pbf_writer constructor taking a parent message. - * @post The pbf_writer is invalid and can't be used any more. - */ - void commit() { - protozero_assert(m_parent_writer && "you can't call commit() on a pbf_writer without a parent"); - protozero_assert(m_pos == 0 && "you can't call commit() on a pbf_writer that has an open nested submessage"); - m_parent_writer->close_submessage(); - m_parent_writer = nullptr; - m_data = nullptr; - } - - /** - * Cancel writing of this submessage. The complete submessage will be - * removed as if it was never created and no fields were added. - * - * @pre Must be a pbf_writer of a submessage, ie one opened with the - * pbf_writer constructor taking a parent message. - * @post The pbf_writer is invalid and can't be used any more. - */ - void rollback() { - protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent"); - protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage"); - m_parent_writer->rollback_submessage(); - m_parent_writer = nullptr; - m_data = nullptr; - } - - ///@{ - /** - * @name Scalar field writer functions - */ - - /** - * Add "bool" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_bool(pbf_tag_type tag, bool value) { - add_field(tag, pbf_wire_type::varint); - protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); - protozero_assert(m_data); - m_data->append(1, char(value)); - } - - /** - * Add "enum" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_enum(pbf_tag_type tag, int32_t value) { - add_tagged_varint(tag, uint64_t(value)); - } - - /** - * Add "int32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_int32(pbf_tag_type tag, int32_t value) { - add_tagged_varint(tag, uint64_t(value)); - } - - /** - * Add "sint32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sint32(pbf_tag_type tag, int32_t value) { - add_tagged_varint(tag, encode_zigzag32(value)); - } - - /** - * Add "uint32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_uint32(pbf_tag_type tag, uint32_t value) { - add_tagged_varint(tag, value); - } - - /** - * Add "int64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_int64(pbf_tag_type tag, int64_t value) { - add_tagged_varint(tag, uint64_t(value)); - } - - /** - * Add "sint64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sint64(pbf_tag_type tag, int64_t value) { - add_tagged_varint(tag, encode_zigzag64(value)); - } - - /** - * Add "uint64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_uint64(pbf_tag_type tag, uint64_t value) { - add_tagged_varint(tag, value); - } - - /** - * Add "fixed32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_fixed32(pbf_tag_type tag, uint32_t value) { - add_field(tag, pbf_wire_type::fixed32); - add_fixed(value); - } - - /** - * Add "sfixed32" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sfixed32(pbf_tag_type tag, int32_t value) { - add_field(tag, pbf_wire_type::fixed32); - add_fixed(value); - } - - /** - * Add "fixed64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_fixed64(pbf_tag_type tag, uint64_t value) { - add_field(tag, pbf_wire_type::fixed64); - add_fixed(value); - } - - /** - * Add "sfixed64" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_sfixed64(pbf_tag_type tag, int64_t value) { - add_field(tag, pbf_wire_type::fixed64); - add_fixed(value); - } - - /** - * Add "float" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_float(pbf_tag_type tag, float value) { - add_field(tag, pbf_wire_type::fixed32); - add_fixed(value); - } - - /** - * Add "double" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_double(pbf_tag_type tag, double value) { - add_field(tag, pbf_wire_type::fixed64); - add_fixed(value); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag (field number) of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { - protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); - protozero_assert(m_data); - protozero_assert(size <= std::numeric_limits::max()); - add_length_varint(tag, pbf_length_type(size)); - m_data->append(value, size); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_bytes(pbf_tag_type tag, const data_view& value) { - add_bytes(tag, value.data(), value.size()); - } - - /** - * Add "bytes" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_bytes(pbf_tag_type tag, const std::string& value) { - add_bytes(tag, value.data(), value.size()); - } - - /** - * Add "bytes" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag (field number) of the field - * @param value Pointer to zero-delimited value to be written - */ - void add_bytes(pbf_tag_type tag, const char* value) { - add_bytes(tag, value, std::strlen(value)); - } - - /** - * Add "bytes" field to data using vectored input. All the data in the - * 2nd and further arguments is "concatenated" with only a single copy - * into the final buffer. - * - * This will work with objects of any type supporting the data() and - * size() methods like std::string or protozero::data_view. - * - * Example: - * @code - * std::string data1 = "abc"; - * std::string data2 = "xyz"; - * writer.add_bytes_vectored(1, data1, data2); - * @endcode - * - * @tparam Ts List of types supporting data() and size() methods. - * @param tag Tag (field number) of the field - * @param values List of objects of types Ts with data to be appended. - */ - template - void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) { - protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage"); - protozero_assert(m_data); - size_t sum_size = 0; - (void)std::initializer_list{sum_size += values.size()...}; - protozero_assert(sum_size <= std::numeric_limits::max()); - add_length_varint(tag, pbf_length_type(sum_size)); - m_data->reserve(m_data->size() + sum_size); - (void)std::initializer_list{(m_data->append(values.data(), values.size()), 0)...}; - } - - /** - * Add "string" field to data. - * - * @param tag Tag (field number) of the field - * @param value Pointer to value to be written - * @param size Number of bytes to be written - */ - void add_string(pbf_tag_type tag, const char* value, std::size_t size) { - add_bytes(tag, value, size); - } - - /** - * Add "string" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_string(pbf_tag_type tag, const data_view& value) { - add_bytes(tag, value.data(), value.size()); - } - - /** - * Add "string" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written - */ - void add_string(pbf_tag_type tag, const std::string& value) { - add_bytes(tag, value.data(), value.size()); - } - - /** - * Add "string" field to data. Bytes from the value are written until - * a null byte is encountered. The null byte is not added. - * - * @param tag Tag (field number) of the field - * @param value Pointer to value to be written - */ - void add_string(pbf_tag_type tag, const char* value) { - add_bytes(tag, value, std::strlen(value)); - } - - /** - * Add "message" field to data. - * - * @param tag Tag (field number) of the field - * @param value Pointer to message to be written - * @param size Length of the message - */ - void add_message(pbf_tag_type tag, const char* value, std::size_t size) { - add_bytes(tag, value, size); - } - - /** - * Add "message" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written. The value must be a complete message. - */ - void add_message(pbf_tag_type tag, const data_view& value) { - add_bytes(tag, value.data(), value.size()); - } - - /** - * Add "message" field to data. - * - * @param tag Tag (field number) of the field - * @param value Value to be written. The value must be a complete message. - */ - void add_message(pbf_tag_type tag, const std::string& value) { - add_bytes(tag, value.data(), value.size()); - } - - ///@} - - ///@{ - /** - * @name Repeated packed field writer functions - */ - - /** - * Add "repeated packed bool" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to bool. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add "repeated packed enum" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int32_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add "repeated packed int32" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int32_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add "repeated packed sint32" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int32_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_svarint(tag, first, last); - } - - /** - * Add "repeated packed uint32" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to uint32_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add "repeated packed int64" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int64_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add "repeated packed sint64" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int64_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_svarint(tag, first, last); - } - - /** - * Add "repeated packed uint64" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to uint64_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_varint(tag, first, last); - } - - /** - * Add "repeated packed fixed32" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to uint32_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category()); - } - - /** - * Add "repeated packed sfixed32" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int32_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category()); - } - - /** - * Add "repeated packed fixed64" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to uint64_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category()); - } - - /** - * Add "repeated packed sfixed64" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to int64_t. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category()); - } - - /** - * Add "repeated packed float" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to float. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category()); - } - - /** - * Add "repeated packed double" field to data. - * - * @tparam InputIterator A type satisfying the InputIterator concept. - * Dereferencing the iterator must yield a type assignable to double. - * @param tag Tag (field number) of the field - * @param first Iterator pointing to the beginning of the data - * @param last Iterator pointing one past the end of data - */ - template - void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { - add_packed_fixed(tag, first, last, - typename std::iterator_traits::iterator_category()); - } - - ///@} - - template friend class detail::packed_field_varint; - template friend class detail::packed_field_svarint; - template friend class detail::packed_field_fixed; - -}; // class pbf_writer - -/** - * Swap two pbf_writer objects. - * - * @param lhs First object. - * @param rhs Second object. - */ -inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept { - lhs.swap(rhs); -} - -namespace detail { - - class packed_field { - - protected: - - pbf_writer m_writer{}; - - public: - - packed_field(const packed_field&) = delete; - packed_field& operator=(const packed_field&) = delete; - - packed_field(packed_field&&) noexcept = default; - packed_field& operator=(packed_field&&) noexcept = default; - - packed_field() = default; - - packed_field(pbf_writer& parent_writer, pbf_tag_type tag) : - m_writer(parent_writer, tag) { - } - - packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : - m_writer(parent_writer, tag, size) { - } - - ~packed_field() noexcept = default; - - bool valid() const noexcept { - return m_writer.valid(); - } - - void commit() { - m_writer.commit(); - } - - void rollback() { - m_writer.rollback(); - } - - }; // class packed_field - - template - class packed_field_fixed : public packed_field { - - public: - - packed_field_fixed() : - packed_field() { - } - - template - packed_field_fixed(pbf_writer& parent_writer, P tag) : - packed_field(parent_writer, static_cast(tag)) { - } - - template - packed_field_fixed(pbf_writer& parent_writer, P tag, std::size_t size) : - packed_field(parent_writer, static_cast(tag), size * sizeof(T)) { - } - - void add_element(T value) { - m_writer.add_fixed(value); - } - - }; // class packed_field_fixed - - template - class packed_field_varint : public packed_field { - - public: - - packed_field_varint() : - packed_field() { - } - - template - packed_field_varint(pbf_writer& parent_writer, P tag) : - packed_field(parent_writer, static_cast(tag)) { - } - - void add_element(T value) { - m_writer.add_varint(uint64_t(value)); - } - - }; // class packed_field_varint - - template - class packed_field_svarint : public packed_field { - - public: - - packed_field_svarint() : - packed_field() { - } - - template - packed_field_svarint(pbf_writer& parent_writer, P tag) : - packed_field(parent_writer, static_cast(tag)) { - } - - void add_element(T value) { - m_writer.add_varint(encode_zigzag64(value)); - } - - }; // class packed_field_svarint - -} // end namespace detail +using pbf_writer = basic_pbf_writer; /// Class for generating packed repeated bool fields. -using packed_field_bool = detail::packed_field_varint; +using packed_field_bool = detail::packed_field_varint; /// Class for generating packed repeated enum fields. -using packed_field_enum = detail::packed_field_varint; +using packed_field_enum = detail::packed_field_varint; /// Class for generating packed repeated int32 fields. -using packed_field_int32 = detail::packed_field_varint; +using packed_field_int32 = detail::packed_field_varint; /// Class for generating packed repeated sint32 fields. -using packed_field_sint32 = detail::packed_field_svarint; +using packed_field_sint32 = detail::packed_field_svarint; /// Class for generating packed repeated uint32 fields. -using packed_field_uint32 = detail::packed_field_varint; +using packed_field_uint32 = detail::packed_field_varint; /// Class for generating packed repeated int64 fields. -using packed_field_int64 = detail::packed_field_varint; +using packed_field_int64 = detail::packed_field_varint; /// Class for generating packed repeated sint64 fields. -using packed_field_sint64 = detail::packed_field_svarint; +using packed_field_sint64 = detail::packed_field_svarint; /// Class for generating packed repeated uint64 fields. -using packed_field_uint64 = detail::packed_field_varint; +using packed_field_uint64 = detail::packed_field_varint; /// Class for generating packed repeated fixed32 fields. -using packed_field_fixed32 = detail::packed_field_fixed; +using packed_field_fixed32 = detail::packed_field_fixed; /// Class for generating packed repeated sfixed32 fields. -using packed_field_sfixed32 = detail::packed_field_fixed; +using packed_field_sfixed32 = detail::packed_field_fixed; /// Class for generating packed repeated fixed64 fields. -using packed_field_fixed64 = detail::packed_field_fixed; +using packed_field_fixed64 = detail::packed_field_fixed; /// Class for generating packed repeated sfixed64 fields. -using packed_field_sfixed64 = detail::packed_field_fixed; +using packed_field_sfixed64 = detail::packed_field_fixed; /// Class for generating packed repeated float fields. -using packed_field_float = detail::packed_field_fixed; +using packed_field_float = detail::packed_field_fixed; /// Class for generating packed repeated double fields. -using packed_field_double = detail::packed_field_fixed; +using packed_field_double = detail::packed_field_fixed; } // end namespace protozero diff --git a/include/protozero/types.hpp b/include/protozero/types.hpp index b5241274e..3aefddfb5 100644 --- a/include/protozero/types.hpp +++ b/include/protozero/types.hpp @@ -16,7 +16,7 @@ documentation. * @brief Contains the declaration of low-level types used in the pbf format. */ -#include +#include "config.hpp" #include #include @@ -53,7 +53,7 @@ enum class pbf_wire_type : uint32_t { */ template constexpr inline uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept { - return (static_cast(static_cast(tag)) << 3u) | static_cast(wire_type); + return (static_cast(static_cast(tag)) << 3U) | static_cast(wire_type); } /** diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp index dd79a6ef9..b4648a442 100644 --- a/include/protozero/varint.hpp +++ b/include/protozero/varint.hpp @@ -16,7 +16,8 @@ documentation. * @brief Contains low-level varint and zigzag encoding and decoding functions. */ -#include +#include "buffer_tmpl.hpp" +#include "exception.hpp" #include @@ -31,30 +32,30 @@ namespace detail { // from https://github.com/facebook/folly/blob/master/folly/Varint.h inline uint64_t decode_varint_impl(const char** data, const char* end) { - const auto begin = reinterpret_cast(*data); - const auto iend = reinterpret_cast(end); + const auto* begin = reinterpret_cast(*data); + const auto* iend = reinterpret_cast(end); const int8_t* p = begin; uint64_t val = 0; if (iend - begin >= max_varint_length) { // fast path do { - int64_t b; - b = *p++; val = uint64_t((b & 0x7fu) ); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 7u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 14u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 21u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 28u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 35u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 42u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 49u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x7fu) << 56u); if (b >= 0) { break; } - b = *p++; val |= uint64_t((b & 0x01u) << 63u); if (b >= 0) { break; } + int64_t b = *p++; + val = ((uint64_t(b) & 0x7fU) ); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 7U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 14U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 21U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 28U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 35U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 42U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 49U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x7fU) << 56U); if (b >= 0) { break; } + b = *p++; val |= ((uint64_t(b) & 0x01U) << 63U); if (b >= 0) { break; } throw varint_too_long_exception{}; } while (false); } else { unsigned int shift = 0; while (p != iend && *p < 0) { - val |= uint64_t(*p++ & 0x7fu) << shift; + val |= (uint64_t(*p++) & 0x7fU) << shift; shift += 7; } if (p == iend) { @@ -88,7 +89,7 @@ namespace detail { */ inline uint64_t decode_varint(const char** data, const char* end) { // If this is a one-byte varint, decode it here. - if (end != *data && ((**data & 0x80u) == 0)) { + if (end != *data && ((static_cast(**data) & 0x80U) == 0)) { const auto val = static_cast(**data); ++(*data); return val; @@ -110,15 +111,15 @@ inline uint64_t decode_varint(const char** data, const char* end) { * before the end of the varint. */ inline void skip_varint(const char** data, const char* end) { - const auto begin = reinterpret_cast(*data); - const auto iend = reinterpret_cast(end); + const auto* begin = reinterpret_cast(*data); + const auto* iend = reinterpret_cast(end); const int8_t* p = begin; while (p != iend && *p < 0) { ++p; } - if (p >= begin + max_varint_length) { + if (p - begin >= max_varint_length) { throw varint_too_long_exception{}; } @@ -140,17 +141,73 @@ inline void skip_varint(const char** data, const char* end) { * @param value The integer that will be encoded. * @returns the number of bytes written * @throws Any exception thrown by increment or dereference operator on data. + * @deprecated Use add_varint_to_buffer() instead. */ template inline int write_varint(T data, uint64_t value) { int n = 1; - while (value >= 0x80u) { - *data++ = char((value & 0x7fu) | 0x80u); - value >>= 7u; + while (value >= 0x80U) { + *data++ = char((value & 0x7fU) | 0x80U); + value >>= 7U; + ++n; + } + *data = char(value); + + return n; +} + +/** + * Varint encode a 64 bit integer. + * + * @tparam TBuffer A buffer type. + * @param buffer Output buffer the varint will be written to. + * @param value The integer that will be encoded. + * @returns the number of bytes written + * @throws Any exception thrown by calling the buffer_push_back() function. + */ +template +inline void add_varint_to_buffer(TBuffer* buffer, uint64_t value) { + while (value >= 0x80U) { + buffer_customization::push_back(buffer, char((value & 0x7fU) | 0x80U)); + value >>= 7U; + } + buffer_customization::push_back(buffer, char(value)); +} + +/** + * Varint encode a 64 bit integer. + * + * @param data Where to add the varint. There must be enough space available! + * @param value The integer that will be encoded. + * @returns the number of bytes written + */ +inline int add_varint_to_buffer(char* data, uint64_t value) noexcept { + int n = 1; + + while (value >= 0x80U) { + *data++ = char((value & 0x7fU) | 0x80U); + value >>= 7U; + ++n; + } + *data = char(value); + + return n; +} + +/** + * Get the length of the varint the specified value would produce. + * + * @param value The integer to be encoded. + * @returns the number of bytes the varint would have if we created it. + */ +inline int length_of_varint(uint64_t value) noexcept { + int n = 1; + + while (value >= 0x80U) { + value >>= 7U; ++n; } - *data++ = char(value); return n; } @@ -159,28 +216,28 @@ inline int write_varint(T data, uint64_t value) { * ZigZag encodes a 32 bit integer. */ inline constexpr uint32_t encode_zigzag32(int32_t value) noexcept { - return (static_cast(value) << 1u) ^ (static_cast(value >> 31u)); + return (static_cast(value) << 1U) ^ static_cast(-static_cast(static_cast(value) >> 31U)); } /** * ZigZag encodes a 64 bit integer. */ inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept { - return (static_cast(value) << 1u) ^ (static_cast(value >> 63u)); + return (static_cast(value) << 1U) ^ static_cast(-static_cast(static_cast(value) >> 63U)); } /** * Decodes a 32 bit ZigZag-encoded integer. */ inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept { - return static_cast(value >> 1u) ^ -static_cast(value & 1u); + return static_cast((value >> 1U) ^ static_cast(-static_cast(value & 1U))); } /** * Decodes a 64 bit ZigZag-encoded integer. */ inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept { - return static_cast(value >> 1u) ^ -static_cast(value & 1u); + return static_cast((value >> 1U) ^ static_cast(-static_cast(value & 1U))); } } // end namespace protozero diff --git a/include/protozero/version.hpp b/include/protozero/version.hpp index 32a494f4b..fc9b92879 100644 --- a/include/protozero/version.hpp +++ b/include/protozero/version.hpp @@ -20,15 +20,15 @@ documentation. #define PROTOZERO_VERSION_MAJOR 1 /// The minor version number -#define PROTOZERO_VERSION_MINOR 6 +#define PROTOZERO_VERSION_MINOR 7 /// The patch number -#define PROTOZERO_VERSION_PATCH 2 +#define PROTOZERO_VERSION_PATCH 1 /// The complete version number #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) /// Version number as string -#define PROTOZERO_VERSION_STRING "1.6.2" +#define PROTOZERO_VERSION_STRING "1.7.1" #endif // PROTOZERO_VERSION_HPP diff --git a/test/README.md b/test/README.md index 843d8d4de..01936ba9f 100644 --- a/test/README.md +++ b/test/README.md @@ -1,7 +1,7 @@ # Tests -Tests are using the [Catch Unit Test Framework](https://github.com/philsquared/Catch). +Tests are using the [Catch Unit Test Framework](https://github.com/catchorg/Catch2). ## Organization of the unit tests diff --git a/test/catch/catch.hpp b/test/catch/catch.hpp index 6b5129d60..db1fed3b9 100644 --- a/test/catch/catch.hpp +++ b/test/catch/catch.hpp @@ -1,17 +1,21 @@ /* - * Catch v1.12.0 - * Generated: 2018-01-11 21:56:34.893972 + * Catch v2.13.8 + * Generated: 2022-01-03 21:20:09.589503 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */ #ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp -#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 8 #ifdef __clang__ # pragma clang system_header @@ -19,36 +23,69 @@ # pragma GCC system_header #endif -// #included from: internal/catch_suppress_warnings.h +// start catch_suppress_warnings.h #ifdef __clang__ # ifdef __ICC // icpc defines the __clang__ macro # pragma warning(push) # pragma warning(disable: 161 1682) # else // __ICC -# pragma clang diagnostic ignored "-Wglobal-constructors" -# pragma clang diagnostic ignored "-Wvariadic-macros" -# pragma clang diagnostic ignored "-Wc99-extensions" -# pragma clang diagnostic ignored "-Wunused-variable" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wc++98-compat" -# pragma clang diagnostic ignored "-Wc++98-compat-pedantic" # pragma clang diagnostic ignored "-Wswitch-enum" # pragma clang diagnostic ignored "-Wcovered-switch-default" # endif #elif defined __GNUC__ -# pragma GCC diagnostic ignored "-Wvariadic-macros" -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wparentheses" + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details # pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" # pragma GCC diagnostic ignored "-Wpadded" #endif +// end catch_suppress_warnings.h #if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) # define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS #endif +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + #ifdef CATCH_IMPL # ifndef CLARA_CONFIG_MAIN # define CLARA_CONFIG_MAIN_NOT_DEFINED @@ -56,93 +93,106 @@ # endif #endif -// #included from: internal/catch_notimplemented_exception.h -#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED +// start catch_user_interfaces.h -// #included from: catch_common.h -#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED +namespace Catch { + unsigned int rngSeed(); +} -// #included from: catch_compiler_capabilities.h -#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h -// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler // The following features are defined: // -// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported? -// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported? -// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods -// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported? -// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported -// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported? -// CATCH_CONFIG_CPP11_OVERRIDE : is override supported? -// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) -// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported? -// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported? - -// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported? - -// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported? // CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? // CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? // CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? // **************** // Note to maintainers: if new toggles are added please document them // in configuration.md, too // **************** // In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. // Many features, at point of detection, define an _INTERNAL_ macro, so they // can be combined, en-mass, with the _NO_ forms later. -// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 - #ifdef __cplusplus -# if __cplusplus >= 201103L -# define CATCH_CPP11_OR_GREATER +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER # endif -# if __cplusplus >= 201402L -# define CATCH_CPP14_OR_GREATER +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER # endif #endif -#ifdef __clang__ +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) -# if __has_feature(cxx_nullptr) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ # endif -# if __has_feature(cxx_noexcept) -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# if defined(CATCH_CPP11_OR_GREATER) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic pop" ) -# endif +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) #endif // __clang__ +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + //////////////////////////////////////////////////////////////////////////////// // We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) - -# if !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# endif - +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ @@ -150,6 +200,25 @@ # define CATCH_CONFIG_COLOUR_NONE #endif +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + //////////////////////////////////////////////////////////////////////////////// // Cygwin #ifdef __CYGWIN__ @@ -157,219 +226,245 @@ // Required for some versions of Cygwin to declare gettimeofday // see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin # define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif #endif // __CYGWIN__ -//////////////////////////////////////////////////////////////////////////////// -// Borland -#ifdef __BORLANDC__ - -#endif // __BORLANDC__ - -//////////////////////////////////////////////////////////////////////////////// -// EDG -#ifdef __EDG_VERSION__ - -#endif // __EDG_VERSION__ - -//////////////////////////////////////////////////////////////////////////////// -// Digital Mars -#ifdef __DMC__ - -#endif // __DMC__ - -//////////////////////////////////////////////////////////////////////////////// -// GCC -#ifdef __GNUC__ - -# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -// - otherwise more recent versions define __cplusplus >= 201103L -// and will get picked up below - -#endif // __GNUC__ - //////////////////////////////////////////////////////////////////////////////// // Visual C++ -#ifdef _MSC_VER +#if defined(_MSC_VER) -#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif -#if (_MSC_VER >= 1600) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -#endif +# if !defined(__clang__) // Handle Clang masquerading for msvc -#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) -#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -#endif +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ #endif // _MSC_VER +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + //////////////////////////////////////////////////////////////////////////////// - -// Use variadic macros if the compiler supports them -#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ - ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ - ( defined __GNUC__ && __GNUC__ >= 3 ) || \ - ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) - -#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS - +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED #endif -// Use __COUNTER__ if the compiler supports it -#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ - ( defined __GNUC__ && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \ - ( defined __clang__ && __clang_major__ >= 3 ) +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ -// Use of __COUNTER__ is suppressed during code analysis in CLion/AppCode 2017.2.x and former, -// because __COUNTER__ is not properly handled by it. -// This does not affect compilation -#if ( !defined __JETBRAINS_IDE__ || __JETBRAINS_IDE__ >= 20170300L ) +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) #define CATCH_INTERNAL_CONFIG_COUNTER #endif -#endif - //////////////////////////////////////////////////////////////////////////////// -// C++ language feature support -// catch all support for C++11 -#if defined(CATCH_CPP11_OR_GREATER) - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) -# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM -# endif - -# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE -# endif - -# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) -# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG -# endif - -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) -# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) -# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) -# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE -# endif -# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) -# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS -# endif - -#endif // __cplusplus >= 201103L - -// Now set the actual defines based on the above + anything the user has configured -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NULLPTR +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_NOEXCEPT -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_GENERATED_METHODS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_IS_ENUM -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TUPLE -#endif -#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) -# define CATCH_CONFIG_VARIADIC_MACROS -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_LONG_LONG -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_OVERRIDE -#endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_UNIQUE_PTR + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER #endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + #if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) # define CATCH_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_SHUFFLE -#endif -# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) -# define CATCH_CONFIG_CPP11_TYPE_TRAITS -# endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) # define CATCH_CONFIG_WINDOWS_SEH #endif // This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) # define CATCH_CONFIG_POSIX_SIGNALS #endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif -// noexcept support: -#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) -# define CATCH_NOEXCEPT noexcept -# define CATCH_NOEXCEPT_IS(x) noexcept(x) +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) #else -# define CATCH_NOEXCEPT throw() -# define CATCH_NOEXCEPT_IS(x) +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) #endif -// nullptr support -#ifdef CATCH_CONFIG_CPP11_NULLPTR -# define CATCH_NULL nullptr -#else -# define CATCH_NULL NULL -#endif - -// override support -#ifdef CATCH_CONFIG_CPP11_OVERRIDE -# define CATCH_OVERRIDE override -#else -# define CATCH_OVERRIDE -#endif - -// unique_ptr support -#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR -# define CATCH_AUTO_PTR( T ) std::unique_ptr -#else -# define CATCH_AUTO_PTR( T ) std::auto_ptr +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR #endif +// end catch_compiler_capabilities.h #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) #ifdef CATCH_CONFIG_COUNTER @@ -378,95 +473,48 @@ # define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) #endif -#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr -#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) +#include +#include +#include -#include -#include +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); namespace Catch { - struct IConfig; - struct CaseSensitive { enum Choice { Yes, No }; }; class NonCopyable { -#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS NonCopyable( NonCopyable const& ) = delete; NonCopyable( NonCopyable && ) = delete; NonCopyable& operator = ( NonCopyable const& ) = delete; NonCopyable& operator = ( NonCopyable && ) = delete; -#else - NonCopyable( NonCopyable const& info ); - NonCopyable& operator = ( NonCopyable const& ); -#endif protected: - NonCopyable() {} + NonCopyable(); virtual ~NonCopyable(); }; - class SafeBool { - public: - typedef void (SafeBool::*type)() const; - - static type makeSafe( bool value ) { - return value ? &SafeBool::trueValue : 0; - } - private: - void trueValue() const {} - }; - - template - void deleteAll( ContainerT& container ) { - typename ContainerT::const_iterator it = container.begin(); - typename ContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete *it; - } - template - void deleteAllValues( AssociativeContainerT& container ) { - typename AssociativeContainerT::const_iterator it = container.begin(); - typename AssociativeContainerT::const_iterator itEnd = container.end(); - for(; it != itEnd; ++it ) - delete it->second; - } - - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); - - struct pluralise { - pluralise( std::size_t count, std::string const& label ); - - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); - - std::size_t m_count; - std::string m_label; - }; - struct SourceLineInfo { - SourceLineInfo(); - SourceLineInfo( char const* _file, std::size_t _line ); -# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS - SourceLineInfo(SourceLineInfo const& other) = default; - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; -# endif - bool empty() const; - bool operator == ( SourceLineInfo const& other ) const; - bool operator < ( SourceLineInfo const& other ) const; + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; char const* file; std::size_t line; @@ -474,24 +522,17 @@ namespace Catch { std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - // This is just here to avoid compiler warnings with macro constants and boolean literals - inline bool isTrue( bool value ){ return value; } - inline bool alwaysTrue() { return true; } - inline bool alwaysFalse() { return false; } - - void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); - - void seedRng( IConfig const& config ); - unsigned int rngSeed(); + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop struct StreamEndStop { - std::string operator+() { - return std::string(); - } + std::string operator+() const; }; template T const& operator + ( T const& value, StreamEndStop ) { @@ -499,180 +540,28 @@ namespace Catch { } } -#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +// end catch_common.h namespace Catch { - class NotImplementedException : public std::exception - { - public: - NotImplementedException( SourceLineInfo const& lineInfo ); - - virtual ~NotImplementedException() CATCH_NOEXCEPT {} - - virtual const char* what() const CATCH_NOEXCEPT; - - private: - std::string m_what; - SourceLineInfo m_lineInfo; + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); }; } // end namespace Catch -/////////////////////////////////////////////////////////////////////////////// -#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -// #included from: internal/catch_context.h -#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h -// #included from: catch_interfaces_generators.h -#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED - -#include - -namespace Catch { - - struct IGeneratorInfo { - virtual ~IGeneratorInfo(); - virtual bool moveNext() = 0; - virtual std::size_t getCurrentIndex() const = 0; - }; - - struct IGeneratorsForTest { - virtual ~IGeneratorsForTest(); - - virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; - virtual bool moveNext() = 0; - }; - - IGeneratorsForTest* createGeneratorsForTest(); - -} // end namespace Catch - -// #included from: catch_ptr.hpp -#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -namespace Catch { - - // An intrusive reference counting smart pointer. - // T must implement addRef() and release() methods - // typically implementing the IShared interface - template - class Ptr { - public: - Ptr() : m_p( CATCH_NULL ){} - Ptr( T* p ) : m_p( p ){ - if( m_p ) - m_p->addRef(); - } - Ptr( Ptr const& other ) : m_p( other.m_p ){ - if( m_p ) - m_p->addRef(); - } - ~Ptr(){ - if( m_p ) - m_p->release(); - } - void reset() { - if( m_p ) - m_p->release(); - m_p = CATCH_NULL; - } - Ptr& operator = ( T* p ){ - Ptr temp( p ); - swap( temp ); - return *this; - } - Ptr& operator = ( Ptr const& other ){ - Ptr temp( other ); - swap( temp ); - return *this; - } - void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } - T* get() const{ return m_p; } - T& operator*() const { return *m_p; } - T* operator->() const { return m_p; } - bool operator !() const { return m_p == CATCH_NULL; } - operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } - - private: - T* m_p; - }; - - struct IShared : NonCopyable { - virtual ~IShared(); - virtual void addRef() const = 0; - virtual void release() const = 0; - }; - - template - struct SharedImpl : T { - - SharedImpl() : m_rc( 0 ){} - - virtual void addRef() const { - ++m_rc; - } - virtual void release() const { - if( --m_rc == 0 ) - delete this; - } - - mutable unsigned int m_rc; - }; - -} // end namespace Catch - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -namespace Catch { - - class TestCase; - class Stream; - struct IResultCapture; - struct IRunner; - struct IGeneratorsForTest; - struct IConfig; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; - virtual bool advanceGeneratorsForCurrentTest() = 0; - virtual Ptr getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( Ptr const& config ) = 0; - }; - - IContext& getCurrentContext(); - IMutableContext& getCurrentMutableContext(); - void cleanUpContext(); - Stream createStream( std::string const& streamName ); - -} - -// #included from: internal/catch_test_registry.hpp -#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED - -// #included from: catch_interfaces_testcase.h -#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED +// start catch_interfaces_testcase.h #include @@ -680,10 +569,9 @@ namespace Catch { class TestSpec; - struct ITestCase : IShared { + struct ITestInvoker { virtual void invoke () const = 0; - protected: - virtual ~ITestCase(); + virtual ~ITestInvoker(); }; class TestCase; @@ -695,167 +583,769 @@ namespace Catch { virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); } +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template