Upgrade protozero to 1.5.1 (e94728c)

This commit is contained in:
Eric Fischer 2017-02-17 14:30:01 -08:00
parent 1cc65d61ba
commit 618efaf412
11 changed files with 1293 additions and 738 deletions

View File

@ -16,56 +16,69 @@ documentation.
* @brief Contains functions to swap bytes in values (for different endianness). * @brief Contains functions to swap bytes in values (for different endianness).
*/ */
#include <cstdint>
#include <cassert> #include <cassert>
#include <cstdint>
#include <protozero/config.hpp> #include <protozero/config.hpp>
namespace protozero { namespace protozero {
namespace detail {
/** inline uint32_t byteswap_impl(uint32_t value) noexcept {
* Swap N byte value between endianness formats. This template function must
* be specialized to actually work.
*/
template <int N>
inline void byteswap(const char* /*data*/, char* /*result*/) {
static_assert(N == 1, "Can only swap 4 or 8 byte values");
}
/**
* Swap 4 byte value (int32_t, uint32_t, float) between endianness formats.
*/
template <>
inline void byteswap<4>(const char* data, char* result) {
#ifdef PROTOZERO_USE_BUILTIN_BSWAP #ifdef PROTOZERO_USE_BUILTIN_BSWAP
*reinterpret_cast<uint32_t*>(result) = __builtin_bswap32(*reinterpret_cast<const uint32_t*>(data)); return __builtin_bswap32(value);
#else #else
result[3] = data[0]; return ((value & 0xff000000) >> 24) |
result[2] = data[1]; ((value & 0x00ff0000) >> 8) |
result[1] = data[2]; ((value & 0x0000ff00) << 8) |
result[0] = data[3]; ((value & 0x000000ff) << 24);
#endif #endif
} }
/** inline uint64_t byteswap_impl(uint64_t value) noexcept {
* Swap 8 byte value (int64_t, uint64_t, double) between endianness formats.
*/
template <>
inline void byteswap<8>(const char* data, char* result) {
#ifdef PROTOZERO_USE_BUILTIN_BSWAP #ifdef PROTOZERO_USE_BUILTIN_BSWAP
*reinterpret_cast<uint64_t*>(result) = __builtin_bswap64(*reinterpret_cast<const uint64_t*>(data)); return __builtin_bswap64(value);
#else #else
result[7] = data[0]; return ((value & 0xff00000000000000ULL) >> 56) |
result[6] = data[1]; ((value & 0x00ff000000000000ULL) >> 40) |
result[5] = data[2]; ((value & 0x0000ff0000000000ULL) >> 24) |
result[4] = data[3]; ((value & 0x000000ff00000000ULL) >> 8) |
result[3] = data[4]; ((value & 0x00000000ff000000ULL) << 8) |
result[2] = data[5]; ((value & 0x0000000000ff0000ULL) << 24) |
result[1] = data[6]; ((value & 0x000000000000ff00ULL) << 40) |
result[0] = data[7]; ((value & 0x00000000000000ffULL) << 56);
#endif #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 } // end namespace protozero
#endif // PROTOZERO_BYTESWAP_HPP #endif // PROTOZERO_BYTESWAP_HPP

View File

@ -35,17 +35,6 @@ documentation.
# define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN # define PROTOZERO_BYTE_ORDER PROTOZERO_LITTLE_ENDIAN
#endif #endif
// On some ARM machines and depending on compiler settings access to unaligned
// floating point values will result in a SIGBUS. Do not use the bare pointers
// in this case.
#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
# if !defined(__arm__) && !defined(_M_ARM)
# ifndef PROTOZERO_DO_NOT_USE_BARE_POINTER
# define PROTOZERO_USE_BARE_POINTER_FOR_PACKED_FIXED
# endif
# endif
#endif
// Check whether __builtin_bswap is available // Check whether __builtin_bswap is available
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
# define PROTOZERO_USE_BUILTIN_BSWAP # define PROTOZERO_USE_BUILTIN_BSWAP

View File

@ -29,7 +29,7 @@ namespace protozero {
*/ */
struct exception : std::exception { struct exception : std::exception {
/// Returns the explanatory string. /// Returns the explanatory string.
const char *what() const noexcept override { return "pbf exception"; } const char* what() const noexcept override { return "pbf exception"; }
}; };
/** /**
@ -38,7 +38,7 @@ struct exception : std::exception {
*/ */
struct varint_too_long_exception : exception { struct varint_too_long_exception : exception {
/// Returns the explanatory string. /// Returns the explanatory string.
const char *what() const noexcept override { return "varint too long exception"; } const char* what() const noexcept override { return "varint too long exception"; }
}; };
/** /**
@ -47,7 +47,7 @@ struct varint_too_long_exception : exception {
*/ */
struct unknown_pbf_wire_type_exception : exception { struct unknown_pbf_wire_type_exception : exception {
/// Returns the explanatory string. /// Returns the explanatory string.
const char *what() const noexcept override { return "unknown pbf field type exception"; } const char* what() const noexcept override { return "unknown pbf field type exception"; }
}; };
/** /**
@ -60,7 +60,7 @@ struct unknown_pbf_wire_type_exception : exception {
*/ */
struct end_of_buffer_exception : exception { struct end_of_buffer_exception : exception {
/// Returns the explanatory string. /// Returns the explanatory string.
const char *what() const noexcept override { return "end of buffer exception"; } const char* what() const noexcept override { return "end of buffer exception"; }
}; };
} // end namespace protozero } // end namespace protozero

328
protozero/iterators.hpp Normal file
View File

@ -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

View File

@ -18,8 +18,8 @@ documentation.
#include <type_traits> #include <type_traits>
#include <protozero/types.hpp>
#include <protozero/pbf_writer.hpp> #include <protozero/pbf_writer.hpp>
#include <protozero/types.hpp>
namespace protozero { namespace protozero {
@ -57,7 +57,7 @@ public:
/// @cond INTERNAL /// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \ #define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \
inline void add_##name(T tag, type value) { \ void add_##name(T tag, type value) { \
pbf_writer::add_##name(pbf_tag_type(tag), value); \ pbf_writer::add_##name(pbf_tag_type(tag), value); \
} }
@ -79,38 +79,43 @@ public:
#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR #undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
/// @endcond /// @endcond
inline void add_bytes(T tag, const char* value, std::size_t size) { void add_bytes(T tag, const char* value, std::size_t size) {
pbf_writer::add_bytes(pbf_tag_type(tag), value, size); pbf_writer::add_bytes(pbf_tag_type(tag), value, size);
} }
inline void add_bytes(T tag, const std::string& value) { void add_bytes(T tag, const std::string& value) {
pbf_writer::add_bytes(pbf_tag_type(tag), value); pbf_writer::add_bytes(pbf_tag_type(tag), value);
} }
inline void add_string(T tag, const char* value, std::size_t size) { 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); pbf_writer::add_string(pbf_tag_type(tag), value, size);
} }
inline void add_string(T tag, const std::string& value) { void add_string(T tag, const std::string& value) {
pbf_writer::add_string(pbf_tag_type(tag), value); pbf_writer::add_string(pbf_tag_type(tag), value);
} }
inline void add_string(T tag, const char* value) { void add_string(T tag, const char* value) {
pbf_writer::add_string(pbf_tag_type(tag), value); pbf_writer::add_string(pbf_tag_type(tag), value);
} }
inline void add_message(T tag, const char* value, std::size_t size) { void add_message(T tag, const char* value, std::size_t size) {
pbf_writer::add_message(pbf_tag_type(tag), value, size); pbf_writer::add_message(pbf_tag_type(tag), value, size);
} }
inline void add_message(T tag, const std::string& value) { void add_message(T tag, const std::string& value) {
pbf_writer::add_message(pbf_tag_type(tag), value); pbf_writer::add_message(pbf_tag_type(tag), value);
} }
/// @cond INTERNAL /// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \ #define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \
template <typename InputIterator> \ template <typename InputIterator> \
inline void add_packed_##name(T tag, InputIterator first, InputIterator last) { \ void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \ pbf_writer::add_packed_##name(pbf_tag_type(tag), first, last); \
} }
@ -132,7 +137,7 @@ public:
#undef PROTOZERO_WRITER_WRAP_ADD_PACKED #undef PROTOZERO_WRITER_WRAP_ADD_PACKED
/// @endcond /// @endcond
}; }; // class pbf_builder
} // end namespace protozero } // end namespace protozero

View File

@ -13,7 +13,7 @@ documentation.
/** /**
* @file pbf_message.hpp * @file pbf_message.hpp
* *
* @brief Contains the pbf_message class. * @brief Contains the pbf_message template class.
*/ */
#include <type_traits> #include <type_traits>
@ -75,19 +75,19 @@ public:
pbf_reader(std::forward<Args>(args)...) { pbf_reader(std::forward<Args>(args)...) {
} }
inline bool next() { bool next() {
return pbf_reader::next(); return pbf_reader::next();
} }
inline bool next(T tag) { bool next(T next_tag) {
return pbf_reader::next(pbf_tag_type(tag)); return pbf_reader::next(pbf_tag_type(next_tag));
} }
inline T tag() const noexcept { T tag() const noexcept {
return T(pbf_reader::tag()); return T(pbf_reader::tag());
} }
}; }; // class pbf_message
} // end namespace protozero } // end namespace protozero

File diff suppressed because it is too large Load Diff

View File

@ -22,6 +22,7 @@ documentation.
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <string> #include <string>
#include <utility>
#include <protozero/config.hpp> #include <protozero/config.hpp>
#include <protozero/types.hpp> #include <protozero/types.hpp>
@ -68,38 +69,35 @@ class pbf_writer {
// parent to the position where the data of the submessage is written to. // parent to the position where the data of the submessage is written to.
std::size_t m_pos = 0; std::size_t m_pos = 0;
inline void add_varint(uint64_t value) { 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_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(m_data);
write_varint(std::back_inserter(*m_data), value); write_varint(std::back_inserter(*m_data), value);
} }
inline void add_field(pbf_tag_type tag, pbf_wire_type type) { 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"); protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
uint32_t b = (tag << 3) | uint32_t(type); const uint32_t b = (tag << 3) | uint32_t(type);
add_varint(b); add_varint(b);
} }
inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) { void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
add_field(tag, pbf_wire_type::varint); add_field(tag, pbf_wire_type::varint);
add_varint(value); add_varint(value);
} }
template <typename T> template <typename T>
inline void add_fixed(T value) { 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_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(m_data);
#if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
m_data->append(reinterpret_cast<const char*>(&value), sizeof(T)); detail::byteswap_inplace(&value);
#else
auto size = m_data->size();
m_data->resize(size + sizeof(T));
byteswap<sizeof(T)>(reinterpret_cast<const char*>(&value), const_cast<char*>(m_data->data() + size));
#endif #endif
m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
} }
template <typename T, typename It> template <typename T, typename It>
inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) { void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
if (first == last) { if (first == last) {
return; return;
} }
@ -112,12 +110,12 @@ class pbf_writer {
} }
template <typename T, typename It> template <typename T, typename It>
inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) { void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
if (first == last) { if (first == last) {
return; return;
} }
auto length = std::distance(first, last); const auto length = std::distance(first, last);
add_length_varint(tag, sizeof(T) * pbf_length_type(length)); add_length_varint(tag, sizeof(T) * pbf_length_type(length));
reserve(sizeof(T) * std::size_t(length)); reserve(sizeof(T) * std::size_t(length));
@ -127,7 +125,7 @@ class pbf_writer {
} }
template <typename It> template <typename It>
inline void add_packed_varint(pbf_tag_type tag, It first, It last) { void add_packed_varint(pbf_tag_type tag, It first, It last) {
if (first == last) { if (first == last) {
return; return;
} }
@ -140,7 +138,7 @@ class pbf_writer {
} }
template <typename It> template <typename It>
inline void add_packed_svarint(pbf_tag_type tag, It first, It last) { void add_packed_svarint(pbf_tag_type tag, It first, It last) {
if (first == last) { if (first == last) {
return; return;
} }
@ -155,14 +153,18 @@ class pbf_writer {
// The number of bytes to reserve for the varint holding the length of // 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, // a length-delimited field. The length has to fit into pbf_length_type,
// and a varint needs 8 bit for every 7 bit. // and a varint needs 8 bit for every 7 bit.
static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1; 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 // 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 // the submessage is closed, nothing needs to be done, because the length
// of the submessage has already been written correctly. // of the submessage has already been written correctly.
static const std::size_t size_is_known = std::numeric_limits<std::size_t>::max(); enum constant_size_is_known : std::size_t {
size_is_known = std::numeric_limits<std::size_t>::max()
};
inline void open_submessage(pbf_tag_type tag, std::size_t size) { void open_submessage(pbf_tag_type tag, std::size_t size) {
protozero_assert(m_pos == 0); protozero_assert(m_pos == 0);
protozero_assert(m_data); protozero_assert(m_data);
if (size == 0) { if (size == 0) {
@ -177,7 +179,7 @@ class pbf_writer {
m_pos = m_data->size(); m_pos = m_data->size();
} }
inline void rollback_submessage() { void rollback_submessage() {
protozero_assert(m_pos != 0); protozero_assert(m_pos != 0);
protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_rollback_pos != size_is_known);
protozero_assert(m_data); protozero_assert(m_data);
@ -185,20 +187,20 @@ class pbf_writer {
m_pos = 0; m_pos = 0;
} }
inline void commit_submessage() { void commit_submessage() {
protozero_assert(m_pos != 0); protozero_assert(m_pos != 0);
protozero_assert(m_rollback_pos != size_is_known); protozero_assert(m_rollback_pos != size_is_known);
protozero_assert(m_data); protozero_assert(m_data);
auto length = pbf_length_type(m_data->size() - m_pos); const auto length = pbf_length_type(m_data->size() - m_pos);
protozero_assert(m_data->size() >= m_pos - reserve_bytes); protozero_assert(m_data->size() >= m_pos - reserve_bytes);
auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length); 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_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
m_pos = 0; m_pos = 0;
} }
inline void close_submessage() { void close_submessage() {
protozero_assert(m_data); protozero_assert(m_data);
if (m_pos == 0 || m_rollback_pos == size_is_known) { if (m_pos == 0 || m_rollback_pos == size_is_known) {
return; return;
@ -210,7 +212,7 @@ class pbf_writer {
} }
} }
inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) { void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
add_field(tag, pbf_wire_type::length_delimited); add_field(tag, pbf_wire_type::length_delimited);
add_varint(length); add_varint(length);
} }
@ -222,20 +224,18 @@ public:
* stores a reference to that string and adds all data to it. The string * 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. * doesn't have to be empty. The pbf_writer will just append data.
*/ */
inline explicit pbf_writer(std::string& data) noexcept : explicit pbf_writer(std::string& data) noexcept :
m_data(&data), m_data(&data),
m_parent_writer(nullptr), m_parent_writer(nullptr) {
m_pos(0) {
} }
/** /**
* Create a writer without a data store. In this form the writer can not * Create a writer without a data store. In this form the writer can not
* be used! * be used!
*/ */
inline pbf_writer() noexcept : pbf_writer() noexcept :
m_data(nullptr), m_data(nullptr),
m_parent_writer(nullptr), m_parent_writer(nullptr) {
m_pos(0) {
} }
/** /**
@ -248,10 +248,9 @@ public:
* Setting this allows some optimizations but is only possible in * Setting this allows some optimizations but is only possible in
* a few very specific cases. * a few very specific cases.
*/ */
inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) : pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
m_data(parent_writer.m_data), m_data(parent_writer.m_data),
m_parent_writer(&parent_writer), m_parent_writer(&parent_writer) {
m_pos(0) {
m_parent_writer->open_submessage(tag, size); m_parent_writer->open_submessage(tag, size);
} }
@ -262,17 +261,30 @@ public:
pbf_writer& operator=(const pbf_writer&) noexcept = default; pbf_writer& operator=(const pbf_writer&) noexcept = default;
/// A pbf_writer object can be moved /// A pbf_writer object can be moved
inline pbf_writer(pbf_writer&&) noexcept = default; pbf_writer(pbf_writer&&) noexcept = default;
/// A pbf_writer object can be moved /// A pbf_writer object can be moved
inline pbf_writer& operator=(pbf_writer&&) noexcept = default; pbf_writer& operator=(pbf_writer&&) noexcept = default;
inline ~pbf_writer() { ~pbf_writer() {
if (m_parent_writer) { if (m_parent_writer) {
m_parent_writer->close_submessage(); 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 * Reserve size bytes in the underlying message store in addition to
* whatever the message store already holds. So unlike * whatever the message store already holds. So unlike
@ -286,7 +298,14 @@ public:
m_data->reserve(m_data->size() + size); m_data->reserve(m_data->size() + size);
} }
inline void rollback() { /**
* 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_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"); 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_parent_writer->rollback_submessage();
@ -304,7 +323,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_bool(pbf_tag_type tag, bool value) { void add_bool(pbf_tag_type tag, bool value) {
add_field(tag, pbf_wire_type::varint); 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_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(m_data);
@ -317,7 +336,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_enum(pbf_tag_type tag, int32_t value) { void add_enum(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, uint64_t(value)); add_tagged_varint(tag, uint64_t(value));
} }
@ -327,7 +346,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_int32(pbf_tag_type tag, int32_t value) { void add_int32(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, uint64_t(value)); add_tagged_varint(tag, uint64_t(value));
} }
@ -337,7 +356,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_sint32(pbf_tag_type tag, int32_t value) { void add_sint32(pbf_tag_type tag, int32_t value) {
add_tagged_varint(tag, encode_zigzag32(value)); add_tagged_varint(tag, encode_zigzag32(value));
} }
@ -347,7 +366,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_uint32(pbf_tag_type tag, uint32_t value) { void add_uint32(pbf_tag_type tag, uint32_t value) {
add_tagged_varint(tag, value); add_tagged_varint(tag, value);
} }
@ -357,7 +376,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_int64(pbf_tag_type tag, int64_t value) { void add_int64(pbf_tag_type tag, int64_t value) {
add_tagged_varint(tag, uint64_t(value)); add_tagged_varint(tag, uint64_t(value));
} }
@ -367,7 +386,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_sint64(pbf_tag_type tag, int64_t value) { void add_sint64(pbf_tag_type tag, int64_t value) {
add_tagged_varint(tag, encode_zigzag64(value)); add_tagged_varint(tag, encode_zigzag64(value));
} }
@ -377,7 +396,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_uint64(pbf_tag_type tag, uint64_t value) { void add_uint64(pbf_tag_type tag, uint64_t value) {
add_tagged_varint(tag, value); add_tagged_varint(tag, value);
} }
@ -387,7 +406,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_fixed32(pbf_tag_type tag, uint32_t value) { void add_fixed32(pbf_tag_type tag, uint32_t value) {
add_field(tag, pbf_wire_type::fixed32); add_field(tag, pbf_wire_type::fixed32);
add_fixed<uint32_t>(value); add_fixed<uint32_t>(value);
} }
@ -398,7 +417,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_sfixed32(pbf_tag_type tag, int32_t value) { void add_sfixed32(pbf_tag_type tag, int32_t value) {
add_field(tag, pbf_wire_type::fixed32); add_field(tag, pbf_wire_type::fixed32);
add_fixed<int32_t>(value); add_fixed<int32_t>(value);
} }
@ -409,7 +428,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_fixed64(pbf_tag_type tag, uint64_t value) { void add_fixed64(pbf_tag_type tag, uint64_t value) {
add_field(tag, pbf_wire_type::fixed64); add_field(tag, pbf_wire_type::fixed64);
add_fixed<uint64_t>(value); add_fixed<uint64_t>(value);
} }
@ -420,7 +439,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_sfixed64(pbf_tag_type tag, int64_t value) { void add_sfixed64(pbf_tag_type tag, int64_t value) {
add_field(tag, pbf_wire_type::fixed64); add_field(tag, pbf_wire_type::fixed64);
add_fixed<int64_t>(value); add_fixed<int64_t>(value);
} }
@ -431,7 +450,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_float(pbf_tag_type tag, float value) { void add_float(pbf_tag_type tag, float value) {
add_field(tag, pbf_wire_type::fixed32); add_field(tag, pbf_wire_type::fixed32);
add_fixed<float>(value); add_fixed<float>(value);
} }
@ -442,7 +461,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_double(pbf_tag_type tag, double value) { void add_double(pbf_tag_type tag, double value) {
add_field(tag, pbf_wire_type::fixed64); add_field(tag, pbf_wire_type::fixed64);
add_fixed<double>(value); add_fixed<double>(value);
} }
@ -454,7 +473,7 @@ public:
* @param value Pointer to value to be written * @param value Pointer to value to be written
* @param size Number of bytes to be written * @param size Number of bytes to be written
*/ */
inline void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) { 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_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(m_data);
protozero_assert(size <= std::numeric_limits<pbf_length_type>::max()); protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
@ -468,10 +487,51 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_bytes(pbf_tag_type tag, const std::string& value) { void add_bytes(pbf_tag_type tag, const data_view& value) {
add_bytes(tag, value.data(), value.size()); 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 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. * Add "string" field to data.
* *
@ -479,7 +539,7 @@ public:
* @param value Pointer to value to be written * @param value Pointer to value to be written
* @param size Number of bytes to be written * @param size Number of bytes to be written
*/ */
inline void add_string(pbf_tag_type tag, const char* value, std::size_t size) { void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
add_bytes(tag, value, size); add_bytes(tag, value, size);
} }
@ -489,7 +549,17 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written * @param value Value to be written
*/ */
inline void add_string(pbf_tag_type tag, const std::string& value) { 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_bytes(tag, value.data(), value.size());
} }
@ -500,7 +570,7 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Pointer to value to be written * @param value Pointer to value to be written
*/ */
inline void add_string(pbf_tag_type tag, const char* value) { void add_string(pbf_tag_type tag, const char* value) {
add_bytes(tag, value, std::strlen(value)); add_bytes(tag, value, std::strlen(value));
} }
@ -511,7 +581,7 @@ public:
* @param value Pointer to message to be written * @param value Pointer to message to be written
* @param size Length of the message * @param size Length of the message
*/ */
inline void add_message(pbf_tag_type tag, const char* value, std::size_t size) { void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
add_bytes(tag, value, size); add_bytes(tag, value, size);
} }
@ -521,7 +591,17 @@ public:
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param value Value to be written. The value must be a complete message. * @param value Value to be written. The value must be a complete message.
*/ */
inline void add_message(pbf_tag_type tag, const std::string& value) { 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()); add_bytes(tag, value.data(), value.size());
} }
@ -535,126 +615,126 @@ public:
/** /**
* Add "repeated packed bool" field to data. * Add "repeated packed bool" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to bool. * Dereferencing the iterator must yield a type assignable to bool.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last); add_packed_varint(tag, first, last);
} }
/** /**
* Add "repeated packed enum" field to data. * Add "repeated packed enum" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t. * Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last); add_packed_varint(tag, first, last);
} }
/** /**
* Add "repeated packed int32" field to data. * Add "repeated packed int32" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t. * Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last); add_packed_varint(tag, first, last);
} }
/** /**
* Add "repeated packed sint32" field to data. * Add "repeated packed sint32" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t. * Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_svarint(tag, first, last); add_packed_svarint(tag, first, last);
} }
/** /**
* Add "repeated packed uint32" field to data. * Add "repeated packed uint32" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint32_t. * Dereferencing the iterator must yield a type assignable to uint32_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last); add_packed_varint(tag, first, last);
} }
/** /**
* Add "repeated packed int64" field to data. * Add "repeated packed int64" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t. * Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last); add_packed_varint(tag, first, last);
} }
/** /**
* Add "repeated packed sint64" field to data. * Add "repeated packed sint64" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t. * Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_svarint(tag, first, last); add_packed_svarint(tag, first, last);
} }
/** /**
* Add "repeated packed uint64" field to data. * Add "repeated packed uint64" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint64_t. * Dereferencing the iterator must yield a type assignable to uint64_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_varint(tag, first, last); add_packed_varint(tag, first, last);
} }
/** /**
* Add "repeated packed fixed32" field to data. * Add "repeated packed fixed32" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint32_t. * Dereferencing the iterator must yield a type assignable to uint32_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<uint32_t, InputIterator>(tag, first, last, add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category()); typename std::iterator_traits<InputIterator>::iterator_category());
} }
@ -662,14 +742,14 @@ public:
/** /**
* Add "repeated packed sfixed32" field to data. * Add "repeated packed sfixed32" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int32_t. * Dereferencing the iterator must yield a type assignable to int32_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<int32_t, InputIterator>(tag, first, last, add_packed_fixed<int32_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category()); typename std::iterator_traits<InputIterator>::iterator_category());
} }
@ -677,14 +757,14 @@ public:
/** /**
* Add "repeated packed fixed64" field to data. * Add "repeated packed fixed64" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to uint64_t. * Dereferencing the iterator must yield a type assignable to uint64_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<uint64_t, InputIterator>(tag, first, last, add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category()); typename std::iterator_traits<InputIterator>::iterator_category());
} }
@ -692,14 +772,14 @@ public:
/** /**
* Add "repeated packed sfixed64" field to data. * Add "repeated packed sfixed64" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to int64_t. * Dereferencing the iterator must yield a type assignable to int64_t.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<int64_t, InputIterator>(tag, first, last, add_packed_fixed<int64_t, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category()); typename std::iterator_traits<InputIterator>::iterator_category());
} }
@ -707,14 +787,14 @@ public:
/** /**
* Add "repeated packed float" field to data. * Add "repeated packed float" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to float. * Dereferencing the iterator must yield a type assignable to float.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<float, InputIterator>(tag, first, last, add_packed_fixed<float, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category()); typename std::iterator_traits<InputIterator>::iterator_category());
} }
@ -722,14 +802,14 @@ public:
/** /**
* Add "repeated packed double" field to data. * Add "repeated packed double" field to data.
* *
* @tparam InputIterator An type satisfying the InputIterator concept. * @tparam InputIterator A type satisfying the InputIterator concept.
* Dereferencing the iterator must yield a type assignable to double. * Dereferencing the iterator must yield a type assignable to double.
* @param tag Tag (field number) of the field * @param tag Tag (field number) of the field
* @param first Iterator pointing to the beginning of the data * @param first Iterator pointing to the beginning of the data
* @param last Iterator pointing one past the end of data * @param last Iterator pointing one past the end of data
*/ */
template <typename InputIterator> template <typename InputIterator>
inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
add_packed_fixed<double, InputIterator>(tag, first, last, add_packed_fixed<double, InputIterator>(tag, first, last,
typename std::iterator_traits<InputIterator>::iterator_category()); typename std::iterator_traits<InputIterator>::iterator_category());
} }
@ -742,6 +822,16 @@ public:
}; // class pbf_writer }; // 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 { namespace detail {
class packed_field { class packed_field {
@ -752,6 +842,12 @@ namespace detail {
public: 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) : packed_field(pbf_writer& parent_writer, pbf_tag_type tag) :
m_writer(parent_writer, tag) { m_writer(parent_writer, tag) {
} }
@ -771,12 +867,14 @@ namespace detail {
public: public:
packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag) : template <typename P>
packed_field(parent_writer, tag) { packed_field_fixed(pbf_writer& parent_writer, P tag) :
packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
} }
packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) : template <typename P>
packed_field(parent_writer, tag, size * sizeof(T)) { 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) { void add_element(T value) {
@ -790,8 +888,9 @@ namespace detail {
public: public:
packed_field_varint(pbf_writer& parent_writer, pbf_tag_type tag) : template <typename P>
packed_field(parent_writer, tag) { packed_field_varint(pbf_writer& parent_writer, P tag) :
packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
} }
void add_element(T value) { void add_element(T value) {
@ -805,8 +904,9 @@ namespace detail {
public: public:
packed_field_svarint(pbf_writer& parent_writer, pbf_tag_type tag) : template <typename P>
packed_field(parent_writer, tag) { packed_field_svarint(pbf_writer& parent_writer, P tag) :
packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
} }
void add_element(T value) { void add_element(T value) {
@ -817,19 +917,46 @@ namespace detail {
} // end namespace detail } // end namespace detail
/// Class for generating packed repeated bool fields.
using packed_field_bool = detail::packed_field_varint<bool>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; 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>; using packed_field_double = detail::packed_field_fixed<double>;
} // end namespace protozero } // end namespace protozero

View File

@ -16,33 +16,184 @@ documentation.
* @brief Contains the declaration of low-level types used in the pbf format. * @brief Contains the declaration of low-level types used in the pbf format.
*/ */
#include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstring>
#include <string>
#include <utility>
#include <protozero/config.hpp>
namespace protozero { namespace protozero {
/** /**
* The type used for field tags (field numbers). * The type used for field tags (field numbers).
*/ */
typedef uint32_t pbf_tag_type; 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:
/** /**
* The type used to encode type information. * Default constructor. Construct an empty data_view.
* See the table on
* https://developers.google.com/protocol-buffers/docs/encoding
*/ */
enum class pbf_wire_type : uint32_t { constexpr data_view() noexcept
varint = 0, // int32/64, uint32/64, sint32/64, bool, enum : m_data(nullptr),
fixed64 = 1, // fixed64, sfixed64, double m_size(0) {
length_delimited = 2, // string, bytes, embedded messages, }
// packed repeated fields
fixed32 = 5, // fixed32, sfixed32, float
unknown = 99 // used for default setting in this library
};
/** /**
* The type used for length values, such as the length of a field. * Create data_view from pointer and size.
*
* @param ptr Pointer to the data.
* @param length Length of the data.
*/ */
typedef uint32_t pbf_length_type; 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;
}
/**
* 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::strcmp(lhs.data(), 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 } // end namespace protozero

View File

@ -23,13 +23,54 @@ documentation.
namespace protozero { namespace protozero {
/** /**
* The maximum length of a 64bit varint. * The maximum length of a 64 bit varint.
*/ */
constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1; constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
// from https://github.com/facebook/folly/blob/master/folly/Varint.h 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 64bit varint. * Decode a 64 bit varint.
* *
* Strong exception guarantee: if there is an exception the data pointer will * Strong exception guarantee: if there is an exception the data pointer will
* not be changed. * not be changed.
@ -39,55 +80,69 @@ constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
* @param[in] end Pointer one past the end of the input data. * @param[in] end Pointer one past the end of the input data.
* @returns The decoded integer * @returns The decoded integer
* @throws varint_too_long_exception if the varint is longer then the maximum * @throws varint_too_long_exception if the varint is longer then the maximum
* length that would fit in a 64bit int. Usually this means your data * 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 * is corrupted or you are trying to read something as a varint that
* isn't. * isn't.
* @throws end_of_buffer_exception if the *end* of the buffer was reached * @throws end_of_buffer_exception if the *end* of the buffer was reached
* before the end of the varint. * before the end of the varint.
*/ */
inline uint64_t decode_varint(const char** data, const char* end) { inline uint64_t decode_varint(const char** data, const char* end) {
const int8_t* begin = reinterpret_cast<const int8_t*>(*data); // If this is a one-byte varint, decode it here.
const int8_t* iend = reinterpret_cast<const int8_t*>(end); if (end != *data && ((**data & 0x80) == 0)) {
const int8_t* p = begin; uint64_t val = uint64_t(**data);
uint64_t val = 0; ++(*data);
return val;
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;
} }
// If this varint is more than one byte, defer to complete implementation.
*data = reinterpret_cast<const char*>(p); return detail::decode_varint_impl(data, end);
return val;
} }
/** /**
* Varint-encode a 64bit integer. * 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.
*/ */
template <typename OutputIterator> inline void skip_varint(const char** data, const char* end) {
inline int write_varint(OutputIterator data, uint64_t value) { const int8_t* begin = reinterpret_cast<const int8_t*>(*data);
int n=1; 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) { while (value >= 0x80) {
*data++ = char((value & 0x7f) | 0x80); *data++ = char((value & 0x7f) | 0x80);
@ -102,29 +157,29 @@ inline int write_varint(OutputIterator data, uint64_t value) {
/** /**
* ZigZag encodes a 32 bit integer. * ZigZag encodes a 32 bit integer.
*/ */
inline uint32_t encode_zigzag32(int32_t value) noexcept { inline constexpr uint32_t encode_zigzag32(int32_t value) noexcept {
return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31)); return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
} }
/** /**
* ZigZag encodes a 64 bit integer. * ZigZag encodes a 64 bit integer.
*/ */
inline uint64_t encode_zigzag64(int64_t value) noexcept { inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept {
return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63)); return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
} }
/** /**
* Decodes a 32 bit ZigZag-encoded integer. * Decodes a 32 bit ZigZag-encoded integer.
*/ */
inline int32_t decode_zigzag32(uint32_t value) noexcept { inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept {
return int32_t(value >> 1) ^ -int32_t(value & 1); return static_cast<int32_t>(value >> 1) ^ -static_cast<int32_t>(value & 1);
} }
/** /**
* Decodes a 64 bit ZigZag-encoded integer. * Decodes a 64 bit ZigZag-encoded integer.
*/ */
inline int64_t decode_zigzag64(uint64_t value) noexcept { inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept {
return int64_t(value >> 1) ^ -int64_t(value & 1); return static_cast<int64_t>(value >> 1) ^ -static_cast<int64_t>(value & 1);
} }
} // end namespace protozero } // end namespace protozero

View File

@ -10,13 +10,25 @@ documentation.
*****************************************************************************/ *****************************************************************************/
#define PROTOZERO_VERSION_MAJOR 1 /**
#define PROTOZERO_VERSION_MINOR 3 * @file version.hpp
#define PROTOZERO_VERSION_PATCH 0 *
* @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 1
/// The complete version number
#define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH) #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
#define PROTOZERO_VERSION_STRING "1.3.0" /// Version number as string
#define PROTOZERO_VERSION_STRING "1.5.1"
#endif // PROTOZERO_VERSION_HPP #endif // PROTOZERO_VERSION_HPP