Merge commit '6eb4f090f98f6b17a23c57768c16b7716b6c9cbd' as 'third_party/libosmium'
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
#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 <cassert>
|
||||
#include <cstdint>
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
|
||||
namespace protozero {
|
||||
namespace detail {
|
||||
|
||||
inline uint32_t byteswap_impl(uint32_t value) noexcept {
|
||||
#ifdef PROTOZERO_USE_BUILTIN_BSWAP
|
||||
return __builtin_bswap32(value);
|
||||
#else
|
||||
return ((value & 0xff000000) >> 24) |
|
||||
((value & 0x00ff0000) >> 8) |
|
||||
((value & 0x0000ff00) << 8) |
|
||||
((value & 0x000000ff) << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint64_t byteswap_impl(uint64_t value) noexcept {
|
||||
#ifdef PROTOZERO_USE_BUILTIN_BSWAP
|
||||
return __builtin_bswap64(value);
|
||||
#else
|
||||
return ((value & 0xff00000000000000ULL) >> 56) |
|
||||
((value & 0x00ff000000000000ULL) >> 40) |
|
||||
((value & 0x0000ff0000000000ULL) >> 24) |
|
||||
((value & 0x000000ff00000000ULL) >> 8) |
|
||||
((value & 0x00000000ff000000ULL) << 8) |
|
||||
((value & 0x0000000000ff0000ULL) << 24) |
|
||||
((value & 0x000000000000ff00ULL) << 40) |
|
||||
((value & 0x00000000000000ffULL) << 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(uint32_t* ptr) noexcept {
|
||||
*ptr = byteswap_impl(*ptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(uint64_t* ptr) noexcept {
|
||||
*ptr = byteswap_impl(*ptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(int32_t* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint32_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(int64_t* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint64_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(float* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint32_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
inline void byteswap_inplace(double* ptr) noexcept {
|
||||
auto bptr = reinterpret_cast<uint64_t*>(ptr);
|
||||
*bptr = byteswap_impl(*bptr);
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_BYTESWAP_HPP
|
||||
@@ -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
|
||||
@@ -0,0 +1,68 @@
|
||||
#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"; }
|
||||
};
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_EXCEPTION_HPP
|
||||
@@ -0,0 +1,328 @@
|
||||
#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 <cstring>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
# include <protozero/byteswap.hpp>
|
||||
#endif
|
||||
|
||||
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 or range.
|
||||
* @param last_iterator Iterator to end or 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.
|
||||
constexpr std::size_t empty() const noexcept {
|
||||
return 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;
|
||||
|
||||
/// Pointer to end iterator position
|
||||
const char* m_end;
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
const_fixed_iterator() noexcept :
|
||||
m_data(nullptr),
|
||||
m_end(nullptr) {
|
||||
}
|
||||
|
||||
const_fixed_iterator(const char* data, const char* end) noexcept :
|
||||
m_data(data),
|
||||
m_end(end) {
|
||||
}
|
||||
|
||||
const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
|
||||
const_fixed_iterator(const_fixed_iterator&&) noexcept = default;
|
||||
|
||||
const_fixed_iterator& operator=(const const_fixed_iterator&) noexcept = default;
|
||||
const_fixed_iterator& operator=(const_fixed_iterator&&) noexcept = default;
|
||||
|
||||
~const_fixed_iterator() noexcept = default;
|
||||
|
||||
value_type operator*() const {
|
||||
value_type result;
|
||||
std::memcpy(&result, m_data, sizeof(value_type));
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
detail::byteswap_inplace(&result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
const_fixed_iterator& operator++() {
|
||||
m_data += sizeof(value_type);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_fixed_iterator operator++(int) {
|
||||
const const_fixed_iterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_fixed_iterator& rhs) const noexcept {
|
||||
return m_data == rhs.m_data && m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
bool operator!=(const const_fixed_iterator& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
}; // 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;
|
||||
|
||||
/// Pointer to end iterator position
|
||||
const char* m_end;
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
const_varint_iterator() noexcept :
|
||||
m_data(nullptr),
|
||||
m_end(nullptr) {
|
||||
}
|
||||
|
||||
const_varint_iterator(const char* data, const char* end) noexcept :
|
||||
m_data(data),
|
||||
m_end(end) {
|
||||
}
|
||||
|
||||
const_varint_iterator(const const_varint_iterator&) noexcept = default;
|
||||
const_varint_iterator(const_varint_iterator&&) noexcept = default;
|
||||
|
||||
const_varint_iterator& operator=(const const_varint_iterator&) noexcept = default;
|
||||
const_varint_iterator& operator=(const_varint_iterator&&) noexcept = default;
|
||||
|
||||
~const_varint_iterator() noexcept = default;
|
||||
|
||||
value_type operator*() const {
|
||||
const char* d = m_data; // will be thrown away
|
||||
return static_cast<value_type>(decode_varint(&d, m_end));
|
||||
}
|
||||
|
||||
const_varint_iterator& operator++() {
|
||||
skip_varint(&m_data, m_end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_varint_iterator operator++(int) {
|
||||
const const_varint_iterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_varint_iterator& rhs) const noexcept {
|
||||
return m_data == rhs.m_data && m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
bool operator!=(const const_varint_iterator& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
}; // class const_varint_iterator
|
||||
|
||||
/**
|
||||
* A forward iterator used for accessing packed repeated svarint fields
|
||||
* (sint32, sint64).
|
||||
*/
|
||||
template <typename T>
|
||||
class const_svarint_iterator : public const_varint_iterator<T> {
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
const_svarint_iterator() noexcept :
|
||||
const_varint_iterator<T>() {
|
||||
}
|
||||
|
||||
const_svarint_iterator(const char* data, const char* end) noexcept :
|
||||
const_varint_iterator<T>(data, end) {
|
||||
}
|
||||
|
||||
const_svarint_iterator(const const_svarint_iterator&) = default;
|
||||
const_svarint_iterator(const_svarint_iterator&&) = default;
|
||||
|
||||
const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
|
||||
const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
|
||||
|
||||
~const_svarint_iterator() = default;
|
||||
|
||||
value_type operator*() const {
|
||||
const char* d = this->m_data; // will be thrown away
|
||||
return static_cast<value_type>(decode_zigzag64(decode_varint(&d, this->m_end)));
|
||||
}
|
||||
|
||||
const_svarint_iterator& operator++() {
|
||||
skip_varint(&this->m_data, this->m_end);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_svarint_iterator operator++(int) {
|
||||
const const_svarint_iterator tmp(*this);
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
}; // class const_svarint_iterator
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_ITERATORS_HPP
|
||||
@@ -0,0 +1,160 @@
|
||||
#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 <type_traits>
|
||||
|
||||
#include <protozero/pbf_writer.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
namespace protozero {
|
||||
|
||||
/**
|
||||
* The pbf_builder is used to write PBF formatted messages into a buffer. It
|
||||
* is based on the pbf_writer class and has all the same methods. The
|
||||
* difference is that while the pbf_writer class takes an integer tag,
|
||||
* this template class takes a tag of the template type T. The idea is that
|
||||
* T will be an enumeration value and this helps reduce the possibility of
|
||||
* programming errors.
|
||||
*
|
||||
* Almost all methods in this class can throw an std::bad_alloc exception if
|
||||
* the std::string used as a buffer wants to resize.
|
||||
*
|
||||
* Read the tutorial to understand how this class is used.
|
||||
*/
|
||||
template <typename T>
|
||||
class pbf_builder : public pbf_writer {
|
||||
|
||||
static_assert(std::is_same<pbf_tag_type, typename std::underlying_type<T>::type>::value,
|
||||
"T must be enum with underlying type protozero::pbf_tag_type");
|
||||
|
||||
public:
|
||||
|
||||
using enum_type = T;
|
||||
|
||||
explicit pbf_builder(std::string& data) noexcept :
|
||||
pbf_writer(data) {
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
pbf_builder(pbf_writer& parent_writer, P tag) noexcept :
|
||||
pbf_writer(parent_writer, pbf_tag_type(tag)) {
|
||||
}
|
||||
|
||||
/// @cond INTERNAL
|
||||
#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \
|
||||
void add_##name(T tag, type value) { \
|
||||
pbf_writer::add_##name(pbf_tag_type(tag), value); \
|
||||
}
|
||||
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float)
|
||||
PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double)
|
||||
|
||||
#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
|
||||
/// @endcond
|
||||
|
||||
void add_bytes(T tag, const char* value, std::size_t size) {
|
||||
pbf_writer::add_bytes(pbf_tag_type(tag), value, size);
|
||||
}
|
||||
|
||||
void add_bytes(T tag, const data_view& value) {
|
||||
pbf_writer::add_bytes(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
void add_bytes(T tag, const std::string& value) {
|
||||
pbf_writer::add_bytes(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
void add_bytes(T tag, const char* value) {
|
||||
pbf_writer::add_bytes(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
void add_bytes_vectored(T tag, Ts&&... values) {
|
||||
pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward<Ts>(values)...);
|
||||
}
|
||||
|
||||
void add_string(T tag, const char* value, std::size_t size) {
|
||||
pbf_writer::add_string(pbf_tag_type(tag), value, size);
|
||||
}
|
||||
|
||||
void add_string(T tag, const data_view& value) {
|
||||
pbf_writer::add_string(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
void add_string(T tag, const std::string& value) {
|
||||
pbf_writer::add_string(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
void add_string(T tag, const char* value) {
|
||||
pbf_writer::add_string(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
void add_message(T tag, const char* value, std::size_t size) {
|
||||
pbf_writer::add_message(pbf_tag_type(tag), value, size);
|
||||
}
|
||||
|
||||
void add_message(T tag, const data_view& value) {
|
||||
pbf_writer::add_message(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
void add_message(T tag, const std::string& value) {
|
||||
pbf_writer::add_message(pbf_tag_type(tag), value);
|
||||
}
|
||||
|
||||
/// @cond INTERNAL
|
||||
#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \
|
||||
template <typename InputIterator> \
|
||||
void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
|
||||
pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \
|
||||
}
|
||||
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(bool)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(enum)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(int32)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(int64)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(float)
|
||||
PROTOZERO_WRITER_WRAP_ADD_PACKED(double)
|
||||
|
||||
#undef PROTOZERO_WRITER_WRAP_ADD_PACKED
|
||||
/// @endcond
|
||||
|
||||
}; // class pbf_builder
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_PBF_BUILDER_HPP
|
||||
@@ -0,0 +1,98 @@
|
||||
#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 <type_traits>
|
||||
|
||||
#include <protozero/pbf_reader.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
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:
|
||||
|
||||
using enum_type = T;
|
||||
|
||||
template <typename... Args>
|
||||
pbf_message(Args&&... args) noexcept :
|
||||
pbf_reader(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
bool next() {
|
||||
return pbf_reader::next();
|
||||
}
|
||||
|
||||
bool next(T next_tag) {
|
||||
return pbf_reader::next(pbf_tag_type(next_tag));
|
||||
}
|
||||
|
||||
bool next(T next_tag, pbf_wire_type type) {
|
||||
return pbf_reader::next(pbf_tag_type(next_tag), type);
|
||||
}
|
||||
|
||||
T tag() const noexcept {
|
||||
return T(pbf_reader::tag());
|
||||
}
|
||||
|
||||
}; // class pbf_message
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_PBF_MESSAGE_HPP
|
||||
@@ -0,0 +1,952 @@
|
||||
#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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
#include <protozero/exception.hpp>
|
||||
#include <protozero/iterators.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
# include <protozero/byteswap.hpp>
|
||||
#endif
|
||||
|
||||
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;
|
||||
skip_bytes(sizeof(T));
|
||||
std::memcpy(&result, m_data - sizeof(T), sizeof(T));
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
detail::byteswap_inplace(&result);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
iterator_range<const_fixed_iterator<T>> packed_fixed() {
|
||||
protozero_assert(tag() != 0 && "call next() before accessing field value");
|
||||
const auto len = get_len_and_skip();
|
||||
protozero_assert(len % sizeof(T) == 0);
|
||||
return iterator_range<const_fixed_iterator<T>>{const_fixed_iterator<T>(m_data - len, m_data),
|
||||
const_fixed_iterator<T>(m_data, m_data)};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_varint() {
|
||||
return static_cast<T>(decode_varint(&m_data, m_end));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_svarint() {
|
||||
protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
|
||||
return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
|
||||
}
|
||||
|
||||
pbf_length_type get_length() {
|
||||
return get_varint<pbf_length_type>();
|
||||
}
|
||||
|
||||
void skip_bytes(pbf_length_type len) {
|
||||
if (m_data + len > m_end) {
|
||||
throw end_of_buffer_exception();
|
||||
}
|
||||
m_data += len;
|
||||
|
||||
// In debug builds reset the tag to zero so that we can detect (some)
|
||||
// wrong code.
|
||||
#ifndef NDEBUG
|
||||
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 iterator_range<T>{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()),
|
||||
m_wire_type(pbf_wire_type::unknown),
|
||||
m_tag(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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),
|
||||
m_wire_type(pbf_wire_type::unknown),
|
||||
m_tag(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
|
||||
: m_data(data.first),
|
||||
m_end(data.first + data.second),
|
||||
m_wire_type(pbf_wire_type::unknown),
|
||||
m_tag(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()),
|
||||
m_wire_type(pbf_wire_type::unknown),
|
||||
m_tag(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
return m_data < m_end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length in bytes of the current message. If you have
|
||||
* already called next() and/or any of the get_*() functions, this will
|
||||
* return the remaining length.
|
||||
*
|
||||
* This can, for instance, be used to estimate the space needed for a
|
||||
* buffer. Of course you have to know reasonably well what data to expect
|
||||
* and how it is encoded for this number to have any meaning.
|
||||
*/
|
||||
std::size_t length() const noexcept {
|
||||
return std::size_t(m_end - m_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set next field in the message as the current field. This is usually
|
||||
* called in a while loop:
|
||||
*
|
||||
* @code
|
||||
* pbf_reader message(...);
|
||||
* while (message.next()) {
|
||||
* // handle field
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @returns `true` if there is a next field, `false` if not.
|
||||
* @pre There must be no current field.
|
||||
* @post If it returns `true` there is a current field now.
|
||||
*/
|
||||
bool next() {
|
||||
if (m_data == m_end) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto value = get_varint<uint32_t>();
|
||||
m_tag = pbf_tag_type(value >> 3);
|
||||
|
||||
// tags 0 and 19000 to 19999 are not allowed as per
|
||||
// https://developers.google.com/protocol-buffers/docs/proto
|
||||
protozero_assert(((m_tag > 0 && m_tag < 19000) ||
|
||||
(m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
|
||||
|
||||
m_wire_type = pbf_wire_type(value & 0x07);
|
||||
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;
|
||||
} else {
|
||||
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;
|
||||
} else {
|
||||
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:
|
||||
protozero_assert(false && "can not be here because next() should have thrown already");
|
||||
}
|
||||
}
|
||||
|
||||
///@{
|
||||
/**
|
||||
* @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");
|
||||
protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
|
||||
skip_bytes(1);
|
||||
return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 data_view{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 std::make_pair(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>;
|
||||
|
||||
///@{
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
|
||||
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.
|
||||
*/
|
||||
auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
|
||||
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.
|
||||
*/
|
||||
auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
|
||||
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.
|
||||
*/
|
||||
auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
|
||||
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.
|
||||
*/
|
||||
auto get_packed_float() -> decltype(packed_fixed<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.
|
||||
*/
|
||||
auto get_packed_double() -> decltype(packed_fixed<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
|
||||
@@ -0,0 +1,975 @@
|
||||
#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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
#include <protozero/types.hpp>
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
# include <protozero/byteswap.hpp>
|
||||
#endif
|
||||
|
||||
namespace protozero {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T> class packed_field_varint;
|
||||
template <typename T> class packed_field_svarint;
|
||||
template <typename T> class packed_field_fixed;
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/**
|
||||
* The pbf_writer is used to write PBF formatted messages into a buffer.
|
||||
*
|
||||
* Almost all methods in this class can throw an std::bad_alloc exception if
|
||||
* the std::string used as a buffer wants to resize.
|
||||
*/
|
||||
class pbf_writer {
|
||||
|
||||
// A pointer to a string buffer holding the data already written to the
|
||||
// PBF message. For default constructed writers or writers that have been
|
||||
// rolled back, this is a nullptr.
|
||||
std::string* m_data;
|
||||
|
||||
// A pointer to a parent writer object if this is a submessage. If this
|
||||
// is a top-level writer, it is a nullptr.
|
||||
pbf_writer* m_parent_writer;
|
||||
|
||||
// This is usually 0. If there is an open submessage, this is set in the
|
||||
// parent to the rollback position, ie. the last position before the
|
||||
// submessage was started. This is the position where the header of the
|
||||
// submessage starts.
|
||||
std::size_t m_rollback_pos = 0;
|
||||
|
||||
// This is usually 0. If there is an open submessage, this is set in the
|
||||
// parent to the position where the data of the submessage is written to.
|
||||
std::size_t m_pos = 0;
|
||||
|
||||
void add_varint(uint64_t value) {
|
||||
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
|
||||
protozero_assert(m_data);
|
||||
write_varint(std::back_inserter(*m_data), value);
|
||||
}
|
||||
|
||||
void add_field(pbf_tag_type tag, pbf_wire_type type) {
|
||||
protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
|
||||
const uint32_t b = (tag << 3) | 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 pbf_writer if there is an existing pbf_writer for a submessage");
|
||||
protozero_assert(m_data);
|
||||
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
|
||||
detail::byteswap_inplace(&value);
|
||||
#endif
|
||||
m_data->append(reinterpret_cast<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) {
|
||||
if (first == last) {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pbf_writer sw(*this, tag);
|
||||
|
||||
while (first != last) {
|
||||
sw.add_varint(encode_zigzag64(*first++));
|
||||
}
|
||||
}
|
||||
|
||||
// The number of bytes to reserve for the varint holding the length of
|
||||
// a length-delimited field. The length has to fit into pbf_length_type,
|
||||
// and a varint needs 8 bit for every 7 bit.
|
||||
enum constant_reserve_bytes : int {
|
||||
reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1
|
||||
};
|
||||
|
||||
// If m_rollpack_pos is set to this special value, it means that when
|
||||
// the submessage is closed, nothing needs to be done, because the length
|
||||
// of the submessage has already been written correctly.
|
||||
enum constant_size_is_known : std::size_t {
|
||||
size_is_known = std::numeric_limits<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 = m_data->size();
|
||||
add_field(tag, pbf_wire_type::length_delimited);
|
||||
m_data->append(std::size_t(reserve_bytes), '\0');
|
||||
} else {
|
||||
m_rollback_pos = size_is_known;
|
||||
add_length_varint(tag, pbf_length_type(size));
|
||||
reserve(size);
|
||||
}
|
||||
m_pos = m_data->size();
|
||||
}
|
||||
|
||||
void rollback_submessage() {
|
||||
protozero_assert(m_pos != 0);
|
||||
protozero_assert(m_rollback_pos != size_is_known);
|
||||
protozero_assert(m_data);
|
||||
m_data->resize(m_rollback_pos);
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
void commit_submessage() {
|
||||
protozero_assert(m_pos != 0);
|
||||
protozero_assert(m_rollback_pos != size_is_known);
|
||||
protozero_assert(m_data);
|
||||
const auto length = pbf_length_type(m_data->size() - m_pos);
|
||||
|
||||
protozero_assert(m_data->size() >= m_pos - reserve_bytes);
|
||||
const auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
|
||||
|
||||
m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
|
||||
m_pos = 0;
|
||||
}
|
||||
|
||||
void close_submessage() {
|
||||
protozero_assert(m_data);
|
||||
if (m_pos == 0 || m_rollback_pos == size_is_known) {
|
||||
return;
|
||||
}
|
||||
if (m_data->size() - m_pos == 0) {
|
||||
rollback_submessage();
|
||||
} else {
|
||||
commit_submessage();
|
||||
}
|
||||
}
|
||||
|
||||
void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
|
||||
add_field(tag, pbf_wire_type::length_delimited);
|
||||
add_varint(length);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Create a writer using the given string as a data store. The pbf_writer
|
||||
* stores a reference to that string and adds all data to it. The string
|
||||
* doesn't have to be empty. The pbf_writer will just append data.
|
||||
*/
|
||||
explicit pbf_writer(std::string& data) noexcept :
|
||||
m_data(&data),
|
||||
m_parent_writer(nullptr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a writer without a data store. In this form the writer can not
|
||||
* be used!
|
||||
*/
|
||||
pbf_writer() noexcept :
|
||||
m_data(nullptr),
|
||||
m_parent_writer(nullptr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a pbf_writer for a submessage from the pbf_writer of the
|
||||
* parent message.
|
||||
*
|
||||
* @param parent_writer The pbf_writer
|
||||
* @param tag Tag (field number) of the field that will be written
|
||||
* @param size Optional size of the submessage in bytes (use 0 for unknown).
|
||||
* Setting this allows some optimizations but is only possible in
|
||||
* a few very specific cases.
|
||||
*/
|
||||
pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
|
||||
m_data(parent_writer.m_data),
|
||||
m_parent_writer(&parent_writer) {
|
||||
m_parent_writer->open_submessage(tag, size);
|
||||
}
|
||||
|
||||
/// A pbf_writer object can be copied
|
||||
pbf_writer(const pbf_writer&) noexcept = default;
|
||||
|
||||
/// A pbf_writer object can be copied
|
||||
pbf_writer& operator=(const pbf_writer&) noexcept = default;
|
||||
|
||||
/// A pbf_writer object can be moved
|
||||
pbf_writer(pbf_writer&&) noexcept = default;
|
||||
|
||||
/// A pbf_writer object can be moved
|
||||
pbf_writer& operator=(pbf_writer&&) noexcept = default;
|
||||
|
||||
~pbf_writer() {
|
||||
if (m_parent_writer) {
|
||||
m_parent_writer->close_submessage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap the contents of this object with the other.
|
||||
*
|
||||
* @param other Other object to swap data with.
|
||||
*/
|
||||
void swap(pbf_writer& other) noexcept {
|
||||
using std::swap;
|
||||
swap(m_data, other.m_data);
|
||||
swap(m_parent_writer, other.m_parent_writer);
|
||||
swap(m_rollback_pos, other.m_rollback_pos);
|
||||
swap(m_pos, other.m_pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve size bytes in the underlying message store in addition to
|
||||
* whatever the message store already holds. So unlike
|
||||
* the `std::string::reserve()` method this is not an absolute size,
|
||||
* but additional memory that should be reserved.
|
||||
*
|
||||
* @param size Number of bytes to reserve in underlying message store.
|
||||
*/
|
||||
void reserve(std::size_t size) {
|
||||
protozero_assert(m_data);
|
||||
m_data->reserve(m_data->size() + size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel writing of this submessage. The complete submessage will be
|
||||
* removed as if it was never created and no fields were added.
|
||||
*
|
||||
* @pre Must be a pbf_writer of a submessage, ie one opened with the
|
||||
* pbf_writer constructor taking a parent message.
|
||||
*/
|
||||
void rollback() {
|
||||
protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent");
|
||||
protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage");
|
||||
m_parent_writer->rollback_submessage();
|
||||
m_data = nullptr;
|
||||
}
|
||||
|
||||
///@{
|
||||
/**
|
||||
* @name Scalar field writer functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add "bool" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written
|
||||
*/
|
||||
void add_bool(pbf_tag_type tag, bool value) {
|
||||
add_field(tag, pbf_wire_type::varint);
|
||||
protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
|
||||
protozero_assert(m_data);
|
||||
m_data->append(1, 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 pbf_writer if there is an existing 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));
|
||||
m_data->append(value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "bytes" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written
|
||||
*/
|
||||
void add_bytes(pbf_tag_type tag, const data_view& value) {
|
||||
add_bytes(tag, value.data(), value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "bytes" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written
|
||||
*/
|
||||
void add_bytes(pbf_tag_type tag, const std::string& value) {
|
||||
add_bytes(tag, value.data(), value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "bytes" field to data. Bytes from the value are written until
|
||||
* a null byte is encountered. The null byte is not added.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Pointer to zero-delimited value to be written
|
||||
*/
|
||||
void add_bytes(pbf_tag_type tag, const char* value) {
|
||||
add_bytes(tag, value, std::strlen(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "bytes" field to data using vectored input. All the data in the
|
||||
* 2nd and further arguments is "concatenated" with only a single copy
|
||||
* into the final buffer.
|
||||
*
|
||||
* This will work with objects of any type supporting the data() and
|
||||
* size() methods like std::string or protozero::data_view.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* std::string data1 = "abc";
|
||||
* std::string data2 = "xyz";
|
||||
* writer.add_bytes_vectored(1, data1, data2);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Ts List of types supporting data() and size() methods.
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param values List of objects of types Ts with data to be appended.
|
||||
*/
|
||||
template <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 pbf_writer if there is an existing pbf_writer for a submessage");
|
||||
protozero_assert(m_data);
|
||||
size_t sum_size = 0;
|
||||
(void)std::initializer_list<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));
|
||||
m_data->reserve(m_data->size() + sum_size);
|
||||
(void)std::initializer_list<int>{(m_data->append(values.data(), values.size()), 0)...};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "string" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Pointer to value to be written
|
||||
* @param size Number of bytes to be written
|
||||
*/
|
||||
void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
|
||||
add_bytes(tag, value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "string" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written
|
||||
*/
|
||||
void add_string(pbf_tag_type tag, const data_view& value) {
|
||||
add_bytes(tag, value.data(), value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "string" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written
|
||||
*/
|
||||
void add_string(pbf_tag_type tag, const std::string& value) {
|
||||
add_bytes(tag, value.data(), value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "string" field to data. Bytes from the value are written until
|
||||
* a null byte is encountered. The null byte is not added.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Pointer to value to be written
|
||||
*/
|
||||
void add_string(pbf_tag_type tag, const char* value) {
|
||||
add_bytes(tag, value, std::strlen(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "message" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Pointer to message to be written
|
||||
* @param size Length of the message
|
||||
*/
|
||||
void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
|
||||
add_bytes(tag, value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "message" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written. The value must be a complete message.
|
||||
*/
|
||||
void add_message(pbf_tag_type tag, const data_view& value) {
|
||||
add_bytes(tag, value.data(), value.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "message" field to data.
|
||||
*
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param value Value to be written. The value must be a complete message.
|
||||
*/
|
||||
void add_message(pbf_tag_type tag, const std::string& value) {
|
||||
add_bytes(tag, value.data(), value.size());
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
///@{
|
||||
/**
|
||||
* @name Repeated packed field writer functions
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add "repeated packed bool" field to data.
|
||||
*
|
||||
* @tparam InputIterator A type satisfying the InputIterator concept.
|
||||
* Dereferencing the iterator must yield a type assignable to bool.
|
||||
* @param tag Tag (field number) of the field
|
||||
* @param first Iterator pointing to the beginning of the data
|
||||
* @param last Iterator pointing one past the end of data
|
||||
*/
|
||||
template <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 "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 T> friend class detail::packed_field_varint;
|
||||
template <typename T> friend class detail::packed_field_svarint;
|
||||
template <typename T> friend class detail::packed_field_fixed;
|
||||
|
||||
}; // class pbf_writer
|
||||
|
||||
/**
|
||||
* Swap two pbf_writer objects.
|
||||
*
|
||||
* @param lhs First object.
|
||||
* @param rhs Second object.
|
||||
*/
|
||||
inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
class packed_field {
|
||||
|
||||
protected:
|
||||
|
||||
pbf_writer m_writer;
|
||||
|
||||
public:
|
||||
|
||||
packed_field(const packed_field&) = delete;
|
||||
packed_field& operator=(const packed_field&) = delete;
|
||||
|
||||
packed_field(packed_field&&) = default;
|
||||
packed_field& operator=(packed_field&&) = default;
|
||||
|
||||
packed_field(pbf_writer& parent_writer, pbf_tag_type tag) :
|
||||
m_writer(parent_writer, tag) {
|
||||
}
|
||||
|
||||
packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
|
||||
m_writer(parent_writer, tag, size) {
|
||||
}
|
||||
|
||||
void rollback() {
|
||||
m_writer.rollback();
|
||||
}
|
||||
|
||||
}; // class packed_field
|
||||
|
||||
template <typename T>
|
||||
class packed_field_fixed : public packed_field {
|
||||
|
||||
public:
|
||||
|
||||
template <typename P>
|
||||
packed_field_fixed(pbf_writer& parent_writer, P tag) :
|
||||
packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
|
||||
}
|
||||
|
||||
template <typename P>
|
||||
packed_field_fixed(pbf_writer& parent_writer, P tag, std::size_t size) :
|
||||
packed_field(parent_writer, static_cast<pbf_tag_type>(tag), size * sizeof(T)) {
|
||||
}
|
||||
|
||||
void add_element(T value) {
|
||||
m_writer.add_fixed<T>(value);
|
||||
}
|
||||
|
||||
}; // class packed_field_fixed
|
||||
|
||||
template <typename T>
|
||||
class packed_field_varint : public packed_field {
|
||||
|
||||
public:
|
||||
|
||||
template <typename P>
|
||||
packed_field_varint(pbf_writer& parent_writer, P tag) :
|
||||
packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
|
||||
}
|
||||
|
||||
void add_element(T value) {
|
||||
m_writer.add_varint(uint64_t(value));
|
||||
}
|
||||
|
||||
}; // class packed_field_varint
|
||||
|
||||
template <typename T>
|
||||
class packed_field_svarint : public packed_field {
|
||||
|
||||
public:
|
||||
|
||||
template <typename P>
|
||||
packed_field_svarint(pbf_writer& parent_writer, P tag) :
|
||||
packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
|
||||
}
|
||||
|
||||
void add_element(T value) {
|
||||
m_writer.add_varint(encode_zigzag64(value));
|
||||
}
|
||||
|
||||
}; // class packed_field_svarint
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/// Class for generating packed repeated bool fields.
|
||||
using packed_field_bool = detail::packed_field_varint<bool>;
|
||||
|
||||
/// Class for generating packed repeated enum fields.
|
||||
using packed_field_enum = detail::packed_field_varint<int32_t>;
|
||||
|
||||
/// Class for generating packed repeated int32 fields.
|
||||
using packed_field_int32 = detail::packed_field_varint<int32_t>;
|
||||
|
||||
/// Class for generating packed repeated sint32 fields.
|
||||
using packed_field_sint32 = detail::packed_field_svarint<int32_t>;
|
||||
|
||||
/// Class for generating packed repeated uint32 fields.
|
||||
using packed_field_uint32 = detail::packed_field_varint<uint32_t>;
|
||||
|
||||
/// Class for generating packed repeated int64 fields.
|
||||
using packed_field_int64 = detail::packed_field_varint<int64_t>;
|
||||
|
||||
/// Class for generating packed repeated sint64 fields.
|
||||
using packed_field_sint64 = detail::packed_field_svarint<int64_t>;
|
||||
|
||||
/// Class for generating packed repeated uint64 fields.
|
||||
using packed_field_uint64 = detail::packed_field_varint<uint64_t>;
|
||||
|
||||
/// Class for generating packed repeated fixed32 fields.
|
||||
using packed_field_fixed32 = detail::packed_field_fixed<uint32_t>;
|
||||
|
||||
/// Class for generating packed repeated sfixed32 fields.
|
||||
using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
|
||||
|
||||
/// Class for generating packed repeated fixed64 fields.
|
||||
using packed_field_fixed64 = detail::packed_field_fixed<uint64_t>;
|
||||
|
||||
/// Class for generating packed repeated sfixed64 fields.
|
||||
using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
|
||||
|
||||
/// Class for generating packed repeated float fields.
|
||||
using packed_field_float = detail::packed_field_fixed<float>;
|
||||
|
||||
/// Class for generating packed repeated double fields.
|
||||
using packed_field_double = detail::packed_field_fixed<double>;
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_PBF_WRITER_HPP
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#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 <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/config.hpp>
|
||||
|
||||
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, embedded 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)) << 3) | 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;
|
||||
|
||||
#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;
|
||||
std::size_t m_size;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Default constructor. Construct an empty data_view.
|
||||
*/
|
||||
constexpr data_view() noexcept
|
||||
: m_data(nullptr),
|
||||
m_size(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
: 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
|
||||
: 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert data view to string.
|
||||
*
|
||||
* @pre Must not be default constructed data_view.
|
||||
*/
|
||||
std::string to_string() const {
|
||||
protozero_assert(m_data);
|
||||
return std::string{m_data, m_size};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert data view to string.
|
||||
*
|
||||
* @pre Must not be default constructed data_view.
|
||||
*/
|
||||
explicit operator std::string() const {
|
||||
protozero_assert(m_data);
|
||||
return std::string{m_data, m_size};
|
||||
}
|
||||
|
||||
}; // 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 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 bool operator!=(const data_view& lhs, const data_view& rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_TYPES_HPP
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
#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 <cstdint>
|
||||
|
||||
#include <protozero/exception.hpp>
|
||||
|
||||
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 int8_t* begin = reinterpret_cast<const int8_t*>(*data);
|
||||
const int8_t* iend = reinterpret_cast<const int8_t*>(end);
|
||||
const int8_t* p = begin;
|
||||
uint64_t val = 0;
|
||||
|
||||
if (iend - begin >= max_varint_length) { // fast path
|
||||
do {
|
||||
int64_t b;
|
||||
b = *p++; val = uint64_t((b & 0x7f) ); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 7); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 14); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 21); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 28); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 35); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 42); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 49); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 56); if (b >= 0) break;
|
||||
b = *p++; val |= uint64_t((b & 0x7f) << 63); if (b >= 0) break;
|
||||
throw varint_too_long_exception();
|
||||
} while (false);
|
||||
} else {
|
||||
int shift = 0;
|
||||
while (p != iend && *p < 0) {
|
||||
val |= uint64_t(*p++ & 0x7f) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
if (p == iend) {
|
||||
throw end_of_buffer_exception();
|
||||
}
|
||||
val |= uint64_t(*p++) << shift;
|
||||
}
|
||||
|
||||
*data = reinterpret_cast<const char*>(p);
|
||||
return val;
|
||||
}
|
||||
|
||||
} // end namespace detail
|
||||
|
||||
/**
|
||||
* Decode a 64 bit varint.
|
||||
*
|
||||
* Strong exception guarantee: if there is an exception the data pointer will
|
||||
* not be changed.
|
||||
*
|
||||
* @param[in,out] data Pointer to pointer to the input data. After the function
|
||||
* returns this will point to the next data to be read.
|
||||
* @param[in] end Pointer one past the end of the input data.
|
||||
* @returns The decoded integer
|
||||
* @throws varint_too_long_exception if the varint is longer then the maximum
|
||||
* length that would fit in a 64 bit int. Usually this means your data
|
||||
* is corrupted or you are trying to read something as a varint that
|
||||
* isn't.
|
||||
* @throws end_of_buffer_exception if the *end* of the buffer was reached
|
||||
* before the end of the varint.
|
||||
*/
|
||||
inline uint64_t decode_varint(const char** data, const char* end) {
|
||||
// If this is a one-byte varint, decode it here.
|
||||
if (end != *data && ((**data & 0x80) == 0)) {
|
||||
uint64_t val = 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 int8_t* begin = reinterpret_cast<const int8_t*>(*data);
|
||||
const int8_t* 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.
|
||||
* @throws Any exception thrown by increment or dereference operator on data.
|
||||
*/
|
||||
template <typename T>
|
||||
inline int write_varint(T data, uint64_t value) {
|
||||
int n = 1;
|
||||
|
||||
while (value >= 0x80) {
|
||||
*data++ = char((value & 0x7f) | 0x80);
|
||||
value >>= 7;
|
||||
++n;
|
||||
}
|
||||
*data++ = char(value);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* ZigZag encodes a 32 bit integer.
|
||||
*/
|
||||
inline constexpr uint32_t encode_zigzag32(int32_t value) noexcept {
|
||||
return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
|
||||
}
|
||||
|
||||
/**
|
||||
* ZigZag encodes a 64 bit integer.
|
||||
*/
|
||||
inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept {
|
||||
return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a 32 bit ZigZag-encoded integer.
|
||||
*/
|
||||
inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept {
|
||||
return static_cast<int32_t>(value >> 1) ^ -static_cast<int32_t>(value & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a 64 bit ZigZag-encoded integer.
|
||||
*/
|
||||
inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept {
|
||||
return static_cast<int64_t>(value >> 1) ^ -static_cast<int64_t>(value & 1);
|
||||
}
|
||||
|
||||
} // end namespace protozero
|
||||
|
||||
#endif // PROTOZERO_VARINT_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 5
|
||||
|
||||
/// The patch number
|
||||
#define PROTOZERO_VERSION_PATCH 2
|
||||
|
||||
/// The complete version number
|
||||
#define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
|
||||
|
||||
/// Version number as string
|
||||
#define PROTOZERO_VERSION_STRING "1.5.2"
|
||||
|
||||
#endif // PROTOZERO_VERSION_HPP
|
||||
Reference in New Issue
Block a user