Copyright: 2013, Pierre KRIEGER
License: BSD-3
+Files: ext/protozero/*
+Copyright: protozero copyright (c) Mapbox.
+License: Protozero
+
License: BSD-3
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
`/usr/share/common-licenses/GPL-2'.
+
+License: Protozero
+ 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.
--- /dev/null
+
+# 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.7.0] - 2020-06-08
+
+### Added
+
+- Support for buffer types other that `std::string`. `pbf_writer` is now
+ just a typedef for `basic_pbf_writer<std::string>`. Other buffer types
+ can be used with `basic_pbf_writer`. See `doc/advanced.md` for details.
+
+### Changed
+
+- Switched to *catch2* for testing.
+- Some minor tweaks.
+
+### Fixed
+
+- Removed some undefined behaviour.
+
+
+## [1.6.8] - 2019-08-15
+
+### Changed
+
+- Various code cleanups due to clang-tidy warnings.
+
+### Fixed
+
+- Made `data_view::compare` noexcept.
+
+
+## [1.6.7] - 2018-02-21
+
+### Fixed
+
+- Signed-unsigned comparison on 32 bit systems.
+
+
+## [1.6.6] - 2018-02-20
+
+### Fixed
+
+- Fixed several place with possible undefined behaviour.
+
+
+## [1.6.5] - 2018-02-05
+
+### Fixed
+
+- Avoid UB: Do not calculate pointer outside array bounds.
+- Specify proto2 syntax in .proto files to appease protoc.
+
+
+## [1.6.4] - 2018-11-08
+
+### Added
+
+- Add function `data()` to get the not yet read data from a `pbf_reader`.
+- New `add_packed_fixed()` template function for `pbf_writer`.
+- New `length_of_varint()` helper function calculates how long a varint
+ would be for a specified value.
+
+### Changed
+
+- More consistent implementation of operators as free friend functions.
+
+### Fixed
+
+- Fixed some zigzag encoding tests on MSVC.
+- Add extra cast so we do an xor with unsigned ints.
+- No more bitwise operations on signed integers in varint decoder.
+- No more bitwise operations on signed integers in zigzag encoder/decoder.
+
+
+## [1.6.3] - 2018-07-17
+
+### Changed
+
+- Moved `byteswap_inplace` functions from detail into protozero namespace.
+ They can be useful outsize protozero.
+- More asserts and unit tests and small cleanups.
+
+
+## [1.6.2] - 2018-03-09
+
+### Changed
+
+- 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.7.0...HEAD
+[1.7.0]: https://github.com/osmcode/libosmium/compare/v1.6.8...v1.7.0
+[1.6.8]: https://github.com/osmcode/libosmium/compare/v1.6.7...v1.6.8
+[1.6.7]: https://github.com/osmcode/libosmium/compare/v1.6.6...v1.6.7
+[1.6.6]: https://github.com/osmcode/libosmium/compare/v1.6.5...v1.6.6
+[1.6.5]: https://github.com/osmcode/libosmium/compare/v1.6.4...v1.6.5
+[1.6.4]: https://github.com/osmcode/libosmium/compare/v1.6.3...v1.6.4
+[1.6.3]: https://github.com/osmcode/libosmium/compare/v1.6.2...v1.6.3
+[1.6.2]: https://github.com/osmcode/libosmium/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/osmcode/libosmium/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/osmcode/libosmium/compare/v1.5.3...v1.6.0
+[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
+
--- /dev/null
+
+ 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
--- /dev/null
+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.
--- /dev/null
+#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP
+#define PROTOZERO_BASIC_PBF_BUILDER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file basic_pbf_builder.hpp
+ *
+ * @brief Contains the basic_pbf_builder template class.
+ */
+
+#include "basic_pbf_writer.hpp"
+#include "types.hpp"
+
+#include <type_traits>
+
+namespace protozero {
+
+/**
+ * The basic_pbf_builder is used to write PBF formatted messages into a buffer.
+ * It is based on the basic_pbf_writer class and has all the same methods. The
+ * difference is that while the pbf_writer class takes an integer tag,
+ * this template class takes a tag of the template type T. The idea is that
+ * T will be an enumeration value and this helps reduce the possibility of
+ * programming errors.
+ *
+ * Almost all methods in this class can throw an std::bad_alloc exception if
+ * the underlying buffer class wants to resize.
+ *
+ * Read the tutorial to understand how this class is used. In most cases you
+ * want to use the pbf_builder class which uses a std::string as buffer type.
+ */
+template <typename TBuffer, typename T>
+class basic_pbf_builder : public basic_pbf_writer<TBuffer> {
+
+ 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;
+
+ basic_pbf_builder() = default;
+
+ /**
+ * Create a builder using the given string as a data store. The object
+ * stores a reference to that string and adds all data to it. The string
+ * doesn't have to be empty. The pbf_message object will just append data.
+ */
+ explicit basic_pbf_builder(TBuffer& data) noexcept :
+ basic_pbf_writer<TBuffer>{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>
+ basic_pbf_builder(basic_pbf_writer<TBuffer>& parent_writer, P tag) noexcept :
+ basic_pbf_writer<TBuffer>{parent_writer, pbf_tag_type(tag)} {
+ }
+
+/// @cond INTERNAL
+#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \
+ void add_##name(T tag, type value) { \
+ basic_pbf_writer<TBuffer>::add_##name(pbf_tag_type(tag), value); \
+ }
+
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float)
+ PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double)
+
+#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
+/// @endcond
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Pointer to value to be written
+ * @param size Number of bytes to be written
+ */
+ void add_bytes(T tag, const char* value, std::size_t size) {
+ basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value, size);
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Value to be written
+ */
+ void add_bytes(T tag, const data_view& value) {
+ basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value);
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Value to be written
+ */
+ void add_bytes(T tag, const std::string& value) {
+ basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value);
+ }
+
+ /**
+ * Add "bytes" field to data. Bytes from the value are written until
+ * a null byte is encountered. The null byte is not added.
+ *
+ * @param tag Tag of the field
+ * @param value Pointer to zero-delimited value to be written
+ */
+ void add_bytes(T tag, const char* value) {
+ basic_pbf_writer<TBuffer>::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) {
+ basic_pbf_writer<TBuffer>::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) {
+ basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value, size);
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Value to be written
+ */
+ void add_string(T tag, const data_view& value) {
+ basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value);
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Value to be written
+ */
+ void add_string(T tag, const std::string& value) {
+ basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value);
+ }
+
+ /**
+ * Add "string" field to data. Bytes from the value are written until
+ * a null byte is encountered. The null byte is not added.
+ *
+ * @param tag Tag of the field
+ * @param value Pointer to value to be written
+ */
+ void add_string(T tag, const char* value) {
+ basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value);
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Pointer to message to be written
+ * @param size Length of the message
+ */
+ void add_message(T tag, const char* value, std::size_t size) {
+ basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value, size);
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Value to be written. The value must be a complete message.
+ */
+ void add_message(T tag, const data_view& value) {
+ basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value);
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag of the field
+ * @param value Value to be written. The value must be a complete message.
+ */
+ void add_message(T tag, const std::string& value) {
+ basic_pbf_writer<TBuffer>::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) { \
+ basic_pbf_writer<TBuffer>::add_packed_##name(pbf_tag_type(tag), first, last); \
+ }
+
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(bool)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(enum)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(int32)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(int64)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(float)
+ PROTOZERO_WRITER_WRAP_ADD_PACKED(double)
+
+#undef PROTOZERO_WRITER_WRAP_ADD_PACKED
+/// @endcond
+
+}; // class basic_pbf_builder
+
+} // end namespace protozero
+
+#endif // PROTOZERO_BASIC_PBF_BUILDER_HPP
--- /dev/null
+#ifndef PROTOZERO_BASIC_PBF_WRITER_HPP
+#define PROTOZERO_BASIC_PBF_WRITER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file basic_pbf_writer.hpp
+ *
+ * @brief Contains the basic_pbf_writer template class.
+ */
+
+#include "buffer_tmpl.hpp"
+#include "config.hpp"
+#include "data_view.hpp"
+#include "types.hpp"
+#include "varint.hpp"
+
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+# include <protozero/byteswap.hpp>
+#endif
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <initializer_list>
+#include <iterator>
+#include <limits>
+#include <string>
+#include <utility>
+
+namespace protozero {
+
+namespace detail {
+
+ template <typename B, typename T> class packed_field_varint;
+ template <typename B, typename T> class packed_field_svarint;
+ template <typename B, typename T> class packed_field_fixed;
+
+} // end namespace detail
+
+/**
+ * The basic_pbf_writer is used to write PBF formatted messages into a buffer.
+ *
+ * This uses TBuffer as the type for the underlaying buffer. In typical uses
+ * this is std::string, but you can use a different type that must support
+ * the right interface. Please see the documentation for details.
+ *
+ * Almost all methods in this class can throw an std::bad_alloc exception if
+ * the underlying buffer class wants to resize.
+ */
+template <typename TBuffer>
+class basic_pbf_writer {
+
+ // A pointer to a buffer holding the data already written to the PBF
+ // message. For default constructed writers or writers that have been
+ // rolled back, this is a nullptr.
+ TBuffer* m_data = nullptr;
+
+ // A pointer to a parent writer object if this is a submessage. If this
+ // is a top-level writer, it is a nullptr.
+ basic_pbf_writer* m_parent_writer = nullptr;
+
+ // This is usually 0. If there is an open submessage, this is set in the
+ // parent to the rollback position, ie. the last position before the
+ // submessage was started. This is the position where the header of the
+ // submessage starts.
+ std::size_t m_rollback_pos = 0;
+
+ // This is usually 0. If there is an open submessage, this is set in the
+ // parent to the position where the data of the submessage is written to.
+ std::size_t m_pos = 0;
+
+ void add_varint(uint64_t value) {
+ protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
+ protozero_assert(m_data);
+ add_varint_to_buffer(m_data, value);
+ }
+
+ void add_field(pbf_tag_type tag, pbf_wire_type type) {
+ protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1U << 29U) - 1))) && "tag out of range");
+ const uint32_t b = (tag << 3U) | uint32_t(type);
+ add_varint(b);
+ }
+
+ void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
+ add_field(tag, pbf_wire_type::varint);
+ add_varint(value);
+ }
+
+ template <typename T>
+ void add_fixed(T value) {
+ protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
+ protozero_assert(m_data);
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+ byteswap_inplace(&value);
+#endif
+ buffer_customization<TBuffer>::append(m_data, reinterpret_cast<const char*>(&value), sizeof(T));
+ }
+
+ template <typename T, typename It>
+ void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag /*unused*/) {
+ if (first == last) {
+ return;
+ }
+
+ basic_pbf_writer sw{*this, tag};
+
+ while (first != last) {
+ sw.add_fixed<T>(*first++);
+ }
+ }
+
+ template <typename T, typename It>
+ void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag /*unused*/) {
+ if (first == last) {
+ return;
+ }
+
+ const auto length = std::distance(first, last);
+ add_length_varint(tag, sizeof(T) * pbf_length_type(length));
+ reserve(sizeof(T) * std::size_t(length));
+
+ while (first != last) {
+ add_fixed<T>(*first++);
+ }
+ }
+
+ template <typename It>
+ void add_packed_varint(pbf_tag_type tag, It first, It last) {
+ if (first == last) {
+ return;
+ }
+
+ basic_pbf_writer sw{*this, tag};
+
+ while (first != last) {
+ sw.add_varint(uint64_t(*first++));
+ }
+ }
+
+ template <typename It>
+ void add_packed_svarint(pbf_tag_type tag, It first, It last) {
+ if (first == last) {
+ return;
+ }
+
+ basic_pbf_writer sw{*this, tag};
+
+ while (first != last) {
+ sw.add_varint(encode_zigzag64(*first++));
+ }
+ }
+
+ // The number of bytes to reserve for the varint holding the length of
+ // a length-delimited field. The length has to fit into pbf_length_type,
+ // and a varint needs 8 bit for every 7 bit.
+ enum : int {
+ reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1
+ };
+
+ // If m_rollpack_pos is set to this special value, it means that when
+ // the submessage is closed, nothing needs to be done, because the length
+ // of the submessage has already been written correctly.
+ enum : std::size_t {
+ size_is_known = std::numeric_limits<std::size_t>::max()
+ };
+
+ void open_submessage(pbf_tag_type tag, std::size_t size) {
+ protozero_assert(m_pos == 0);
+ protozero_assert(m_data);
+ if (size == 0) {
+ m_rollback_pos = buffer_customization<TBuffer>::size(m_data);
+ add_field(tag, pbf_wire_type::length_delimited);
+ buffer_customization<TBuffer>::append_zeros(m_data, std::size_t(reserve_bytes));
+ } else {
+ m_rollback_pos = size_is_known;
+ add_length_varint(tag, pbf_length_type(size));
+ reserve(size);
+ }
+ m_pos = buffer_customization<TBuffer>::size(m_data);
+ }
+
+ void rollback_submessage() {
+ protozero_assert(m_pos != 0);
+ protozero_assert(m_rollback_pos != size_is_known);
+ protozero_assert(m_data);
+ buffer_customization<TBuffer>::resize(m_data, m_rollback_pos);
+ m_pos = 0;
+ }
+
+ void commit_submessage() {
+ protozero_assert(m_pos != 0);
+ protozero_assert(m_rollback_pos != size_is_known);
+ protozero_assert(m_data);
+ const auto length = pbf_length_type(buffer_customization<TBuffer>::size(m_data) - m_pos);
+
+ protozero_assert(buffer_customization<TBuffer>::size(m_data) >= m_pos - reserve_bytes);
+ const auto n = add_varint_to_buffer(buffer_customization<TBuffer>::at_pos(m_data, m_pos - reserve_bytes), length);
+
+ buffer_customization<TBuffer>::erase_range(m_data, m_pos - reserve_bytes + n, m_pos);
+ m_pos = 0;
+ }
+
+ void close_submessage() {
+ protozero_assert(m_data);
+ if (m_pos == 0 || m_rollback_pos == size_is_known) {
+ return;
+ }
+ if (buffer_customization<TBuffer>::size(m_data) - m_pos == 0) {
+ rollback_submessage();
+ } else {
+ commit_submessage();
+ }
+ }
+
+ void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
+ add_field(tag, pbf_wire_type::length_delimited);
+ add_varint(length);
+ }
+
+public:
+
+ /**
+ * Create a writer using the specified buffer as a data store. The
+ * basic_pbf_writer stores a pointer to that buffer and adds all data to
+ * it. The buffer doesn't have to be empty. The basic_pbf_writer will just
+ * append data.
+ */
+ explicit basic_pbf_writer(TBuffer& buffer) noexcept :
+ m_data{&buffer} {
+ }
+
+ /**
+ * Create a writer without a data store. In this form the writer can not
+ * be used!
+ */
+ basic_pbf_writer() noexcept = default;
+
+ /**
+ * Construct a basic_pbf_writer for a submessage from the basic_pbf_writer
+ * of the parent message.
+ *
+ * @param parent_writer The basic_pbf_writer
+ * @param tag Tag (field number) of the field that will be written
+ * @param size Optional size of the submessage in bytes (use 0 for unknown).
+ * Setting this allows some optimizations but is only possible in
+ * a few very specific cases.
+ */
+ basic_pbf_writer(basic_pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size = 0) :
+ m_data{parent_writer.m_data},
+ m_parent_writer{&parent_writer} {
+ m_parent_writer->open_submessage(tag, size);
+ }
+
+ /// A basic_pbf_writer object can not be copied
+ basic_pbf_writer(const basic_pbf_writer&) = delete;
+
+ /// A basic_pbf_writer object can not be copied
+ basic_pbf_writer& operator=(const basic_pbf_writer&) = delete;
+
+ /**
+ * A basic_pbf_writer object can be moved. After this the other
+ * basic_pbf_writer will be invalid.
+ */
+ basic_pbf_writer(basic_pbf_writer&& other) noexcept :
+ m_data{other.m_data},
+ m_parent_writer{other.m_parent_writer},
+ m_rollback_pos{other.m_rollback_pos},
+ m_pos{other.m_pos} {
+ other.m_data = nullptr;
+ other.m_parent_writer = nullptr;
+ other.m_rollback_pos = 0;
+ other.m_pos = 0;
+ }
+
+ /**
+ * A basic_pbf_writer object can be moved. After this the other
+ * basic_pbf_writer will be invalid.
+ */
+ basic_pbf_writer& operator=(basic_pbf_writer&& other) noexcept {
+ m_data = other.m_data;
+ m_parent_writer = other.m_parent_writer;
+ m_rollback_pos = other.m_rollback_pos;
+ m_pos = other.m_pos;
+ other.m_data = nullptr;
+ other.m_parent_writer = nullptr;
+ other.m_rollback_pos = 0;
+ other.m_pos = 0;
+ return *this;
+ }
+
+ ~basic_pbf_writer() noexcept {
+ try {
+ if (m_parent_writer != nullptr) {
+ m_parent_writer->close_submessage();
+ }
+ } catch (...) {
+ // This try/catch is used to make the destructor formally noexcept.
+ // close_submessage() is not noexcept, but will not throw the way
+ // it is called here, so we are good. But to be paranoid, call...
+ std::terminate();
+ }
+ }
+
+ /**
+ * Check if this writer is valid. A writer is invalid if it was default
+ * constructed, moved from, or if commit() has been called on it.
+ * Otherwise it is valid.
+ */
+ bool valid() const noexcept {
+ return m_data != nullptr;
+ }
+
+ /**
+ * Swap the contents of this object with the other.
+ *
+ * @param other Other object to swap data with.
+ */
+ void swap(basic_pbf_writer& other) noexcept {
+ using std::swap;
+ swap(m_data, other.m_data);
+ swap(m_parent_writer, other.m_parent_writer);
+ swap(m_rollback_pos, other.m_rollback_pos);
+ swap(m_pos, other.m_pos);
+ }
+
+ /**
+ * Reserve size bytes in the underlying message store in addition to
+ * whatever the message store already holds. So unlike
+ * the `std::string::reserve()` method this is not an absolute size,
+ * but additional memory that should be reserved.
+ *
+ * @param size Number of bytes to reserve in underlying message store.
+ */
+ void reserve(std::size_t size) {
+ protozero_assert(m_data);
+ buffer_customization<TBuffer>::reserve_additional(m_data, size);
+ }
+
+ /**
+ * Commit this submessage. This does the same as when the basic_pbf_writer
+ * goes out of scope and is destructed.
+ *
+ * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the
+ * basic_pbf_writer constructor taking a parent message.
+ * @post The basic_pbf_writer is invalid and can't be used any more.
+ */
+ void commit() {
+ protozero_assert(m_parent_writer && "you can't call commit() on a basic_pbf_writer without a parent");
+ protozero_assert(m_pos == 0 && "you can't call commit() on a basic_pbf_writer that has an open nested submessage");
+ m_parent_writer->close_submessage();
+ m_parent_writer = nullptr;
+ m_data = nullptr;
+ }
+
+ /**
+ * Cancel writing of this submessage. The complete submessage will be
+ * removed as if it was never created and no fields were added.
+ *
+ * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the
+ * basic_pbf_writer constructor taking a parent message.
+ * @post The basic_pbf_writer is invalid and can't be used any more.
+ */
+ void rollback() {
+ protozero_assert(m_parent_writer && "you can't call rollback() on a basic_pbf_writer without a parent");
+ protozero_assert(m_pos == 0 && "you can't call rollback() on a basic_pbf_writer that has an open nested submessage");
+ m_parent_writer->rollback_submessage();
+ m_parent_writer = nullptr;
+ m_data = nullptr;
+ }
+
+ ///@{
+ /**
+ * @name Scalar field writer functions
+ */
+
+ /**
+ * Add "bool" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_bool(pbf_tag_type tag, bool value) {
+ add_field(tag, pbf_wire_type::varint);
+ protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
+ protozero_assert(m_data);
+ m_data->push_back(char(value));
+ }
+
+ /**
+ * Add "enum" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_enum(pbf_tag_type tag, int32_t value) {
+ add_tagged_varint(tag, uint64_t(value));
+ }
+
+ /**
+ * Add "int32" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_int32(pbf_tag_type tag, int32_t value) {
+ add_tagged_varint(tag, uint64_t(value));
+ }
+
+ /**
+ * Add "sint32" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_sint32(pbf_tag_type tag, int32_t value) {
+ add_tagged_varint(tag, encode_zigzag32(value));
+ }
+
+ /**
+ * Add "uint32" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_uint32(pbf_tag_type tag, uint32_t value) {
+ add_tagged_varint(tag, value);
+ }
+
+ /**
+ * Add "int64" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_int64(pbf_tag_type tag, int64_t value) {
+ add_tagged_varint(tag, uint64_t(value));
+ }
+
+ /**
+ * Add "sint64" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_sint64(pbf_tag_type tag, int64_t value) {
+ add_tagged_varint(tag, encode_zigzag64(value));
+ }
+
+ /**
+ * Add "uint64" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_uint64(pbf_tag_type tag, uint64_t value) {
+ add_tagged_varint(tag, value);
+ }
+
+ /**
+ * Add "fixed32" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_fixed32(pbf_tag_type tag, uint32_t value) {
+ add_field(tag, pbf_wire_type::fixed32);
+ add_fixed<uint32_t>(value);
+ }
+
+ /**
+ * Add "sfixed32" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_sfixed32(pbf_tag_type tag, int32_t value) {
+ add_field(tag, pbf_wire_type::fixed32);
+ add_fixed<int32_t>(value);
+ }
+
+ /**
+ * Add "fixed64" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_fixed64(pbf_tag_type tag, uint64_t value) {
+ add_field(tag, pbf_wire_type::fixed64);
+ add_fixed<uint64_t>(value);
+ }
+
+ /**
+ * Add "sfixed64" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_sfixed64(pbf_tag_type tag, int64_t value) {
+ add_field(tag, pbf_wire_type::fixed64);
+ add_fixed<int64_t>(value);
+ }
+
+ /**
+ * Add "float" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_float(pbf_tag_type tag, float value) {
+ add_field(tag, pbf_wire_type::fixed32);
+ add_fixed<float>(value);
+ }
+
+ /**
+ * Add "double" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_double(pbf_tag_type tag, double value) {
+ add_field(tag, pbf_wire_type::fixed64);
+ add_fixed<double>(value);
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Pointer to value to be written
+ * @param size Number of bytes to be written
+ */
+ void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
+ protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
+ protozero_assert(m_data);
+ protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
+ add_length_varint(tag, pbf_length_type(size));
+ buffer_customization<TBuffer>::append(m_data, value, size);
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_bytes(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "bytes" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_bytes(pbf_tag_type tag, const std::string& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "bytes" field to data. Bytes from the value are written until
+ * a null byte is encountered. The null byte is not added.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Pointer to zero-delimited value to be written
+ */
+ void add_bytes(pbf_tag_type tag, const char* value) {
+ add_bytes(tag, value, std::strlen(value));
+ }
+
+ /**
+ * Add "bytes" field to data using vectored input. All the data in the
+ * 2nd and further arguments is "concatenated" with only a single copy
+ * into the final buffer.
+ *
+ * This will work with objects of any type supporting the data() and
+ * size() methods like std::string or protozero::data_view.
+ *
+ * Example:
+ * @code
+ * std::string data1 = "abc";
+ * std::string data2 = "xyz";
+ * writer.add_bytes_vectored(1, data1, data2);
+ * @endcode
+ *
+ * @tparam Ts List of types supporting data() and size() methods.
+ * @param tag Tag (field number) of the field
+ * @param values List of objects of types Ts with data to be appended.
+ */
+ template <typename... Ts>
+ void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) {
+ protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
+ protozero_assert(m_data);
+ size_t sum_size = 0;
+ (void)std::initializer_list<size_t>{sum_size += values.size()...};
+ protozero_assert(sum_size <= std::numeric_limits<pbf_length_type>::max());
+ add_length_varint(tag, pbf_length_type(sum_size));
+ buffer_customization<TBuffer>::reserve_additional(m_data, sum_size);
+ (void)std::initializer_list<int>{(buffer_customization<TBuffer>::append(m_data, values.data(), values.size()), 0)...};
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Pointer to value to be written
+ * @param size Number of bytes to be written
+ */
+ void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
+ add_bytes(tag, value, size);
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_string(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "string" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written
+ */
+ void add_string(pbf_tag_type tag, const std::string& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "string" field to data. Bytes from the value are written until
+ * a null byte is encountered. The null byte is not added.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Pointer to value to be written
+ */
+ void add_string(pbf_tag_type tag, const char* value) {
+ add_bytes(tag, value, std::strlen(value));
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Pointer to message to be written
+ * @param size Length of the message
+ */
+ void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
+ add_bytes(tag, value, size);
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written. The value must be a complete message.
+ */
+ void add_message(pbf_tag_type tag, const data_view& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ /**
+ * Add "message" field to data.
+ *
+ * @param tag Tag (field number) of the field
+ * @param value Value to be written. The value must be a complete message.
+ */
+ void add_message(pbf_tag_type tag, const std::string& value) {
+ add_bytes(tag, value.data(), value.size());
+ }
+
+ ///@}
+
+ ///@{
+ /**
+ * @name Repeated packed field writer functions
+ */
+
+ /**
+ * Add "repeated packed bool" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to bool.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_varint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed enum" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int32_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_varint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed int32" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int32_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_varint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed sint32" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int32_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_svarint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed uint32" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to uint32_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_varint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed int64" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int64_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_varint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed sint64" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int64_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_svarint(tag, first, last);
+ }
+
+ /**
+ * Add "repeated packed uint64" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to uint64_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_varint(tag, first, last);
+ }
+
+ /**
+ * Add a "repeated packed" fixed-size field to data. The following
+ * fixed-size fields are available:
+ *
+ * uint32_t -> repeated packed fixed32
+ * int32_t -> repeated packed sfixed32
+ * uint64_t -> repeated packed fixed64
+ * int64_t -> repeated packed sfixed64
+ * double -> repeated packed double
+ * float -> repeated packed float
+ *
+ * @tparam ValueType One of the following types: (u)int32/64_t, double, float.
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename ValueType, typename InputIterator>
+ void add_packed_fixed(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ static_assert(std::is_same<ValueType, uint32_t>::value ||
+ std::is_same<ValueType, int32_t>::value ||
+ std::is_same<ValueType, int64_t>::value ||
+ std::is_same<ValueType, uint64_t>::value ||
+ std::is_same<ValueType, double>::value ||
+ std::is_same<ValueType, float>::value, "Only some types are allowed");
+ add_packed_fixed<ValueType, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ /**
+ * Add "repeated packed fixed32" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to uint32_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ /**
+ * Add "repeated packed sfixed32" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int32_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_fixed<int32_t, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ /**
+ * Add "repeated packed fixed64" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to uint64_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ /**
+ * Add "repeated packed sfixed64" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to int64_t.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_fixed<int64_t, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ /**
+ * Add "repeated packed float" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to float.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_fixed<float, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ /**
+ * Add "repeated packed double" field to data.
+ *
+ * @tparam InputIterator A type satisfying the InputIterator concept.
+ * Dereferencing the iterator must yield a type assignable to double.
+ * @param tag Tag (field number) of the field
+ * @param first Iterator pointing to the beginning of the data
+ * @param last Iterator pointing one past the end of data
+ */
+ template <typename InputIterator>
+ void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
+ add_packed_fixed<double, InputIterator>(tag, first, last,
+ typename std::iterator_traits<InputIterator>::iterator_category{});
+ }
+
+ ///@}
+
+ template <typename B, typename T> friend class detail::packed_field_varint;
+ template <typename B, typename T> friend class detail::packed_field_svarint;
+ template <typename B, typename T> friend class detail::packed_field_fixed;
+
+}; // class basic_pbf_writer
+
+/**
+ * Swap two basic_pbf_writer objects.
+ *
+ * @param lhs First object.
+ * @param rhs Second object.
+ */
+template <typename TBuffer>
+inline void swap(basic_pbf_writer<TBuffer>& lhs, basic_pbf_writer<TBuffer>& rhs) noexcept {
+ lhs.swap(rhs);
+}
+
+namespace detail {
+
+ template <typename TBuffer>
+ class packed_field {
+
+ basic_pbf_writer<TBuffer> m_writer{};
+
+ public:
+
+ packed_field(const packed_field&) = delete;
+ packed_field& operator=(const packed_field&) = delete;
+
+ packed_field(packed_field&&) noexcept = default;
+ packed_field& operator=(packed_field&&) noexcept = default;
+
+ packed_field() = default;
+
+ packed_field(basic_pbf_writer<TBuffer>& parent_writer, pbf_tag_type tag) :
+ m_writer{parent_writer, tag} {
+ }
+
+ packed_field(basic_pbf_writer<TBuffer>& parent_writer, pbf_tag_type tag, std::size_t size) :
+ m_writer{parent_writer, tag, size} {
+ }
+
+ ~packed_field() noexcept = default;
+
+ bool valid() const noexcept {
+ return m_writer.valid();
+ }
+
+ void commit() {
+ m_writer.commit();
+ }
+
+ void rollback() {
+ m_writer.rollback();
+ }
+
+ basic_pbf_writer<TBuffer>& writer() noexcept {
+ return m_writer;
+ }
+
+ }; // class packed_field
+
+ template <typename TBuffer, typename T>
+ class packed_field_fixed : public packed_field<TBuffer> {
+
+ public:
+
+ packed_field_fixed() :
+ packed_field<TBuffer>{} {
+ }
+
+ template <typename P>
+ packed_field_fixed(basic_pbf_writer<TBuffer>& parent_writer, P tag) :
+ packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag)} {
+ }
+
+ template <typename P>
+ packed_field_fixed(basic_pbf_writer<TBuffer>& parent_writer, P tag, std::size_t size) :
+ packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag), size * sizeof(T)} {
+ }
+
+ void add_element(T value) {
+ this->writer().template add_fixed<T>(value);
+ }
+
+ }; // class packed_field_fixed
+
+ template <typename TBuffer, typename T>
+ class packed_field_varint : public packed_field<TBuffer> {
+
+ public:
+
+ packed_field_varint() :
+ packed_field<TBuffer>{} {
+ }
+
+ template <typename P>
+ packed_field_varint(basic_pbf_writer<TBuffer>& parent_writer, P tag) :
+ packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag)} {
+ }
+
+ void add_element(T value) {
+ this->writer().add_varint(uint64_t(value));
+ }
+
+ }; // class packed_field_varint
+
+ template <typename TBuffer, typename T>
+ class packed_field_svarint : public packed_field<TBuffer> {
+
+ public:
+
+ packed_field_svarint() :
+ packed_field<TBuffer>{} {
+ }
+
+ template <typename P>
+ packed_field_svarint(basic_pbf_writer<TBuffer>& parent_writer, P tag) :
+ packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag)} {
+ }
+
+ void add_element(T value) {
+ this->writer().add_varint(encode_zigzag64(value));
+ }
+
+ }; // class packed_field_svarint
+
+} // end namespace detail
+
+} // end namespace protozero
+
+#endif // PROTOZERO_BASIC_PBF_WRITER_HPP
--- /dev/null
+#ifndef PROTOZERO_BUFFER_FIXED_HPP
+#define PROTOZERO_BUFFER_FIXED_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_fixed.hpp
+ *
+ * @brief Contains the fixed_size_buffer_adaptor class.
+ */
+
+#include "buffer_tmpl.hpp"
+#include "config.hpp"
+
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <stdexcept>
+
+namespace protozero {
+
+/**
+ * This class can be used instead of std::string if you want to create a
+ * vector tile in a fixed-size buffer. Any operation that needs more space
+ * than is available will fail with a std::length_error exception.
+ */
+class fixed_size_buffer_adaptor {
+
+ char* m_data;
+ std::size_t m_capacity;
+ std::size_t m_size = 0;
+
+public:
+
+ /// @cond usual container typedefs not documented
+
+ using size_type = std::size_t;
+
+ using value_type = char;
+ using reference = value_type&;
+ using const_reference = const value_type&;
+ using pointer = value_type*;
+ using const_pointer = const value_type*;
+
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+
+ /// @endcond
+
+ /**
+ * Constructor.
+ *
+ * @param data Pointer to some memory allocated for the buffer.
+ * @param capacity Number of bytes available.
+ */
+ fixed_size_buffer_adaptor(char* data, std::size_t capacity) noexcept :
+ m_data(data),
+ m_capacity(capacity) {
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param container Some container class supporting the member functions
+ * data() and size().
+ */
+ template <typename T>
+ explicit fixed_size_buffer_adaptor(T& container) :
+ m_data(container.data()),
+ m_capacity(container.size()) {
+ }
+
+ /// Returns a pointer to the data in the buffer.
+ const char* data() const noexcept {
+ return m_data;
+ }
+
+ /// Returns a pointer to the data in the buffer.
+ char* data() noexcept {
+ return m_data;
+ }
+
+ /// The capacity this buffer was created with.
+ std::size_t capacity() const noexcept {
+ return m_capacity;
+ }
+
+ /// The number of bytes used in the buffer. Always <= capacity().
+ std::size_t size() const noexcept {
+ return m_size;
+ }
+
+ /// Return iterator to beginning of data.
+ char* begin() noexcept {
+ return m_data;
+ }
+
+ /// Return iterator to beginning of data.
+ const char* begin() const noexcept {
+ return m_data;
+ }
+
+ /// Return iterator to beginning of data.
+ const char* cbegin() const noexcept {
+ return m_data;
+ }
+
+ /// Return iterator to end of data.
+ char* end() noexcept {
+ return m_data + m_size;
+ }
+
+ /// Return iterator to end of data.
+ const char* end() const noexcept {
+ return m_data + m_size;
+ }
+
+ /// Return iterator to end of data.
+ const char* cend() const noexcept {
+ return m_data + m_size;
+ }
+
+/// @cond INTERNAL
+
+ // Do not rely on anything beyond this point
+
+ void append(const char* data, std::size_t count) {
+ if (m_size + count > m_capacity) {
+ throw std::length_error{"fixed size data store exhausted"};
+ }
+ std::copy_n(data, count, m_data + m_size);
+ m_size += count;
+ }
+
+ void append_zeros(std::size_t count) {
+ if (m_size + count > m_capacity) {
+ throw std::length_error{"fixed size data store exhausted"};
+ }
+ std::fill_n(m_data + m_size, count, '\0');
+ m_size += count;
+ }
+
+ void resize(std::size_t size) {
+ protozero_assert(size < m_size);
+ if (size > m_capacity) {
+ throw std::length_error{"fixed size data store exhausted"};
+ }
+ m_size = size;
+ }
+
+ void erase_range(std::size_t from, std::size_t to) {
+ protozero_assert(from <= m_size);
+ protozero_assert(to <= m_size);
+ protozero_assert(from < to);
+ std::copy(m_data + to, m_data + m_size, m_data + from);
+ m_size -= (to - from);
+ }
+
+ char* at_pos(std::size_t pos) {
+ protozero_assert(pos <= m_size);
+ return m_data + pos;
+ }
+
+ void push_back(char ch) {
+ if (m_size >= m_capacity) {
+ throw std::length_error{"fixed size data store exhausted"};
+ }
+ m_data[m_size++] = ch;
+ }
+/// @endcond
+
+}; // class fixed_size_buffer_adaptor
+
+/// @cond INTERNAL
+template <>
+struct buffer_customization<fixed_size_buffer_adaptor> {
+
+ static std::size_t size(const fixed_size_buffer_adaptor* buffer) noexcept {
+ return buffer->size();
+ }
+
+ static void append(fixed_size_buffer_adaptor* buffer, const char* data, std::size_t count) {
+ buffer->append(data, count);
+ }
+
+ static void append_zeros(fixed_size_buffer_adaptor* buffer, std::size_t count) {
+ buffer->append_zeros(count);
+ }
+
+ static void resize(fixed_size_buffer_adaptor* buffer, std::size_t size) {
+ buffer->resize(size);
+ }
+
+ static void reserve_additional(fixed_size_buffer_adaptor* /*buffer*/, std::size_t /*size*/) {
+ /* nothing to be done for fixed-size buffers */
+ }
+
+ static void erase_range(fixed_size_buffer_adaptor* buffer, std::size_t from, std::size_t to) {
+ buffer->erase_range(from, to);
+ }
+
+ static char* at_pos(fixed_size_buffer_adaptor* buffer, std::size_t pos) {
+ return buffer->at_pos(pos);
+ }
+
+ static void push_back(fixed_size_buffer_adaptor* buffer, char ch) {
+ buffer->push_back(ch);
+ }
+
+};
+/// @endcond
+
+} // namespace protozero
+
+#endif // PROTOZERO_BUFFER_FIXED_HPP
--- /dev/null
+#ifndef PROTOZERO_BUFFER_STRING_HPP
+#define PROTOZERO_BUFFER_STRING_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_string.hpp
+ *
+ * @brief Contains the customization points for buffer implementation based
+ * on std::string
+ */
+
+#include "buffer_tmpl.hpp"
+
+#include <cstddef>
+#include <iterator>
+#include <string>
+
+namespace protozero {
+
+// Implementation of buffer customizations points for std::string
+
+/// @cond INTERNAL
+template <>
+struct buffer_customization<std::string> {
+
+ static std::size_t size(const std::string* buffer) noexcept {
+ return buffer->size();
+ }
+
+ static void append(std::string* buffer, const char* data, std::size_t count) {
+ buffer->append(data, count);
+ }
+
+ static void append_zeros(std::string* buffer, std::size_t count) {
+ buffer->append(count, '\0');
+ }
+
+ static void resize(std::string* buffer, std::size_t size) {
+ protozero_assert(size < buffer->size());
+ buffer->resize(size);
+ }
+
+ static void reserve_additional(std::string* buffer, std::size_t size) {
+ buffer->reserve(buffer->size() + size);
+ }
+
+ static void erase_range(std::string* buffer, std::size_t from, std::size_t to) {
+ protozero_assert(from <= buffer->size());
+ protozero_assert(to <= buffer->size());
+ protozero_assert(from <= to);
+ buffer->erase(std::next(buffer->begin(), from), std::next(buffer->begin(), to));
+ }
+
+ static char* at_pos(std::string* buffer, std::size_t pos) {
+ protozero_assert(pos <= buffer->size());
+ return (&*buffer->begin()) + pos;
+ }
+
+ static void push_back(std::string* buffer, char ch) {
+ buffer->push_back(ch);
+ }
+
+};
+/// @endcond
+
+} // namespace protozero
+
+#endif // PROTOZERO_BUFFER_STRING_HPP
--- /dev/null
+#ifndef PROTOZERO_BUFFER_TMPL_HPP
+#define PROTOZERO_BUFFER_TMPL_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_tmpl.hpp
+ *
+ * @brief Contains the customization points for buffer implementations.
+ */
+
+#include <cstddef>
+#include <iterator>
+#include <string>
+
+namespace protozero {
+
+// Implementation of buffer customizations points for std::string
+
+/// @cond INTERNAL
+template <typename T>
+struct buffer_customization {
+
+ /**
+ * Get the number of bytes currently used in the buffer.
+ *
+ * @param buffer Pointer to the buffer.
+ * @returns number of bytes used in the buffer.
+ */
+ static std::size_t size(const std::string* buffer);
+
+ /**
+ * Append count bytes from data to the buffer.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param data Pointer to the data.
+ * @param count Number of bytes to be added to the buffer.
+ */
+ static void append(std::string* buffer, const char* data, std::size_t count);
+
+ /**
+ * Append count zero bytes to the buffer.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param count Number of bytes to be added to the buffer.
+ */
+ static void append_zeros(std::string* buffer, std::size_t count);
+
+ /**
+ * Shrink the buffer to the specified size. The new size will always be
+ * smaller than the current size.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param size New size of the buffer.
+ *
+ * @pre size < current size of buffer
+ */
+ static void resize(std::string* buffer, std::size_t size);
+
+ /**
+ * Reserve an additional size bytes for use in the buffer. This is used for
+ * variable-sized buffers to tell the buffer implementation that soon more
+ * memory will be used. The implementation can ignore this.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param size Number of bytes to reserve.
+ */
+ static void reserve_additional(std::string* buffer, std::size_t size);
+
+ /**
+ * Delete data from the buffer. This must move back the data after the
+ * part being deleted and resize the buffer accordingly.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param from Offset into the buffer where we want to erase from.
+ * @param to Offset into the buffer one past the last byte we want to erase.
+ *
+ * @pre from, to <= size of the buffer, from < to
+ */
+ static void erase_range(std::string* buffer, std::size_t from, std::size_t to);
+
+ /**
+ * Return a pointer to the memory at the specified position in the buffer.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param pos The position in the buffer.
+ * @returns pointer to the memory in the buffer at the specified position.
+ *
+ * @pre pos <= size of the buffer
+ */
+ static char* at_pos(std::string* buffer, std::size_t pos);
+
+ /**
+ * Add a char to the buffer incrementing the number of chars in the buffer.
+ *
+ * @param buffer Pointer to the buffer.
+ * @param ch The character to add.
+ */
+ static void push_back(std::string* buffer, char ch);
+
+};
+/// @endcond
+
+} // namespace protozero
+
+#endif // PROTOZERO_BUFFER_TMPL_HPP
--- /dev/null
+#ifndef PROTOZERO_BUFFER_VECTOR_HPP
+#define PROTOZERO_BUFFER_VECTOR_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_vector.hpp
+ *
+ * @brief Contains the customization points for buffer implementation based
+ * on std::vector<char>
+ */
+
+#include "buffer_tmpl.hpp"
+
+#include <cstddef>
+#include <iterator>
+#include <vector>
+
+namespace protozero {
+
+// Implementation of buffer customizations points for std::vector<char>
+
+/// @cond INTERNAL
+template <>
+struct buffer_customization<std::vector<char>> {
+
+ static std::size_t size(const std::vector<char>* buffer) noexcept {
+ return buffer->size();
+ }
+
+ static void append(std::vector<char>* buffer, const char* data, std::size_t count) {
+ buffer->insert(buffer->end(), data, data + count);
+ }
+
+ static void append_zeros(std::vector<char>* buffer, std::size_t count) {
+ buffer->insert(buffer->end(), count, '\0');
+ }
+
+ static void resize(std::vector<char>* buffer, std::size_t size) {
+ protozero_assert(size < buffer->size());
+ buffer->resize(size);
+ }
+
+ static void reserve_additional(std::vector<char>* buffer, std::size_t size) {
+ buffer->reserve(buffer->size() + size);
+ }
+
+ static void erase_range(std::vector<char>* buffer, std::size_t from, std::size_t to) {
+ protozero_assert(from <= buffer->size());
+ protozero_assert(to <= buffer->size());
+ protozero_assert(from <= to);
+ buffer->erase(std::next(buffer->begin(), from), std::next(buffer->begin(), to));
+ }
+
+ static char* at_pos(std::vector<char>* buffer, std::size_t pos) {
+ protozero_assert(pos <= buffer->size());
+ return (&*buffer->begin()) + pos;
+ }
+
+ static void push_back(std::vector<char>* buffer, char ch) {
+ buffer->push_back(ch);
+ }
+
+};
+/// @endcond
+
+} // namespace protozero
+
+#endif // PROTOZERO_BUFFER_VECTOR_HPP
--- /dev/null
+#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 "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 & 0xff000000U) >> 24U) |
+ ((value & 0x00ff0000U) >> 8U) |
+ ((value & 0x0000ff00U) << 8U) |
+ ((value & 0x000000ffU) << 24U);
+#endif
+}
+
+inline uint64_t byteswap_impl(uint64_t value) noexcept {
+#ifdef PROTOZERO_USE_BUILTIN_BSWAP
+ return __builtin_bswap64(value);
+#else
+ return ((value & 0xff00000000000000ULL) >> 56U) |
+ ((value & 0x00ff000000000000ULL) >> 40U) |
+ ((value & 0x0000ff0000000000ULL) >> 24U) |
+ ((value & 0x000000ff00000000ULL) >> 8U) |
+ ((value & 0x00000000ff000000ULL) << 8U) |
+ ((value & 0x0000000000ff0000ULL) << 24U) |
+ ((value & 0x000000000000ff00ULL) << 40U) |
+ ((value & 0x00000000000000ffULL) << 56U);
+#endif
+}
+
+} // end namespace detail
+
+/// byteswap the data pointed to by ptr in-place.
+inline void byteswap_inplace(uint32_t* ptr) noexcept {
+ *ptr = detail::byteswap_impl(*ptr);
+}
+
+/// byteswap the data pointed to by ptr in-place.
+inline void byteswap_inplace(uint64_t* ptr) noexcept {
+ *ptr = detail::byteswap_impl(*ptr);
+}
+
+/// byteswap the data pointed to by ptr in-place.
+inline void byteswap_inplace(int32_t* ptr) noexcept {
+ auto* bptr = reinterpret_cast<uint32_t*>(ptr);
+ *bptr = detail::byteswap_impl(*bptr);
+}
+
+/// byteswap the data pointed to by ptr in-place.
+inline void byteswap_inplace(int64_t* ptr) noexcept {
+ auto* bptr = reinterpret_cast<uint64_t*>(ptr);
+ *bptr = detail::byteswap_impl(*bptr);
+}
+
+/// byteswap the data pointed to by ptr in-place.
+inline void byteswap_inplace(float* ptr) noexcept {
+ auto* bptr = reinterpret_cast<uint32_t*>(ptr);
+ *bptr = detail::byteswap_impl(*bptr);
+}
+
+/// byteswap the data pointed to by ptr in-place.
+inline void byteswap_inplace(double* ptr) noexcept {
+ auto* bptr = reinterpret_cast<uint64_t*>(ptr);
+ *bptr = detail::byteswap_impl(*bptr);
+}
+
+namespace detail {
+
+ // Added for backwards compatibility with any code that might use this
+ // function (even if it shouldn't have). Will be removed in a later
+ // version of protozero.
+ using ::protozero::byteswap_inplace;
+
+} // end namespace detail
+
+} // end namespace protozero
+
+#endif // PROTOZERO_BYTESWAP_HPP
--- /dev/null
+#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
--- /dev/null
+#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 "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 noexcept {
+ 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
--- /dev/null
+#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
--- /dev/null
+#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 "config.hpp"
+#include "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:
+
+ /// @cond usual iterator functions not documented
+
+ 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 noexcept {
+ value_type result;
+ std::memcpy(&result, m_data, sizeof(value_type));
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+ 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;
+ }
+
+ 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 !(lhs == rhs);
+ }
+
+ 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);
+ }
+
+ /// @endcond
+
+}; // 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; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors)
+
+ /// Pointer to end iterator position
+ const char* m_end = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors)
+
+public:
+
+ /// @cond usual iterator functions not documented
+
+ using iterator_category = std::forward_iterator_tag;
+ using value_type = T;
+ using difference_type = std::ptrdiff_t;
+ using pointer = value_type*;
+ using reference = value_type&;
+
+ static difference_type distance(const_varint_iterator begin, const_varint_iterator end) noexcept {
+ // The "distance" between default initialized const_varint_iterator's
+ // is always 0.
+ if (!begin.m_data) {
+ return 0;
+ }
+ // We know that each varint contains exactly one byte with the most
+ // significant bit not set. We can use this to quickly figure out
+ // how many varints there are without actually decoding the varints.
+ return std::count_if(begin.m_data, end.m_data, [](char c) noexcept {
+ return (static_cast<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 {
+ protozero_assert(m_data);
+ const char* d = m_data; // will be thrown away
+ return static_cast<value_type>(decode_varint(&d, m_end));
+ }
+
+ const_varint_iterator& operator++() {
+ protozero_assert(m_data);
+ skip_varint(&m_data, m_end);
+ return *this;
+ }
+
+ const_varint_iterator operator++(int) {
+ protozero_assert(m_data);
+ const const_varint_iterator tmp{*this};
+ ++(*this);
+ return tmp;
+ }
+
+ 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);
+ }
+
+ /// @endcond
+
+}; // 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:
+
+ /// @cond usual iterator functions not documented
+
+ 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 {
+ protozero_assert(this->m_data);
+ 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++() {
+ protozero_assert(this->m_data);
+ skip_varint(&this->m_data, this->m_end);
+ return *this;
+ }
+
+ const_svarint_iterator operator++(int) {
+ protozero_assert(this->m_data);
+ const const_svarint_iterator tmp{*this};
+ ++(*this);
+ return tmp;
+ }
+
+ /// @endcond
+
+}; // class const_svarint_iterator
+
+} // end namespace protozero
+
+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.
+
+ /// @cond individual overloads do not need to be documented
+
+ 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);
+ }
+
+ /// @endcond
+
+} // end namespace std
+
+#endif // PROTOZERO_ITERATORS_HPP
--- /dev/null
+#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 "basic_pbf_builder.hpp"
+#include "pbf_writer.hpp"
+
+#include <string>
+
+namespace protozero {
+
+/// Specialization of basic_pbf_builder using std::string as buffer type.
+template <typename T>
+using pbf_builder = basic_pbf_builder<std::string, T>;
+
+} // end namespace protozero
+
+#endif // PROTOZERO_PBF_BUILDER_HPP
--- /dev/null
+#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 "pbf_reader.hpp"
+#include "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
--- /dev/null
+#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 "config.hpp"
+#include "data_view.hpp"
+#include "exception.hpp"
+#include "iterators.hpp"
+#include "types.hpp"
+#include "varint.hpp"
+
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+# include <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
+ 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() {
+ const auto val = static_cast<T>(decode_varint(&m_data, m_end));
+ return val;
+ }
+
+ 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_end - m_data < static_cast<ptrdiff_t>(len)) {
+ 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;
+ }
+
+ /**
+ * Get a view of the not yet read data.
+ */
+ data_view data() const noexcept {
+ return {m_data, static_cast<std::size_t>(m_end - m_data)};
+ }
+
+ /**
+ * 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 bool result = m_data[0] != 0;
+ skip_varint(&m_data, m_end);
+ return result;
+ }
+
+ /**
+ * 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
--- /dev/null
+#ifndef PROTOZERO_PBF_WRITER_HPP
+#define PROTOZERO_PBF_WRITER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file pbf_writer.hpp
+ *
+ * @brief Contains the pbf_writer class.
+ */
+
+#include "basic_pbf_writer.hpp"
+#include "buffer_string.hpp"
+
+#include <cstdint>
+#include <string>
+
+namespace protozero {
+
+/**
+ * Specialization of basic_pbf_writer using std::string as buffer type.
+ */
+using pbf_writer = basic_pbf_writer<std::string>;
+
+/// Class for generating packed repeated bool fields.
+using packed_field_bool = detail::packed_field_varint<std::string, bool>;
+
+/// Class for generating packed repeated enum fields.
+using packed_field_enum = detail::packed_field_varint<std::string, int32_t>;
+
+/// Class for generating packed repeated int32 fields.
+using packed_field_int32 = detail::packed_field_varint<std::string, int32_t>;
+
+/// Class for generating packed repeated sint32 fields.
+using packed_field_sint32 = detail::packed_field_svarint<std::string, int32_t>;
+
+/// Class for generating packed repeated uint32 fields.
+using packed_field_uint32 = detail::packed_field_varint<std::string, uint32_t>;
+
+/// Class for generating packed repeated int64 fields.
+using packed_field_int64 = detail::packed_field_varint<std::string, int64_t>;
+
+/// Class for generating packed repeated sint64 fields.
+using packed_field_sint64 = detail::packed_field_svarint<std::string, int64_t>;
+
+/// Class for generating packed repeated uint64 fields.
+using packed_field_uint64 = detail::packed_field_varint<std::string, uint64_t>;
+
+/// Class for generating packed repeated fixed32 fields.
+using packed_field_fixed32 = detail::packed_field_fixed<std::string, uint32_t>;
+
+/// Class for generating packed repeated sfixed32 fields.
+using packed_field_sfixed32 = detail::packed_field_fixed<std::string, int32_t>;
+
+/// Class for generating packed repeated fixed64 fields.
+using packed_field_fixed64 = detail::packed_field_fixed<std::string, uint64_t>;
+
+/// Class for generating packed repeated sfixed64 fields.
+using packed_field_sfixed64 = detail::packed_field_fixed<std::string, int64_t>;
+
+/// Class for generating packed repeated float fields.
+using packed_field_float = detail::packed_field_fixed<std::string, float>;
+
+/// Class for generating packed repeated double fields.
+using packed_field_double = detail::packed_field_fixed<std::string, double>;
+
+} // end namespace protozero
+
+#endif // PROTOZERO_PBF_WRITER_HPP
--- /dev/null
+#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 "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
--- /dev/null
+#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 "buffer_tmpl.hpp"
+#include "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 = *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 && ((static_cast<uint64_t>(**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.
+ * @deprecated Use add_varint_to_buffer() instead.
+ */
+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;
+}
+
+/**
+ * Varint encode a 64 bit integer.
+ *
+ * @tparam TBuffer A buffer type.
+ * @param buffer Output buffer the varint will be written to.
+ * @param value The integer that will be encoded.
+ * @returns the number of bytes written
+ * @throws Any exception thrown by calling the buffer_push_back() function.
+ */
+template <typename TBuffer>
+inline void add_varint_to_buffer(TBuffer* buffer, uint64_t value) {
+ while (value >= 0x80U) {
+ buffer_customization<TBuffer>::push_back(buffer, char((value & 0x7fU) | 0x80U));
+ value >>= 7U;
+ }
+ buffer_customization<TBuffer>::push_back(buffer, char(value));
+}
+
+/**
+ * Varint encode a 64 bit integer.
+ *
+ * @param data Where to add the varint. There must be enough space available!
+ * @param value The integer that will be encoded.
+ * @returns the number of bytes written
+ */
+inline int add_varint_to_buffer(char* data, uint64_t value) noexcept {
+ int n = 1;
+
+ while (value >= 0x80U) {
+ *data++ = char((value & 0x7fU) | 0x80U);
+ value >>= 7U;
+ ++n;
+ }
+ *data = char(value);
+
+ return n;
+}
+
+/**
+ * Get the length of the varint the specified value would produce.
+ *
+ * @param value The integer to be encoded.
+ * @returns the number of bytes the varint would have if we created it.
+ */
+inline int length_of_varint(uint64_t value) noexcept {
+ int n = 1;
+
+ while (value >= 0x80U) {
+ value >>= 7U;
+ ++n;
+ }
+
+ 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>(-static_cast<int32_t>(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>(-static_cast<int64_t>(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<uint32_t>(-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<uint64_t>(-static_cast<int64_t>(value & 1U)));
+}
+
+} // end namespace protozero
+
+#endif // PROTOZERO_VARINT_HPP
--- /dev/null
+#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 7
+
+/// The patch number
+#define PROTOZERO_VERSION_PATCH 0
+
+/// 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.7.0"
+
+#endif // PROTOZERO_VERSION_HPP
(On older versions of Debian/Ubuntu, you'll need to pass `--with-lua=lua5.1` instead.)
Documentation
--------------
+=============
After compiling, run `pdns\_recursor --config` to view the configuration options
and a short description. The full documentation is online at
https://doc.powerdns.com/recursor/
Reporting bugs
---------------
+==============
Bugs can be reported on GitHub: https://github.com/PowerDNS/pdns/issues, please
check first if your issue is not fixed in the latest version or has already been
reported.
License
--------
+=======
PowerDNS is copyright © 2001-2019 by PowerDNS.COM BV and lots of
contributors, using the GNU GPLv2 license (see NOTICE for the
exact license and exception used).
+
+Third party software
+====================
+We use code from the following projects:
+
+Protozero
+---------
+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.
+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We like to work on new things!
You can file a feature request on `GitHub feature request <https://github.com/PowerDNS/pdns/issues/new?template=feature_request.md>`_.
+
+Third party software
+--------------------
+We use code from the project listed below.
+
+Protozero
+^^^^^^^^^
+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.