summaryrefslogtreecommitdiffstats
path: root/ext/protozero/include
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:34:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:34:30 +0000
commit4fc2f55f761d71aae1f145d5aa94ba929cc39676 (patch)
tree5c1e1db3b46dd4edbe11f612d93cb94b96891ce3 /ext/protozero/include
parentInitial commit. (diff)
downloaddnsdist-upstream/1.7.3.tar.xz
dnsdist-upstream/1.7.3.zip
Adding upstream version 1.7.3.upstream/1.7.3upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ext/protozero/include')
-rw-r--r--ext/protozero/include/protozero/basic_pbf_builder.hpp266
-rw-r--r--ext/protozero/include/protozero/basic_pbf_writer.hpp1054
-rw-r--r--ext/protozero/include/protozero/buffer_fixed.hpp222
-rw-r--r--ext/protozero/include/protozero/buffer_string.hpp76
-rw-r--r--ext/protozero/include/protozero/buffer_tmpl.hpp113
-rw-r--r--ext/protozero/include/protozero/buffer_vector.hpp76
-rw-r--r--ext/protozero/include/protozero/byteswap.hpp99
-rw-r--r--ext/protozero/include/protozero/config.hpp48
-rw-r--r--ext/protozero/include/protozero/data_view.hpp236
-rw-r--r--ext/protozero/include/protozero/exception.hpp101
-rw-r--r--ext/protozero/include/protozero/iterators.hpp481
-rw-r--r--ext/protozero/include/protozero/pbf_builder.hpp32
-rw-r--r--ext/protozero/include/protozero/pbf_message.hpp184
-rw-r--r--ext/protozero/include/protozero/pbf_reader.hpp977
-rw-r--r--ext/protozero/include/protozero/pbf_writer.hpp76
-rw-r--r--ext/protozero/include/protozero/types.hpp66
-rw-r--r--ext/protozero/include/protozero/varint.hpp245
-rw-r--r--ext/protozero/include/protozero/version.hpp34
18 files changed, 4386 insertions, 0 deletions
diff --git a/ext/protozero/include/protozero/basic_pbf_builder.hpp b/ext/protozero/include/protozero/basic_pbf_builder.hpp
new file mode 100644
index 0000000..0ede726
--- /dev/null
+++ b/ext/protozero/include/protozero/basic_pbf_builder.hpp
@@ -0,0 +1,266 @@
+#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP
+#define PROTOZERO_BASIC_PBF_BUILDER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file basic_pbf_builder.hpp
+ *
+ * @brief Contains the basic_pbf_builder template class.
+ */
+
+#include "basic_pbf_writer.hpp"
+#include "types.hpp"
+
+#include <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
index 0000000..f167c4d
--- /dev/null
+++ b/ext/protozero/include/protozero/basic_pbf_writer.hpp
@@ -0,0 +1,1054 @@
+#ifndef PROTOZERO_BASIC_PBF_WRITER_HPP
+#define PROTOZERO_BASIC_PBF_WRITER_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file basic_pbf_writer.hpp
+ *
+ * @brief Contains the basic_pbf_writer template class.
+ */
+
+#include "buffer_tmpl.hpp"
+#include "config.hpp"
+#include "data_view.hpp"
+#include "types.hpp"
+#include "varint.hpp"
+
+#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
+# include <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
index 0000000..b2e6d1d
--- /dev/null
+++ b/ext/protozero/include/protozero/buffer_fixed.hpp
@@ -0,0 +1,222 @@
+#ifndef PROTOZERO_BUFFER_FIXED_HPP
+#define PROTOZERO_BUFFER_FIXED_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_fixed.hpp
+ *
+ * @brief Contains the fixed_size_buffer_adaptor class.
+ */
+
+#include "buffer_tmpl.hpp"
+#include "config.hpp"
+
+#include <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
index 0000000..1f0f902
--- /dev/null
+++ b/ext/protozero/include/protozero/buffer_string.hpp
@@ -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
index 0000000..ac22399
--- /dev/null
+++ b/ext/protozero/include/protozero/buffer_tmpl.hpp
@@ -0,0 +1,113 @@
+#ifndef PROTOZERO_BUFFER_TMPL_HPP
+#define PROTOZERO_BUFFER_TMPL_HPP
+
+/*****************************************************************************
+
+protozero - Minimalistic protocol buffer decoder and encoder in C++.
+
+This file is from https://github.com/mapbox/protozero where you can find more
+documentation.
+
+*****************************************************************************/
+
+/**
+ * @file buffer_tmpl.hpp
+ *
+ * @brief Contains the customization points for buffer implementations.
+ */
+
+#include <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
index 0000000..6a34b07
--- /dev/null
+++ b/ext/protozero/include/protozero/buffer_vector.hpp
@@ -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
index 0000000..799d179
--- /dev/null
+++ b/ext/protozero/include/protozero/byteswap.hpp
@@ -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
index 0000000..6fc7749
--- /dev/null
+++ b/ext/protozero/include/protozero/config.hpp
@@ -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
index 0000000..3ec87af
--- /dev/null
+++ b/ext/protozero/include/protozero/data_view.hpp
@@ -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
index 0000000..a3cd0f1
--- /dev/null
+++ b/ext/protozero/include/protozero/exception.hpp
@@ -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
index 0000000..ee8ef8e
--- /dev/null
+++ b/ext/protozero/include/protozero/iterators.hpp
@@ -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
index 0000000..71a2dec
--- /dev/null
+++ b/ext/protozero/include/protozero/pbf_builder.hpp
@@ -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
index 0000000..d7fd8b5
--- /dev/null
+++ b/ext/protozero/include/protozero/pbf_message.hpp
@@ -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
index 0000000..92bfdee
--- /dev/null
+++ b/ext/protozero/include/protozero/pbf_reader.hpp
@@ -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
index 0000000..9a07bd5
--- /dev/null
+++ b/ext/protozero/include/protozero/pbf_writer.hpp
@@ -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
index 0000000..3aefddf
--- /dev/null
+++ b/ext/protozero/include/protozero/types.hpp
@@ -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
index 0000000..b4648a4
--- /dev/null
+++ b/ext/protozero/include/protozero/varint.hpp
@@ -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
index 0000000..9a0e4cc
--- /dev/null
+++ b/ext/protozero/include/protozero/version.hpp
@@ -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