Merge commit '68019a1fb20928beaa7b0cb2d8310af29ffe789e' as 'third_party/protozero'
This commit is contained in:
commit
ba92674c6e
34
third_party/protozero/.clang-tidy
vendored
Normal file
34
third_party/protozero/.clang-tidy
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
---
|
||||
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'
|
||||
#
|
||||
# Disabled checks:
|
||||
#
|
||||
# cert-dcl21-cpp
|
||||
# It is unclear whether this is still a good recommendation in modern C++.
|
||||
#
|
||||
# cert-err60-cpp
|
||||
# Reports std::runtime_error as broken which we can't do anything about.
|
||||
#
|
||||
# cppcoreguidelines-pro-bounds-array-to-pointer-decay
|
||||
# Limited use and many false positives including for all asserts.
|
||||
#
|
||||
# cppcoreguidelines-pro-bounds-pointer-arithmetic
|
||||
# This is a low-level library, it needs to do pointer arithmetic.
|
||||
#
|
||||
# cppcoreguidelines-pro-type-reinterpret-cast
|
||||
# This is a low-level library, it needs to do reinterpret-casts.
|
||||
#
|
||||
# fuchsia-*
|
||||
# Much too strict.
|
||||
#
|
||||
# google-runtime-references
|
||||
# This is just a matter of preference, and we can't change the interfaces
|
||||
# now anyways.
|
||||
#
|
||||
# hicpp-no-array-decay
|
||||
# Limited use and many false positives including for all asserts.
|
||||
#
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '\/include\/'
|
||||
AnalyzeTemporaryDtors: false
|
||||
...
|
1
third_party/protozero/.gitattributes
vendored
Normal file
1
third_party/protozero/.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.pbf -text
|
178
third_party/protozero/.travis.yml
vendored
Normal file
178
third_party/protozero/.travis.yml
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Configuration for continuous integration service at travis-ci.org
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
language: generic
|
||||
|
||||
sudo: false
|
||||
|
||||
dist: trusty
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
# Save common build configurations as shortcuts, so we can reference them later.
|
||||
addons_shortcuts:
|
||||
addons_clang35: &clang35
|
||||
apt:
|
||||
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-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' ]
|
||||
addons_clang39: &clang39
|
||||
apt:
|
||||
sources: [ 'ubuntu-toolchain-r-test', 'llvm-toolchain-trusty-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' ]
|
||||
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
|
||||
apt:
|
||||
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||
packages: [ 'libprotobuf-dev','protobuf-compiler', 'g++-4.7', 'gcc-4.7' ]
|
||||
addons_gcc48: &gcc48
|
||||
apt:
|
||||
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||
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' ]
|
||||
addons_gcc5: &gcc5
|
||||
apt:
|
||||
sources: [ 'ubuntu-toolchain-r-test' ]
|
||||
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' ]
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: "clang-3.5"
|
||||
env: BUILD='Debug' CC=clang-3.5 CXX=clang++-3.5
|
||||
addons: *clang35
|
||||
- os: linux
|
||||
compiler: "clang-3.8"
|
||||
env: BUILD='Debug' CC=clang-3.8 CXX=clang++-3.8
|
||||
addons: *clang38
|
||||
- os: linux
|
||||
compiler: "clang-3.9"
|
||||
env: BUILD='Debug' CC=clang-3.9 CXX=clang++-3.9
|
||||
addons: *clang39
|
||||
- os: linux
|
||||
compiler: "clang-4.0"
|
||||
env: BUILD='Debug' CC=clang-4.0 CXX=clang++-4.0
|
||||
addons: *clang40
|
||||
- os: linux
|
||||
compiler: "clang-5.0"
|
||||
env: BUILD='Debug' CC=clang-5.0 CXX=clang++-5.0
|
||||
CLANG_TIDY=clang-tidy-5.0
|
||||
addons: *clang50
|
||||
- os: linux
|
||||
compiler: "clang-5.0"
|
||||
env: BUILD='Release' CC=clang-5.0 CXX=clang++-5.0
|
||||
addons: *clang50
|
||||
- os: linux
|
||||
compiler: "clang-5.0"
|
||||
env: BUILD='Debug' CC=clang-5.0 CXX=clang++-5.0
|
||||
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
|
||||
- os: linux
|
||||
compiler: "gcc-4.7"
|
||||
env: BUILD='Debug' CC=gcc-4.7 CXX=g++-4.7
|
||||
addons: *gcc47
|
||||
- os: linux
|
||||
compiler: "gcc-4.8"
|
||||
env: BUILD='Debug' CC=gcc-4.8 CXX=g++-4.8
|
||||
addons: *gcc48
|
||||
- os: linux
|
||||
compiler: "gcc-4.9"
|
||||
env: BUILD='Debug' CC=gcc-4.9 CXX=g++-4.9
|
||||
COVERAGE=gcov-4.9
|
||||
CXXFLAGS="--coverage" LDFLAGS="--coverage"
|
||||
addons: *gcc49
|
||||
- os: linux
|
||||
compiler: "gcc-5"
|
||||
env: BUILD='Debug' CC=gcc-5 CXX=g++-5
|
||||
CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=0"
|
||||
addons: *gcc5
|
||||
- os: linux
|
||||
compiler: "gcc-5"
|
||||
env: BUILD='Debug' CC=gcc-5 CXX=g++-5
|
||||
CXXFLAGS="-D_GLIBCXX_USE_CXX11_ABI=1"
|
||||
addons: *gcc5
|
||||
- os: linux
|
||||
compiler: "gcc-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
|
||||
- os: linux
|
||||
compiler: "gcc-6"
|
||||
env: BUILD='Release' CC=gcc-6 CXX=g++-6
|
||||
addons: *gcc6
|
||||
- os: osx
|
||||
osx_image: xcode6.4
|
||||
compiler: clang
|
||||
env: BUILD='Debug'
|
||||
- os: osx
|
||||
osx_image: xcode7.3
|
||||
compiler: clang
|
||||
env: BUILD='Debug'
|
||||
- os: osx
|
||||
osx_image: xcode8.3
|
||||
compiler: clang
|
||||
env: BUILD='Debug'
|
||||
- os: osx
|
||||
osx_image: xcode9.1
|
||||
compiler: clang
|
||||
env: BUILD='Debug'
|
||||
- os: osx
|
||||
osx_image: xcode9.1
|
||||
compiler: clang
|
||||
env: BUILD='Release'
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
install:
|
||||
- if [[ $(uname -s) == 'Darwin' ]]; then
|
||||
brew update;
|
||||
brew install protobuf;
|
||||
fi
|
||||
|
||||
script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. -LA -DCMAKE_BUILD_TYPE=${BUILD} -DPROTOZERO_DATA_VIEW=$PROTOZERO_DATA_VIEW -DCLANG_TIDY=$(which ${CLANG_TIDY})
|
||||
- make VERBOSE=1
|
||||
- ctest --output-on-failure
|
||||
- if [ -n "${CLANG_TIDY}" ]; then make clang-tidy; fi
|
||||
- |
|
||||
if [ -n "${COVERAGE}" ]; then
|
||||
which ${COVERAGE}
|
||||
curl -S -f https://codecov.io/bash -o codecov
|
||||
chmod +x codecov
|
||||
${COVERAGE} -p $(find test/ tools/ -name '*.o')
|
||||
./codecov -Z -f '*protozero*' -f '*tools*' -f '!*catch*' -X search
|
||||
fi
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
324
third_party/protozero/CHANGELOG.md
vendored
Normal file
324
third_party/protozero/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,324 @@
|
||||
|
||||
# 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/).
|
||||
|
||||
|
||||
## [unreleased] -
|
||||
|
||||
### Added
|
||||
|
||||
### Changed
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
## [1.6.2] - 2018-03-09
|
||||
|
||||
### Changed
|
||||
|
||||
- Update included catch.hpp to v1.12.0.
|
||||
- Move basic unit tests into their own directory (`test/unit`).
|
||||
- Improved clang-tidy config and fixed some code producing warnings.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Buffer overflow in pbf-decoder tool.
|
||||
|
||||
|
||||
## [1.6.1] - 2017-11-16
|
||||
|
||||
### Added
|
||||
|
||||
- Document internal handling of varints.
|
||||
- Add aliases for fixed iterators, too.
|
||||
|
||||
### Changed
|
||||
|
||||
- The `const_fixed_iterator` is now a random access iterator making code
|
||||
using it potentially more performant (for instance when using
|
||||
`std::distance`)
|
||||
- Overloads `std::distance` for the varint and svarint iterators. This is
|
||||
better than the workaround with the `rage_size` function used before.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Rename `.proto` files in some tests to be unique. This solves a problem
|
||||
when building with newer versions of the Google Protobuf library.
|
||||
- Floating point comparisons in tests are now always correctly done using
|
||||
`Approx()`.
|
||||
|
||||
|
||||
## [1.6.0] - 2017-10-24
|
||||
|
||||
### Added
|
||||
|
||||
- Comparison functions (<, <=, >, >=) for `data_view`. Allows use in `std::map`
|
||||
for instance.
|
||||
- Tool `pbf-decoder` for decoding raw messages. This has limited use for
|
||||
normal users, but it can be used for fuzzing.
|
||||
|
||||
### Changed
|
||||
|
||||
- Protozero now uses CMake to build the tests etc. This does not affect
|
||||
simple users of the library, but if you are using CMake yourself you might
|
||||
want to use the `cmake/FindProtozero.cmake` module provided. The README
|
||||
contains more information about build options.
|
||||
- Moved `data_view` class from `types.hpp` into its own header file
|
||||
`data_view.hpp`.
|
||||
- Implementation of the `const_fixed_iterator` to use only a single pointer
|
||||
instead of two.
|
||||
- Made `operator==` and `operator!=` on `data_view` constexpr.
|
||||
- The `pbf_reader` constructor taking a `std::pair` is deprecated. Use one
|
||||
of the other constructors instead.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Varints where the last byte was larger than what would fit in 64bit were
|
||||
triggering undefined behaviour. This can only happen when the message
|
||||
being decoded was corrupt in some way.
|
||||
- Do not assert when reading too long varints for bools any more. A valid
|
||||
encoder should never generate varints with more than one byte for bools,
|
||||
but if they are longer that's not really a problem, so just handle it.
|
||||
- Throw exception if the length of a packed repeated field of a fixed-length
|
||||
type is invalid. The length must always be a multiple of the size of the
|
||||
underlying type. This can only happen if the data is corrupted in some way,
|
||||
a valid encoder would never generate data like this.
|
||||
- Throw an exception when reading invalid tags. This can only happen if the
|
||||
data is corrupted in some way, a valid encoder would never generate invalid
|
||||
tags.
|
||||
|
||||
|
||||
## [1.5.3] - 2017-09-22
|
||||
|
||||
### Added
|
||||
|
||||
- More documentation.
|
||||
- New `size()` method on iterator range used for packed repeated fields to
|
||||
find out how many elements there are in the range. This is much faster
|
||||
compared to the `std::difference()` call you had to do before, because the
|
||||
varints don't have to be fully decoded. See [Advanced
|
||||
Topics](doc/advanced.md) for details.
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated clang-tidy settings in Makefiles and fixed a lot of minor issues
|
||||
reported by clang-tidy.
|
||||
- Update included catch.hpp to version 1.10.0.
|
||||
- Miscellaneous code cleanups.
|
||||
- Support for invalid state in `pbf_writer` and `packed_repeated_fields`.
|
||||
This fixes move construction and move assignement in `pbf_writer` and
|
||||
disables the copy construction and copy assignement which don't have
|
||||
clear semantics. It introduces an invalid or empty state in the
|
||||
`pbf_writer`, `pbf_builder`, and `packed_repeated_fields` classes used for
|
||||
default-constructed, moved from, or committed objects. There is a new
|
||||
`commit()` function for `pbf_writer` and the `packed_repeated_fields` which
|
||||
basically does the same as the destructor but can be called explicitly.
|
||||
|
||||
### Fixed
|
||||
|
||||
- The `empty()` method of the iterator range now returns a `bool` instead of
|
||||
a `size_t`.
|
||||
|
||||
|
||||
## [1.5.2] - 2017-06-30
|
||||
|
||||
### Added
|
||||
|
||||
- Add missing two-parameter version of `pbf_message::next()` function.
|
||||
- Add `data_view::empty()` function.
|
||||
- Add missing versions of `add_bytes()`, `add_string()`, and `add_message()`
|
||||
to `pbf_builder`.
|
||||
|
||||
### Changed
|
||||
|
||||
- Clarify include file usage in tutorial.
|
||||
- Updated included Catch unit test framework to version 1.9.6 and updated
|
||||
tests to work with the current version.
|
||||
- Make some constructors explicit (best practice to avoid silent conversions).
|
||||
|
||||
### Fixed
|
||||
|
||||
- Important bugfix in `data_view` equality operator. The equality operator is
|
||||
actually never used in the protozero code itself, but users of protozero
|
||||
might use it. This is a serious bug that could lead to buffer overrun type
|
||||
problems.
|
||||
|
||||
|
||||
## [1.5.1] - 2017-01-14
|
||||
|
||||
### Added
|
||||
|
||||
- Better documentation for `tag_and_type()` in doc/advanced.md.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed broken "make doc" build.
|
||||
|
||||
|
||||
## [1.5.0] - 2017-01-12
|
||||
|
||||
### Added
|
||||
|
||||
- Add `add_bytes_vectored()` methods to `pbf_writer` and `pbf_builder`. This
|
||||
allows single-copy scatter-gather type adding of data that has been prepared
|
||||
in pieces to a protobuf message.
|
||||
- New functions to check the tag and wire type at the same time: Two parameter
|
||||
version of `pbf_reader::next()` and `pbf_reader::tag_and_type()` can be used
|
||||
together with the free function `tag_and_type()` to easily and quickly check
|
||||
that not only the tag but also the wire type is correct for a field.
|
||||
|
||||
### Changed
|
||||
|
||||
- `packed_field_*` classes now work with `pbf_builder`.
|
||||
- Reorganized documentation. Advanced docs are now under doc/advanced.md.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `packed_field` class is now non-copyable because data can get corrupted if
|
||||
you copy it around.
|
||||
- Comparison operators of `data_view` now have const& parameters.
|
||||
- Make zigzag encoding/decoding functions constexpr.
|
||||
|
||||
|
||||
## [1.4.5] - 2016-11-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Undefined behaviour in packed fixed iterator. As a result, the macro
|
||||
`PROTOZERO_DO_NOT_USE_BARE_POINTER` is not used any more.
|
||||
|
||||
|
||||
## [1.4.4] - 2016-11-15
|
||||
|
||||
### Fixed
|
||||
|
||||
- Byteswap implementation.
|
||||
|
||||
|
||||
## [1.4.3] - 2016-11-15
|
||||
|
||||
### Fixed
|
||||
|
||||
- Undefined behaviour in byte swapping code.
|
||||
- Rename some parameters to avoid "shadow" warning from some compilers.
|
||||
|
||||
|
||||
## [1.4.2] - 2016-08-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- Compile fix: Variable shadowing.
|
||||
|
||||
|
||||
## [1.4.1] - 2016-08-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- GCC 4.8 compile fixed
|
||||
|
||||
### Added
|
||||
|
||||
- New ability to dynamically require the module as a node module to ease
|
||||
building against from other node C++ modules.
|
||||
|
||||
## [1.4.0] - 2016-07-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Use more efficient new `skip_varint()` function when iterating over
|
||||
packed varints.
|
||||
- Split `decode_varint()` function into two functions speeding up the
|
||||
common case where a varint is only one byte long.
|
||||
- Introduce new class `iterator_range` used instead of `std::pair` of
|
||||
iterators. This way the objects can be used in range-based for loops.
|
||||
Read UPGRADING.md for details.
|
||||
- Introduce new class `data_view` and functions using and returning it.
|
||||
Read UPGRADING.md for details.
|
||||
|
||||
|
||||
## [1.3.0] - 2016-02-18
|
||||
|
||||
### Added
|
||||
|
||||
- Added `config.hpp` header which now includes all the macro magic to
|
||||
configure the library for different architectures etc.
|
||||
- New way to create repeated packed fields without using an iterator.
|
||||
- Add `rollback()` function to `pbf_writer` for "manual" rollback.
|
||||
|
||||
### Changed
|
||||
|
||||
- Various test and documentation cleanups.
|
||||
- Rename `pbf_types.hpp` to `types.hpp`.
|
||||
|
||||
|
||||
## [1.2.3] - 2015-11-30
|
||||
|
||||
### Added
|
||||
|
||||
- Added `config.hpp` header which now includes all the macro magic to
|
||||
configure the library for different architectures etc.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Unaligned access to floats/doubles on some ARM architectures.
|
||||
|
||||
|
||||
## [1.2.2] - 2015-10-13
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix the recently broken writing of bools on big-endian architectures.
|
||||
|
||||
|
||||
## [1.2.1] - 2015-10-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Removed unneeded code (1-byte "swap") which lead to test failures.
|
||||
|
||||
|
||||
## [1.2.0] - 2015-10-08
|
||||
|
||||
### Added
|
||||
|
||||
- `pbf_message` and `pbf_builder` template classes wrapping `pbf_reader`
|
||||
and `pbf_writer`, respectively. The new classes are the preferred
|
||||
interface now.
|
||||
|
||||
### Changed
|
||||
|
||||
- Improved byte swapping operation.
|
||||
- Detect some types of data corruption earlier and throw.
|
||||
|
||||
|
||||
## [1.1.0] - 2015-08-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Make pbf reader and writer code endianess-aware.
|
||||
|
||||
|
||||
[unreleased]: https://github.com/osmcode/libosmium/compare/v1.6.2...HEAD
|
||||
[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
|
||||
[1.5.3]: https://github.com/osmcode/libosmium/compare/v1.5.2...v1.5.3
|
||||
[1.5.2]: https://github.com/osmcode/libosmium/compare/v1.5.1...v1.5.2
|
||||
[1.5.1]: https://github.com/osmcode/libosmium/compare/v1.5.0...v1.5.1
|
||||
[1.5.0]: https://github.com/osmcode/libosmium/compare/v1.4.5...v1.5.0
|
||||
[1.4.5]: https://github.com/osmcode/libosmium/compare/v1.4.4...v1.4.5
|
||||
[1.4.4]: https://github.com/osmcode/libosmium/compare/v1.4.3...v1.4.4
|
||||
[1.4.3]: https://github.com/osmcode/libosmium/compare/v1.4.2...v1.4.3
|
||||
[1.4.2]: https://github.com/osmcode/libosmium/compare/v1.4.1...v1.4.2
|
||||
[1.4.1]: https://github.com/osmcode/libosmium/compare/v1.4.0...v1.4.1
|
||||
[1.4.0]: https://github.com/osmcode/libosmium/compare/v1.3.0...v1.4.0
|
||||
[1.3.0]: https://github.com/osmcode/libosmium/compare/v1.2.3...v1.3.0
|
||||
[1.2.3]: https://github.com/osmcode/libosmium/compare/v1.2.2...v1.2.3
|
||||
[1.2.2]: https://github.com/osmcode/libosmium/compare/v1.2.1...v1.2.2
|
||||
[1.2.1]: https://github.com/osmcode/libosmium/compare/v1.2.0...v1.2.1
|
||||
[1.2.0]: https://github.com/osmcode/libosmium/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/osmcode/libosmium/compare/v1.0.0...v1.1.0
|
||||
|
145
third_party/protozero/CMakeLists.txt
vendored
Normal file
145
third_party/protozero/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# CMake config
|
||||
#
|
||||
# protozero
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
cmake_minimum_required(VERSION 2.8 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}")
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
option(WERROR "Add -Werror flag to build (turns warnings into errors)" ON)
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(-std=c++11 /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)
|
||||
if(WERROR)
|
||||
add_definitions(-Werror)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include_directories("${CMAKE_SOURCE_DIR}/include")
|
||||
|
||||
set(PROTOZERO_DATA_VIEW "" CACHE STRING "Type used for protozero::data_view")
|
||||
if(NOT PROTOZERO_DATA_VIEW STREQUAL "")
|
||||
add_definitions(-DPROTOZERO_DATA_VIEW=${PROTOZERO_DATA_VIEW})
|
||||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Find dependencies
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
find_package(Protobuf)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Optional "clang-tidy" target
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
message(STATUS "Looking for clang-tidy")
|
||||
find_program(CLANG_TIDY NAMES clang-tidy clang-tidy-6.0 clang-tidy-5.0)
|
||||
|
||||
if(CLANG_TIDY)
|
||||
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/unit/*.cpp
|
||||
${CMAKE_SOURCE_DIR}/tools/*.cpp
|
||||
)
|
||||
add_dependencies(clang-tidy writer_tests)
|
||||
else()
|
||||
message(STATUS "Looking for clang-tidy - not found")
|
||||
message(STATUS " Build target 'clang-tidy' will not be available.")
|
||||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Optional "cppcheck" target
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
message(STATUS "Looking for cppcheck")
|
||||
find_program(CPPCHECK NAMES cppcheck)
|
||||
|
||||
if(CPPCHECK)
|
||||
message(STATUS "Looking for cppcheck - found")
|
||||
add_custom_target(cppcheck
|
||||
${CPPCHECK}
|
||||
-Uassert --std=c++11 --enable=all
|
||||
${CMAKE_SOURCE_DIR}/include/protozero/*.hpp
|
||||
${CMAKE_SOURCE_DIR}/test/*.cpp
|
||||
${CMAKE_SOURCE_DIR}/test/include/*.hpp
|
||||
${CMAKE_SOURCE_DIR}/test/t/*/*.cpp
|
||||
${CMAKE_SOURCE_DIR}/test/unit/*.cpp
|
||||
${CMAKE_SOURCE_DIR}/tools/*.cpp
|
||||
)
|
||||
else()
|
||||
message(STATUS "Looking for cppcheck - not found")
|
||||
message(STATUS " Build target 'cppcheck' will not be available.")
|
||||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Include what you use
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
message(STATUS "Looking for iwyu")
|
||||
find_program(IWYU_TOOL NAMES iwyu_tool)
|
||||
|
||||
if(IWYU_TOOL)
|
||||
message(STATUS "Looking for iwyu - found")
|
||||
add_custom_target(iwyu
|
||||
${IWYU_TOOL} -p ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
else()
|
||||
message(STATUS "Looking for iwyu - not found")
|
||||
message(STATUS " Build target 'iwyu' will not be available.")
|
||||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Installation
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
install(DIRECTORY include/protozero DESTINATION include)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_subdirectory(doc)
|
||||
|
||||
add_subdirectory(tools)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
21
third_party/protozero/CONTRIBUTING.md
vendored
Normal file
21
third_party/protozero/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Contributing to protozero
|
||||
|
||||
## Releasing
|
||||
|
||||
To release a new protozero version:
|
||||
|
||||
- Make sure all tests are passing locally, on travis and on appveyor
|
||||
- Make sure "make doc" builds
|
||||
- Update version number in
|
||||
- include/protozero/version.hpp (two places)
|
||||
- CMakeLists.txt (one place)
|
||||
- Update CHANGELOG.md
|
||||
(don't forget links at the bottom of the file)
|
||||
- Update UPGRADING.md if necessary
|
||||
- `git commit -m "Release X.Y.Z" include/protozero/version.hpp CMakeLists.txt CHANGELOG.md UPGRADING.md`
|
||||
- `git tag vX.Y.Z`
|
||||
- `git push`
|
||||
- `git push --tags`
|
||||
- Go to https://github.com/mapbox/protozero/releases
|
||||
and edit the new release. Put "Version x.y.z" in title and
|
||||
cut-and-paste entry from CHANGELOG.md.
|
22
third_party/protozero/FUZZING.md
vendored
Normal file
22
third_party/protozero/FUZZING.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
To do fuzz testing using [AFL](http://lcamtuf.coredump.cx/afl/) compile with
|
||||
the AFL compiler wrappers:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
CC=afl-clang CXX=afl-clang++ cmake ..
|
||||
mkdir testcase_dir
|
||||
|
||||
You need some data to start the fuzzing. In this case I am using all the test
|
||||
messages from the unit tests:
|
||||
|
||||
find ../test/t/ -name data-\*.pbf -a -not -empty -exec cp {} testcase_dir/ \;
|
||||
|
||||
Then do the actual fuzzing:
|
||||
|
||||
afl-fuzz -i testcase_dir -o findings_dir -- tools/pbf-decoder -
|
||||
|
||||
See the AFL documentation for more information.
|
||||
|
||||
This only checkes the reading side of Protozero!
|
||||
|
177
third_party/protozero/LICENSE.from_folly
vendored
Normal file
177
third_party/protozero/LICENSE.from_folly
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
24
third_party/protozero/LICENSE.md
vendored
Normal file
24
third_party/protozero/LICENSE.md
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
protozero copyright (c) Mapbox.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
156
third_party/protozero/README.md
vendored
Normal file
156
third_party/protozero/README.md
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
# protozero
|
||||
|
||||
Minimalistic protocol buffer decoder and encoder in C++.
|
||||
|
||||
Designed for high performance. Suitable for writing zero copy parsers and
|
||||
encoders with minimal need for run-time allocation of memory.
|
||||
|
||||
Low-level: this is designed to be a building block for writing a very
|
||||
customized decoder for a stable protobuf schema. If your protobuf schema is
|
||||
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.
|
||||
|
||||
[](https://travis-ci.org/mapbox/protozero)
|
||||
[](https://ci.appveyor.com/project/Mapbox/protozero)
|
||||
[](https://codecov.io/gh/mapbox/protozero)
|
||||
[](https://repology.org/metapackage/protozero)
|
||||
|
||||
## Depends
|
||||
|
||||
* C++11 compiler
|
||||
* CMake
|
||||
* Some tests depend on the Google Protobuf library, but use of Protozero
|
||||
doesn't need it
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
The protozero code does **not** read `.proto` files used by the usual Protobuf
|
||||
implementations. The developer using protozero has to manually "translate" the
|
||||
`.proto` description into code. This means there is no way to access any of the
|
||||
information from the `.proto` description. This results in a few restrictions:
|
||||
|
||||
* The names of the fields are not available.
|
||||
* Enum names are not available, you'll have to use the values they are defined
|
||||
with.
|
||||
* Default values are not available.
|
||||
* Field types have to be hardcoded. The library does not know which types to
|
||||
expect, so the user of the library has to supply the right types. Some checks
|
||||
are made using `assert()`, but mostly the user has to take care of that.
|
||||
|
||||
The library will make sure not to overrun the buffer it was given, but
|
||||
basically all other checks have to be made in user code!
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
You have to have a working knowledge of how
|
||||
[protocol buffer encoding works](https://developers.google.com/protocol-buffers/docs/encoding).
|
||||
|
||||
* Read the [tutorial](doc/tutorial.md) for an introduction on how to use
|
||||
Protozero.
|
||||
* Some advanced topics are described in an [extra document](doc/advanced.md).
|
||||
* There is a table of all types and functions in the
|
||||
[cheat sheet](doc/cheatsheet.md).
|
||||
* 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.
|
||||
|
||||
|
||||
## Endianness
|
||||
|
||||
Protozero uses a very simplistic test to check the byte order of the system it
|
||||
compiles on. If this check is wrong, you'll get test failures. If this is the
|
||||
case, please [open an issue](https://github.com/mapbox/protozero/issues) and
|
||||
tell us about your system.
|
||||
|
||||
|
||||
## Building tests
|
||||
|
||||
Extensive tests are included. Build them using CMake:
|
||||
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
|
||||
Call `ctest` to run the tests.
|
||||
|
||||
The unit and reader tests are always build, the writer tests are only build if
|
||||
the Google Protobuf library is found when running CMake.
|
||||
|
||||
See `test/README.md` for more details about the test.
|
||||
|
||||
|
||||
## Coverage report
|
||||
|
||||
To get a coverage report set `CXXFLAGS` and `LDFLAGS` before calling CMake:
|
||||
|
||||
CXXFLAGS="--coverage" LDFLAGS="--coverage" cmake ..
|
||||
|
||||
Then call `make` as usual and run the tests using `ctest`.
|
||||
|
||||
If you are using `g++` use `gcov` to generate a report (results are in `*.gcov`
|
||||
files):
|
||||
|
||||
gcov -lp $(find test/ -name '*.o')
|
||||
|
||||
If you are using `clang++` use `llvm-cov` instead:
|
||||
|
||||
llvm-cov gcov -lp $(find test/ -name '*.o')
|
||||
|
||||
If you are using `g++` you can use `gcovr` to generate nice HTML output:
|
||||
|
||||
mkdir -p coverage
|
||||
gcovr . -r SRCDIR --html --html-details -o coverage/index.html
|
||||
|
||||
Open `coverage/index.html` in your browser to see the report.
|
||||
|
||||
|
||||
## Clang-tidy
|
||||
|
||||
After the CMake step, run
|
||||
|
||||
make clang-tidy
|
||||
|
||||
to check the code with [clang-tidy](https://clang.llvm.org/extra/clang-tidy/).
|
||||
You might have to set `CLANG_TIDY` in CMake config.
|
||||
|
||||
|
||||
## Cppcheck
|
||||
|
||||
For extra checks with [Cppcheck](http://cppcheck.sourceforge.net/) you can,
|
||||
after the CMake step, call
|
||||
|
||||
make cppcheck
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
After the CMake step, call `make install` to install the include files in
|
||||
`/usr/local/include/protozero`.
|
||||
|
||||
If you are using CMake to build your own software, you can copy the file
|
||||
`cmake/FindProtozero.cmake` and use it in your build. See the file for
|
||||
details.
|
||||
|
||||
|
||||
## Who is using Protozero?
|
||||
|
||||
* [Carmen](https://github.com/mapbox/carmen-cache)
|
||||
* [Libosmium](https://github.com/osmcode/libosmium)
|
||||
* [Mapbox GL Native](https://github.com/mapbox/mapbox-gl-native)
|
||||
* [Mapbox Vector Tile library](https://github.com/mapbox/vector-tile)
|
||||
* [Mapnik](https://github.com/mapbox/mapnik-vector-tile)
|
||||
* [OSRM](https://github.com/Project-OSRM/osrm-backend)
|
||||
* [Tippecanoe](https://github.com/mapbox/tippecanoe)
|
||||
* [Vtzero](https://github.com/mapbox/vtzero)
|
||||
|
||||
Are you using Protozero? Tell us! Send a pull request with changes to this
|
||||
README.
|
||||
|
||||
|
96
third_party/protozero/UPGRADING.md
vendored
Normal file
96
third_party/protozero/UPGRADING.md
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
# Upgrading
|
||||
|
||||
This file contains instructions for users of Protozero who are upgrading from
|
||||
one version to another.
|
||||
|
||||
You do not need to change anything if only the minor version changes, but it
|
||||
is better to keep up with changes if you can. The switch to the next major
|
||||
version will be easier then. And you might get some more convenient usage.
|
||||
|
||||
To help you with upgrading to new versions, you can define the C++ preprocessor
|
||||
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.5* to *v1.6.0*
|
||||
|
||||
* The `data_view` class moved from `types.hpp` into its own header file
|
||||
`data_view.hpp`. Most people should not include those headers directly,
|
||||
but if you do, you might have to change your includes.
|
||||
* There are two new exceptions `invalid_tag_exception` and
|
||||
`invalid_length_exception` which cover cases that were only checked by
|
||||
`assert` before this version. If you catch specific exceptions in your code
|
||||
you might have to amend it. But just catching `protozero::exception` is
|
||||
usually fine for most code (if you catch exceptions at all).
|
||||
* The `pbf_reader` constructor taking a `std::pair` is now deprecated. If you
|
||||
are compiling with `PROTOZERO_STRICT_API` it is not available any more. Use
|
||||
one of the other constructors instead.
|
||||
|
||||
## Upgrading from *v1.4.5* to *v1.5.0*
|
||||
|
||||
* New functions for checking tag and type at the same time to make your
|
||||
program more robust. Read the section "Repeated fields in messages" in
|
||||
the new [Advanced Topics documentation](doc/advanced.md).
|
||||
|
||||
## Upgrading from *v1.4.4* to *v1.4.5*
|
||||
|
||||
* The macro `PROTOZERO_DO_NOT_USE_BARE_POINTER` is not used any more. If you
|
||||
have been setting this, remove it.
|
||||
|
||||
## Upgrading from *v1.4.0* to *v1.4.1*
|
||||
|
||||
* You can now do `require('protozero')` in nodejs to print the path
|
||||
to the include paths for the protozero headers.
|
||||
|
||||
## Upgrading from *v1.3.0* to *v1.4.0*
|
||||
|
||||
* Functions in `pbf_reader` (and the derived `pbf_message`) called
|
||||
`get_packed_*()` now return an `iterator_range` instead of a `std::pair`.
|
||||
The new class is derived from `std::pair`, so changes are usually not
|
||||
strictly necessary. For future compatibility, you should change all
|
||||
attribute accesses on the returned objects from `first` and `second` to
|
||||
`begin()` and `end()`, respectively. So change something like this:
|
||||
|
||||
auto x = message.get_packed_int32();
|
||||
for (auto it = x.first; it != x.second; ++it) {
|
||||
....
|
||||
}
|
||||
|
||||
to:
|
||||
|
||||
auto x = message.get_packed_int32();
|
||||
for (auto it = x.begin(); it != x.end(); ++it) {
|
||||
....
|
||||
}
|
||||
|
||||
or even better use the range-based for loop:
|
||||
|
||||
auto x = message.get_packed_int32();
|
||||
for (auto val : x) {
|
||||
....
|
||||
}
|
||||
|
||||
Ranges can also be used in this way. This will change the range in-place:
|
||||
|
||||
auto range = message.get_packed_int32();
|
||||
while (!range.empty()) {
|
||||
auto value = range.front();
|
||||
range.drop_front();
|
||||
....
|
||||
}
|
||||
|
||||
* The class `pbf_reader` has a new method `get_view()` returning an object
|
||||
of the new `protozero::data_view` class. The `data_view` only has minimal
|
||||
functionality, but what it has is compatible to the `std::string_view` class
|
||||
which will be coming in C++17. The view autoconverts to a `std::string` if
|
||||
needed. Use `get_view()` instead of `get_data()` giving you a more intuitive
|
||||
interface (call `data()` and `size()` on the view instead of using `first`
|
||||
and `second` on the `std::pair` returned by `get_data()`).
|
||||
|
||||
You can set the macro `PROTOZERO_USE_VIEW` (before including `types.hpp`) to
|
||||
the name of any class that behaves like `protozero::data_view` and
|
||||
`data_view` will be an alias to that class instead of the implementation
|
||||
from protozero. This way you can use the C++17 `string_view` or a similar
|
||||
class if it is already available on your system.
|
||||
|
57
third_party/protozero/appveyor.yml
vendored
Normal file
57
third_party/protozero/appveyor.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# Configuration for continuous integration service at appveyor.com
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
platform: x64
|
||||
|
||||
image: Visual Studio 2017
|
||||
|
||||
clone_depth: 1
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- config: MSYS2
|
||||
autocrlf: true
|
||||
- config: Debug
|
||||
autocrlf: true
|
||||
- config: RelWithDebInfo
|
||||
autocrlf: true
|
||||
- config: Debug
|
||||
autocrlf: false
|
||||
- config: RelWithDebInfo
|
||||
autocrlf: false
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
init:
|
||||
- git config --global core.autocrlf %autocrlf%
|
||||
- git config --get core.autocrlf
|
||||
|
||||
# The option --ask=20 is a workaround for a problem with the MSYS2 update
|
||||
# process. Without it the following error is printed and the appveyor script
|
||||
# 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
|
||||
)
|
||||
|
||||
build_script:
|
||||
- if [%config%]==[MSYS2] (
|
||||
build-msys2.bat
|
||||
) else (
|
||||
build-appveyor.bat
|
||||
)
|
||||
|
||||
# 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"
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
13
third_party/protozero/bench/data/README.md
vendored
Normal file
13
third_party/protozero/bench/data/README.md
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
mapbox-streets-v6/14/8714/8017.vector.pbf
|
||||
|
||||
- http://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/
|
||||
|
||||
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/
|
BIN
third_party/protozero/bench/data/enf-14-4824-6157.vector.pbf
vendored
Normal file
BIN
third_party/protozero/bench/data/enf-14-4824-6157.vector.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/bench/data/mapbox-streets-v6-14-8714-8017.vector.pbf
vendored
Normal file
BIN
third_party/protozero/bench/data/mapbox-streets-v6-14-8714-8017.vector.pbf
vendored
Normal file
Binary file not shown.
65
third_party/protozero/build-appveyor.bat
vendored
Normal file
65
third_party/protozero/build-appveyor.bat
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
@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%
|
29
third_party/protozero/build-local.bat
vendored
Normal file
29
third_party/protozero/build-local.bat
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
@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%
|
18
third_party/protozero/build-msys2.bat
vendored
Normal file
18
third_party/protozero/build-msys2.bat
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
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
|
||||
|
63
third_party/protozero/cmake/FindProtozero.cmake
vendored
Normal file
63
third_party/protozero/cmake/FindProtozero.cmake
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
#----------------------------------------------------------------------
|
||||
#
|
||||
# FindProtozero.cmake
|
||||
#
|
||||
# Find the protozero headers.
|
||||
#
|
||||
#----------------------------------------------------------------------
|
||||
#
|
||||
# Usage:
|
||||
#
|
||||
# Copy this file somewhere into your project directory, where cmake can
|
||||
# find it. Usually this will be a directory called "cmake" which you can
|
||||
# add to the CMake module search path with the following line in your
|
||||
# CMakeLists.txt:
|
||||
#
|
||||
# list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
#
|
||||
# Then add the following in your CMakeLists.txt:
|
||||
#
|
||||
# find_package(Protozero [version] [REQUIRED])
|
||||
# include_directories(SYSTEM ${PROTOZERO_INCLUDE_DIR})
|
||||
#
|
||||
# The version number is optional. If it is not set, any version of
|
||||
# protozero will do.
|
||||
#
|
||||
# if(NOT PROTOZERO_FOUND)
|
||||
# message(WARNING "Protozero not found!\n")
|
||||
# endif()
|
||||
#
|
||||
#----------------------------------------------------------------------
|
||||
#
|
||||
# Variables:
|
||||
#
|
||||
# PROTOZERO_FOUND - True if Protozero was found.
|
||||
# PROTOZERO_INCLUDE_DIR - Where to find include files.
|
||||
#
|
||||
#----------------------------------------------------------------------
|
||||
|
||||
# find include path
|
||||
find_path(PROTOZERO_INCLUDE_DIR protozero/version.hpp
|
||||
PATH_SUFFIXES include
|
||||
PATHS ${CMAKE_SOURCE_DIR}/../protozero
|
||||
)
|
||||
|
||||
# Check version number
|
||||
if(Protozero_FIND_VERSION)
|
||||
file(STRINGS "${PROTOZERO_INCLUDE_DIR}/protozero/version.hpp" _version_define REGEX "#define PROTOZERO_VERSION_STRING")
|
||||
if("${_version_define}" MATCHES "#define PROTOZERO_VERSION_STRING \"([0-9.]+)\"")
|
||||
set(_version "${CMAKE_MATCH_1}")
|
||||
else()
|
||||
set(_version "unknown")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#set(PROTOZERO_INCLUDE_DIRS "${PROTOZERO_INCLUDE_DIR}")
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Protozero
|
||||
REQUIRED_VARS PROTOZERO_INCLUDE_DIR
|
||||
VERSION_VAR _version)
|
||||
|
||||
|
||||
#----------------------------------------------------------------------
|
37
third_party/protozero/doc/CMakeLists.txt
vendored
Normal file
37
third_party/protozero/doc/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# CMake Config
|
||||
#
|
||||
# protozero documentation
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
message(STATUS "Configuring documentation")
|
||||
|
||||
message(STATUS "Looking for doxygen")
|
||||
find_package(Doxygen)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
message(STATUS "Looking for doxygen - found")
|
||||
configure_file(Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
|
||||
|
||||
file(GLOB HEADER_FILES "${CMAKE_SOURCE_DIR}/include/protozero/*.hpp")
|
||||
add_custom_command(OUTPUT html/index.html
|
||||
COMMAND ${DOXYGEN_EXECUTABLE}
|
||||
ARGS ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
|
||||
DEPENDS Doxyfile.in advanced.md cheatsheet.md tutorial.md
|
||||
${HEADER_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
COMMENT "Generating API documentation with Doxygen" VERBATIM)
|
||||
add_custom_target(doc ALL
|
||||
DEPENDS html/index.html)
|
||||
else()
|
||||
message(STATUS "Looking for doxygen - not found")
|
||||
message(STATUS " Disabled making of documentation.")
|
||||
endif()
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
message(STATUS "Configuring documentation - done")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
2355
third_party/protozero/doc/Doxyfile.in
vendored
Normal file
2355
third_party/protozero/doc/Doxyfile.in
vendored
Normal file
File diff suppressed because it is too large
Load Diff
271
third_party/protozero/doc/advanced.md
vendored
Normal file
271
third_party/protozero/doc/advanced.md
vendored
Normal file
@ -0,0 +1,271 @@
|
||||
|
||||
# Protozero Advanced Topics
|
||||
|
||||
This documentation contains some mixed advanced topics for Protozero users.
|
||||
Read the [tutorial](tutorial.md) first if you are new to Protozero.
|
||||
|
||||
|
||||
## Limitations of Protozero
|
||||
|
||||
* A protobuf message has to fit into memory completely, otherwise it can not
|
||||
be parsed with this library. There is no streaming support.
|
||||
* The length of a string, bytes, or submessage can't be more than 2^31-1.
|
||||
* There is no specific support for maps but they can be used as described in
|
||||
the "Backwards compatibility" section of
|
||||
https://developers.google.com/protocol-buffers/docs/proto3#maps.
|
||||
|
||||
|
||||
## Checking the Protozero version number
|
||||
|
||||
If `protozero/version.hpp` is included, the following macros are set:
|
||||
|
||||
| Macro | Example | Description |
|
||||
| -------------------------- | ------- | ---------------------------------------------- |
|
||||
| `PROTOZERO_VERSION_MAJOR` | 1 | Major version number |
|
||||
| `PROTOZERO_VERSION_MINOR` | 3 | Minor version number |
|
||||
| `PROTOZERO_VERSION_PATCH` | 2 | Patch number |
|
||||
| `PROTOZERO_VERSION_CODE` | 10302 | Version (major * 10,000 + minor * 100 + patch) |
|
||||
| `PROTOZERO_VERSION_STRING` | "1.3.2" | Version string |
|
||||
|
||||
|
||||
## Changing Protozero behaviour with macros
|
||||
|
||||
The behaviour of Protozero can be changed by defining the following macros.
|
||||
They have to be set before including any of the Protozero headers.
|
||||
|
||||
### `PROTOZERO_STRICT_API`
|
||||
|
||||
If this is set, you will get some extra warnings or errors during compilation
|
||||
if you are using an old (deprecated) interface to Protozero. Enable this if
|
||||
you want to make sure your code will work with future versions of Protozero.
|
||||
|
||||
### `PROTOZERO_USE_VIEW`
|
||||
|
||||
Protozero uses the class `protozero::data_view` as the return type of the
|
||||
`pbf_reader::get_view()` method and a few other functions take a
|
||||
`protozero::data_view` as parameter.
|
||||
|
||||
If `PROTOZERO_USE_VIEW` is unset, `protozero::data_view` is Protozero's own
|
||||
implementation of a *string view* class.
|
||||
|
||||
Set this macro if you want to use a different implementation such as the C++17
|
||||
`std::string_view` class. In this case `protozero::data_view` will simply be
|
||||
an alias to the class you specify.
|
||||
|
||||
#define PROTOZERO_USE_VIEW std::string_view
|
||||
|
||||
|
||||
## Repeated fields in messages
|
||||
|
||||
The Google Protobuf spec documents that a non-repeated field can actually
|
||||
appear several times in a message and the implementation is required to return
|
||||
the value of the last version of that field in this case. `pbf_reader.hpp` does
|
||||
not enforce this. If this feature is needed in your case, you have to do this
|
||||
yourself.
|
||||
|
||||
The [spec also
|
||||
says](https://developers.google.com/protocol-buffers/docs/encoding#packed)
|
||||
that you must be able to read a packed repeated field where a not-packed
|
||||
repeated field is expected and vice versa. Also there can be several (packed or
|
||||
not-packed) repeated fields with the same tag and their contents must be
|
||||
concatenated. It is your responsibility to do this, Protozero doesn't do that
|
||||
for you.
|
||||
|
||||
### Using `tag_and_type()`
|
||||
|
||||
The `tag_and_type()` free function and the method of the same name on the
|
||||
`pbf_reader` and `pbf_message` classes can be used to access both packed and
|
||||
unpacked repeated fields. (It can also be used to check that you have the
|
||||
right type of encoding for other fields.)
|
||||
|
||||
Here is the outline:
|
||||
|
||||
```cpp
|
||||
enum class ExampleMsg : protozero::pbf_tag_type {
|
||||
repeated_uint32_x = 1
|
||||
};
|
||||
|
||||
std::string data = ...
|
||||
pbf_message<ExampleMsg> message{data};
|
||||
while (message.next()) {
|
||||
switch (message.tag_and_type()) {
|
||||
case tag_and_type(ExampleMsg::repeated_uint32_x, pbf_wire_type::length_delimited): {
|
||||
auto xit = message.get_packed_uint32();
|
||||
... // handle the repeated field when it is packed
|
||||
}
|
||||
break;
|
||||
case tag_and_type(ExampleMsg::repeated_uint32_x, pbf_wire_type::varint): {
|
||||
auto x = message.get_uint32();
|
||||
... // handle the repeated field when it is not packed
|
||||
}
|
||||
break;
|
||||
default:
|
||||
message.skip();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All this works on `pbf_reader` in the same way as with `pbf_message` with the
|
||||
usual difference that `pbf_reader` takes a numeric field tag and `pbf_message`
|
||||
an enum field.
|
||||
|
||||
If you only want to check for one specific tag and type you can use the
|
||||
two-argument version of `pbf_reader::next()`. In this case `17` is the field
|
||||
tag we are looking for:
|
||||
|
||||
```cpp
|
||||
std::string data = ...
|
||||
pbf_reader message{data};
|
||||
while (message.next(17, pbf_wire_type::varint)) {
|
||||
auto foo = message.get_int32();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
See the test under `test/t/tag_and_type/` for a complete example.
|
||||
|
||||
|
||||
## Reserving memory when writing messages
|
||||
|
||||
If you know beforehand how large a message will become or can take an educated
|
||||
guess, you can call the usual `std::string::reserve()` on the underlying string
|
||||
before you give it to an `pbf_writer` or `pbf_builder` object.
|
||||
|
||||
Or you can (at any time) call `reserve()` on the `pbf_writer` or `pbf_builder`.
|
||||
This will reserve the given amount of bytes *in addition to whatever is already
|
||||
in that message*. (Note that this behaviour is different then what `reserve()`
|
||||
does on `std::string` or `std::vector`.)
|
||||
|
||||
In the general case it is not easy to figure out how much memory you will need
|
||||
because of the varint packing of integers. But sometimes you can make at least
|
||||
a rough estimate. Still, you should probably only use this facility if you have
|
||||
benchmarks proving that it actually makes your program faster.
|
||||
|
||||
|
||||
## Using the low-level varint and zigzag encoding and decoding functions
|
||||
|
||||
Protozero gives you access to the low-level functions for encoding and
|
||||
decoding varint and zigzag integer encodings, because these functions can
|
||||
sometimes be useful outside the Protocol Buffer context.
|
||||
|
||||
### Using low-level functions
|
||||
|
||||
To use the low-level functions, add this include to your C++ program:
|
||||
|
||||
```cpp
|
||||
#include <protozero/varint.hpp>
|
||||
```
|
||||
|
||||
### Functions
|
||||
|
||||
The following functions are then available:
|
||||
|
||||
```cpp
|
||||
decode_varint()
|
||||
write_varint()
|
||||
encode_zigzag32()
|
||||
encode_zigzag64()
|
||||
decode_zigzag32()
|
||||
decode_zigzag64()
|
||||
```
|
||||
|
||||
See the reference documentation created by `make doc` for details.
|
||||
|
||||
|
||||
## Vectored input for length-delimited fields
|
||||
|
||||
Length-delimited fields (like string fields, byte fields and messages) are
|
||||
usually set by calling `add_string()`, `add_message()`, etc. These functions
|
||||
have several forms, but they basically all take a *tag*, a *size*, and a
|
||||
*pointer to the data*. They write the length of the data into the message
|
||||
and then copy the data over.
|
||||
|
||||
Sometimes you have the data not in one place, but spread over several
|
||||
buffers. In this case you have to consolidate those buffers first, which needs
|
||||
an extra copy. Say you have two very long strings that should be concatenated
|
||||
into a message:
|
||||
|
||||
```cpp
|
||||
std::string a{"very long string..."};
|
||||
std::string b{"another very long string..."};
|
||||
|
||||
std::string data;
|
||||
protozero::pbf_writer writer{data};
|
||||
|
||||
a.append(b); // expensive extra copy
|
||||
|
||||
writer.add_string(1, a);
|
||||
```
|
||||
|
||||
To avoid this, the function `add_bytes_vectored()` can be used which allows
|
||||
vectored (or scatter/gather) input like this:
|
||||
|
||||
```cpp
|
||||
std::string a{"very long string..."};
|
||||
std::string b{"another very long string..."};
|
||||
|
||||
std::string data;
|
||||
protozero::pbf_writer writer{data};
|
||||
|
||||
writer.add_bytes_vectored(1, a, b);
|
||||
```
|
||||
|
||||
`add_bytes_vectored()` will add up the sizes of all its arguments and copy over
|
||||
all the data only once.
|
||||
|
||||
The function takes any number of arguments. The arguments must be of a type
|
||||
supporting the `data()` and `size()` methods like `protozero::data_view()`,
|
||||
`std::string` or the C++17 `std::string_view`.
|
||||
|
||||
Note that there is only one version of the function which can be used for any
|
||||
length-delimited field including strings, bytes, messages and repeated packed
|
||||
fields.
|
||||
|
||||
The function is also available in the `pbf_builder` class.
|
||||
|
||||
|
||||
## Internal handling of varints
|
||||
|
||||
When varints are decoded they are always decoded as 64bit unsigned integers and
|
||||
after that casted to the type you are requesting (using `static_cast`). This
|
||||
means that if the protocol buffer message was created with a different integer
|
||||
type than what you are reading it with, you might get wrong results without any
|
||||
warning or error. This is the same behaviour as the Google Protocol Buffers
|
||||
library has.
|
||||
|
||||
In normal use, this should never matter, because presumably you are using the
|
||||
same types to write that data as you are using to read it later. It can happen
|
||||
if the data is corrupted intentionally or unintentionally in some way. But
|
||||
this can't be used to feed you any data that it wasn't possible to feed you
|
||||
without this behaviour, so it doesn't open up any potential problems. You
|
||||
always have to check anyway that the integers are in the range you expected
|
||||
them to be in if the expected range is different than the range of the integer
|
||||
type. This is especially true for enums which protozero will return as
|
||||
`int32_t`.
|
||||
|
||||
|
||||
## How many items are there in a repeated packed field?
|
||||
|
||||
Sometimes it is useful to know how many values there are in a repeated packed
|
||||
field. For instance when you want to reserve space in a `std::vector`.
|
||||
|
||||
```cpp
|
||||
protozero::pbf_reader message{...};
|
||||
message.next(...);
|
||||
const auto range = message.get_packed_sint32();
|
||||
|
||||
std::vector<int> myvalues;
|
||||
myvalues.reserve(range.size());
|
||||
|
||||
for (auto value : range) {
|
||||
myvalues.push_back(value);
|
||||
}
|
||||
```
|
||||
|
||||
It depends on the type of range how expensive the `size()` call is. For ranges
|
||||
derived from packed repeated fixed sized values the effort will be constant,
|
||||
for ranges derived from packed repeated varints, the effort will be linear, but
|
||||
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.
|
||||
|
67
third_party/protozero/doc/cheatsheet.md
vendored
Normal file
67
third_party/protozero/doc/cheatsheet.md
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
|
||||
# Protozero Cheat Sheet
|
||||
|
||||
See also this
|
||||
[handy table](https://developers.google.com/protocol-buffers/docs/proto#scalar)
|
||||
from the Google Protocol Buffers documentation.
|
||||
|
||||
## Scalar types
|
||||
|
||||
| PBF Type | Underlying Storage | C++ Type | Getter | Notes |
|
||||
| -------- | ------------------ | ------------- | ---------------- | ----- |
|
||||
| int32 | varint | `int32_t` | `get_int32()` | |
|
||||
| sint32 | varint (zigzag) | `int32_t` | `get_sint32()` | |
|
||||
| uint32 | varint | `uint32_t` | `get_uint32()` | |
|
||||
| int64 | varint | `int64_t` | `get_int64()` | |
|
||||
| sint64 | varint (zigzag) | `int64_t` | `get_sint64()` | |
|
||||
| uint64 | varint | `uint64_t` | `get_uint64()` | |
|
||||
| bool | varint | `bool` | `get_bool()` | |
|
||||
| enum | varint | `int32_t` | `get_enum()` | |
|
||||
| fixed32 | 32bit fixed | `uint32_t` | `get_fixed32()` | |
|
||||
| sfixed32 | 32bit fixed | `int32_t` | `get_sfixed32()` | |
|
||||
| fixed64 | 64bit fixed | `uint64_t` | `get_fixed64()` | |
|
||||
| sfixed64 | 64bit fixed | `int64_t` | `get_sfixed64()` | |
|
||||
| float | 32bit fixed | `float` | `get_float()` | |
|
||||
| double | 64bit fixed | `double` | `get_double()` | |
|
||||
| string | length-delimited | `data_view` | `get_view()` | (1) |
|
||||
| string | length-delimited | pair | `get_data()` | (2) |
|
||||
| string | length-delimited | `std::string` | `get_string()` | |
|
||||
| bytes | length-delimited | `data_view` | `get_view()` | (1) |
|
||||
| bytes | length-delimited | pair | `get_data()` | (2) |
|
||||
| bytes | length-delimited | `std::string` | `get_bytes()` | |
|
||||
| message | length-delimited | `data_view` | `get_view()` | (1) |
|
||||
| message | length-delimited | pair | `get_data()` | (2) |
|
||||
| message | length-delimited | `pbf_reader` | `get_message()` | |
|
||||
|
||||
### Notes:
|
||||
|
||||
* (1) preferred form, returns `protozero::data_view` which is convertible to
|
||||
`std::string` if needed.
|
||||
* (2) deprecated form, returns `std::pair<const char*, pbf_length_type>`,
|
||||
use `get_view()` instead. This form is only available if
|
||||
`PROTOZERO_STRICT_API` is not defined.
|
||||
* The setter function of `pbf_writer` is always `add_` + the PBF type. Several
|
||||
overloads are available.
|
||||
|
||||
|
||||
## Packed repeated fields
|
||||
|
||||
| PBF Type | Getter |
|
||||
| -------- | ----------------------- |
|
||||
| int32 | `get_packed_int32()` |
|
||||
| sint32 | `get_packed_sint32()` |
|
||||
| uint32 | `get_packed_uint32()` |
|
||||
| int64 | `get_packed_int64()` |
|
||||
| sint64 | `get_packed_sint64()` |
|
||||
| uint64 | `get_packed_uint64()` |
|
||||
| bool | `get_packed_bool()` |
|
||||
| enum | `get_packed_enum()` |
|
||||
| fixed32 | `get_packed_fixed32()` |
|
||||
| sfixed32 | `get_packed_sfixed32()` |
|
||||
| fixed64 | `get_packed_fixed64()` |
|
||||
| sfixed64 | `get_packed_sfixed64()` |
|
||||
| float | `get_packed_float()` |
|
||||
| double | `get_packed_double()` |
|
||||
|
||||
Packed repeated fields for string, bytes, and message types are not possible.
|
||||
|
626
third_party/protozero/doc/tutorial.md
vendored
Normal file
626
third_party/protozero/doc/tutorial.md
vendored
Normal file
@ -0,0 +1,626 @@
|
||||
|
||||
# Protozero Tutorial
|
||||
|
||||
## Getting to know Protocol Buffers
|
||||
|
||||
Protozero is a very low level library. You really have to know some of the
|
||||
insides of Protocol Buffers to work with it!
|
||||
|
||||
So before reading any further in this document, read the following from the
|
||||
Protocol Buffer documentation:
|
||||
|
||||
* [Developer Guide - Overview](https://developers.google.com/protocol-buffers/docs/overview)
|
||||
* [Language Guide](https://developers.google.com/protocol-buffers/docs/proto)
|
||||
* [Encoding](https://developers.google.com/protocol-buffers/docs/encoding)
|
||||
|
||||
Make sure you understand the basic types of values supported by Protocol
|
||||
Buffers. Refer to this
|
||||
[handy table](https://developers.google.com/protocol-buffers/docs/proto#scalar)
|
||||
and [the cheat sheet](cheatsheet.md) if you are getting lost.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You need a C++11-capable compiler for Protozero to work. Copy the files in the
|
||||
`include/protozero` directory somewhere where your build system can find them.
|
||||
Keep the `protozero` directory and include the files in the form
|
||||
|
||||
```cpp
|
||||
#include <protozero/FILENAME.hpp>
|
||||
```
|
||||
|
||||
|
||||
## Parsing protobuf-encoded messages
|
||||
|
||||
### Using `pbf_reader`
|
||||
|
||||
To use the `pbf_reader` class, add this include to your C++ program:
|
||||
|
||||
```cpp
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
```
|
||||
|
||||
The `pbf_reader` class contains asserts that will detect some programming
|
||||
errors. We encourage you to compile with asserts enabled in your debug builds.
|
||||
|
||||
|
||||
### An introductory example
|
||||
|
||||
Lets say you have a protocol description in a `.proto` file like this:
|
||||
|
||||
```cpp
|
||||
message Example1 {
|
||||
required uint32 x = 1;
|
||||
optional string s = 2;
|
||||
repeated fixed64 r = 17;
|
||||
}
|
||||
```
|
||||
|
||||
To read messages created according to that description, you will have code that
|
||||
looks somewhat like this:
|
||||
|
||||
```cpp
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
|
||||
// get data from somewhere into the input string
|
||||
std::string input = get_input_data();
|
||||
|
||||
// initialize pbf message with this data
|
||||
protozero::pbf_reader message{input};
|
||||
|
||||
// iterate over fields in the message
|
||||
while (message.next()) {
|
||||
|
||||
// switch depending on the field tag (the field name is not available)
|
||||
switch (message.tag()) {
|
||||
case 1:
|
||||
// get data for tag 1 (in this case an uint32)
|
||||
auto x = message.get_uint32();
|
||||
break;
|
||||
case 2:
|
||||
// get data for tag 2 (in this case a string)
|
||||
std::string s = message.get_string();
|
||||
break;
|
||||
case 17:
|
||||
// ignore data for tag 17
|
||||
message.skip();
|
||||
break;
|
||||
default:
|
||||
// ignore data for unknown tags to allow for future extensions
|
||||
message.skip();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You always have to call `next()` and then either one of the accessor functions
|
||||
(like `get_uint32()` or `get_string()`) to get the field value or `skip()` to
|
||||
ignore this field. Then call `next()` again, and so forth. Never call `next()`
|
||||
twice in a row or any if the accessor or skip functions twice in a row.
|
||||
|
||||
Because the `pbf_reader` class doesn't know the `.proto` file it doesn't know
|
||||
which field names or tags there are and it doesn't known the types of the
|
||||
fields. You have to make sure to call the right `get_...()` function for each
|
||||
tag. Some `assert()s` are done to check you are calling the right functions,
|
||||
but not all errors can be detected.
|
||||
|
||||
Note that it doesn't matter whether a field is defined as `required`,
|
||||
`optional`, or `repeated`. You always have to be prepared to get zero, one, or
|
||||
more instances of a field and you always have to be prepared to get other
|
||||
fields, too, unless you want your program to break if somebody adds a new
|
||||
field.
|
||||
|
||||
|
||||
### If you only need a single field
|
||||
|
||||
If, out of a protocol buffer message, you only need the value of a single
|
||||
field, you can use the version of the `next()` function with a parameter:
|
||||
|
||||
```cpp
|
||||
// same .proto file and initialization as above
|
||||
|
||||
// get all fields with tag 17, skip all others
|
||||
while (message.next(17)) {
|
||||
auto r = message.get_fixed64();
|
||||
std::cout << r << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Handling scalar fields
|
||||
|
||||
As you saw in the example, handling scalar field types is reasonably easy. You
|
||||
just check the `.proto` file for the type of a field and call the corresponding
|
||||
function called `get_` + _field type_.
|
||||
|
||||
For `string` and `bytes` types the internal handling is exactly the same, but
|
||||
both `get_string()` and `get_bytes()` are provided to make the code
|
||||
self-documenting. Both theses calls allocate and return a `std::string` which
|
||||
can add some overhead. You can call the `get_view()` function instead which
|
||||
returns a `data_view` containing a pointer into the data (access with `data()`)
|
||||
and the length of the data (access with `size()`).
|
||||
|
||||
|
||||
### Handling repeated packed fields
|
||||
|
||||
Fields that are marked as `[packed=true]` in the `.proto` file are handled
|
||||
somewhat differently. `get_packed_...()` functions returning an iterator range
|
||||
are used to access the data.
|
||||
|
||||
So, for example, if you have a protocol description in a `.proto` file like
|
||||
this:
|
||||
|
||||
```cpp
|
||||
message Example2 {
|
||||
repeated sint32 i = 1 [packed=true];
|
||||
}
|
||||
```
|
||||
|
||||
You can get to the data like this:
|
||||
|
||||
```cpp
|
||||
protozero::pbf_reader message{input.data(), input.size()};
|
||||
|
||||
// set current field
|
||||
message.next(1);
|
||||
|
||||
// get an iterator range
|
||||
auto pi = message.get_packed_sint32();
|
||||
|
||||
// iterate to get to all values
|
||||
for (auto it = pi.begin(); it != pi.end(); ++it) {
|
||||
std::cout << *it << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
Or, with a range-based for-loop:
|
||||
|
||||
```cpp
|
||||
for (auto value : pi) {
|
||||
std::cout << v << '\n';
|
||||
}
|
||||
```
|
||||
|
||||
So you are getting a pair of normal forward iterators wrapped in an iterator
|
||||
range object. The iterators can be used with any STL algorithms etc.
|
||||
|
||||
Note that the previous only applies to repeated **packed** fields, normal
|
||||
repeated fields are handled in the usual way for scalar fields.
|
||||
|
||||
|
||||
### Handling embedded messages
|
||||
|
||||
Protocol Buffers can embed any message inside another message. To access an
|
||||
embedded message use the `get_message()` function. So for this description:
|
||||
|
||||
```cpp
|
||||
message Point {
|
||||
required double x = 1;
|
||||
required double y = 2;
|
||||
}
|
||||
|
||||
message Example3 {
|
||||
repeated Point point = 10;
|
||||
}
|
||||
```
|
||||
|
||||
you can parse with this code:
|
||||
|
||||
```cpp
|
||||
protozero::pbf_reader message{input};
|
||||
|
||||
while (message.next(10)) {
|
||||
protozero::pbf_reader point = message.get_message();
|
||||
double x, y;
|
||||
while (point.next()) {
|
||||
switch (point.tag()) {
|
||||
case 1:
|
||||
x = point.get_double();
|
||||
break;
|
||||
case 2:
|
||||
y = point.get_double();
|
||||
break;
|
||||
default:
|
||||
point.skip();
|
||||
}
|
||||
}
|
||||
std::cout << "x=" << x << " y=" << y << "\n";
|
||||
}
|
||||
```
|
||||
|
||||
### Handling enums
|
||||
|
||||
Enums are stored as varints and they can't be differentiated from them. Use
|
||||
the `get_enum()` function to get the value of the enum, you have to translate
|
||||
this into the symbolic name yourself. See the `enum` test case for an example.
|
||||
|
||||
|
||||
### Asserts and exceptions in the Protozero library
|
||||
|
||||
Protozero uses `assert()` liberally to help you find bugs in your own code when
|
||||
compiled in debug mode (ie with `NDEBUG` not set). If such an assert "fires",
|
||||
this is a very strong indication that there is a bug in your code somewhere.
|
||||
|
||||
(Protozero will disable those asserts and "convert" them into exception in its
|
||||
own test code. This is done to make sure the asserts actually work as intended.
|
||||
Your test code will not need this!)
|
||||
|
||||
Exceptions, on the other hand, are thrown by Protozero if some kind of data
|
||||
corruption was detected while it is trying to parse the data. This could also
|
||||
be an indicator for a bug in the user code, but because it can happen if the
|
||||
data was (intentionally or not intentionally) been messed with, it is reported
|
||||
to the user code using exceptions.
|
||||
|
||||
Most of the functions on the writer side can throw a `std::bad_alloc`
|
||||
exception if there is no space to grow a buffer. Other than that no exceptions
|
||||
can occur on the writer side.
|
||||
|
||||
All exceptions thrown by the reader side derive from `protozero::exception`.
|
||||
|
||||
Note that all exceptions can also happen if you are expecting a data field of
|
||||
a certain type in your code but the field actually has a different type. In
|
||||
that case the `pbf_reader` class might interpret the bytes in the buffer in
|
||||
the wrong way and anything can happen.
|
||||
|
||||
#### `end_of_buffer_exception`
|
||||
|
||||
This will be thrown whenever any of the functions "runs out of input data".
|
||||
It means you either have an incomplete message in your input or some other
|
||||
data corruption has taken place.
|
||||
|
||||
#### `unknown_pbf_wire_type_exception`
|
||||
|
||||
This will be thrown if an unsupported wire type is encountered. Either your
|
||||
input data is corrupted or it was written with an unsupported version of a
|
||||
Protocol Buffers implementation.
|
||||
|
||||
#### `varint_too_long_exception`
|
||||
|
||||
This exception indicates an illegal encoding of a varint. It means your input
|
||||
data is corrupted in some way.
|
||||
|
||||
#### `invalid_tag_exception`
|
||||
|
||||
This exception is thrown when a tag has an invalid value. Tags must be
|
||||
unsigned integers between 1 and 2^29-1. Tags between 19000 and 19999 are not
|
||||
allowed. See
|
||||
https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
|
||||
|
||||
#### `invalid_length_exception`
|
||||
|
||||
This exception is thrown when a length field of a packed repeated field is
|
||||
invalid. For fixed size types the length must be a multiple of the size of
|
||||
the type.
|
||||
|
||||
### The `pbf_reader` class
|
||||
|
||||
The `pbf_reader` class behaves like a value type. Objects are reasonably small
|
||||
(two pointers and two `uint32_t`, so 24 bytes on a 64bit system) and they can
|
||||
be copied and moved around trivially.
|
||||
|
||||
`pbf_reader` objects can be constructed from a `std::string` or a `const char*`
|
||||
and a length field (either supplied as separate arguments or as a `std::pair`).
|
||||
In all cases objects of the `pbf_reader` class store a pointer into the input
|
||||
data that was given to the constructor. You have to make sure this pointer
|
||||
stays valid for the duration of the objects lifetime.
|
||||
|
||||
## Parsing protobuf-encoded messages using `pbf_message`
|
||||
|
||||
One problem in the code above are the "magic numbers" used as tags for the
|
||||
different fields that you got from the `.proto` file. Instead of spreading
|
||||
these magic numbers around your code you can define them once in an `enum
|
||||
class` and then use the `pbf_message` template class instead of the
|
||||
`pbf_reader` class.
|
||||
|
||||
Here is the first example again, this time using this new technique. So you
|
||||
have the following in a `.proto` file:
|
||||
|
||||
```cpp
|
||||
message Example1 {
|
||||
required uint32 x = 1;
|
||||
optional string s = 2;
|
||||
repeated fixed64 r = 17;
|
||||
}
|
||||
```
|
||||
|
||||
Add the following declaration in one of your header files:
|
||||
|
||||
```cpp
|
||||
enum class Example1 : protozero::pbf_tag_type {
|
||||
required_uint32_x = 1,
|
||||
optional_string_s = 2,
|
||||
repeated_fixed64_r = 17
|
||||
};
|
||||
```
|
||||
|
||||
The message name becomes the name of the `enum class` which is always built
|
||||
on top of the `protozero::pbf_tag_type` type. Each field in the message
|
||||
becomes one value of the enum. In this case the name is created from the
|
||||
type (including the modifiers like `required` or `optional`) and the name of
|
||||
the field. You can use any name you want, but this convention makes it easier
|
||||
later, to get everything right.
|
||||
|
||||
To read messages created according to that description, you will have code that
|
||||
looks somewhat like this, this time using `pbf_message` instead of
|
||||
`pbf_reader`:
|
||||
|
||||
```cpp
|
||||
#include <protozero/pbf_message.hpp>
|
||||
|
||||
// get data from somewhere into the input string
|
||||
std::string input = get_input_data();
|
||||
|
||||
// initialize pbf message with this data
|
||||
protozero::pbf_message<Example1> message{input};
|
||||
|
||||
// iterate over fields in the message
|
||||
while (message.next()) {
|
||||
|
||||
// switch depending on the field tag (the field name is not available)
|
||||
switch (message.tag()) {
|
||||
case Example1::required_uint32_x:
|
||||
auto x = message.get_uint32();
|
||||
break;
|
||||
case Example1::optional_string_s:
|
||||
std::string s = message.get_string();
|
||||
break;
|
||||
case Example1::repeated_fixed64_r:
|
||||
message.skip();
|
||||
break;
|
||||
default:
|
||||
// ignore data for unknown tags to allow for future extensions
|
||||
message.skip();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note the correspondance between the enum value (for instance
|
||||
`required_uint32_x`) and the name of the getter function (for instance
|
||||
`get_uint32()`). This makes it easier to get the correct types. Also the
|
||||
naming makes it easier to keep different message types apart if you have
|
||||
multiple (or embedded) messages.
|
||||
|
||||
See the `test/t/complex` test case for a complete example using this interface.
|
||||
|
||||
Using `pbf_message` in favour of `pbf_reader` is recommended for all code.
|
||||
Note that `pbf_message` derives from `pbf_reader`, so you can always fall
|
||||
back to the more generic interface if necessary.
|
||||
|
||||
One problem you might run into is the following: The enum class lists all
|
||||
possible values you know about and you'll have lots of `switch` statements
|
||||
checking those values. Some compilers will know that your `switch` covers
|
||||
all possible cases and warn you if you have a `default` case that looks
|
||||
unneccessary to the compiler. But you still want that `default` case to allow
|
||||
for future extension of those messages (and maybe also to detect corrupted
|
||||
data). You can switch of this warning with `-Wno-covered-switch-default`).
|
||||
|
||||
|
||||
## Writing protobuf-encoded messages
|
||||
|
||||
### Using `pbf_writer`
|
||||
|
||||
To use the `pbf_writer` class, add this include to your C++ program:
|
||||
|
||||
```cpp
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
```
|
||||
|
||||
The `pbf_writer` class contains asserts that will detect some programming
|
||||
errors. We encourage you to compile with asserts enabled in your debug builds.
|
||||
|
||||
|
||||
### An introductory example
|
||||
|
||||
Lets say you have a protocol description in a `.proto` file like this:
|
||||
|
||||
```cpp
|
||||
message Example {
|
||||
required uint32 x = 1;
|
||||
optional string s = 2;
|
||||
repeated fixed64 r = 17;
|
||||
}
|
||||
```
|
||||
|
||||
To write messages created according to that description, you will have code
|
||||
that looks somewhat like this:
|
||||
|
||||
```cpp
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
|
||||
std::string data;
|
||||
protozero::pbf_writer pbf_example{data};
|
||||
|
||||
pbf_example.add_uint32(1, 27); // uint32_t x
|
||||
pbf_example.add_fixed64(17, 1); // fixed64 r
|
||||
pbf_example.add_fixed64(17, 2);
|
||||
pbf_example.add_fixed64(17, 3);
|
||||
pbf_example.add_string(2, "foobar"); // string s
|
||||
```
|
||||
|
||||
First you need a string which will be used as buffer to assemble the
|
||||
protobuf-formatted message. The `pbf_writer` object contains a reference to
|
||||
this string buffer and through it you add data to that buffer piece by piece.
|
||||
The buffer doesn't have to be empty, the `pbf_writer` will simply append its
|
||||
data to whatever is there already.
|
||||
|
||||
|
||||
### Handling scalar fields
|
||||
|
||||
As you could see in the introductory example handling any kind of scalar field
|
||||
is easy. The type of field doesn't matter and it doesn't matter whether it is
|
||||
optional, required or repeated. You always call one of the `add_TYPE()` method
|
||||
on the pbf writer object.
|
||||
|
||||
The first parameter of these methods is always the *tag* of the field (the
|
||||
field number) from the `.proto` file. The second parameter is the value you
|
||||
want to set. For the `bytes` and `string` types several versions of the add
|
||||
method are available taking a `const std::string&` or a `const char*` and a
|
||||
length.
|
||||
|
||||
For `enum` types you have to use the numeric value as the symbolic names from
|
||||
the `.proto` file are not available.
|
||||
|
||||
|
||||
### Handling repeated packed fields
|
||||
|
||||
Repeated packed fields can easily be set from a pair of iterators:
|
||||
|
||||
```cpp
|
||||
std::string data;
|
||||
protozero::pbf_writer pw{data};
|
||||
|
||||
std::vector<int> v = { 1, 4, 9, 16, 25, 36 };
|
||||
pw.add_packed_int32(1, std::begin(v), std::end(v));
|
||||
```
|
||||
|
||||
If you don't have an iterator you can use the alternative form:
|
||||
|
||||
```cpp
|
||||
std::string data;
|
||||
protozero::pbf_writer pw{data};
|
||||
{
|
||||
protozero::packed_field_int32 field{pw, 1};
|
||||
field.add_element(1);
|
||||
field.add_element(10);
|
||||
field.add_element(100);
|
||||
}
|
||||
```
|
||||
|
||||
Of course you can add as many elements as you want. If you add no elements
|
||||
at all, this code will still work, Protozero detects this special case and
|
||||
pretends you never even initialized this field.
|
||||
|
||||
The nested scope is important in this case, because the destructor of the
|
||||
`field` object will make sure the length stored inside the field is set to
|
||||
the right value. You must close that scope before adding other fields to the
|
||||
`pw` pbf writer.
|
||||
|
||||
If you know how many elements you will add to the field and your field contains
|
||||
fixed length elements, you can tell Protozero and it can optimize this case:
|
||||
|
||||
```cpp
|
||||
std::string data;
|
||||
protozero::pbf_writer pw{data};
|
||||
{
|
||||
protozero::packed_field_fixed32 field{pw, 1, 2}; // exactly two elements
|
||||
field.add_element(42);
|
||||
field.add_element(13);
|
||||
}
|
||||
```
|
||||
|
||||
In this case you have to supply exactly as many elements as you promised,
|
||||
otherwise you will get a broken protobuf message.
|
||||
|
||||
This works for `packed_field_fixed32`, `packed_field_sfixed32`,
|
||||
`packed_field_fixed64`, `packed_field_sfixed64`, `packed_field_float`, and
|
||||
`packed_field_double`.
|
||||
|
||||
You can abandon writing of the packed field if this becomes necessary by
|
||||
calling `rollback()`:
|
||||
|
||||
```cpp
|
||||
std::string data;
|
||||
protozero::pbf_writer pw{data};
|
||||
{
|
||||
protozero::packed_field_int32 field{pw, 1};
|
||||
field.add_element(42);
|
||||
// some error occurs, you don't want to have this field at all
|
||||
field.rollback();
|
||||
}
|
||||
```
|
||||
|
||||
The result is the same as if the lines inside the nested brackets had never
|
||||
been called. Do not try to call `add_element()` after a rollback.
|
||||
|
||||
|
||||
### Handling sub-messages
|
||||
|
||||
Nested sub-messages can be handled by first creating the submessage and then
|
||||
adding to the parent message:
|
||||
|
||||
```cpp
|
||||
std::string buffer_sub;
|
||||
protozero::pbf_writer pbf_sub{buffer_sub};
|
||||
|
||||
// add fields to sub-message
|
||||
pbf_sub.add_...(...);
|
||||
// ...
|
||||
|
||||
// sub-message is finished here
|
||||
|
||||
std::string buffer_parent;
|
||||
protozero::pbf_writer pbf_parent{buffer_parent};
|
||||
pbf_parent.add_message(1, buffer_sub);
|
||||
```
|
||||
|
||||
This is easy to do but it has the drawback of needing a separate `std::string`
|
||||
buffer. If this concerns you (and why would you use protozero and not the
|
||||
Google protobuf library if it doesn't?) there is another way:
|
||||
|
||||
```cpp
|
||||
std::string data;
|
||||
protozero::pbf_writer pbf_parent{data};
|
||||
|
||||
// optionally add fields to parent here
|
||||
pbf_parent.add_...(...);
|
||||
|
||||
// open a new scope
|
||||
{
|
||||
// create new pbf_writer with parent and the tag (field number)
|
||||
// as parameters
|
||||
protozero::pbf_writer pbf_sub{pbf_parent, 1};
|
||||
|
||||
// add fields to sub here...
|
||||
pbf_sub.add_...(...);
|
||||
|
||||
} // closing the scope will close the sub-message
|
||||
|
||||
// optionally add more fields to parent here
|
||||
pbf_parent.add_...(...);
|
||||
```
|
||||
|
||||
This can be nested arbitrarily deep.
|
||||
|
||||
Internally the sub-message writer re-uses the buffer from the parent. It
|
||||
reserves enough space in the buffer to later write the length of the submessage
|
||||
into it. It then adds the contents of the submessage to the buffer. When the
|
||||
`pbf_sub` writer is destructed the length of the submessage is calculated and
|
||||
written in the reserved space. If less space was needed for the length field
|
||||
than was available, the rest of the buffer is moved over a few bytes.
|
||||
|
||||
You can abandon writing of submessage if this becomes necessary by
|
||||
calling `rollback()`:
|
||||
|
||||
```cpp
|
||||
std::string data;
|
||||
protozero::pbf_writer pbf_parent{data};
|
||||
|
||||
// open a new scope
|
||||
{
|
||||
// create new pbf_writer with parent and the tag (field number)
|
||||
// as parameters
|
||||
protozero::pbf_writer pbf_sub{pbf_parent, 1};
|
||||
|
||||
// add fields to sub here...
|
||||
pbf_sub.add_...(...);
|
||||
|
||||
// some problem occurs and you want to abandon the submessage:
|
||||
pbf_sub.rollback();
|
||||
}
|
||||
|
||||
// optionally add more fields to parent here
|
||||
pbf_parent.add_...(...);
|
||||
```
|
||||
|
||||
The result is the same as if the lines inside the nested brackets had never
|
||||
been called. Do not try to call any of the `add_*` functions on the submessage
|
||||
after a rollback.
|
||||
|
||||
## Writing protobuf-encoded messages using `pbf_builder`
|
||||
|
||||
Just like the `pbf_message` template class wraps the `pbf_reader` class, there
|
||||
is a `pbf_builder` template class wrapping the `pbf_writer` class. It is
|
||||
instantiated using the same `enum class` described above and used exactly
|
||||
like the `pbf_writer` class but using the values of the enum instead of bare
|
||||
integers.
|
||||
|
||||
See the `test/t/complex` test case for a complete example using this interface.
|
||||
|
83
third_party/protozero/include/protozero/byteswap.hpp
vendored
Normal file
83
third_party/protozero/include/protozero/byteswap.hpp
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
#ifndef PROTOZERO_BYTESWAP_HPP
|
||||
#define PROTOZERO_BYTESWAP_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 byteswap.hpp
|
||||
*
|
||||
* @brief Contains functions to swap bytes in values (for different endianness).
|
||||
*/
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace protozero {
|
||||
namespace detail {
|
||||
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(uint32_t* ptr) noexcept {
|
||||
*ptr = byteswap_impl(*ptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(uint64_t* ptr) noexcept {
|
||||
*ptr = byteswap_impl(*ptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(int32_t* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint32_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(int64_t* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint64_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(float* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint32_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(double* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint64_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_BYTESWAP_HPP
|
48
third_party/protozero/include/protozero/config.hpp
vendored
Normal file
48
third_party/protozero/include/protozero/config.hpp
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef PROTOZERO_CONFIG_HPP
|
||||
#define PROTOZERO_CONFIG_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.
|
||||
|
||||
*****************************************************************************/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
/**
|
||||
* @file config.hpp
|
||||
*
|
||||
* @brief Contains macro checks for different configurations.
|
||||
*/
|
||||
|
||||
#define PROTOZERO_LITTLE_ENDIAN 1234
|
||||
#define PROTOZERO_BIG_ENDIAN 4321
|
||||
|
||||
// Find out which byte order the machine has.
|
||||
#if defined(__BYTE_ORDER)
|
||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN
|
||||
# endif
|
||||
# if (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
# define PROTOZERO_BYTE_ORDER PROTOZERO_BIG_ENDIAN
|
||||
# endif
|
||||
#else
|
||||
// This probably isn't a very good default, but might do until we figure
|
||||
// out something better.
|
||||
# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
// Check whether __builtin_bswap is available
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define PROTOZERO_USE_BUILTIN_BSWAP
|
||||
#endif
|
||||
|
||||
// Wrapper for assert() used for testing
|
||||
#ifndef protozero_assert
|
||||
# define protozero_assert(x) assert(x)
|
||||
#endif
|
||||
|
||||
#endif // PROTOZERO_CONFIG_HPP
|
236
third_party/protozero/include/protozero/data_view.hpp
vendored
Normal file
236
third_party/protozero/include/protozero/data_view.hpp
vendored
Normal file
@ -0,0 +1,236 @@
|
||||
#ifndef PROTOZERO_DATA_VIEW_HPP
|
||||
#define PROTOZERO_DATA_VIEW_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 data_view.hpp
|
||||
*
|
||||
* @brief Contains the implementation of the data_view class.
|
||||
*/
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
#ifdef PROTOZERO_USE_VIEW
|
||||
using data_view = PROTOZERO_USE_VIEW;
|
||||
#else
|
||||
|
||||
/**
|
||||
* Holds a pointer to some data and a length.
|
||||
*
|
||||
* This class is supposed to be compatible with the std::string_view
|
||||
* that will be available in C++17.
|
||||
*/
|
||||
class data_view {
|
||||
|
||||
const char* m_data = nullptr;
|
||||
std::size_t m_size = 0;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor. Construct an empty data_view.
|
||||
*/
|
||||
constexpr data_view() noexcept = default;
|
||||
|
||||
/**
|
||||
* Create data_view from pointer and size.
|
||||
*
|
||||
* @param ptr Pointer to the data.
|
||||
* @param length Length of the data.
|
||||
*/
|
||||
constexpr data_view(const char* ptr, std::size_t length) noexcept
|
||||
: m_data(ptr),
|
||||
m_size(length) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create data_view from string.
|
||||
*
|
||||
* @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()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create data_view from zero-terminated string.
|
||||
*
|
||||
* @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)) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the contents of this object with the other.
|
||||
*
|
||||
* @param other Other object to swap data with.
|
||||
*/
|
||||
void swap(data_view& other) noexcept {
|
||||
using std::swap;
|
||||
swap(m_data, other.m_data);
|
||||
swap(m_size, other.m_size);
|
||||
}
|
||||
|
||||
/// Return pointer to data.
|
||||
constexpr const char* data() const noexcept {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/// Return length of data in bytes.
|
||||
constexpr std::size_t size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
/// Returns true if size is 0.
|
||||
constexpr bool empty() const noexcept {
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
#ifndef PROTOZERO_STRICT_API
|
||||
/**
|
||||
* Convert data view to string.
|
||||
*
|
||||
* @pre Must not be default constructed data_view.
|
||||
*
|
||||
* @deprecated to_string() is not available in C++17 string_view so it
|
||||
* should not be used to make conversion to that class easier
|
||||
* in the future.
|
||||
*/
|
||||
std::string to_string() const {
|
||||
protozero_assert(m_data);
|
||||
return {m_data, m_size};
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Convert data view to string.
|
||||
*
|
||||
* @pre Must not be default constructed data_view.
|
||||
*/
|
||||
explicit operator std::string() const {
|
||||
protozero_assert(m_data);
|
||||
return {m_data, m_size};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the contents of this object with the given other object.
|
||||
*
|
||||
* @returns 0 if they are the same, <0 if this object is smaller than
|
||||
* the other or >0 if it is larger. If both objects have the
|
||||
* same size returns <0 if this object is lexicographically
|
||||
* before the other, >0 otherwise.
|
||||
*
|
||||
* @pre Must not be default constructed data_view.
|
||||
*/
|
||||
int compare(data_view other) const {
|
||||
protozero_assert(m_data && other.m_data);
|
||||
const int cmp = std::memcmp(data(), other.data(),
|
||||
std::min(size(), other.size()));
|
||||
if (cmp == 0) {
|
||||
if (size() == other.size()) {
|
||||
return 0;
|
||||
}
|
||||
return size() < other.size() ? -1 : 1;
|
||||
}
|
||||
return cmp;
|
||||
}
|
||||
|
||||
}; // class data_view
|
||||
|
||||
/**
|
||||
* Swap two data_view objects.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline void swap(data_view& lhs, data_view& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Two data_view instances are equal if they have the same size and the
|
||||
* same content.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline constexpr bool operator==(const data_view lhs, const data_view rhs) noexcept {
|
||||
return lhs.size() == rhs.size() &&
|
||||
std::equal(lhs.data(), lhs.data() + lhs.size(), rhs.data());
|
||||
}
|
||||
|
||||
/**
|
||||
* Two data_view instances are not equal if they have different sizes or the
|
||||
* content differs.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline constexpr bool operator!=(const data_view lhs, const data_view rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if lhs.compare(rhs) < 0.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline bool operator<(const data_view lhs, const data_view rhs) noexcept {
|
||||
return lhs.compare(rhs) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if lhs.compare(rhs) <= 0.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline bool operator<=(const data_view lhs, const data_view rhs) noexcept {
|
||||
return lhs.compare(rhs) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if lhs.compare(rhs) > 0.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline bool operator>(const data_view lhs, const data_view rhs) noexcept {
|
||||
return lhs.compare(rhs) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if lhs.compare(rhs) >= 0.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline bool operator>=(const data_view lhs, const data_view rhs) noexcept {
|
||||
return lhs.compare(rhs) >= 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_DATA_VIEW_HPP
|
101
third_party/protozero/include/protozero/exception.hpp
vendored
Normal file
101
third_party/protozero/include/protozero/exception.hpp
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef PROTOZERO_EXCEPTION_HPP
|
||||
#define PROTOZERO_EXCEPTION_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 exception.hpp
|
||||
*
|
||||
* @brief Contains the exceptions used in the protozero library.
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
|
||||
/**
|
||||
* @brief All parts of the protozero header-only library are in this namespace.
|
||||
*/
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* All exceptions explicitly thrown by the functions of the protozero library
|
||||
* derive from this exception.
|
||||
*/
|
||||
struct exception : std::exception {
|
||||
/// Returns the explanatory string.
|
||||
const char* what() const noexcept override {
|
||||
return "pbf exception";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This exception is thrown when parsing a varint thats larger than allowed.
|
||||
* This should never happen unless the data is corrupted.
|
||||
*/
|
||||
struct varint_too_long_exception : exception {
|
||||
/// Returns the explanatory string.
|
||||
const char* what() const noexcept override {
|
||||
return "varint too long exception";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This exception is thrown when the wire type of a pdf field is unknown.
|
||||
* This should never happen unless the data is corrupted.
|
||||
*/
|
||||
struct unknown_pbf_wire_type_exception : exception {
|
||||
/// Returns the explanatory string.
|
||||
const char* what() const noexcept override {
|
||||
return "unknown pbf field type exception";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This exception is thrown when we are trying to read a field and there
|
||||
* are not enough bytes left in the buffer to read it. Almost all functions
|
||||
* of the pbf_reader class can throw this exception.
|
||||
*
|
||||
* This should never happen unless the data is corrupted or you have
|
||||
* initialized the pbf_reader object with incomplete data.
|
||||
*/
|
||||
struct end_of_buffer_exception : exception {
|
||||
/// Returns the explanatory string.
|
||||
const char* what() const noexcept override {
|
||||
return "end of buffer exception";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This exception is thrown when a tag has an invalid value. Tags must be
|
||||
* unsigned integers between 1 and 2^29-1. Tags between 19000 and 19999 are
|
||||
* not allowed. See
|
||||
* https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
|
||||
*/
|
||||
struct invalid_tag_exception : exception {
|
||||
/// Returns the explanatory string.
|
||||
const char* what() const noexcept override {
|
||||
return "invalid tag exception";
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This exception is thrown when a length field of a packed repeated field is
|
||||
* invalid. For fixed size types the length must be a multiple of the size of
|
||||
* the type.
|
||||
*/
|
||||
struct invalid_length_exception : exception {
|
||||
/// Returns the explanatory string.
|
||||
const char* what() const noexcept override {
|
||||
return "invalid length exception";
|
||||
}
|
||||
};
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_EXCEPTION_HPP
|
455
third_party/protozero/include/protozero/iterators.hpp
vendored
Normal file
455
third_party/protozero/include/protozero/iterators.hpp
vendored
Normal file
@ -0,0 +1,455 @@
|
||||
#ifndef PROTOZERO_ITERATORS_HPP
|
||||
#define PROTOZERO_ITERATORS_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 iterators.hpp
|
||||
*
|
||||
* @brief Contains the iterators for access to packed repeated fields.
|
||||
*/
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
# include <protozero/byteswap.hpp>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* A range of iterators based on std::pair. Created from beginning and
|
||||
* end iterators. Used as a return type from some pbf_reader methods
|
||||
* that is easy to use with range-based for loops.
|
||||
*/
|
||||
template <typename T, typename P = std::pair<T, T>>
|
||||
class iterator_range :
|
||||
#ifdef PROTOZERO_STRICT_API
|
||||
protected
|
||||
#else
|
||||
public
|
||||
#endif
|
||||
P {
|
||||
|
||||
public:
|
||||
|
||||
/// The type of the iterators in this range.
|
||||
using iterator = T;
|
||||
|
||||
/// The value type of the underlying iterator.
|
||||
using value_type = typename std::iterator_traits<T>::value_type;
|
||||
|
||||
/**
|
||||
* Default constructor. Create empty iterator_range.
|
||||
*/
|
||||
constexpr iterator_range() :
|
||||
P(iterator{}, iterator{}) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create iterator range from two iterators.
|
||||
*
|
||||
* @param first_iterator Iterator to beginning of range.
|
||||
* @param last_iterator Iterator to end of range.
|
||||
*/
|
||||
constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) :
|
||||
P(std::forward<iterator>(first_iterator),
|
||||
std::forward<iterator>(last_iterator)) {
|
||||
}
|
||||
|
||||
/// Return iterator to beginning of range.
|
||||
constexpr iterator begin() const noexcept {
|
||||
return this->first;
|
||||
}
|
||||
|
||||
/// Return iterator to end of range.
|
||||
constexpr iterator end() const noexcept {
|
||||
return this->second;
|
||||
}
|
||||
|
||||
/// Return iterator to beginning of range.
|
||||
constexpr iterator cbegin() const noexcept {
|
||||
return this->first;
|
||||
}
|
||||
|
||||
/// Return iterator to end of range.
|
||||
constexpr iterator cend() const noexcept {
|
||||
return this->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this range is empty.
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
constexpr bool empty() const noexcept {
|
||||
return begin() == end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of the range, ie the number of elements it contains.
|
||||
*
|
||||
* Complexity: Constant or linear depending on the underlaying iterator.
|
||||
*/
|
||||
std::size_t size() const noexcept {
|
||||
return static_cast<size_t>(std::distance(begin(), end()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get element at the beginning of the range.
|
||||
*
|
||||
* @pre Range must not be empty.
|
||||
*/
|
||||
value_type front() const {
|
||||
protozero_assert(!empty());
|
||||
return *(this->first);
|
||||
}
|
||||
|
||||
/**
|
||||
* Advance beginning of range by one.
|
||||
*
|
||||
* @pre Range must not be empty.
|
||||
*/
|
||||
void drop_front() {
|
||||
protozero_assert(!empty());
|
||||
++this->first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the contents of this range with the other.
|
||||
*
|
||||
* @param other Other range to swap data with.
|
||||
*/
|
||||
void swap(iterator_range& other) noexcept {
|
||||
using std::swap;
|
||||
swap(this->first, other.first);
|
||||
swap(this->second, other.second);
|
||||
}
|
||||
|
||||
}; // struct iterator_range
|
||||
|
||||
/**
|
||||
* Swap two iterator_ranges.
|
||||
*
|
||||
* @param lhs First range.
|
||||
* @param rhs Second range.
|
||||
*/
|
||||
template <typename T>
|
||||
inline void swap(iterator_range<T>& lhs, iterator_range<T>& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* A forward iterator used for accessing packed repeated fields of fixed
|
||||
* length (fixed32, sfixed32, float, double).
|
||||
*/
|
||||
template <typename T>
|
||||
class const_fixed_iterator {
|
||||
|
||||
/// Pointer to current iterator position
|
||||
const char* m_data = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
const_fixed_iterator() noexcept = default;
|
||||
|
||||
explicit const_fixed_iterator(const char* data) noexcept :
|
||||
m_data(data) {
|
||||
}
|
||||
|
||||
const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
|
||||
const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
|
||||
|
||||
const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
|
||||
const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
|
||||
|
||||
~const_fixed_iterator() noexcept = default;
|
||||
|
||||
value_type operator*() const {
|
||||
value_type result;
|
||||
std::memcpy(&result, m_data, sizeof(value_type));
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
detail::byteswap_inplace(&result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
const_fixed_iterator& operator++() noexcept {
|
||||
m_data += sizeof(value_type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_fixed_iterator operator++(int) noexcept {
|
||||
const const_fixed_iterator tmp{*this};
|
||||
++(*this);
|
||||
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;
|
||||
}
|
||||
|
||||
const_fixed_iterator operator--(int) noexcept {
|
||||
const const_fixed_iterator tmp{*this};
|
||||
--(*this);
|
||||
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 rhs < lhs;
|
||||
}
|
||||
|
||||
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 < rhs);
|
||||
|
||||
}
|
||||
|
||||
const_fixed_iterator& operator+=(difference_type val) noexcept {
|
||||
m_data += (sizeof(value_type) * val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend const_fixed_iterator operator+(const_fixed_iterator lhs, difference_type rhs) noexcept {
|
||||
const_fixed_iterator tmp{lhs};
|
||||
tmp.m_data += (sizeof(value_type) * rhs);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend const_fixed_iterator operator+(difference_type lhs, const_fixed_iterator rhs) noexcept {
|
||||
const_fixed_iterator tmp{rhs};
|
||||
tmp.m_data += (sizeof(value_type) * lhs);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
const_fixed_iterator& operator-=(difference_type val) noexcept {
|
||||
m_data -= (sizeof(value_type) * val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend const_fixed_iterator operator-(const_fixed_iterator lhs, difference_type rhs) noexcept {
|
||||
const_fixed_iterator tmp{lhs};
|
||||
tmp.m_data -= (sizeof(value_type) * rhs);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
friend difference_type operator-(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
|
||||
return static_cast<difference_type>(lhs.m_data - rhs.m_data) / static_cast<difference_type>(sizeof(T));
|
||||
}
|
||||
|
||||
value_type operator[](difference_type n) const noexcept {
|
||||
return *(*this + n);
|
||||
}
|
||||
|
||||
}; // class const_fixed_iterator
|
||||
|
||||
/**
|
||||
* A forward iterator used for accessing packed repeated varint fields
|
||||
* (int32, uint32, int64, uint64, bool, enum).
|
||||
*/
|
||||
template <typename T>
|
||||
class const_varint_iterator {
|
||||
|
||||
protected:
|
||||
|
||||
/// Pointer to current iterator position
|
||||
const char* m_data = nullptr;
|
||||
|
||||
/// Pointer to end iterator position
|
||||
const char* m_end = nullptr;
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
static difference_type distance(const_varint_iterator begin, const_varint_iterator end) noexcept {
|
||||
// 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<unsigned char>(c) & 0x80u) == 0;
|
||||
});
|
||||
}
|
||||
|
||||
const_varint_iterator() noexcept = default;
|
||||
|
||||
const_varint_iterator(const char* data, const char* end) noexcept :
|
||||
m_data(data),
|
||||
m_end(end) {
|
||||
}
|
||||
|
||||
const_varint_iterator(const const_varint_iterator&) noexcept = default;
|
||||
const_varint_iterator(const_varint_iterator&&) noexcept = default;
|
||||
|
||||
const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
|
||||
const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
|
||||
|
||||
~const_varint_iterator() noexcept = default;
|
||||
|
||||
value_type operator*() const {
|
||||
const char* d = m_data; // will be thrown away
|
||||
return static_cast<value_type>(decode_varint(&d, m_end));
|
||||
}
|
||||
|
||||
const_varint_iterator& operator++() {
|
||||
skip_varint(&m_data, m_end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_varint_iterator operator++(int) {
|
||||
const const_varint_iterator tmp{*this};
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_varint_iterator& rhs) const noexcept {
|
||||
return m_data == rhs.m_data && m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
bool operator!=(const const_varint_iterator& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
}; // class const_varint_iterator
|
||||
|
||||
/**
|
||||
* A forward iterator used for accessing packed repeated svarint fields
|
||||
* (sint32, sint64).
|
||||
*/
|
||||
template <typename T>
|
||||
class const_svarint_iterator : public const_varint_iterator<T> {
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
const_svarint_iterator() noexcept :
|
||||
const_varint_iterator<T>() {
|
||||
}
|
||||
|
||||
const_svarint_iterator(const char* data, const char* end) noexcept :
|
||||
const_varint_iterator<T>(data, end) {
|
||||
}
|
||||
|
||||
const_svarint_iterator(const const_svarint_iterator&) = default;
|
||||
const_svarint_iterator(const_svarint_iterator&&) noexcept = default;
|
||||
|
||||
const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
|
||||
const_svarint_iterator& operator=(const_svarint_iterator&&) noexcept = default;
|
||||
|
||||
~const_svarint_iterator() = default;
|
||||
|
||||
value_type operator*() const {
|
||||
const char* d = this->m_data; // will be thrown away
|
||||
return static_cast<value_type>(decode_zigzag64(decode_varint(&d, this->m_end)));
|
||||
}
|
||||
|
||||
const_svarint_iterator& operator++() {
|
||||
skip_varint(&this->m_data, this->m_end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_svarint_iterator operator++(int) {
|
||||
const const_svarint_iterator tmp{*this};
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
}; // class const_svarint_iterator
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
namespace std {
|
||||
|
||||
// Specialize std::distance for all the protozero iterators. Because
|
||||
// functions can't be partially specialized, we have to do this for
|
||||
// every value_type we are using.
|
||||
|
||||
template <>
|
||||
inline typename protozero::const_varint_iterator<int32_t>::difference_type
|
||||
distance<protozero::const_varint_iterator<int32_t>>(protozero::const_varint_iterator<int32_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
|
||||
protozero::const_varint_iterator<int32_t> last) {
|
||||
return protozero::const_varint_iterator<int32_t>::distance(first, last);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline typename protozero::const_varint_iterator<int64_t>::difference_type
|
||||
distance<protozero::const_varint_iterator<int64_t>>(protozero::const_varint_iterator<int64_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
|
||||
protozero::const_varint_iterator<int64_t> last) {
|
||||
return protozero::const_varint_iterator<int64_t>::distance(first, last);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline typename protozero::const_varint_iterator<uint32_t>::difference_type
|
||||
distance<protozero::const_varint_iterator<uint32_t>>(protozero::const_varint_iterator<uint32_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
|
||||
protozero::const_varint_iterator<uint32_t> last) {
|
||||
return protozero::const_varint_iterator<uint32_t>::distance(first, last);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline typename protozero::const_varint_iterator<uint64_t>::difference_type
|
||||
distance<protozero::const_varint_iterator<uint64_t>>(protozero::const_varint_iterator<uint64_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
|
||||
protozero::const_varint_iterator<uint64_t> last) {
|
||||
return protozero::const_varint_iterator<uint64_t>::distance(first, last);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline typename protozero::const_svarint_iterator<int32_t>::difference_type
|
||||
distance<protozero::const_svarint_iterator<int32_t>>(protozero::const_svarint_iterator<int32_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
|
||||
protozero::const_svarint_iterator<int32_t> last) {
|
||||
return protozero::const_svarint_iterator<int32_t>::distance(first, last);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline typename protozero::const_svarint_iterator<int64_t>::difference_type
|
||||
distance<protozero::const_svarint_iterator<int64_t>>(protozero::const_svarint_iterator<int64_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
|
||||
protozero::const_svarint_iterator<int64_t> last) {
|
||||
return protozero::const_svarint_iterator<int64_t>::distance(first, last);
|
||||
}
|
||||
|
||||
} // end namespace std
|
||||
|
||||
#endif // PROTOZERO_ITERATORS_HPP
|
265
third_party/protozero/include/protozero/pbf_builder.hpp
vendored
Normal file
265
third_party/protozero/include/protozero/pbf_builder.hpp
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
#ifndef PROTOZERO_PBF_BUILDER_HPP
|
||||
#define PROTOZERO_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 pbf_builder.hpp
|
||||
*
|
||||
* @brief Contains the pbf_builder template class.
|
||||
*/
|
||||
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
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.
|
||||
*/
|
||||
template <typename T>
|
||||
class pbf_builder : public pbf_writer {
|
||||
|
||||
static_assert(std::is_same<pbf_tag_type, typename std::underlying_type<T>::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 <typename P>
|
||||
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 <typename... Ts>
|
||||
void add_bytes_vectored(T tag, Ts&&... values) {
|
||||
pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward<Ts>(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 <typename InputIterator> \
|
||||
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
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_PBF_BUILDER_HPP
|
184
third_party/protozero/include/protozero/pbf_message.hpp
vendored
Normal file
184
third_party/protozero/include/protozero/pbf_message.hpp
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
#ifndef PROTOZERO_PBF_MESSAGE_HPP
|
||||
#define PROTOZERO_PBF_MESSAGE_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 pbf_message.hpp
|
||||
*
|
||||
* @brief Contains the pbf_message template class.
|
||||
*/
|
||||
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* This class represents a protobuf message. Either a top-level message or
|
||||
* a nested sub-message. Top-level messages can be created from any buffer
|
||||
* with a pointer and length:
|
||||
*
|
||||
* @code
|
||||
* enum class Message : protozero::pbf_tag_type {
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* std::string buffer;
|
||||
* // fill buffer...
|
||||
* pbf_message<Message> message{buffer.data(), buffer.size()};
|
||||
* @endcode
|
||||
*
|
||||
* Sub-messages are created using get_message():
|
||||
*
|
||||
* @code
|
||||
* enum class SubMessage : protozero::pbf_tag_type {
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
* pbf_message<Message> message{...};
|
||||
* message.next();
|
||||
* pbf_message<SubMessage> submessage = message.get_message();
|
||||
* @endcode
|
||||
*
|
||||
* All methods of the pbf_message class except get_bytes() and get_string()
|
||||
* provide the strong exception guarantee, ie they either succeed or do not
|
||||
* change the pbf_message object they are called on. Use the get_data() method
|
||||
* instead of get_bytes() or get_string(), if you need this guarantee.
|
||||
*
|
||||
* This template class is based on the pbf_reader class and has all the same
|
||||
* methods. The difference is that whereever the pbf_reader class takes an
|
||||
* integer tag, this template class takes a tag of the template type T.
|
||||
*
|
||||
* Read the tutorial to understand how this class is used.
|
||||
*/
|
||||
template <typename T>
|
||||
class pbf_message : public pbf_reader {
|
||||
|
||||
static_assert(std::is_same<pbf_tag_type, typename std::underlying_type<T>::type>::value,
|
||||
"T must be enum with underlying type protozero::pbf_tag_type");
|
||||
|
||||
public:
|
||||
|
||||
/// The type of messages this class will read.
|
||||
using enum_type = T;
|
||||
|
||||
/**
|
||||
* Construct a pbf_message. All arguments are forwarded to the pbf_reader
|
||||
* parent class.
|
||||
*/
|
||||
template <typename... Args>
|
||||
pbf_message(Args&&... args) noexcept : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
|
||||
pbf_reader(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field in the message as the current field. This is usually
|
||||
* called in a while loop:
|
||||
*
|
||||
* @code
|
||||
* pbf_message<...> message(...);
|
||||
* while (message.next()) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @returns `true` if there is a next field, `false` if not.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now.
|
||||
*/
|
||||
bool next() {
|
||||
return pbf_reader::next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field with given tag in the message as the current field.
|
||||
* Fields with other tags are skipped. This is usually called in a while
|
||||
* loop for repeated fields:
|
||||
*
|
||||
* @code
|
||||
* pbf_message<Example1> message{...};
|
||||
* while (message.next(Example1::repeated_fixed64_r)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* or you can call it just once to get the one field with this tag:
|
||||
*
|
||||
* @code
|
||||
* pbf_message<Example1> message{...};
|
||||
* if (message.next(Example1::required_uint32_x)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Note that this will not check the wire type. The two-argument version
|
||||
* of this function will also check the wire type.
|
||||
*
|
||||
* @returns `true` if there is a next field with this tag.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now with the given tag.
|
||||
*/
|
||||
bool next(T next_tag) {
|
||||
return pbf_reader::next(pbf_tag_type(next_tag));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field with given tag and wire type in the message as the
|
||||
* current field. Fields with other tags are skipped. This is usually
|
||||
* called in a while loop for repeated fields:
|
||||
*
|
||||
* @code
|
||||
* pbf_message<Example1> message{...};
|
||||
* while (message.next(Example1::repeated_fixed64_r, pbf_wire_type::varint)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* or you can call it just once to get the one field with this tag:
|
||||
*
|
||||
* @code
|
||||
* pbf_message<Example1> message{...};
|
||||
* if (message.next(Example1::required_uint32_x, pbf_wire_type::varint)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Note that this will also check the wire type. The one-argument version
|
||||
* of this function will not check the wire type.
|
||||
*
|
||||
* @returns `true` if there is a next field with this tag.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now with the given tag.
|
||||
*/
|
||||
bool next(T next_tag, pbf_wire_type type) {
|
||||
return pbf_reader::next(pbf_tag_type(next_tag), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The tag of the current field. The tag is the enum value for the field
|
||||
* number from the description in the .proto file.
|
||||
*
|
||||
* Call next() before calling this function to set the current field.
|
||||
*
|
||||
* @returns tag of the current field.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
*/
|
||||
T tag() const noexcept {
|
||||
return T(pbf_reader::tag());
|
||||
}
|
||||
|
||||
}; // class pbf_message
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_PBF_MESSAGE_HPP
|
969
third_party/protozero/include/protozero/pbf_reader.hpp
vendored
Normal file
969
third_party/protozero/include/protozero/pbf_reader.hpp
vendored
Normal file
@ -0,0 +1,969 @@
|
||||
#ifndef PROTOZERO_PBF_READER_HPP
|
||||
#define PROTOZERO_PBF_READER_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 pbf_reader.hpp
|
||||
*
|
||||
* @brief Contains the pbf_reader class.
|
||||
*/
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
#include <protozero/data_view.hpp>
|
||||
#include <protozero/exception.hpp>
|
||||
#include <protozero/iterators.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
# include <protozero/byteswap.hpp>
|
||||
#endif
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* This class represents a protobuf message. Either a top-level message or
|
||||
* a nested sub-message. Top-level messages can be created from any buffer
|
||||
* with a pointer and length:
|
||||
*
|
||||
* @code
|
||||
* std::string buffer;
|
||||
* // fill buffer...
|
||||
* pbf_reader message{buffer.data(), buffer.size()};
|
||||
* @endcode
|
||||
*
|
||||
* Sub-messages are created using get_message():
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message{...};
|
||||
* message.next();
|
||||
* pbf_reader submessage = message.get_message();
|
||||
* @endcode
|
||||
*
|
||||
* All methods of the pbf_reader class except get_bytes() and get_string()
|
||||
* provide the strong exception guarantee, ie they either succeed or do not
|
||||
* change the pbf_reader object they are called on. Use the get_view() method
|
||||
* instead of get_bytes() or get_string(), if you need this guarantee.
|
||||
*/
|
||||
class pbf_reader {
|
||||
|
||||
// A pointer to the next unread data.
|
||||
const char* m_data = nullptr;
|
||||
|
||||
// A pointer to one past the end of data.
|
||||
const char* m_end = nullptr;
|
||||
|
||||
// The wire type of the current field.
|
||||
pbf_wire_type m_wire_type = pbf_wire_type::unknown;
|
||||
|
||||
// The tag of the current field.
|
||||
pbf_tag_type m_tag = 0;
|
||||
|
||||
template <typename T>
|
||||
T get_fixed() {
|
||||
T result;
|
||||
const char* data = m_data;
|
||||
skip_bytes(sizeof(T));
|
||||
std::memcpy(&result, data, sizeof(T));
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
detail::byteswap_inplace(&result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
iterator_range<const_fixed_iterator<T>> packed_fixed() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
const auto len = get_len_and_skip();
|
||||
if (len % sizeof(T) != 0) {
|
||||
throw invalid_length_exception{};
|
||||
}
|
||||
return {const_fixed_iterator<T>(m_data - len),
|
||||
const_fixed_iterator<T>(m_data)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_varint() {
|
||||
return static_cast<T>(decode_varint(&m_data, m_end));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_svarint() {
|
||||
protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
|
||||
return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
|
||||
}
|
||||
|
||||
pbf_length_type get_length() {
|
||||
return get_varint<pbf_length_type>();
|
||||
}
|
||||
|
||||
void skip_bytes(pbf_length_type len) {
|
||||
if (m_data + len > m_end) {
|
||||
throw end_of_buffer_exception{};
|
||||
}
|
||||
m_data += len;
|
||||
|
||||
#ifndef NDEBUG
|
||||
// In debug builds reset the tag to zero so that we can detect (some)
|
||||
// wrong code.
|
||||
m_tag = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
pbf_length_type get_len_and_skip() {
|
||||
const auto len = get_length();
|
||||
skip_bytes(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
iterator_range<T> get_packed() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
const auto len = get_len_and_skip();
|
||||
return {T{m_data - len, m_data},
|
||||
T{m_data, m_data}};
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a pbf_reader message from a data_view. The pointer from the
|
||||
* data_view will be stored inside the pbf_reader object, no data is
|
||||
* copied. So you must make sure the view stays valid as long as the
|
||||
* pbf_reader object is used.
|
||||
*
|
||||
* The buffer must contain a complete protobuf message.
|
||||
*
|
||||
* @post There is no current field.
|
||||
*/
|
||||
explicit pbf_reader(const data_view& view) noexcept
|
||||
: m_data(view.data()),
|
||||
m_end(view.data() + view.size()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a pbf_reader message from a data pointer and a length. The
|
||||
* pointer will be stored inside the pbf_reader object, no data is copied.
|
||||
* So you must make sure the buffer stays valid as long as the pbf_reader
|
||||
* object is used.
|
||||
*
|
||||
* The buffer must contain a complete protobuf message.
|
||||
*
|
||||
* @post There is no current field.
|
||||
*/
|
||||
pbf_reader(const char* data, std::size_t size) noexcept
|
||||
: m_data(data),
|
||||
m_end(data + size) {
|
||||
}
|
||||
|
||||
#ifndef PROTOZERO_STRICT_API
|
||||
/**
|
||||
* Construct a pbf_reader message from a data pointer and a length. The
|
||||
* pointer will be stored inside the pbf_reader object, no data is copied.
|
||||
* So you must make sure the buffer stays valid as long as the pbf_reader
|
||||
* object is used.
|
||||
*
|
||||
* The buffer must contain a complete protobuf message.
|
||||
*
|
||||
* @post There is no current field.
|
||||
* @deprecated Use one of the other constructors.
|
||||
*/
|
||||
explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
|
||||
: m_data(data.first),
|
||||
m_end(data.first + data.second) {
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Construct a pbf_reader message from a std::string. A pointer to the
|
||||
* string internals will be stored inside the pbf_reader object, no data
|
||||
* is copied. So you must make sure the string is unchanged as long as the
|
||||
* pbf_reader object is used.
|
||||
*
|
||||
* The string must contain a complete protobuf message.
|
||||
*
|
||||
* @post There is no current field.
|
||||
*/
|
||||
explicit pbf_reader(const std::string& data) noexcept
|
||||
: m_data(data.data()),
|
||||
m_end(data.data() + data.size()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* pbf_reader can be default constructed and behaves like it has an empty
|
||||
* buffer.
|
||||
*/
|
||||
pbf_reader() noexcept = default;
|
||||
|
||||
/// pbf_reader messages can be copied trivially.
|
||||
pbf_reader(const pbf_reader&) noexcept = default;
|
||||
|
||||
/// pbf_reader messages can be moved trivially.
|
||||
pbf_reader(pbf_reader&&) noexcept = default;
|
||||
|
||||
/// pbf_reader messages can be copied trivially.
|
||||
pbf_reader& operator=(const pbf_reader& other) noexcept = default;
|
||||
|
||||
/// pbf_reader messages can be moved trivially.
|
||||
pbf_reader& operator=(pbf_reader&& other) noexcept = default;
|
||||
|
||||
~pbf_reader() = default;
|
||||
|
||||
/**
|
||||
* Swap the contents of this object with the other.
|
||||
*
|
||||
* @param other Other object to swap data with.
|
||||
*/
|
||||
void swap(pbf_reader& other) noexcept {
|
||||
using std::swap;
|
||||
swap(m_data, other.m_data);
|
||||
swap(m_end, other.m_end);
|
||||
swap(m_wire_type, other.m_wire_type);
|
||||
swap(m_tag, other.m_tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* In a boolean context the pbf_reader class evaluates to `true` if there
|
||||
* are still fields available and to `false` if the last field has been
|
||||
* read.
|
||||
*/
|
||||
operator bool() const noexcept { // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
|
||||
return m_data < m_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length in bytes of the current message. If you have
|
||||
* already called next() and/or any of the get_*() functions, this will
|
||||
* return the remaining length.
|
||||
*
|
||||
* This can, for instance, be used to estimate the space needed for a
|
||||
* buffer. Of course you have to know reasonably well what data to expect
|
||||
* and how it is encoded for this number to have any meaning.
|
||||
*/
|
||||
std::size_t length() const noexcept {
|
||||
return std::size_t(m_end - m_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field in the message as the current field. This is usually
|
||||
* called in a while loop:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message(...);
|
||||
* while (message.next()) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @returns `true` if there is a next field, `false` if not.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now.
|
||||
*/
|
||||
bool next() {
|
||||
if (m_data == m_end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto value = get_varint<uint32_t>();
|
||||
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
|
||||
if (m_tag == 0 || (m_tag >= 19000 && m_tag <= 19999)) {
|
||||
throw invalid_tag_exception{};
|
||||
}
|
||||
|
||||
m_wire_type = pbf_wire_type(value & 0x07u);
|
||||
switch (m_wire_type) {
|
||||
case pbf_wire_type::varint:
|
||||
case pbf_wire_type::fixed64:
|
||||
case pbf_wire_type::length_delimited:
|
||||
case pbf_wire_type::fixed32:
|
||||
break;
|
||||
default:
|
||||
throw unknown_pbf_wire_type_exception{};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field with given tag in the message as the current field.
|
||||
* Fields with other tags are skipped. This is usually called in a while
|
||||
* loop for repeated fields:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message{...};
|
||||
* while (message.next(17)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* or you can call it just once to get the one field with this tag:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message{...};
|
||||
* if (message.next(17)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Note that this will not check the wire type. The two-argument version
|
||||
* of this function will also check the wire type.
|
||||
*
|
||||
* @returns `true` if there is a next field with this tag.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now with the given tag.
|
||||
*/
|
||||
bool next(pbf_tag_type next_tag) {
|
||||
while (next()) {
|
||||
if (m_tag == next_tag) {
|
||||
return true;
|
||||
}
|
||||
skip();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field with given tag and wire type in the message as the
|
||||
* current field. Fields with other tags are skipped. This is usually
|
||||
* called in a while loop for repeated fields:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message{...};
|
||||
* while (message.next(17, pbf_wire_type::varint)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* or you can call it just once to get the one field with this tag:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message{...};
|
||||
* if (message.next(17, pbf_wire_type::varint)) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* Note that this will also check the wire type. The one-argument version
|
||||
* of this function will not check the wire type.
|
||||
*
|
||||
* @returns `true` if there is a next field with this tag.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now with the given tag.
|
||||
*/
|
||||
bool next(pbf_tag_type next_tag, pbf_wire_type type) {
|
||||
while (next()) {
|
||||
if (m_tag == next_tag && m_wire_type == type) {
|
||||
return true;
|
||||
}
|
||||
skip();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The tag of the current field. The tag is the field number from the
|
||||
* description in the .proto file.
|
||||
*
|
||||
* Call next() before calling this function to set the current field.
|
||||
*
|
||||
* @returns tag of the current field.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
*/
|
||||
pbf_tag_type tag() const noexcept {
|
||||
return m_tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the wire type of the current field. The wire types are:
|
||||
*
|
||||
* * 0 - varint
|
||||
* * 1 - 64 bit
|
||||
* * 2 - length-delimited
|
||||
* * 5 - 32 bit
|
||||
*
|
||||
* All other types are illegal.
|
||||
*
|
||||
* Call next() before calling this function to set the current field.
|
||||
*
|
||||
* @returns wire type of the current field.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
*/
|
||||
pbf_wire_type wire_type() const noexcept {
|
||||
return m_wire_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tag and wire type of the current field in one integer suitable
|
||||
* for comparison with a switch statement.
|
||||
*
|
||||
* Use it like this:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message{...};
|
||||
* while (message.next()) {
|
||||
* switch (message.tag_and_type()) {
|
||||
* case tag_and_type(17, pbf_wire_type::length_delimited):
|
||||
* ....
|
||||
* break;
|
||||
* case tag_and_type(21, pbf_wire_type::varint):
|
||||
* ....
|
||||
* break;
|
||||
* default:
|
||||
* message.skip();
|
||||
* }
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
uint32_t tag_and_type() const noexcept {
|
||||
return protozero::tag_and_type(tag(), wire_type());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the wire type of the current field.
|
||||
*
|
||||
* @returns `true` if the current field has the given wire type.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
*/
|
||||
bool has_wire_type(pbf_wire_type type) const noexcept {
|
||||
return wire_type() == type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume the current field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
void skip() {
|
||||
protozero_assert(tag() != 0 && "call next() before calling skip()");
|
||||
switch (wire_type()) {
|
||||
case pbf_wire_type::varint:
|
||||
skip_varint(&m_data, m_end);
|
||||
break;
|
||||
case pbf_wire_type::fixed64:
|
||||
skip_bytes(8);
|
||||
break;
|
||||
case pbf_wire_type::length_delimited:
|
||||
skip_bytes(get_length());
|
||||
break;
|
||||
case pbf_wire_type::fixed32:
|
||||
skip_bytes(4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
///@{
|
||||
/**
|
||||
* @name Scalar field accessor functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Consume and return value of current "bool" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "bool".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
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;
|
||||
skip_varint(&m_data, m_end);
|
||||
return data[0] != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "enum" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "enum".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int32_t get_enum() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_varint<int32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "int32" varint field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "int32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int32_t get_int32() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_varint<int32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "sint32" varint field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "sint32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int32_t get_sint32() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_svarint<int32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "uint32" varint field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "uint32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
uint32_t get_uint32() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_varint<uint32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "int64" varint field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "int64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int64_t get_int64() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_varint<int64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "sint64" varint field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "sint64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int64_t get_sint64() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_svarint<int64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "uint64" varint field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "uint64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
uint64_t get_uint64() {
|
||||
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
|
||||
return get_varint<uint64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "fixed32" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "fixed32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
uint32_t get_fixed32() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
|
||||
return get_fixed<uint32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "sfixed32" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "sfixed32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int32_t get_sfixed32() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
|
||||
return get_fixed<int32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "fixed64" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "fixed64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
uint64_t get_fixed64() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
|
||||
return get_fixed<uint64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "sfixed64" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "sfixed64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
int64_t get_sfixed64() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
|
||||
return get_fixed<int64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "float" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "float".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
float get_float() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
|
||||
return get_fixed<float>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "double" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "double".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
double get_double() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
|
||||
return get_fixed<double>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "bytes", "string", or "message"
|
||||
* field.
|
||||
*
|
||||
* @returns A data_view object.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "bytes", "string", or "message".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
data_view get_view() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
|
||||
const auto len = get_len_and_skip();
|
||||
return {m_data - len, len};
|
||||
}
|
||||
|
||||
#ifndef PROTOZERO_STRICT_API
|
||||
/**
|
||||
* Consume and return value of current "bytes" or "string" field.
|
||||
*
|
||||
* @returns A pair with a pointer to the data and the length of the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "bytes" or "string".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
std::pair<const char*, pbf_length_type> get_data() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
|
||||
const auto len = get_len_and_skip();
|
||||
return {m_data - len, len};
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Consume and return value of current "bytes" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "bytes".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
std::string get_bytes() {
|
||||
return std::string(get_view());
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "string" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "string".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
std::string get_string() {
|
||||
return std::string(get_view());
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume and return value of current "message" field.
|
||||
*
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "message".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
pbf_reader get_message() {
|
||||
return pbf_reader{get_view()};
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
/// Forward iterator for iterating over bool (int32 varint) values.
|
||||
using const_bool_iterator = const_varint_iterator< int32_t>;
|
||||
|
||||
/// Forward iterator for iterating over enum (int32 varint) values.
|
||||
using const_enum_iterator = const_varint_iterator< int32_t>;
|
||||
|
||||
/// Forward iterator for iterating over int32 (varint) values.
|
||||
using const_int32_iterator = const_varint_iterator< int32_t>;
|
||||
|
||||
/// Forward iterator for iterating over sint32 (varint) values.
|
||||
using const_sint32_iterator = const_svarint_iterator<int32_t>;
|
||||
|
||||
/// Forward iterator for iterating over uint32 (varint) values.
|
||||
using const_uint32_iterator = const_varint_iterator<uint32_t>;
|
||||
|
||||
/// Forward iterator for iterating over int64 (varint) values.
|
||||
using const_int64_iterator = const_varint_iterator< int64_t>;
|
||||
|
||||
/// Forward iterator for iterating over sint64 (varint) values.
|
||||
using const_sint64_iterator = const_svarint_iterator<int64_t>;
|
||||
|
||||
/// Forward iterator for iterating over uint64 (varint) values.
|
||||
using const_uint64_iterator = const_varint_iterator<uint64_t>;
|
||||
|
||||
/// Forward iterator for iterating over fixed32 values.
|
||||
using const_fixed32_iterator = const_fixed_iterator<uint32_t>;
|
||||
|
||||
/// Forward iterator for iterating over sfixed32 values.
|
||||
using const_sfixed32_iterator = const_fixed_iterator<int32_t>;
|
||||
|
||||
/// Forward iterator for iterating over fixed64 values.
|
||||
using const_fixed64_iterator = const_fixed_iterator<uint64_t>;
|
||||
|
||||
/// Forward iterator for iterating over sfixed64 values.
|
||||
using const_sfixed64_iterator = const_fixed_iterator<int64_t>;
|
||||
|
||||
/// Forward iterator for iterating over float values.
|
||||
using const_float_iterator = const_fixed_iterator<float>;
|
||||
|
||||
/// Forward iterator for iterating over double values.
|
||||
using const_double_iterator = const_fixed_iterator<double>;
|
||||
|
||||
///@{
|
||||
/**
|
||||
* @name Repeated packed field accessor functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed bool" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed bool".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_bool_iterator> get_packed_bool() {
|
||||
return get_packed<pbf_reader::const_bool_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed enum" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed enum".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_enum_iterator> get_packed_enum() {
|
||||
return get_packed<pbf_reader::const_enum_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed int32" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed int32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_int32_iterator> get_packed_int32() {
|
||||
return get_packed<pbf_reader::const_int32_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed sint32" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed sint32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_sint32_iterator> get_packed_sint32() {
|
||||
return get_packed<pbf_reader::const_sint32_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed uint32" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed uint32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_uint32_iterator> get_packed_uint32() {
|
||||
return get_packed<pbf_reader::const_uint32_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed int64" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed int64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_int64_iterator> get_packed_int64() {
|
||||
return get_packed<pbf_reader::const_int64_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed sint64" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed sint64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_sint64_iterator> get_packed_sint64() {
|
||||
return get_packed<pbf_reader::const_sint64_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed uint64" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed uint64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_uint64_iterator> get_packed_uint64() {
|
||||
return get_packed<pbf_reader::const_uint64_iterator>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed fixed32" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed fixed32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_fixed32_iterator> get_packed_fixed32() {
|
||||
return packed_fixed<uint32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed sfixed32" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed sfixed32".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_sfixed32_iterator> get_packed_sfixed32() {
|
||||
return packed_fixed<int32_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed fixed64" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed fixed64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_fixed64_iterator> get_packed_fixed64() {
|
||||
return packed_fixed<uint64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed sfixed64" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed sfixed64".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_sfixed64_iterator> get_packed_sfixed64() {
|
||||
return packed_fixed<int64_t>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed float" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed float".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_float_iterator> get_packed_float() {
|
||||
return packed_fixed<float>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consume current "repeated packed double" field.
|
||||
*
|
||||
* @returns a pair of iterators to the beginning and one past the end of
|
||||
* the data.
|
||||
* @pre There must be a current field (ie. next() must have returned `true`).
|
||||
* @pre The current field must be of type "repeated packed double".
|
||||
* @post The current field was consumed and there is no current field now.
|
||||
*/
|
||||
iterator_range<pbf_reader::const_double_iterator> get_packed_double() {
|
||||
return packed_fixed<double>();
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
}; // class pbf_reader
|
||||
|
||||
/**
|
||||
* Swap two pbf_reader objects.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_PBF_READER_HPP
|
1049
third_party/protozero/include/protozero/pbf_writer.hpp
vendored
Normal file
1049
third_party/protozero/include/protozero/pbf_writer.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
66
third_party/protozero/include/protozero/types.hpp
vendored
Normal file
66
third_party/protozero/include/protozero/types.hpp
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
#ifndef PROTOZERO_TYPES_HPP
|
||||
#define PROTOZERO_TYPES_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 types.hpp
|
||||
*
|
||||
* @brief Contains the declaration of low-level types used in the pbf format.
|
||||
*/
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* The type used for field tags (field numbers).
|
||||
*/
|
||||
using pbf_tag_type = uint32_t;
|
||||
|
||||
/**
|
||||
* The type used to encode type information.
|
||||
* See the table on
|
||||
* https://developers.google.com/protocol-buffers/docs/encoding
|
||||
*/
|
||||
enum class pbf_wire_type : uint32_t {
|
||||
varint = 0, // int32/64, uint32/64, sint32/64, bool, enum
|
||||
fixed64 = 1, // fixed64, sfixed64, double
|
||||
length_delimited = 2, // string, bytes, nested messages, packed repeated fields
|
||||
fixed32 = 5, // fixed32, sfixed32, float
|
||||
unknown = 99 // used for default setting in this library
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the tag and wire type of the current field in one integer suitable
|
||||
* for comparison with a switch statement.
|
||||
*
|
||||
* See pbf_reader.tag_and_type() for an example how to use this.
|
||||
*/
|
||||
template <typename T>
|
||||
constexpr inline uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept {
|
||||
return (static_cast<uint32_t>(static_cast<pbf_tag_type>(tag)) << 3u) | static_cast<uint32_t>(wire_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* The type used for length values, such as the length of a field.
|
||||
*/
|
||||
using pbf_length_type = uint32_t;
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_TYPES_HPP
|
188
third_party/protozero/include/protozero/varint.hpp
vendored
Normal file
188
third_party/protozero/include/protozero/varint.hpp
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
#ifndef PROTOZERO_VARINT_HPP
|
||||
#define PROTOZERO_VARINT_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 varint.hpp
|
||||
*
|
||||
* @brief Contains low-level varint and zigzag encoding and decoding functions.
|
||||
*/
|
||||
|
||||
#include <protozero/exception.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* The maximum length of a 64 bit varint.
|
||||
*/
|
||||
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
|
||||
|
||||
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<const int8_t*>(*data);
|
||||
const auto iend = reinterpret_cast<const int8_t*>(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; }
|
||||
throw varint_too_long_exception{};
|
||||
} while (false);
|
||||
} else {
|
||||
unsigned int shift = 0;
|
||||
while (p != iend && *p < 0) {
|
||||
val |= uint64_t(*p++ & 0x7fu) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
if (p == iend) {
|
||||
throw end_of_buffer_exception{};
|
||||
}
|
||||
val |= uint64_t(*p++) << shift;
|
||||
}
|
||||
|
||||
*data = reinterpret_cast<const char*>(p);
|
||||
return val;
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/**
|
||||
* Decode a 64 bit varint.
|
||||
*
|
||||
* Strong exception guarantee: if there is an exception the data pointer will
|
||||
* not be changed.
|
||||
*
|
||||
* @param[in,out] data Pointer to pointer to the input data. After the function
|
||||
* returns this will point to the next data to be read.
|
||||
* @param[in] end Pointer one past the end of the input data.
|
||||
* @returns The decoded integer
|
||||
* @throws varint_too_long_exception if the varint is longer then the maximum
|
||||
* length that would fit in a 64 bit int. Usually this means your data
|
||||
* is corrupted or you are trying to read something as a varint that
|
||||
* isn't.
|
||||
* @throws end_of_buffer_exception if the *end* of the buffer was reached
|
||||
* before the end of the varint.
|
||||
*/
|
||||
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)) {
|
||||
const auto val = static_cast<uint64_t>(**data);
|
||||
++(*data);
|
||||
return val;
|
||||
}
|
||||
// If this varint is more than one byte, defer to complete implementation.
|
||||
return detail::decode_varint_impl(data, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip over a varint.
|
||||
*
|
||||
* Strong exception guarantee: if there is an exception the data pointer will
|
||||
* not be changed.
|
||||
*
|
||||
* @param[in,out] data Pointer to pointer to the input data. After the function
|
||||
* returns this will point to the next data to be read.
|
||||
* @param[in] end Pointer one past the end of the input data.
|
||||
* @throws end_of_buffer_exception if the *end* of the buffer was reached
|
||||
* before the end of the varint.
|
||||
*/
|
||||
inline void skip_varint(const char** data, const char* end) {
|
||||
const auto begin = reinterpret_cast<const int8_t*>(*data);
|
||||
const auto iend = reinterpret_cast<const int8_t*>(end);
|
||||
const int8_t* p = begin;
|
||||
|
||||
while (p != iend && *p < 0) {
|
||||
++p;
|
||||
}
|
||||
|
||||
if (p >= begin + max_varint_length) {
|
||||
throw varint_too_long_exception{};
|
||||
}
|
||||
|
||||
if (p == iend) {
|
||||
throw end_of_buffer_exception{};
|
||||
}
|
||||
|
||||
++p;
|
||||
|
||||
*data = reinterpret_cast<const char*>(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Varint encode a 64 bit integer.
|
||||
*
|
||||
* @tparam T An output iterator type.
|
||||
* @param data Output iterator the varint encoded value will be written to
|
||||
* byte by byte.
|
||||
* @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.
|
||||
*/
|
||||
template <typename T>
|
||||
inline int write_varint(T data, uint64_t value) {
|
||||
int n = 1;
|
||||
|
||||
while (value >= 0x80u) {
|
||||
*data++ = char((value & 0x7fu) | 0x80u);
|
||||
value >>= 7u;
|
||||
++n;
|
||||
}
|
||||
*data++ = char(value);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZigZag encodes a 32 bit integer.
|
||||
*/
|
||||
inline constexpr uint32_t encode_zigzag32(int32_t value) noexcept {
|
||||
return (static_cast<uint32_t>(value) << 1u) ^ (static_cast<uint32_t>(value >> 31u));
|
||||
}
|
||||
|
||||
/**
|
||||
* ZigZag encodes a 64 bit integer.
|
||||
*/
|
||||
inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept {
|
||||
return (static_cast<uint64_t>(value) << 1u) ^ (static_cast<uint64_t>(value >> 63u));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a 32 bit ZigZag-encoded integer.
|
||||
*/
|
||||
inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept {
|
||||
return static_cast<int32_t>(value >> 1u) ^ -static_cast<int32_t>(value & 1u);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a 64 bit ZigZag-encoded integer.
|
||||
*/
|
||||
inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept {
|
||||
return static_cast<int64_t>(value >> 1u) ^ -static_cast<int64_t>(value & 1u);
|
||||
}
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_VARINT_HPP
|
34
third_party/protozero/include/protozero/version.hpp
vendored
Normal file
34
third_party/protozero/include/protozero/version.hpp
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef PROTOZERO_VERSION_HPP
|
||||
#define PROTOZERO_VERSION_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 version.hpp
|
||||
*
|
||||
* @brief Contains macros defining the protozero version.
|
||||
*/
|
||||
|
||||
/// The major version number
|
||||
#define PROTOZERO_VERSION_MAJOR 1
|
||||
|
||||
/// The minor version number
|
||||
#define PROTOZERO_VERSION_MINOR 6
|
||||
|
||||
/// The patch number
|
||||
#define PROTOZERO_VERSION_PATCH 2
|
||||
|
||||
/// 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"
|
||||
|
||||
#endif // PROTOZERO_VERSION_HPP
|
116
third_party/protozero/test/CMakeLists.txt
vendored
Normal file
116
third_party/protozero/test/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,116 @@
|
||||
#-----------------------------------------------------------------------------
|
||||
#
|
||||
# CMake config
|
||||
#
|
||||
# protozero tests
|
||||
#
|
||||
#-----------------------------------------------------------------------------
|
||||
|
||||
include_directories(SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/catch")
|
||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
add_subdirectory(unit)
|
||||
|
||||
set(TEST_DIRS alignment
|
||||
bool
|
||||
bytes
|
||||
complex
|
||||
double
|
||||
enum
|
||||
fixed32
|
||||
fixed64
|
||||
float
|
||||
int32
|
||||
int64
|
||||
message
|
||||
nested
|
||||
repeated
|
||||
repeated_packed_bool
|
||||
repeated_packed_double
|
||||
repeated_packed_enum
|
||||
repeated_packed_fixed32
|
||||
repeated_packed_fixed64
|
||||
repeated_packed_float
|
||||
repeated_packed_int32
|
||||
repeated_packed_int64
|
||||
repeated_packed_sfixed32
|
||||
repeated_packed_sfixed64
|
||||
repeated_packed_sint32
|
||||
repeated_packed_sint64
|
||||
repeated_packed_uint32
|
||||
repeated_packed_uint64
|
||||
rollback
|
||||
sfixed32
|
||||
sfixed64
|
||||
sint32
|
||||
sint64
|
||||
skip
|
||||
string
|
||||
tag_and_type
|
||||
tags
|
||||
uint32
|
||||
uint64
|
||||
vector_tile
|
||||
wrong_type_access)
|
||||
|
||||
string(REGEX REPLACE "([^;]+)" "t/\\1/reader_test_cases.cpp" _test_sources "${TEST_DIRS}")
|
||||
|
||||
add_executable(reader_tests reader_tests.cpp ${_test_sources})
|
||||
|
||||
add_test(NAME reader_tests COMMAND reader_tests)
|
||||
|
||||
set_tests_properties(reader_tests PROPERTIES WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
if(PROTOBUF_FOUND)
|
||||
message(STATUS "Found protobuf libraries: Adding writer tests...")
|
||||
|
||||
include_directories(SYSTEM ${PROTOBUF_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(PROTOBUF_GENERATE_CPP_APPEND_PATH false)
|
||||
|
||||
foreach(_dir IN LISTS TEST_DIRS)
|
||||
set(_full_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/t/${_dir}")
|
||||
if(EXISTS "${_full_src_dir}/writer_test_cases.cpp")
|
||||
message(STATUS " Adding ${_dir}")
|
||||
set(_full_bin_dir "${CMAKE_CURRENT_BINARY_DIR}/t/${_dir}")
|
||||
set(_proto_file "${_full_src_dir}/${_dir}_testcase.proto")
|
||||
set(_src_file "${_full_bin_dir}/${_dir}_testcase.pb.cc")
|
||||
set(_hdr_file "${_full_bin_dir}/${_dir}_testcase.pb.h")
|
||||
|
||||
file(MAKE_DIRECTORY ${_full_bin_dir})
|
||||
|
||||
list(APPEND SOURCES "${_full_src_dir}/writer_test_cases.cpp")
|
||||
list(APPEND PROTO_FILES "${_proto_file}")
|
||||
list(APPEND PROTO_SRCS "${_src_file}")
|
||||
list(APPEND PROTO_HDRS "${_hdr_file}")
|
||||
|
||||
set_source_files_properties(${_proto_file} ${_hdr_file}
|
||||
PROPERTIES GENERATED TRUE)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${_src_file} ${_hdr_file}
|
||||
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE}
|
||||
ARGS --cpp_out=${_full_bin_dir} -I ${_full_src_dir} ${_proto_file}
|
||||
DEPENDS ${_proto_file}
|
||||
VERBATIM)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
add_executable(writer_tests writer_tests.cpp ${SOURCES} ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
|
||||
target_link_libraries(writer_tests ${PROTOBUF_LITE_LIBRARY})
|
||||
|
||||
if(NOT MSVC)
|
||||
set_target_properties(writer_tests PROPERTIES COMPILE_FLAGS "-pthread")
|
||||
if(NOT APPLE)
|
||||
set_target_properties(writer_tests PROPERTIES LINK_FLAGS "-pthread")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_test(NAME writer_tests COMMAND writer_tests)
|
||||
else()
|
||||
message(STATUS "Protobuf libraries not found: Disabling writer tests.")
|
||||
endif()
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
52
third_party/protozero/test/README.md
vendored
Normal file
52
third_party/protozero/test/README.md
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
# Tests
|
||||
|
||||
Tests are using the [Catch Unit Test Framework](https://github.com/philsquared/Catch).
|
||||
|
||||
## Organization of the unit tests
|
||||
|
||||
Unit tests test low-level functions of the library. They are in the `unit`
|
||||
directory.
|
||||
|
||||
|
||||
## Organization of the reader/writer test cases
|
||||
|
||||
The hart of the tests are the reader/writer tests checking all aspects of
|
||||
decoding and encoding protobuf files.
|
||||
|
||||
Each test case is in its own directory under the `t` directory. Each directory
|
||||
contains (some of) the following files:
|
||||
|
||||
* `reader_test_cases.cpp`: The C++ source code that runs the reader tests.
|
||||
* `writer_test_cases.cpp`: The C++ source code that runs the writer tests.
|
||||
* `data-*.pbf`: PBF data files used by the tests.
|
||||
* `testcase.proto`: Protobuf file describing the format of the data files.
|
||||
* `testcase.cpp`: C++ file for creating the data files.
|
||||
|
||||
### Reader tests
|
||||
|
||||
The CMake config finds all the `reader_test_cases.cpp` files and compiles them.
|
||||
Together with the `reader_tests.cpp` file they make up the `reader_tests`
|
||||
executable which can be called to execute all the reader tests.
|
||||
|
||||
### Extra writer tests
|
||||
|
||||
The CMake config finds all the `writer_test_cases.cpp` files and compiles them.
|
||||
Together with the `writer_tests.cpp` file they make up the `writer_tests`
|
||||
executable which can be called to execute all the writer tests.
|
||||
|
||||
The writer tests need the Google protobuf library to work.
|
||||
|
||||
|
||||
## Creating test data from scratch
|
||||
|
||||
Most tests use test data stored in PBF format in their directory. The files
|
||||
have the suffix `.pbf`. Most of those files have been generated from the
|
||||
provided `testcase.proto` and `testcase.cpp` files.
|
||||
|
||||
Usually you do not have to do this, but if you want to re-generate the PBF
|
||||
data files, you can do so:
|
||||
|
||||
cd test
|
||||
./create_pbf_test_data.sh
|
||||
|
11678
third_party/protozero/test/catch/catch.hpp
vendored
Normal file
11678
third_party/protozero/test/catch/catch.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
28
third_party/protozero/test/create_pbf_test_data.sh
vendored
Executable file
28
third_party/protozero/test/create_pbf_test_data.sh
vendored
Executable file
@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# create_pbf_test_data.sh [TESTCASE]
|
||||
#
|
||||
# This script creates the test data for the given test case in protobuf format
|
||||
# using the testcase.proto description and the testcase.cpp code.
|
||||
#
|
||||
# If called without a test case it will iterate over all test cases generating
|
||||
# all data.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
for dir in t/*; do
|
||||
$0 $dir
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Generating $1..."
|
||||
cd $1
|
||||
if [ -f testcase.proto ]; then
|
||||
protoc --cpp_out=. testcase.proto
|
||||
$CXX -std=c++11 -I../../include -o testcase testcase.cpp testcase.pb.cc -lprotobuf-lite -pthread
|
||||
./testcase
|
||||
fi
|
||||
cd ../..
|
||||
|
296
third_party/protozero/test/include/packed_access.hpp
vendored
Normal file
296
third_party/protozero/test/include/packed_access.hpp
vendored
Normal file
@ -0,0 +1,296 @@
|
||||
// NOLINT(llvm-header-guard)
|
||||
|
||||
#define PBF_TYPE_NAME PROTOZERO_TEST_STRING(PBF_TYPE)
|
||||
#define GET_TYPE PROTOZERO_TEST_CONCAT(get_packed_, PBF_TYPE)
|
||||
#define ADD_TYPE PROTOZERO_TEST_CONCAT(add_packed_, PBF_TYPE)
|
||||
|
||||
using packed_field_type = PROTOZERO_TEST_CONCAT(protozero::packed_field_, PBF_TYPE);
|
||||
|
||||
TEST_CASE("read repeated packed field: " PBF_TYPE_NAME) {
|
||||
// Run these tests twice, the second time we basically move the data
|
||||
// one byte down in the buffer. It doesn't matter how the data or buffer
|
||||
// is aligned before that, in at least one of these cases the ints will
|
||||
// not be aligned properly. So we test that even in that case the ints
|
||||
// will be extracted properly.
|
||||
|
||||
for (std::string::size_type n = 0; n < 2; ++n) {
|
||||
std::string abuffer;
|
||||
abuffer.reserve(1000);
|
||||
abuffer.append(n, '\0');
|
||||
|
||||
SECTION("empty") {
|
||||
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
|
||||
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("one") {
|
||||
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||||
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
const auto it_range = item.GET_TYPE();
|
||||
REQUIRE_FALSE(item.next());
|
||||
|
||||
REQUIRE(it_range.begin() != it_range.end());
|
||||
REQUIRE(*it_range.begin() == 17);
|
||||
REQUIRE(std::next(it_range.begin()) == it_range.end());
|
||||
}
|
||||
|
||||
SECTION("many") {
|
||||
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||||
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
const auto it_range = item.GET_TYPE();
|
||||
REQUIRE_FALSE(item.next());
|
||||
|
||||
auto it = it_range.begin();
|
||||
REQUIRE(it != it_range.end());
|
||||
REQUIRE(*it++ == 17);
|
||||
REQUIRE(*it++ == 200);
|
||||
REQUIRE(*it++ == 0);
|
||||
REQUIRE(*it++ == 1);
|
||||
REQUIRE(*it++ == std::numeric_limits<cpp_type>::max());
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
REQUIRE(*it++ == -200);
|
||||
REQUIRE(*it++ == -1);
|
||||
REQUIRE(*it++ == std::numeric_limits<cpp_type>::min());
|
||||
#endif
|
||||
REQUIRE(it == it_range.end());
|
||||
}
|
||||
|
||||
SECTION("swap iterator range") {
|
||||
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||||
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
auto it_range1 = item.GET_TYPE();
|
||||
REQUIRE_FALSE(item.next());
|
||||
|
||||
decltype(it_range1) it_range;
|
||||
using std::swap;
|
||||
swap(it_range, it_range1);
|
||||
|
||||
auto it = it_range.begin();
|
||||
REQUIRE(it != it_range.end());
|
||||
REQUIRE(*it++ == 17);
|
||||
REQUIRE(*it++ == 200);
|
||||
REQUIRE(*it++ == 0);
|
||||
REQUIRE(*it++ == 1);
|
||||
REQUIRE(*it++ == std::numeric_limits<cpp_type>::max());
|
||||
}
|
||||
|
||||
SECTION("end_of_buffer") {
|
||||
abuffer.append(load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||||
|
||||
for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
|
||||
protozero::pbf_reader item{abuffer.data() + n, i};
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_THROWS_AS(item.GET_TYPE(), const protozero::end_of_buffer_exception&);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("write repeated packed field: " PBF_TYPE_NAME) {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("empty") {
|
||||
cpp_type data[] = { 17 };
|
||||
pw.ADD_TYPE(1, std::begin(data), std::begin(data) /* !!!! */);
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
|
||||
}
|
||||
|
||||
SECTION("one") {
|
||||
cpp_type data[] = { 17 };
|
||||
pw.ADD_TYPE(1, std::begin(data), std::end(data));
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||||
}
|
||||
|
||||
SECTION("many") {
|
||||
cpp_type data[] = {
|
||||
17
|
||||
, 200
|
||||
, 0
|
||||
, 1
|
||||
,std::numeric_limits<cpp_type>::max()
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
,-200
|
||||
, -1
|
||||
,std::numeric_limits<cpp_type>::min()
|
||||
#endif
|
||||
};
|
||||
pw.ADD_TYPE(1, std::begin(data), std::end(data));
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("write repeated packed field using packed field: " PBF_TYPE_NAME) {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("empty - should do rollback") {
|
||||
{
|
||||
packed_field_type field{pw, 1};
|
||||
}
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-empty"));
|
||||
}
|
||||
|
||||
SECTION("one") {
|
||||
{
|
||||
packed_field_type field{pw, 1};
|
||||
field.add_element(cpp_type(17));
|
||||
}
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||||
}
|
||||
|
||||
SECTION("many") {
|
||||
{
|
||||
packed_field_type field{pw, 1};
|
||||
field.add_element(cpp_type( 17));
|
||||
field.add_element(cpp_type( 200));
|
||||
field.add_element(cpp_type( 0));
|
||||
field.add_element(cpp_type( 1));
|
||||
field.add_element(std::numeric_limits<cpp_type>::max());
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
field.add_element(cpp_type(-200));
|
||||
field.add_element(cpp_type( -1));
|
||||
field.add_element(std::numeric_limits<cpp_type>::min());
|
||||
#endif
|
||||
REQUIRE(field.valid());
|
||||
SECTION("with commit") {
|
||||
field.commit();
|
||||
REQUIRE_FALSE(field.valid());
|
||||
}
|
||||
}
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-many"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("move repeated packed field: " PBF_TYPE_NAME) {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("move rvalue") {
|
||||
packed_field_type field;
|
||||
REQUIRE_FALSE(field.valid());
|
||||
field = packed_field_type{pw, 1};
|
||||
REQUIRE(field.valid());
|
||||
field.add_element(cpp_type(17));
|
||||
}
|
||||
|
||||
SECTION("explicit move") {
|
||||
packed_field_type field2{pw, 1};
|
||||
packed_field_type field;
|
||||
|
||||
REQUIRE(field2.valid());
|
||||
REQUIRE_FALSE(field.valid());
|
||||
|
||||
field = std::move(field2);
|
||||
|
||||
REQUIRE_FALSE(field2.valid()); // NOLINT(hicpp-invalid-access-moved, bugprone-use-after-move)
|
||||
REQUIRE(field.valid());
|
||||
|
||||
field.add_element(cpp_type(17));
|
||||
}
|
||||
|
||||
SECTION("move constructor") {
|
||||
packed_field_type field2{pw, 1};
|
||||
REQUIRE(field2.valid());
|
||||
|
||||
packed_field_type field{std::move(field2)};
|
||||
REQUIRE(field.valid());
|
||||
REQUIRE_FALSE(field2.valid()); // NOLINT(hicpp-invalid-access-moved, bugprone-use-after-move)
|
||||
|
||||
field.add_element(cpp_type(17));
|
||||
}
|
||||
|
||||
SECTION("swap") {
|
||||
packed_field_type field;
|
||||
packed_field_type field2{pw, 1};
|
||||
|
||||
REQUIRE_FALSE(field.valid());
|
||||
REQUIRE(field2.valid());
|
||||
|
||||
using std::swap;
|
||||
swap(field, field2);
|
||||
|
||||
REQUIRE(field.valid());
|
||||
REQUIRE_FALSE(field2.valid());
|
||||
|
||||
field.add_element(cpp_type(17));
|
||||
}
|
||||
|
||||
REQUIRE(buffer == load_data("repeated_packed_" PBF_TYPE_NAME "/data-one"));
|
||||
}
|
||||
|
||||
TEST_CASE("write from different types of iterators: " PBF_TYPE_NAME) {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("from uint16_t") {
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
const int16_t data[] = { 1, 4, 9, 16, 25 };
|
||||
#else
|
||||
const uint16_t data[] = { 1, 4, 9, 16, 25 };
|
||||
#endif
|
||||
|
||||
pw.ADD_TYPE(1, std::begin(data), std::end(data));
|
||||
}
|
||||
|
||||
SECTION("from string") {
|
||||
std::string data{"1 4 9 16 25"};
|
||||
std::stringstream sdata{data};
|
||||
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
using test_type = int32_t;
|
||||
#else
|
||||
using test_type = uint32_t;
|
||||
#endif
|
||||
|
||||
std::istream_iterator<test_type> eod;
|
||||
std::istream_iterator<test_type> it(sdata);
|
||||
|
||||
pw.ADD_TYPE(1, it, eod);
|
||||
}
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
auto it_range = item.GET_TYPE();
|
||||
REQUIRE_FALSE(item.next());
|
||||
REQUIRE_FALSE(it_range.empty());
|
||||
REQUIRE(std::distance(it_range.begin(), it_range.end()) == 5);
|
||||
REQUIRE(it_range.size() == 5);
|
||||
|
||||
REQUIRE(it_range.front() == 1); it_range.drop_front();
|
||||
REQUIRE(it_range.front() == 4); it_range.drop_front();
|
||||
REQUIRE(std::distance(it_range.begin(), it_range.end()) == 3);
|
||||
REQUIRE(it_range.size() == 3);
|
||||
REQUIRE(it_range.front() == 9); it_range.drop_front();
|
||||
REQUIRE(it_range.front() == 16); it_range.drop_front();
|
||||
REQUIRE(it_range.front() == 25); it_range.drop_front();
|
||||
REQUIRE(it_range.empty());
|
||||
REQUIRE(std::distance(it_range.begin(), it_range.end()) == 0);
|
||||
REQUIRE(it_range.size() == 0); // NOLINT(readability-container-size-empty)
|
||||
|
||||
REQUIRE_THROWS_AS(it_range.front(), const assert_error&);
|
||||
REQUIRE_THROWS_AS(it_range.drop_front(), const assert_error&);
|
||||
}
|
||||
|
129
third_party/protozero/test/include/scalar_access.hpp
vendored
Normal file
129
third_party/protozero/test/include/scalar_access.hpp
vendored
Normal file
@ -0,0 +1,129 @@
|
||||
// NOLINT(llvm-header-guard)
|
||||
|
||||
#define PBF_TYPE_NAME PROTOZERO_TEST_STRING(PBF_TYPE)
|
||||
#define GET_TYPE PROTOZERO_TEST_CONCAT(get_, PBF_TYPE)
|
||||
#define ADD_TYPE PROTOZERO_TEST_CONCAT(add_, PBF_TYPE)
|
||||
|
||||
TEST_CASE("read field: " PBF_TYPE_NAME) {
|
||||
|
||||
SECTION("zero") {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-zero");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == 0);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-pos");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == 1);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("pos200") {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-pos200");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == 200);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-max");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == std::numeric_limits<cpp_type>::max());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
SECTION("negative") {
|
||||
if (std::is_signed<cpp_type>::value) {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-neg");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == -1);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("neg200") {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-neg200");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == -200);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("min") {
|
||||
if (std::is_signed<cpp_type>::value) {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-min");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.GET_TYPE() == std::numeric_limits<cpp_type>::min());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
SECTION("end_of_buffer") {
|
||||
const std::string buffer = load_data(PBF_TYPE_NAME "/data-max");
|
||||
|
||||
for (std::string::size_type i = 1; i < buffer.size(); ++i) {
|
||||
protozero::pbf_reader item{buffer.data(), i};
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_THROWS_AS(item.GET_TYPE(), const protozero::end_of_buffer_exception&);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write field: " PBF_TYPE_NAME) {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw(buffer);
|
||||
|
||||
SECTION("zero") {
|
||||
pw.ADD_TYPE(1, 0);
|
||||
REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-zero"));
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
pw.ADD_TYPE(1, 1);
|
||||
REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-pos"));
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
pw.ADD_TYPE(1, std::numeric_limits<cpp_type>::max());
|
||||
REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-max"));
|
||||
}
|
||||
|
||||
#if PBF_TYPE_IS_SIGNED
|
||||
SECTION("negative") {
|
||||
pw.ADD_TYPE(1, -1);
|
||||
REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-neg"));
|
||||
}
|
||||
|
||||
SECTION("min") {
|
||||
if (std::is_signed<cpp_type>::value) {
|
||||
pw.ADD_TYPE(1, std::numeric_limits<cpp_type>::min());
|
||||
REQUIRE(buffer == load_data(PBF_TYPE_NAME "/data-min"));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
28
third_party/protozero/test/include/test.hpp
vendored
Normal file
28
third_party/protozero/test/include/test.hpp
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef TEST_HPP
|
||||
#define TEST_HPP
|
||||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <stdexcept>
|
||||
// Define protozero_assert() to throw this error. This allows the tests to
|
||||
// check that the assert fails.
|
||||
struct assert_error : public std::runtime_error {
|
||||
explicit assert_error(const char* what_arg) : std::runtime_error(what_arg) {
|
||||
}
|
||||
};
|
||||
#define protozero_assert(x) if (!(x)) { throw assert_error{#x}; }
|
||||
|
||||
#include <protozero/pbf_builder.hpp>
|
||||
#include <protozero/pbf_message.hpp>
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
|
||||
extern std::string load_data(const std::string& filename);
|
||||
|
||||
#define PROTOZERO_TEST_CONCAT2(x, y) x##y
|
||||
#define PROTOZERO_TEST_CONCAT(x, y) PROTOZERO_TEST_CONCAT2(x, y)
|
||||
|
||||
#define PROTOZERO_TEST_STRING2(s) #s
|
||||
#define PROTOZERO_TEST_STRING(s) PROTOZERO_TEST_STRING2(s)
|
||||
|
||||
#endif // TEST_HPP
|
21
third_party/protozero/test/include/testcase.hpp
vendored
Normal file
21
third_party/protozero/test/include/testcase.hpp
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef TESTCASE_HPP
|
||||
#define TESTCASE_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
template <class T>
|
||||
std::string write_to_file(const T& msg, const char* filename) {
|
||||
std::string out;
|
||||
|
||||
msg.SerializeToString(&out);
|
||||
std::ofstream d{filename, std::ios_base::out|std::ios_base::binary};
|
||||
assert(d.is_open());
|
||||
d << out;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif // TESTCASE_HPP
|
30
third_party/protozero/test/reader_tests.cpp
vendored
Normal file
30
third_party/protozero/test/reader_tests.cpp
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <test.hpp> // IWYU pragma: keep
|
||||
|
||||
std::string load_data(const std::string& filename) {
|
||||
const char* tests_dir = std::getenv("TESTS_DIR");
|
||||
if (tests_dir == nullptr) {
|
||||
tests_dir = "test";
|
||||
}
|
||||
|
||||
std::string fullname{tests_dir};
|
||||
fullname += "/t/";
|
||||
fullname += filename;
|
||||
fullname += ".pbf";
|
||||
|
||||
std::ifstream stream{fullname, std::ios_base::in | std::ios_base::binary};
|
||||
if (!stream.is_open()) {
|
||||
throw std::runtime_error{"could not open: '" + filename + "'"};
|
||||
}
|
||||
std::string buffer{std::istreambuf_iterator<char>(stream.rdbuf()),
|
||||
std::istreambuf_iterator<char>()};
|
||||
stream.close();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
122
third_party/protozero/test/t/alignment/reader_test_cases.cpp
vendored
Normal file
122
third_party/protozero/test/t/alignment/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
// Run these tests twice, the second time we basically move the data
|
||||
// one byte down in the buffer. It doesn't matter how the data or buffer
|
||||
// is aligned before that, in at least one of these cases the int32 will
|
||||
// not be aligned properly. So we test that even in that case the int32
|
||||
// will be extracted properly.
|
||||
|
||||
TEST_CASE("check alignment issues for fixed32 field") {
|
||||
for (std::string::size_type n = 0; n < 2; ++n) {
|
||||
|
||||
std::string abuffer;
|
||||
abuffer.reserve(1000);
|
||||
abuffer.append(n, '\0');
|
||||
|
||||
SECTION("zero") {
|
||||
abuffer.append(load_data("fixed32/data-zero"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed32() == 0UL);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
abuffer.append(load_data("fixed32/data-pos"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed32() == 1UL);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
abuffer.append(load_data("fixed32/data-max"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed32() == std::numeric_limits<uint32_t>::max());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("end_of_buffer") {
|
||||
abuffer.append(load_data("fixed32/data-pos"));
|
||||
|
||||
for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
|
||||
protozero::pbf_reader item{abuffer.data() + n, i};
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_THROWS_AS(item.get_fixed32(), const protozero::end_of_buffer_exception&);
|
||||
}
|
||||
}
|
||||
|
||||
SECTION("assert detecting tag==0") {
|
||||
abuffer.append(load_data("fixed32/data-zero"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE_THROWS_AS(item.get_fixed32(), const assert_error&);
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed32() == 0UL);
|
||||
REQUIRE_THROWS(item.get_fixed32());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("skip") {
|
||||
abuffer.append(load_data("fixed32/data-zero"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE_THROWS_AS(item.skip(), const assert_error&);
|
||||
REQUIRE(item.next());
|
||||
item.skip();
|
||||
REQUIRE_THROWS(item.skip());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("check alignment issues for fixed64 field") {
|
||||
for (std::string::size_type n = 0; n < 2; ++n) {
|
||||
std::string abuffer;
|
||||
abuffer.reserve(1000);
|
||||
abuffer.append(n, '\0');
|
||||
|
||||
SECTION("zero") {
|
||||
abuffer.append(load_data("fixed64/data-zero"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed64() == 0ULL);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
abuffer.append(load_data("fixed64/data-pos"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed64() == 1ULL);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
abuffer.append(load_data("fixed64/data-max"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_fixed64() == std::numeric_limits<uint64_t>::max());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("end_of_buffer") {
|
||||
abuffer.append(load_data("fixed64/data-pos"));
|
||||
|
||||
for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
|
||||
protozero::pbf_reader item{abuffer.data() + n, i};
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_THROWS_AS(item.get_fixed64(), const protozero::end_of_buffer_exception&);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
14
third_party/protozero/test/t/bool/bool_testcase.proto
vendored
Normal file
14
third_party/protozero/test/t/bool/bool_testcase.proto
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestBoolean;
|
||||
|
||||
message Test {
|
||||
|
||||
// this should be bool, but we are using uint32
|
||||
// to be able to encode values other than 0 (false)
|
||||
// and 1 (true)
|
||||
required uint32 b = 1;
|
||||
|
||||
}
|
||||
|
1
third_party/protozero/test/t/bool/data-also-true.pbf
vendored
Normal file
1
third_party/protozero/test/t/bool/data-also-true.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
|
BIN
third_party/protozero/test/t/bool/data-false.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/bool/data-false.pbf
vendored
Normal file
Binary file not shown.
1
third_party/protozero/test/t/bool/data-still-true.pbf
vendored
Normal file
1
third_party/protozero/test/t/bool/data-still-true.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<08>
|
1
third_party/protozero/test/t/bool/data-true.pbf
vendored
Normal file
1
third_party/protozero/test/t/bool/data-true.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
|
141
third_party/protozero/test/t/bool/reader_test_cases.cpp
vendored
Normal file
141
third_party/protozero/test/t/bool/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
namespace TestBoolean {
|
||||
|
||||
enum class Test : protozero::pbf_tag_type {
|
||||
required_bool_b = 1
|
||||
};
|
||||
|
||||
} // end namespace TestBoolean
|
||||
|
||||
TEST_CASE("read bool field using pbf_reader: false") {
|
||||
const std::string buffer = load_data("bool/data-false");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_FALSE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_reader: true") {
|
||||
const std::string buffer = load_data("bool/data-true");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_reader: also true") {
|
||||
const std::string buffer = load_data("bool/data-also-true");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next(1));
|
||||
REQUIRE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_reader: still true") {
|
||||
const std::string buffer = load_data("bool/data-still-true");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next(1));
|
||||
REQUIRE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_message: false") {
|
||||
const std::string buffer = load_data("bool/data-false");
|
||||
|
||||
protozero::pbf_message<TestBoolean::Test> item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_FALSE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_message: true") {
|
||||
const std::string buffer = load_data("bool/data-true");
|
||||
|
||||
protozero::pbf_message<TestBoolean::Test> item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_message: also true") {
|
||||
const std::string buffer = load_data("bool/data-also-true");
|
||||
|
||||
protozero::pbf_message<TestBoolean::Test> item{buffer};
|
||||
|
||||
REQUIRE(item.next(TestBoolean::Test::required_bool_b));
|
||||
REQUIRE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bool field using pbf_message: still true") {
|
||||
const std::string buffer = load_data("bool/data-still-true");
|
||||
|
||||
protozero::pbf_message<TestBoolean::Test> item{buffer};
|
||||
|
||||
REQUIRE(item.next(TestBoolean::Test::required_bool_b));
|
||||
REQUIRE(item.get_bool());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("write bool field using pbf_writer") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("false") {
|
||||
pw.add_bool(1, false);
|
||||
REQUIRE(buffer == load_data("bool/data-false"));
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
pw.add_bool(1, true);
|
||||
REQUIRE(buffer == load_data("bool/data-true"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write bool field using pbf_builder") {
|
||||
std::string buffer;
|
||||
protozero::pbf_builder<TestBoolean::Test> pw{buffer};
|
||||
|
||||
SECTION("false") {
|
||||
pw.add_bool(TestBoolean::Test::required_bool_b, false);
|
||||
REQUIRE(buffer == load_data("bool/data-false"));
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
pw.add_bool(TestBoolean::Test::required_bool_b, true);
|
||||
REQUIRE(buffer == load_data("bool/data-true"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write bool field using moved pbf_builder") {
|
||||
std::string buffer;
|
||||
protozero::pbf_builder<TestBoolean::Test> pw2{buffer};
|
||||
REQUIRE(pw2.valid());
|
||||
|
||||
protozero::pbf_builder<TestBoolean::Test> pw{std::move(pw2)};
|
||||
REQUIRE(pw.valid());
|
||||
REQUIRE_FALSE(pw2.valid()); // NOLINT(hicpp-invalid-access-moved, bugprone-use-after-move)
|
||||
|
||||
SECTION("false") {
|
||||
pw.add_bool(TestBoolean::Test::required_bool_b, false);
|
||||
REQUIRE(buffer == load_data("bool/data-false"));
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
pw.add_bool(TestBoolean::Test::required_bool_b, true);
|
||||
REQUIRE(buffer == load_data("bool/data-true"));
|
||||
}
|
||||
}
|
||||
|
20
third_party/protozero/test/t/bool/testcase.cpp
vendored
Normal file
20
third_party/protozero/test/t/bool/testcase.cpp
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestBoolean::Test msg;
|
||||
|
||||
msg.set_b(0);
|
||||
write_to_file(msg, "data-false.pbf");
|
||||
|
||||
msg.set_b(1);
|
||||
write_to_file(msg, "data-true.pbf");
|
||||
|
||||
msg.set_b(2);
|
||||
write_to_file(msg, "data-also-true.pbf");
|
||||
|
||||
msg.set_b(2000);
|
||||
write_to_file(msg, "data-still-true.pbf");
|
||||
}
|
||||
|
32
third_party/protozero/test/t/bool/writer_test_cases.cpp
vendored
Normal file
32
third_party/protozero/test/t/bool/writer_test_cases.cpp
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <test.hpp> // IWYU pragma: keep
|
||||
|
||||
#include "t/bool/bool_testcase.pb.h"
|
||||
|
||||
TEST_CASE("write bool field and check with libprotobuf") {
|
||||
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
TestBoolean::Test msg;
|
||||
|
||||
SECTION("false") {
|
||||
pw.add_bool(1, false);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE_FALSE(msg.b());
|
||||
}
|
||||
|
||||
SECTION("true") {
|
||||
pw.add_bool(1, true);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.b());
|
||||
}
|
||||
|
||||
}
|
||||
|
11
third_party/protozero/test/t/bytes/bytes_testcase.proto
vendored
Normal file
11
third_party/protozero/test/t/bytes/bytes_testcase.proto
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestBytes;
|
||||
|
||||
message Test {
|
||||
|
||||
required bytes s = 1;
|
||||
|
||||
}
|
||||
|
2
third_party/protozero/test/t/bytes/data-binary.pbf
vendored
Normal file
2
third_party/protozero/test/t/bytes/data-binary.pbf
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
|
BIN
third_party/protozero/test/t/bytes/data-empty.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/bytes/data-empty.pbf
vendored
Normal file
Binary file not shown.
2
third_party/protozero/test/t/bytes/data-one.pbf
vendored
Normal file
2
third_party/protozero/test/t/bytes/data-one.pbf
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
x
|
2
third_party/protozero/test/t/bytes/data-string.pbf
vendored
Normal file
2
third_party/protozero/test/t/bytes/data-string.pbf
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
foobar
|
139
third_party/protozero/test/t/bytes/reader_test_cases.cpp
vendored
Normal file
139
third_party/protozero/test/t/bytes/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
TEST_CASE("read bytes field: empty") {
|
||||
const std::string buffer = load_data("bytes/data-empty");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_bytes().empty());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bytes field: one") {
|
||||
const std::string buffer = load_data("bytes/data-one");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_bytes() == "x");
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bytes field: string") {
|
||||
const std::string buffer = load_data("bytes/data-string");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_bytes() == "foobar");
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bytes field: binary") {
|
||||
const std::string buffer = load_data("bytes/data-binary");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
const std::string data = item.get_bytes();
|
||||
REQUIRE(data.size() == 3);
|
||||
REQUIRE(data[0] == char(1));
|
||||
REQUIRE(data[1] == char(2));
|
||||
REQUIRE(data[2] == char(3));
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read bytes field: end of buffer") {
|
||||
const std::string buffer = load_data("bytes/data-binary");
|
||||
|
||||
for (std::string::size_type i = 1; i < buffer.size(); ++i) {
|
||||
protozero::pbf_reader item{buffer.data(), i};
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_THROWS_AS(item.get_bytes(), const protozero::end_of_buffer_exception&);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write bytes field") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("empty") {
|
||||
pw.add_string(1, "");
|
||||
REQUIRE(buffer == load_data("bytes/data-empty"));
|
||||
}
|
||||
|
||||
SECTION("one") {
|
||||
pw.add_string(1, "x");
|
||||
REQUIRE(buffer == load_data("bytes/data-one"));
|
||||
}
|
||||
|
||||
SECTION("string") {
|
||||
pw.add_string(1, "foobar");
|
||||
REQUIRE(buffer == load_data("bytes/data-string"));
|
||||
}
|
||||
|
||||
SECTION("binary") {
|
||||
std::string data;
|
||||
data.append(1, char(1));
|
||||
data.append(1, char(2));
|
||||
data.append(1, char(3));
|
||||
|
||||
pw.add_string(1, data);
|
||||
|
||||
REQUIRE(buffer == load_data("bytes/data-binary"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write bytes field using vectored approach") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("using two strings") {
|
||||
std::string d1{"foo"};
|
||||
std::string d2{"bar"};
|
||||
|
||||
pw.add_bytes_vectored(1, d1, d2);
|
||||
}
|
||||
|
||||
SECTION("using a string and a dataview") {
|
||||
std::string d1{"foo"};
|
||||
std::string d2{"bar"};
|
||||
protozero::data_view dv{d2};
|
||||
|
||||
pw.add_bytes_vectored(1, d1, dv);
|
||||
}
|
||||
|
||||
SECTION("using three strings") {
|
||||
std::string d1{"foo"};
|
||||
std::string d2{"ba"};
|
||||
std::string d3{"r"};
|
||||
|
||||
pw.add_bytes_vectored(1, d1, d2, d3);
|
||||
}
|
||||
|
||||
SECTION("with empty string") {
|
||||
std::string d1{"foo"};
|
||||
std::string d2{};
|
||||
std::string d3{"bar"};
|
||||
|
||||
pw.add_bytes_vectored(1, d1, d2, d3);
|
||||
}
|
||||
|
||||
REQUIRE(buffer == load_data("bytes/data-string"));
|
||||
}
|
||||
|
||||
TEST_CASE("write bytes field using vectored approach with builder") {
|
||||
enum class foo : protozero::pbf_tag_type { bar = 1 };
|
||||
std::string buffer;
|
||||
protozero::pbf_builder<foo> pw{buffer};
|
||||
|
||||
const std::string d1{"foo"};
|
||||
const std::string d2{"bar"};
|
||||
|
||||
pw.add_bytes_vectored(foo::bar, d1, d2);
|
||||
|
||||
REQUIRE(buffer == load_data("bytes/data-string"));
|
||||
}
|
||||
|
24
third_party/protozero/test/t/bytes/testcase.cpp
vendored
Normal file
24
third_party/protozero/test/t/bytes/testcase.cpp
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestBytes::Test msg;
|
||||
|
||||
msg.set_s("");
|
||||
write_to_file(msg, "data-empty.pbf");
|
||||
|
||||
msg.set_s("x");
|
||||
write_to_file(msg, "data-one.pbf");
|
||||
|
||||
msg.set_s("foobar");
|
||||
write_to_file(msg, "data-string.pbf");
|
||||
|
||||
std::string data;
|
||||
data.append(1, char(1));
|
||||
data.append(1, char(2));
|
||||
data.append(1, char(3));
|
||||
msg.set_s(data);
|
||||
write_to_file(msg, "data-binary.pbf");
|
||||
}
|
||||
|
54
third_party/protozero/test/t/bytes/writer_test_cases.cpp
vendored
Normal file
54
third_party/protozero/test/t/bytes/writer_test_cases.cpp
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
#include "t/bytes/bytes_testcase.pb.h"
|
||||
|
||||
TEST_CASE("write bytes field and check with libprotobuf") {
|
||||
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
TestBytes::Test msg;
|
||||
|
||||
SECTION("empty") {
|
||||
pw.add_string(1, "");
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.s().empty());
|
||||
}
|
||||
|
||||
SECTION("one") {
|
||||
pw.add_string(1, "x");
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.s() == "x");
|
||||
}
|
||||
|
||||
SECTION("string") {
|
||||
pw.add_string(1, "foobar");
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.s() == "foobar");
|
||||
}
|
||||
|
||||
SECTION("binary") {
|
||||
std::string data;
|
||||
data.append(1, char(1));
|
||||
data.append(1, char(2));
|
||||
data.append(1, char(3));
|
||||
|
||||
pw.add_string(1, data);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.s().size() == 3);
|
||||
REQUIRE(msg.s()[1] == char(2));
|
||||
REQUIRE(msg.s()[2] == char(3));
|
||||
REQUIRE(msg.s()[2] == char(3));
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
third_party/protozero/test/t/complex/data-all.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/complex/data-all.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/test/t/complex/data-minimal.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/complex/data-minimal.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/test/t/complex/data-some.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/complex/data-some.pbf
vendored
Normal file
Binary file not shown.
686
third_party/protozero/test/t/complex/reader_test_cases.cpp
vendored
Normal file
686
third_party/protozero/test/t/complex/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,686 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
namespace TestComplex {
|
||||
|
||||
enum class Test : protozero::pbf_tag_type {
|
||||
required_fixed32_f = 1,
|
||||
optional_int64_i = 2,
|
||||
optional_int64_j = 3,
|
||||
required_Sub_submessage = 5,
|
||||
optional_string_s = 8,
|
||||
repeated_uint32_u = 4,
|
||||
packed_sint32_d = 7
|
||||
};
|
||||
|
||||
enum class Sub : protozero::pbf_tag_type {
|
||||
required_string_s = 1
|
||||
};
|
||||
|
||||
} // namespace TestComplex
|
||||
|
||||
TEST_CASE("read complex data using pbf_reader: minimal") {
|
||||
const std::string buffer = load_data("complex/data-minimal");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_reader: some") {
|
||||
const std::string buffer = load_data("complex/data-some");
|
||||
|
||||
protozero::pbf_reader item2{buffer};
|
||||
protozero::pbf_reader item;
|
||||
using std::swap;
|
||||
swap(item, item2);
|
||||
|
||||
uint32_t sum_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
sum_of_u += item.get_uint32();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(sum_of_u == 66);
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_reader: all") {
|
||||
const std::string buffer = load_data("complex/data-all");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
int number_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
REQUIRE(item.get_int64() == 555555555LL);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
item.skip();
|
||||
++number_of_u;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
const auto pi = item.get_packed_sint32();
|
||||
int32_t sum = 0;
|
||||
for (auto val : pi) {
|
||||
sum += val;
|
||||
}
|
||||
REQUIRE(sum == 5);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
REQUIRE(item.get_string() == "optionalstring");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(number_of_u == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_reader: skip everything") {
|
||||
const std::string buffer = load_data("complex/data-all");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 7:
|
||||
case 8:
|
||||
item.skip();
|
||||
break;
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_message: minimal") {
|
||||
const std::string buffer = load_data("complex/data-minimal");
|
||||
|
||||
protozero::pbf_message<TestComplex::Test> item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case TestComplex::Test::required_fixed32_f: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::required_Sub_submessage: {
|
||||
protozero::pbf_message<TestComplex::Sub> subitem{item.get_message()};
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_message: some") {
|
||||
const std::string buffer = load_data("complex/data-some");
|
||||
|
||||
protozero::pbf_message<TestComplex::Test> item2{buffer};
|
||||
protozero::pbf_message<TestComplex::Test> item;
|
||||
using std::swap;
|
||||
swap(item, item2);
|
||||
|
||||
uint32_t sum_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case TestComplex::Test::required_fixed32_f: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::optional_int64_i: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::repeated_uint32_u: {
|
||||
sum_of_u += item.get_uint32();
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::required_Sub_submessage: {
|
||||
protozero::pbf_message<TestComplex::Sub> subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(sum_of_u == 66);
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_message: all") {
|
||||
const std::string buffer = load_data("complex/data-all");
|
||||
|
||||
protozero::pbf_message<TestComplex::Test> item{buffer};
|
||||
|
||||
int number_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case TestComplex::Test::required_fixed32_f: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::optional_int64_i: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::optional_int64_j: {
|
||||
REQUIRE(item.get_int64() == 555555555LL);
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::repeated_uint32_u: {
|
||||
item.skip();
|
||||
++number_of_u;
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::required_Sub_submessage: {
|
||||
protozero::pbf_message<TestComplex::Sub> subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::packed_sint32_d: {
|
||||
const auto pi = item.get_packed_sint32();
|
||||
int32_t sum = 0;
|
||||
for (auto val : pi) {
|
||||
sum += val;
|
||||
}
|
||||
REQUIRE(sum == 5);
|
||||
break;
|
||||
}
|
||||
case TestComplex::Test::optional_string_s: {
|
||||
REQUIRE(item.get_string() == "optionalstring");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(number_of_u == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("read complex data using pbf_message: skip everything") {
|
||||
const std::string buffer = load_data("complex/data-all");
|
||||
|
||||
protozero::pbf_message<TestComplex::Test> item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case TestComplex::Test::required_fixed32_f:
|
||||
case TestComplex::Test::optional_int64_i:
|
||||
case TestComplex::Test::optional_int64_j:
|
||||
case TestComplex::Test::repeated_uint32_u:
|
||||
case TestComplex::Test::required_Sub_submessage:
|
||||
case TestComplex::Test::packed_sint32_d:
|
||||
case TestComplex::Test::optional_string_s:
|
||||
item.skip();
|
||||
break;
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write complex data using pbf_writer: minimal") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
pw.add_fixed32(1, 12345678);
|
||||
|
||||
std::string submessage;
|
||||
protozero::pbf_writer pws{submessage};
|
||||
pws.add_string(1, "foobar");
|
||||
|
||||
pw.add_message(5, submessage);
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write complex data using pbf_writer: some") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw2{buffer};
|
||||
pw2.add_fixed32(1, 12345678);
|
||||
|
||||
protozero::pbf_writer pw;
|
||||
using std::swap;
|
||||
swap(pw, pw2);
|
||||
|
||||
REQUIRE(pw.valid());
|
||||
REQUIRE_FALSE(pw2.valid());
|
||||
|
||||
std::string submessage;
|
||||
protozero::pbf_writer pws{submessage};
|
||||
pws.add_string(1, "foobar");
|
||||
|
||||
pw.add_uint32(4, 22);
|
||||
pw.add_uint32(4, 44);
|
||||
pw.add_int64(2, -9876543);
|
||||
pw.add_message(5, submessage);
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
uint32_t sum_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
sum_of_u += item.get_uint32();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
const auto view = item.get_view();
|
||||
protozero::pbf_reader subitem{view};
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(std::string(subitem.get_view()) == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(sum_of_u == 66);
|
||||
}
|
||||
|
||||
TEST_CASE("write complex data using pbf_writer: all") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
pw.add_fixed32(1, 12345678);
|
||||
|
||||
std::string submessage;
|
||||
protozero::pbf_writer pws{submessage};
|
||||
pws.add_string(1, "foobar");
|
||||
pw.add_message(5, submessage);
|
||||
|
||||
pw.add_uint32(4, 22);
|
||||
pw.add_uint32(4, 44);
|
||||
pw.add_int64(2, -9876543);
|
||||
pw.add_uint32(4, 44);
|
||||
pw.add_uint32(4, 66);
|
||||
pw.add_uint32(4, 66);
|
||||
|
||||
const int32_t d[] = { -17, 22 };
|
||||
pw.add_packed_sint32(7, std::begin(d), std::end(d));
|
||||
|
||||
pw.add_int64(3, 555555555);
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
int number_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
REQUIRE(item.get_int64() == 555555555LL);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
item.skip();
|
||||
++number_of_u;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
const auto pi = item.get_packed_sint32();
|
||||
int32_t sum = 0;
|
||||
for (auto val : pi) {
|
||||
sum += val;
|
||||
}
|
||||
REQUIRE(sum == 5);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
REQUIRE(item.get_string() == "optionalstring");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(number_of_u == 5);
|
||||
}
|
||||
|
||||
TEST_CASE("write complex data using pbf_builder: minimal") {
|
||||
std::string buffer;
|
||||
protozero::pbf_builder<TestComplex::Test> pw{buffer};
|
||||
pw.add_fixed32(TestComplex::Test::required_fixed32_f, 12345678);
|
||||
|
||||
std::string submessage;
|
||||
protozero::pbf_builder<TestComplex::Sub> pws{submessage};
|
||||
pws.add_string(TestComplex::Sub::required_string_s, "foobar");
|
||||
|
||||
pw.add_message(TestComplex::Test::required_Sub_submessage, submessage);
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write complex data using pbf_builder: some") {
|
||||
std::string buffer;
|
||||
protozero::pbf_builder<TestComplex::Test> pw2{buffer};
|
||||
pw2.add_fixed32(TestComplex::Test::required_fixed32_f, 12345678);
|
||||
|
||||
std::string dummy_buffer;
|
||||
protozero::pbf_builder<TestComplex::Test> pw{dummy_buffer};
|
||||
using std::swap;
|
||||
swap(pw, pw2);
|
||||
|
||||
std::string submessage;
|
||||
protozero::pbf_builder<TestComplex::Sub> pws{submessage};
|
||||
pws.add_string(TestComplex::Sub::required_string_s, "foobar");
|
||||
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 22);
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 44);
|
||||
pw.add_int64(TestComplex::Test::optional_int64_i, -9876543);
|
||||
pw.add_message(TestComplex::Test::required_Sub_submessage, submessage);
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
uint32_t sum_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
sum_of_u += item.get_uint32();
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(sum_of_u == 66);
|
||||
}
|
||||
|
||||
TEST_CASE("write complex data using pbf_builder: all") {
|
||||
std::string buffer;
|
||||
protozero::pbf_builder<TestComplex::Test> pw{buffer};
|
||||
pw.add_fixed32(TestComplex::Test::required_fixed32_f, 12345678);
|
||||
|
||||
std::string submessage;
|
||||
protozero::pbf_builder<TestComplex::Sub> pws{submessage};
|
||||
pws.add_string(TestComplex::Sub::required_string_s, "foobar");
|
||||
pw.add_message(TestComplex::Test::required_Sub_submessage, submessage);
|
||||
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 22);
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 44);
|
||||
pw.add_int64(TestComplex::Test::optional_int64_i, -9876543);
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 44);
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 66);
|
||||
pw.add_uint32(TestComplex::Test::repeated_uint32_u, 66);
|
||||
|
||||
const int32_t d[] = { -17, 22 };
|
||||
pw.add_packed_sint32(TestComplex::Test::packed_sint32_d, std::begin(d), std::end(d));
|
||||
|
||||
pw.add_int64(TestComplex::Test::optional_int64_j, 555555555);
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
int number_of_u = 0;
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 12345678L);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
REQUIRE(true);
|
||||
item.skip();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
REQUIRE(item.get_int64() == 555555555LL);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
item.skip();
|
||||
++number_of_u;
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
const auto pi = item.get_packed_sint32();
|
||||
int32_t sum = 0;
|
||||
for (auto val : pi) {
|
||||
sum += val;
|
||||
}
|
||||
REQUIRE(sum == 5);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
REQUIRE(item.get_string() == "optionalstring");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(number_of_u == 5);
|
||||
}
|
||||
|
||||
static void check_message(const std::string& buffer) {
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
while (item.next()) {
|
||||
switch (item.tag()) {
|
||||
case 1: {
|
||||
REQUIRE(item.get_fixed32() == 42L);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
protozero::pbf_reader subitem = item.get_message();
|
||||
REQUIRE(subitem.next());
|
||||
REQUIRE(subitem.get_string() == "foobar");
|
||||
REQUIRE_FALSE(subitem.next());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
REQUIRE(false); // should not be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write complex with subwriter using pbf_writer") {
|
||||
std::string buffer_test;
|
||||
protozero::pbf_writer pbf_test{buffer_test};
|
||||
pbf_test.add_fixed32(1, 42L);
|
||||
|
||||
SECTION("message in message") {
|
||||
protozero::pbf_writer pbf_submessage{pbf_test, 5};
|
||||
pbf_submessage.add_string(1, "foobar");
|
||||
}
|
||||
|
||||
check_message(buffer_test);
|
||||
}
|
||||
|
||||
TEST_CASE("write complex with subwriter using pbf_builder") {
|
||||
std::string buffer_test;
|
||||
protozero::pbf_builder<TestComplex::Test> pbf_test{buffer_test};
|
||||
pbf_test.add_fixed32(TestComplex::Test::required_fixed32_f, 42L);
|
||||
|
||||
SECTION("message in message") {
|
||||
protozero::pbf_builder<TestComplex::Sub> pbf_submessage{pbf_test, TestComplex::Test::required_Sub_submessage};
|
||||
pbf_submessage.add_string(TestComplex::Sub::required_string_s, "foobar");
|
||||
}
|
||||
|
||||
check_message(buffer_test);
|
||||
}
|
||||
|
30
third_party/protozero/test/t/complex/testcase.cpp
vendored
Normal file
30
third_party/protozero/test/t/complex/testcase.cpp
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestComplex::Test msg;
|
||||
|
||||
msg.set_f(12345678);
|
||||
TestComplex::Sub* submsg = msg.mutable_submessage();
|
||||
submsg->set_s("foobar");
|
||||
|
||||
write_to_file(msg, "data-minimal.pbf");
|
||||
|
||||
msg.add_u(22);
|
||||
msg.add_u(44);
|
||||
msg.set_i(-9876543);
|
||||
|
||||
write_to_file(msg, "data-some.pbf");
|
||||
|
||||
msg.set_s("optionalstring");
|
||||
msg.add_u(44);
|
||||
msg.add_u(66);
|
||||
msg.add_u(66);
|
||||
msg.add_d(-17);
|
||||
msg.add_d(22);
|
||||
msg.set_j(555555555);
|
||||
|
||||
write_to_file(msg, "data-all.pbf");
|
||||
}
|
||||
|
19
third_party/protozero/test/t/complex/testcase.proto
vendored
Normal file
19
third_party/protozero/test/t/complex/testcase.proto
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestComplex;
|
||||
|
||||
message Sub {
|
||||
required string s = 1;
|
||||
}
|
||||
|
||||
message Test {
|
||||
required fixed32 f = 1;
|
||||
optional int64 i = 2;
|
||||
optional int64 j = 3;
|
||||
required Sub submessage = 5;
|
||||
optional string s = 8;
|
||||
repeated uint32 u = 4;
|
||||
repeated sint32 d = 7 [packed=true];
|
||||
}
|
||||
|
1
third_party/protozero/test/t/double/data-neg.pbf
vendored
Normal file
1
third_party/protozero/test/t/double/data-neg.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
ラ」p=*ツタ
|
1
third_party/protozero/test/t/double/data-pos.pbf
vendored
Normal file
1
third_party/protozero/test/t/double/data-pos.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<09>O<EFBFBD><4F>n<EFBFBD>@
|
BIN
third_party/protozero/test/t/double/data-zero.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/double/data-zero.pbf
vendored
Normal file
Binary file not shown.
11
third_party/protozero/test/t/double/double_testcase.proto
vendored
Normal file
11
third_party/protozero/test/t/double/double_testcase.proto
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestDouble;
|
||||
|
||||
message Test {
|
||||
|
||||
required double x = 1;
|
||||
|
||||
}
|
||||
|
73
third_party/protozero/test/t/double/reader_test_cases.cpp
vendored
Normal file
73
third_party/protozero/test/t/double/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
TEST_CASE("read double field") {
|
||||
// Run these tests twice, the second time we basically move the data
|
||||
// one byte down in the buffer. It doesn't matter how the data or buffer
|
||||
// is aligned before that, in at least one of these cases the double will
|
||||
// not be aligned properly. So we test that even in that case the double
|
||||
// will be extracted properly.
|
||||
for (std::string::size_type n = 0; n < 2; ++n) {
|
||||
std::string abuffer;
|
||||
abuffer.reserve(1000);
|
||||
abuffer.append(n, '\0');
|
||||
|
||||
SECTION("zero") {
|
||||
abuffer.append(load_data("double/data-zero"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_double() == Approx(0.0));
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
abuffer.append(load_data("double/data-pos"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_double() == Approx(4.893));
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("negative") {
|
||||
abuffer.append(load_data("double/data-neg"));
|
||||
protozero::pbf_reader item{abuffer.data() + n, abuffer.size() - n};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_double() == Approx(-9232.33));
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
SECTION("end_of_buffer") {
|
||||
abuffer.append(load_data("double/data-neg"));
|
||||
|
||||
for (std::string::size_type i = 1; i < abuffer.size() - n; ++i) {
|
||||
protozero::pbf_reader item{abuffer.data() + n, i};
|
||||
REQUIRE(item.next());
|
||||
REQUIRE_THROWS_AS(item.get_double(), const protozero::end_of_buffer_exception&);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("write double field") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("zero") {
|
||||
pw.add_double(1, 0.0);
|
||||
REQUIRE(buffer == load_data("double/data-zero"));
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
pw.add_double(1, 4.893);
|
||||
REQUIRE(buffer == load_data("double/data-pos"));
|
||||
}
|
||||
|
||||
SECTION("negative") {
|
||||
pw.add_double(1, -9232.33);
|
||||
REQUIRE(buffer == load_data("double/data-neg"));
|
||||
}
|
||||
}
|
||||
|
17
third_party/protozero/test/t/double/testcase.cpp
vendored
Normal file
17
third_party/protozero/test/t/double/testcase.cpp
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestDouble::Test msg;
|
||||
|
||||
msg.set_x(0.0);
|
||||
write_to_file(msg, "data-zero.pbf");
|
||||
|
||||
msg.set_x(4.893);
|
||||
write_to_file(msg, "data-pos.pbf");
|
||||
|
||||
msg.set_x(-9232.33);
|
||||
write_to_file(msg, "data-neg.pbf");
|
||||
}
|
||||
|
38
third_party/protozero/test/t/double/writer_test_cases.cpp
vendored
Normal file
38
third_party/protozero/test/t/double/writer_test_cases.cpp
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
#include "t/double/double_testcase.pb.h"
|
||||
|
||||
TEST_CASE("write double field and check with libprotobuf") {
|
||||
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
TestDouble::Test msg;
|
||||
|
||||
SECTION("zero") {
|
||||
pw.add_double(1, 0.0);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.x() == Approx(0.0));
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
pw.add_double(1, 4.893);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.x() == Approx(4.893));
|
||||
}
|
||||
|
||||
SECTION("negative") {
|
||||
pw.add_double(1, -9232.33);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.x() == Approx(-9232.33));
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
third_party/protozero/test/t/enum/data-black.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/enum/data-black.pbf
vendored
Normal file
Binary file not shown.
1
third_party/protozero/test/t/enum/data-blue.pbf
vendored
Normal file
1
third_party/protozero/test/t/enum/data-blue.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
|
1
third_party/protozero/test/t/enum/data-max.pbf
vendored
Normal file
1
third_party/protozero/test/t/enum/data-max.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<08><><EFBFBD><EFBFBD>
|
1
third_party/protozero/test/t/enum/data-min.pbf
vendored
Normal file
1
third_party/protozero/test/t/enum/data-min.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<08><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
1
third_party/protozero/test/t/enum/data-neg.pbf
vendored
Normal file
1
third_party/protozero/test/t/enum/data-neg.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<08><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
25
third_party/protozero/test/t/enum/enum_testcase.proto
vendored
Normal file
25
third_party/protozero/test/t/enum/enum_testcase.proto
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestEnum;
|
||||
|
||||
enum Color {
|
||||
BLACK = 0;
|
||||
RED = 1;
|
||||
GREEN = 2;
|
||||
BLUE = 3;
|
||||
MAX = 2147483646;
|
||||
NEG = -1;
|
||||
|
||||
// Older versions (before 2.6.0) of the google protobuf compiler have a
|
||||
// bug and don't allow the real minimum of -2147483648, so we are testing
|
||||
// with this.
|
||||
MIN = -2147483647;
|
||||
}
|
||||
|
||||
message Test {
|
||||
|
||||
required Color color = 1;
|
||||
|
||||
}
|
||||
|
83
third_party/protozero/test/t/enum/reader_test_cases.cpp
vendored
Normal file
83
third_party/protozero/test/t/enum/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
TEST_CASE("read enum field: zero") {
|
||||
const std::string buffer = load_data("enum/data-black");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_enum() == 0L);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read enum field: positive") {
|
||||
const std::string buffer = load_data("enum/data-blue");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_enum() == 3L);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read enum field: negative") {
|
||||
const std::string buffer = load_data("enum/data-neg");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_enum() == -1L);
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read enum field: max") {
|
||||
const std::string buffer = load_data("enum/data-max");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_enum() == std::numeric_limits<int32_t>::max());
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("read enum field: min") {
|
||||
const std::string buffer = load_data("enum/data-min");
|
||||
|
||||
protozero::pbf_reader item{buffer};
|
||||
|
||||
REQUIRE(item.next());
|
||||
REQUIRE(item.get_enum() == (std::numeric_limits<int32_t>::min() + 1));
|
||||
REQUIRE_FALSE(item.next());
|
||||
}
|
||||
|
||||
TEST_CASE("write enum field") {
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
SECTION("zero") {
|
||||
pw.add_enum(1, 0L);
|
||||
REQUIRE(buffer == load_data("enum/data-black"));
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
pw.add_enum(1, 3L);
|
||||
REQUIRE(buffer == load_data("enum/data-blue"));
|
||||
}
|
||||
|
||||
SECTION("negative") {
|
||||
pw.add_enum(1, -1L);
|
||||
REQUIRE(buffer == load_data("enum/data-neg"));
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
pw.add_enum(1, std::numeric_limits<int32_t>::max());
|
||||
REQUIRE(buffer == load_data("enum/data-max"));
|
||||
}
|
||||
|
||||
SECTION("min") {
|
||||
pw.add_enum(1, std::numeric_limits<int32_t>::min() + 1);
|
||||
REQUIRE(buffer == load_data("enum/data-min"));
|
||||
}
|
||||
}
|
||||
|
23
third_party/protozero/test/t/enum/testcase.cpp
vendored
Normal file
23
third_party/protozero/test/t/enum/testcase.cpp
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestEnum::Test msg;
|
||||
|
||||
msg.set_color(TestEnum::BLACK);
|
||||
write_to_file(msg, "data-black.pbf");
|
||||
|
||||
msg.set_color(TestEnum::BLUE);
|
||||
write_to_file(msg, "data-blue.pbf");
|
||||
|
||||
msg.set_color(TestEnum::NEG);
|
||||
write_to_file(msg, "data-neg.pbf");
|
||||
|
||||
msg.set_color(TestEnum::MAX);
|
||||
write_to_file(msg, "data-max.pbf");
|
||||
|
||||
msg.set_color(TestEnum::MIN);
|
||||
write_to_file(msg, "data-min.pbf");
|
||||
}
|
||||
|
54
third_party/protozero/test/t/enum/writer_test_cases.cpp
vendored
Normal file
54
third_party/protozero/test/t/enum/writer_test_cases.cpp
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
#include "t/enum/enum_testcase.pb.h"
|
||||
|
||||
TEST_CASE("write enum field and check with libprotobuf") {
|
||||
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
TestEnum::Test msg;
|
||||
|
||||
SECTION("zero") {
|
||||
pw.add_enum(1, 0L);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.color() == TestEnum::Color::BLACK);
|
||||
}
|
||||
|
||||
SECTION("positive") {
|
||||
pw.add_enum(1, 3L);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.color() == TestEnum::Color::BLUE);
|
||||
}
|
||||
|
||||
SECTION("negative") {
|
||||
pw.add_enum(1, -1L);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.color() == TestEnum::Color::NEG);
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
pw.add_enum(1, std::numeric_limits<int32_t>::max() - 1);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.color() == TestEnum::Color::MAX);
|
||||
}
|
||||
|
||||
SECTION("min") {
|
||||
pw.add_enum(1, std::numeric_limits<int32_t>::min() + 1);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.color() == TestEnum::Color::MIN);
|
||||
}
|
||||
|
||||
}
|
||||
|
1
third_party/protozero/test/t/fixed32/data-max.pbf
vendored
Normal file
1
third_party/protozero/test/t/fixed32/data-max.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<0A><><EFBFBD><EFBFBD>
|
BIN
third_party/protozero/test/t/fixed32/data-pos.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/fixed32/data-pos.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/test/t/fixed32/data-pos200.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/fixed32/data-pos200.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/test/t/fixed32/data-zero.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/fixed32/data-zero.pbf
vendored
Normal file
Binary file not shown.
11
third_party/protozero/test/t/fixed32/fixed32_testcase.proto
vendored
Normal file
11
third_party/protozero/test/t/fixed32/fixed32_testcase.proto
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestFixed32;
|
||||
|
||||
message Test {
|
||||
|
||||
required fixed32 i = 1;
|
||||
|
||||
}
|
||||
|
9
third_party/protozero/test/t/fixed32/reader_test_cases.cpp
vendored
Normal file
9
third_party/protozero/test/t/fixed32/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
#define PBF_TYPE fixed32
|
||||
#define PBF_TYPE_IS_SIGNED 0
|
||||
using cpp_type = uint32_t;
|
||||
|
||||
#include <scalar_access.hpp>
|
||||
|
20
third_party/protozero/test/t/fixed32/testcase.cpp
vendored
Normal file
20
third_party/protozero/test/t/fixed32/testcase.cpp
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestFixed32::Test msg;
|
||||
|
||||
msg.set_i(0);
|
||||
write_to_file(msg, "data-zero.pbf");
|
||||
|
||||
msg.set_i(1);
|
||||
write_to_file(msg, "data-pos.pbf");
|
||||
|
||||
msg.set_i(200);
|
||||
write_to_file(msg, "data-pos200.pbf");
|
||||
|
||||
msg.set_i(std::numeric_limits<uint32_t>::max());
|
||||
write_to_file(msg, "data-max.pbf");
|
||||
}
|
||||
|
38
third_party/protozero/test/t/fixed32/writer_test_cases.cpp
vendored
Normal file
38
third_party/protozero/test/t/fixed32/writer_test_cases.cpp
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
#include "t/fixed32/fixed32_testcase.pb.h"
|
||||
|
||||
TEST_CASE("write fixed32 field and check with libprotobuf") {
|
||||
|
||||
std::string buffer;
|
||||
protozero::pbf_writer pw{buffer};
|
||||
|
||||
TestFixed32::Test msg;
|
||||
|
||||
SECTION("zero") {
|
||||
pw.add_fixed32(1, 0);
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.i() == 0);
|
||||
}
|
||||
|
||||
SECTION("max") {
|
||||
pw.add_fixed32(1, std::numeric_limits<uint32_t>::max());
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.i() == std::numeric_limits<uint32_t>::max());
|
||||
}
|
||||
|
||||
SECTION("min") {
|
||||
pw.add_fixed32(1, std::numeric_limits<uint32_t>::min());
|
||||
|
||||
msg.ParseFromString(buffer);
|
||||
|
||||
REQUIRE(msg.i() == std::numeric_limits<uint32_t>::min());
|
||||
}
|
||||
|
||||
}
|
||||
|
1
third_party/protozero/test/t/fixed64/data-max.pbf
vendored
Normal file
1
third_party/protozero/test/t/fixed64/data-max.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
<09><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
BIN
third_party/protozero/test/t/fixed64/data-pos.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/fixed64/data-pos.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/test/t/fixed64/data-pos200.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/fixed64/data-pos200.pbf
vendored
Normal file
Binary file not shown.
BIN
third_party/protozero/test/t/fixed64/data-zero.pbf
vendored
Normal file
BIN
third_party/protozero/test/t/fixed64/data-zero.pbf
vendored
Normal file
Binary file not shown.
9
third_party/protozero/test/t/fixed64/reader_test_cases.cpp
vendored
Normal file
9
third_party/protozero/test/t/fixed64/reader_test_cases.cpp
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
#include <test.hpp>
|
||||
|
||||
#define PBF_TYPE fixed64
|
||||
#define PBF_TYPE_IS_SIGNED 0
|
||||
using cpp_type = uint64_t;
|
||||
|
||||
#include <scalar_access.hpp>
|
||||
|
20
third_party/protozero/test/t/fixed64/testcase.cpp
vendored
Normal file
20
third_party/protozero/test/t/fixed64/testcase.cpp
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
#include <testcase.hpp>
|
||||
#include "testcase.pb.h"
|
||||
|
||||
int main(int c, char *argv[]) {
|
||||
TestFixed64::Test msg;
|
||||
|
||||
msg.set_i(0);
|
||||
write_to_file(msg, "data-zero.pbf");
|
||||
|
||||
msg.set_i(1);
|
||||
write_to_file(msg, "data-pos.pbf");
|
||||
|
||||
msg.set_i(200);
|
||||
write_to_file(msg, "data-pos200.pbf");
|
||||
|
||||
msg.set_i(std::numeric_limits<uint64_t>::max());
|
||||
write_to_file(msg, "data-max.pbf");
|
||||
}
|
||||
|
11
third_party/protozero/test/t/fixed64/testcase.proto
vendored
Normal file
11
third_party/protozero/test/t/fixed64/testcase.proto
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
package TestFixed64;
|
||||
|
||||
message Test {
|
||||
|
||||
required fixed64 i = 1;
|
||||
|
||||
}
|
||||
|
1
third_party/protozero/test/t/float/data-neg.pbf
vendored
Normal file
1
third_party/protozero/test/t/float/data-neg.pbf
vendored
Normal file
@ -0,0 +1 @@
|
||||
H瞽ソ
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user