]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
Version 1.70 of protozero
authorOtto Moerbeek <otto.moerbeek@open-xchange.com>
Tue, 13 Oct 2020 13:50:19 +0000 (15:50 +0200)
committerOtto Moerbeek <otto@drijf.net>
Tue, 10 Nov 2020 07:53:24 +0000 (08:53 +0100)
24 files changed:
builder-support/debian/recursor/debian-buster/copyright
ext/protozero/CHANGELOG.md [new file with mode: 0644]
ext/protozero/LICENSE.from_folly [new file with mode: 0644]
ext/protozero/LICENSE.md [new file with mode: 0644]
ext/protozero/include/protozero/basic_pbf_builder.hpp [new file with mode: 0644]
ext/protozero/include/protozero/basic_pbf_writer.hpp [new file with mode: 0644]
ext/protozero/include/protozero/buffer_fixed.hpp [new file with mode: 0644]
ext/protozero/include/protozero/buffer_string.hpp [new file with mode: 0644]
ext/protozero/include/protozero/buffer_tmpl.hpp [new file with mode: 0644]
ext/protozero/include/protozero/buffer_vector.hpp [new file with mode: 0644]
ext/protozero/include/protozero/byteswap.hpp [new file with mode: 0644]
ext/protozero/include/protozero/config.hpp [new file with mode: 0644]
ext/protozero/include/protozero/data_view.hpp [new file with mode: 0644]
ext/protozero/include/protozero/exception.hpp [new file with mode: 0644]
ext/protozero/include/protozero/iterators.hpp [new file with mode: 0644]
ext/protozero/include/protozero/pbf_builder.hpp [new file with mode: 0644]
ext/protozero/include/protozero/pbf_message.hpp [new file with mode: 0644]
ext/protozero/include/protozero/pbf_reader.hpp [new file with mode: 0644]
ext/protozero/include/protozero/pbf_writer.hpp [new file with mode: 0644]
ext/protozero/include/protozero/types.hpp [new file with mode: 0644]
ext/protozero/include/protozero/varint.hpp [new file with mode: 0644]
ext/protozero/include/protozero/version.hpp [new file with mode: 0644]
pdns/recursordist/README.md
pdns/recursordist/docs/index.rst

index cb8c1422b0d2ff6f3a54b8e7e26652908b605e14..253fcfcac6e8ebaaade843a93a84cd9f3e5a8239 100644 (file)
@@ -46,6 +46,10 @@ Files: ext/luawrapper/*
 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:
@@ -105,3 +109,27 @@ License: GPL-2
  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.
diff --git a/ext/protozero/CHANGELOG.md b/ext/protozero/CHANGELOG.md
new file mode 100644 (file)
index 0000000..cfdb6a6
--- /dev/null
@@ -0,0 +1,411 @@
+
+# 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
+
diff --git a/ext/protozero/LICENSE.from_folly b/ext/protozero/LICENSE.from_folly
new file mode 100644 (file)
index 0000000..f433b1a
--- /dev/null
@@ -0,0 +1,177 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/ext/protozero/LICENSE.md b/ext/protozero/LICENSE.md
new file mode 100644 (file)
index 0000000..d0b3011
--- /dev/null
@@ -0,0 +1,24 @@
+protozero copyright (c) Mapbox.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in
+      the documentation and/or other materials provided with the
+      distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/ext/protozero/include/protozero/basic_pbf_builder.hpp b/ext/protozero/include/protozero/basic_pbf_builder.hpp
new file mode 100644 (file)
index 0000000..0ede726
--- /dev/null
@@ -0,0 +1,266 @@
+#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP
+#define PROTOZERO_BASIC_PBF_BUILDER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file basic_pbf_builder.hpp
+ *
+ * @brief Contains the basic_pbf_builder template class.
+ */
+
+#include "basic_pbf_writer.hpp"
+#include "types.hpp"
+
+#include <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
diff --git a/ext/protozero/include/protozero/basic_pbf_writer.hpp b/ext/protozero/include/protozero/basic_pbf_writer.hpp
new file mode 100644 (file)
index 0000000..f167c4d
--- /dev/null
@@ -0,0 +1,1054 @@
+#ifndef PROTOZERO_BASIC_PBF_WRITER_HPP
+#define PROTOZERO_BASIC_PBF_WRITER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file basic_pbf_writer.hpp
+ *
+ * @brief Contains the basic_pbf_writer template class.
+ */
+
+#include "buffer_tmpl.hpp"
+#include "config.hpp"
+#include "data_view.hpp"
+#include "types.hpp"
+#include "varint.hpp"
+
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+# include <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
diff --git a/ext/protozero/include/protozero/buffer_fixed.hpp b/ext/protozero/include/protozero/buffer_fixed.hpp
new file mode 100644 (file)
index 0000000..b2e6d1d
--- /dev/null
@@ -0,0 +1,222 @@
+#ifndef PROTOZERO_BUFFER_FIXED_HPP
+#define PROTOZERO_BUFFER_FIXED_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_fixed.hpp
+ *
+ * @brief Contains the fixed_size_buffer_adaptor class.
+ */
+
+#include "buffer_tmpl.hpp"
+#include "config.hpp"
+
+#include <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
diff --git a/ext/protozero/include/protozero/buffer_string.hpp b/ext/protozero/include/protozero/buffer_string.hpp
new file mode 100644 (file)
index 0000000..1f0f902
--- /dev/null
@@ -0,0 +1,76 @@
+#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
diff --git a/ext/protozero/include/protozero/buffer_tmpl.hpp b/ext/protozero/include/protozero/buffer_tmpl.hpp
new file mode 100644 (file)
index 0000000..ac22399
--- /dev/null
@@ -0,0 +1,113 @@
+#ifndef PROTOZERO_BUFFER_TMPL_HPP
+#define PROTOZERO_BUFFER_TMPL_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_tmpl.hpp
+ *
+ * @brief Contains the customization points for buffer implementations.
+ */
+
+#include <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
diff --git a/ext/protozero/include/protozero/buffer_vector.hpp b/ext/protozero/include/protozero/buffer_vector.hpp
new file mode 100644 (file)
index 0000000..6a34b07
--- /dev/null
@@ -0,0 +1,76 @@
+#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
diff --git a/ext/protozero/include/protozero/byteswap.hpp b/ext/protozero/include/protozero/byteswap.hpp
new file mode 100644 (file)
index 0000000..799d179
--- /dev/null
@@ -0,0 +1,99 @@
+#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
diff --git a/ext/protozero/include/protozero/config.hpp b/ext/protozero/include/protozero/config.hpp
new file mode 100644 (file)
index 0000000..6fc7749
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef PROTOZERO_CONFIG_HPP
+#define PROTOZERO_CONFIG_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+#include <cassert>
+
+/**
+ * @file config.hpp
+ *
+ * @brief Contains macro checks for different configurations.
+ */
+
+#define PROTOZERO_LITTLE_ENDIAN 1234
+#define PROTOZERO_BIG_ENDIAN    4321
+
+// Find out which byte order the machine has.
+#if defined(__BYTE_ORDER)
+# if (__BYTE_ORDER == __LITTLE_ENDIAN)
+#  define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN
+# endif
+# if (__BYTE_ORDER == __BIG_ENDIAN)
+#  define PROTOZERO_BYTE_ORDER PROTOZERO_BIG_ENDIAN
+# endif
+#else
+// This probably isn't a very good default, but might do until we figure
+// out something better.
+# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN
+#endif
+
+// Check whether __builtin_bswap is available
+#if defined(__GNUC__) || defined(__clang__)
+# define PROTOZERO_USE_BUILTIN_BSWAP
+#endif
+
+// Wrapper for assert() used for testing
+#ifndef protozero_assert
+# define protozero_assert(x) assert(x)
+#endif
+
+#endif // PROTOZERO_CONFIG_HPP
diff --git a/ext/protozero/include/protozero/data_view.hpp b/ext/protozero/include/protozero/data_view.hpp
new file mode 100644 (file)
index 0000000..3ec87af
--- /dev/null
@@ -0,0 +1,236 @@
+#ifndef PROTOZERO_DATA_VIEW_HPP
+#define PROTOZERO_DATA_VIEW_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file data_view.hpp
+ *
+ * @brief Contains the implementation of the data_view class.
+ */
+
+#include "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
diff --git a/ext/protozero/include/protozero/exception.hpp b/ext/protozero/include/protozero/exception.hpp
new file mode 100644 (file)
index 0000000..a3cd0f1
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef PROTOZERO_EXCEPTION_HPP
+#define PROTOZERO_EXCEPTION_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file exception.hpp
+ *
+ * @brief Contains the exceptions used in the protozero library.
+ */
+
+#include <exception>
+
+/**
+ * @brief All parts of the protozero header-only library are in this namespace.
+ */
+namespace protozero {
+
+/**
+ * All exceptions explicitly thrown by the functions of the protozero library
+ * derive from this exception.
+ */
+struct exception : std::exception {
+    /// Returns the explanatory string.
+    const char* what() const noexcept override {
+        return "pbf exception";
+    }
+};
+
+/**
+ * This exception is thrown when parsing a varint thats larger than allowed.
+ * This should never happen unless the data is corrupted.
+ */
+struct varint_too_long_exception : exception {
+    /// Returns the explanatory string.
+    const char* what() const noexcept override {
+        return "varint too long exception";
+    }
+};
+
+/**
+ * This exception is thrown when the wire type of a pdf field is unknown.
+ * This should never happen unless the data is corrupted.
+ */
+struct unknown_pbf_wire_type_exception : exception {
+    /// Returns the explanatory string.
+    const char* what() const noexcept override {
+        return "unknown pbf field type exception";
+    }
+};
+
+/**
+ * This exception is thrown when we are trying to read a field and there
+ * are not enough bytes left in the buffer to read it. Almost all functions
+ * of the pbf_reader class can throw this exception.
+ *
+ * This should never happen unless the data is corrupted or you have
+ * initialized the pbf_reader object with incomplete data.
+ */
+struct end_of_buffer_exception : exception {
+    /// Returns the explanatory string.
+    const char* what() const noexcept override {
+        return "end of buffer exception";
+    }
+};
+
+/**
+ * This exception is thrown when a tag has an invalid value. Tags must be
+ * unsigned integers between 1 and 2^29-1. Tags between 19000 and 19999 are
+ * not allowed. See
+ * https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
+ */
+struct invalid_tag_exception : exception {
+    /// Returns the explanatory string.
+    const char* what() const noexcept override {
+        return "invalid tag exception";
+    }
+};
+
+/**
+ * This exception is thrown when a length field of a packed repeated field is
+ * invalid. For fixed size types the length must be a multiple of the size of
+ * the type.
+ */
+struct invalid_length_exception : exception {
+    /// Returns the explanatory string.
+    const char* what() const noexcept override {
+        return "invalid length exception";
+    }
+};
+
+} // end namespace protozero
+
+#endif // PROTOZERO_EXCEPTION_HPP
diff --git a/ext/protozero/include/protozero/iterators.hpp b/ext/protozero/include/protozero/iterators.hpp
new file mode 100644 (file)
index 0000000..ee8ef8e
--- /dev/null
@@ -0,0 +1,481 @@
+#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
diff --git a/ext/protozero/include/protozero/pbf_builder.hpp b/ext/protozero/include/protozero/pbf_builder.hpp
new file mode 100644 (file)
index 0000000..71a2dec
--- /dev/null
@@ -0,0 +1,32 @@
+#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
diff --git a/ext/protozero/include/protozero/pbf_message.hpp b/ext/protozero/include/protozero/pbf_message.hpp
new file mode 100644 (file)
index 0000000..d7fd8b5
--- /dev/null
@@ -0,0 +1,184 @@
+#ifndef PROTOZERO_PBF_MESSAGE_HPP
+#define PROTOZERO_PBF_MESSAGE_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file pbf_message.hpp
+ *
+ * @brief Contains the pbf_message template class.
+ */
+
+#include "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
diff --git a/ext/protozero/include/protozero/pbf_reader.hpp b/ext/protozero/include/protozero/pbf_reader.hpp
new file mode 100644 (file)
index 0000000..92bfdee
--- /dev/null
@@ -0,0 +1,977 @@
+#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
diff --git a/ext/protozero/include/protozero/pbf_writer.hpp b/ext/protozero/include/protozero/pbf_writer.hpp
new file mode 100644 (file)
index 0000000..9a07bd5
--- /dev/null
@@ -0,0 +1,76 @@
+#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
diff --git a/ext/protozero/include/protozero/types.hpp b/ext/protozero/include/protozero/types.hpp
new file mode 100644 (file)
index 0000000..3aefddf
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef PROTOZERO_TYPES_HPP
+#define PROTOZERO_TYPES_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file types.hpp
+ *
+ * @brief Contains the declaration of low-level types used in the pbf format.
+ */
+
+#include "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
diff --git a/ext/protozero/include/protozero/varint.hpp b/ext/protozero/include/protozero/varint.hpp
new file mode 100644 (file)
index 0000000..b4648a4
--- /dev/null
@@ -0,0 +1,245 @@
+#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
diff --git a/ext/protozero/include/protozero/version.hpp b/ext/protozero/include/protozero/version.hpp
new file mode 100644 (file)
index 0000000..9a0e4cc
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef PROTOZERO_VERSION_HPP
+#define PROTOZERO_VERSION_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file version.hpp
+ *
+ * @brief Contains macros defining the protozero version.
+ */
+
+/// The major version number
+#define PROTOZERO_VERSION_MAJOR 1
+
+/// The minor version number
+#define PROTOZERO_VERSION_MINOR 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
index 49fc8bcf50225e3525c5a4e1ef8ee7db402b0dc3..f5df7ed3d90d208f17b79187ebb858b5b761b181 100644 (file)
@@ -78,19 +78,51 @@ desired version. e.g.:
 (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.
+
index 483a39b7c3d2eac913d94bb6618902fce1998cb2..50d7880d50d8cfd5182defe7fb780d20b4f5f3fd 100644 (file)
@@ -75,3 +75,34 @@ I have a good idea for a feature!
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 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.