Upgrade to protozero 1.7.1 (#5)

* Upgrade to protozero 1.7.1

* Bump version number
This commit is contained in:
Erica Fischer 2022-09-13 11:24:01 -07:00 committed by GitHub
parent 073700aa38
commit 8e46650da7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2650 additions and 1387 deletions

View File

@ -1,3 +1,7 @@
## 2.6.1
Upgrade protozero to version 1.7.1
## 2.6.0
* Add another drop rate guessing options, from the same metrics -zg uses

View File

@ -0,0 +1,266 @@
#ifndef PROTOZERO_BASIC_PBF_BUILDER_HPP
#define PROTOZERO_BASIC_PBF_BUILDER_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
/**
* @file basic_pbf_builder.hpp
*
* @brief Contains the basic_pbf_builder template class.
*/
#include "basic_pbf_writer.hpp"
#include "types.hpp"
#include <type_traits>
namespace protozero {
/**
* The basic_pbf_builder is used to write PBF formatted messages into a buffer.
* It is based on the basic_pbf_writer class and has all the same methods. The
* difference is that while the pbf_writer class takes an integer tag,
* this template class takes a tag of the template type T. The idea is that
* T will be an enumeration value and this helps reduce the possibility of
* programming errors.
*
* Almost all methods in this class can throw an std::bad_alloc exception if
* the underlying buffer class wants to resize.
*
* Read the tutorial to understand how this class is used. In most cases you
* want to use the pbf_builder class which uses a std::string as buffer type.
*/
template <typename TBuffer, typename T>
class basic_pbf_builder : public basic_pbf_writer<TBuffer> {
static_assert(std::is_same<pbf_tag_type, typename std::underlying_type<T>::type>::value,
"T must be enum with underlying type protozero::pbf_tag_type");
public:
/// The type of messages this class will build.
using enum_type = T;
basic_pbf_builder() = default;
/**
* Create a builder using the given string as a data store. The object
* stores a reference to that string and adds all data to it. The string
* doesn't have to be empty. The pbf_message object will just append data.
*/
explicit basic_pbf_builder(TBuffer& data) noexcept :
basic_pbf_writer<TBuffer>{data} {
}
/**
* Construct a pbf_builder for a submessage from the pbf_message or
* pbf_writer of the parent message.
*
* @param parent_writer The parent pbf_message or pbf_writer
* @param tag Tag of the field that will be written
*/
template <typename P>
basic_pbf_builder(basic_pbf_writer<TBuffer>& parent_writer, P tag) noexcept :
basic_pbf_writer<TBuffer>{parent_writer, pbf_tag_type(tag)} {
}
/// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_SCALAR(name, type) \
void add_##name(T tag, type value) { \
basic_pbf_writer<TBuffer>::add_##name(pbf_tag_type(tag), value); \
}
PROTOZERO_WRITER_WRAP_ADD_SCALAR(bool, bool)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(enum, int32_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(int32, int32_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint32, int32_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint32, uint32_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(int64, int64_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sint64, int64_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(uint64, uint64_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed32, uint32_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed32, int32_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(fixed64, uint64_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(sfixed64, int64_t)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(float, float)
PROTOZERO_WRITER_WRAP_ADD_SCALAR(double, double)
#undef PROTOZERO_WRITER_WRAP_ADD_SCALAR
/// @endcond
/**
* Add "bytes" field to data.
*
* @param tag Tag of the field
* @param value Pointer to value to be written
* @param size Number of bytes to be written
*/
void add_bytes(T tag, const char* value, std::size_t size) {
basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value, size);
}
/**
* Add "bytes" field to data.
*
* @param tag Tag of the field
* @param value Value to be written
*/
void add_bytes(T tag, const data_view& value) {
basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value);
}
/**
* Add "bytes" field to data.
*
* @param tag Tag of the field
* @param value Value to be written
*/
void add_bytes(T tag, const std::string& value) {
basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value);
}
/**
* Add "bytes" field to data. Bytes from the value are written until
* a null byte is encountered. The null byte is not added.
*
* @param tag Tag of the field
* @param value Pointer to zero-delimited value to be written
*/
void add_bytes(T tag, const char* value) {
basic_pbf_writer<TBuffer>::add_bytes(pbf_tag_type(tag), value);
}
/**
* Add "bytes" field to data using vectored input. All the data in the
* 2nd and further arguments is "concatenated" with only a single copy
* into the final buffer.
*
* This will work with objects of any type supporting the data() and
* size() methods like std::string or protozero::data_view.
*
* Example:
* @code
* std::string data1 = "abc";
* std::string data2 = "xyz";
* builder.add_bytes_vectored(1, data1, data2);
* @endcode
*
* @tparam Ts List of types supporting data() and size() methods.
* @param tag Tag of the field
* @param values List of objects of types Ts with data to be appended.
*/
template <typename... Ts>
void add_bytes_vectored(T tag, Ts&&... values) {
basic_pbf_writer<TBuffer>::add_bytes_vectored(pbf_tag_type(tag), std::forward<Ts>(values)...);
}
/**
* Add "string" field to data.
*
* @param tag Tag of the field
* @param value Pointer to value to be written
* @param size Number of bytes to be written
*/
void add_string(T tag, const char* value, std::size_t size) {
basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value, size);
}
/**
* Add "string" field to data.
*
* @param tag Tag of the field
* @param value Value to be written
*/
void add_string(T tag, const data_view& value) {
basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value);
}
/**
* Add "string" field to data.
*
* @param tag Tag of the field
* @param value Value to be written
*/
void add_string(T tag, const std::string& value) {
basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value);
}
/**
* Add "string" field to data. Bytes from the value are written until
* a null byte is encountered. The null byte is not added.
*
* @param tag Tag of the field
* @param value Pointer to value to be written
*/
void add_string(T tag, const char* value) {
basic_pbf_writer<TBuffer>::add_string(pbf_tag_type(tag), value);
}
/**
* Add "message" field to data.
*
* @param tag Tag of the field
* @param value Pointer to message to be written
* @param size Length of the message
*/
void add_message(T tag, const char* value, std::size_t size) {
basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value, size);
}
/**
* Add "message" field to data.
*
* @param tag Tag of the field
* @param value Value to be written. The value must be a complete message.
*/
void add_message(T tag, const data_view& value) {
basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value);
}
/**
* Add "message" field to data.
*
* @param tag Tag of the field
* @param value Value to be written. The value must be a complete message.
*/
void add_message(T tag, const std::string& value) {
basic_pbf_writer<TBuffer>::add_message(pbf_tag_type(tag), value);
}
/// @cond INTERNAL
#define PROTOZERO_WRITER_WRAP_ADD_PACKED(name) \
template <typename InputIterator> \
void add_packed_##name(T tag, InputIterator first, InputIterator last) { \
basic_pbf_writer<TBuffer>::add_packed_##name(pbf_tag_type(tag), first, last); \
}
PROTOZERO_WRITER_WRAP_ADD_PACKED(bool)
PROTOZERO_WRITER_WRAP_ADD_PACKED(enum)
PROTOZERO_WRITER_WRAP_ADD_PACKED(int32)
PROTOZERO_WRITER_WRAP_ADD_PACKED(sint32)
PROTOZERO_WRITER_WRAP_ADD_PACKED(uint32)
PROTOZERO_WRITER_WRAP_ADD_PACKED(int64)
PROTOZERO_WRITER_WRAP_ADD_PACKED(sint64)
PROTOZERO_WRITER_WRAP_ADD_PACKED(uint64)
PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed32)
PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed32)
PROTOZERO_WRITER_WRAP_ADD_PACKED(fixed64)
PROTOZERO_WRITER_WRAP_ADD_PACKED(sfixed64)
PROTOZERO_WRITER_WRAP_ADD_PACKED(float)
PROTOZERO_WRITER_WRAP_ADD_PACKED(double)
#undef PROTOZERO_WRITER_WRAP_ADD_PACKED
/// @endcond
}; // class basic_pbf_builder
} // end namespace protozero
#endif // PROTOZERO_BASIC_PBF_BUILDER_HPP

File diff suppressed because it is too large Load Diff

222
protozero/buffer_fixed.hpp Normal file
View File

@ -0,0 +1,222 @@
#ifndef PROTOZERO_BUFFER_FIXED_HPP
#define PROTOZERO_BUFFER_FIXED_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
/**
* @file buffer_fixed.hpp
*
* @brief Contains the fixed_size_buffer_adaptor class.
*/
#include "buffer_tmpl.hpp"
#include "config.hpp"
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <stdexcept>
namespace protozero {
/**
* This class can be used instead of std::string if you want to create a
* vector tile in a fixed-size buffer. Any operation that needs more space
* than is available will fail with a std::length_error exception.
*/
class fixed_size_buffer_adaptor {
char* m_data;
std::size_t m_capacity;
std::size_t m_size = 0;
public:
/// @cond usual container typedefs not documented
using size_type = std::size_t;
using value_type = char;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
using iterator = pointer;
using const_iterator = const_pointer;
/// @endcond
/**
* Constructor.
*
* @param data Pointer to some memory allocated for the buffer.
* @param capacity Number of bytes available.
*/
fixed_size_buffer_adaptor(char* data, std::size_t capacity) noexcept :
m_data(data),
m_capacity(capacity) {
}
/**
* Constructor.
*
* @param container Some container class supporting the member functions
* data() and size().
*/
template <typename T>
explicit fixed_size_buffer_adaptor(T& container) :
m_data(container.data()),
m_capacity(container.size()) {
}
/// Returns a pointer to the data in the buffer.
const char* data() const noexcept {
return m_data;
}
/// Returns a pointer to the data in the buffer.
char* data() noexcept {
return m_data;
}
/// The capacity this buffer was created with.
std::size_t capacity() const noexcept {
return m_capacity;
}
/// The number of bytes used in the buffer. Always <= capacity().
std::size_t size() const noexcept {
return m_size;
}
/// Return iterator to beginning of data.
char* begin() noexcept {
return m_data;
}
/// Return iterator to beginning of data.
const char* begin() const noexcept {
return m_data;
}
/// Return iterator to beginning of data.
const char* cbegin() const noexcept {
return m_data;
}
/// Return iterator to end of data.
char* end() noexcept {
return m_data + m_size;
}
/// Return iterator to end of data.
const char* end() const noexcept {
return m_data + m_size;
}
/// Return iterator to end of data.
const char* cend() const noexcept {
return m_data + m_size;
}
/// @cond INTERNAL
// Do not rely on anything beyond this point
void append(const char* data, std::size_t count) {
if (m_size + count > m_capacity) {
throw std::length_error{"fixed size data store exhausted"};
}
std::copy_n(data, count, m_data + m_size);
m_size += count;
}
void append_zeros(std::size_t count) {
if (m_size + count > m_capacity) {
throw std::length_error{"fixed size data store exhausted"};
}
std::fill_n(m_data + m_size, count, '\0');
m_size += count;
}
void resize(std::size_t size) {
protozero_assert(size < m_size);
if (size > m_capacity) {
throw std::length_error{"fixed size data store exhausted"};
}
m_size = size;
}
void erase_range(std::size_t from, std::size_t to) {
protozero_assert(from <= m_size);
protozero_assert(to <= m_size);
protozero_assert(from < to);
std::copy(m_data + to, m_data + m_size, m_data + from);
m_size -= (to - from);
}
char* at_pos(std::size_t pos) {
protozero_assert(pos <= m_size);
return m_data + pos;
}
void push_back(char ch) {
if (m_size >= m_capacity) {
throw std::length_error{"fixed size data store exhausted"};
}
m_data[m_size++] = ch;
}
/// @endcond
}; // class fixed_size_buffer_adaptor
/// @cond INTERNAL
template <>
struct buffer_customization<fixed_size_buffer_adaptor> {
static std::size_t size(const fixed_size_buffer_adaptor* buffer) noexcept {
return buffer->size();
}
static void append(fixed_size_buffer_adaptor* buffer, const char* data, std::size_t count) {
buffer->append(data, count);
}
static void append_zeros(fixed_size_buffer_adaptor* buffer, std::size_t count) {
buffer->append_zeros(count);
}
static void resize(fixed_size_buffer_adaptor* buffer, std::size_t size) {
buffer->resize(size);
}
static void reserve_additional(fixed_size_buffer_adaptor* /*buffer*/, std::size_t /*size*/) {
/* nothing to be done for fixed-size buffers */
}
static void erase_range(fixed_size_buffer_adaptor* buffer, std::size_t from, std::size_t to) {
buffer->erase_range(from, to);
}
static char* at_pos(fixed_size_buffer_adaptor* buffer, std::size_t pos) {
return buffer->at_pos(pos);
}
static void push_back(fixed_size_buffer_adaptor* buffer, char ch) {
buffer->push_back(ch);
}
};
/// @endcond
} // namespace protozero
#endif // PROTOZERO_BUFFER_FIXED_HPP

View File

@ -0,0 +1,78 @@
#ifndef PROTOZERO_BUFFER_STRING_HPP
#define PROTOZERO_BUFFER_STRING_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
/**
* @file buffer_string.hpp
*
* @brief Contains the customization points for buffer implementation based
* on std::string
*/
#include "buffer_tmpl.hpp"
#include "config.hpp"
#include <cstddef>
#include <iterator>
#include <string>
namespace protozero {
// Implementation of buffer customizations points for std::string
/// @cond INTERNAL
template <>
struct buffer_customization<std::string> {
static std::size_t size(const std::string* buffer) noexcept {
return buffer->size();
}
static void append(std::string* buffer, const char* data, std::size_t count) {
buffer->append(data, count);
}
static void append_zeros(std::string* buffer, std::size_t count) {
buffer->append(count, '\0');
}
static void resize(std::string* buffer, std::size_t size) {
protozero_assert(size < buffer->size());
buffer->resize(size);
}
static void reserve_additional(std::string* buffer, std::size_t size) {
buffer->reserve(buffer->size() + size);
}
static void erase_range(std::string* buffer, std::size_t from, std::size_t to) {
protozero_assert(from <= buffer->size());
protozero_assert(to <= buffer->size());
protozero_assert(from <= to);
buffer->erase(std::next(buffer->begin(), static_cast<std::string::iterator::difference_type>(from)),
std::next(buffer->begin(), static_cast<std::string::iterator::difference_type>(to)));
}
static char* at_pos(std::string* buffer, std::size_t pos) {
protozero_assert(pos <= buffer->size());
return (&*buffer->begin()) + pos;
}
static void push_back(std::string* buffer, char ch) {
buffer->push_back(ch);
}
};
/// @endcond
} // namespace protozero
#endif // PROTOZERO_BUFFER_STRING_HPP

113
protozero/buffer_tmpl.hpp Normal file
View File

@ -0,0 +1,113 @@
#ifndef PROTOZERO_BUFFER_TMPL_HPP
#define PROTOZERO_BUFFER_TMPL_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
/**
* @file buffer_tmpl.hpp
*
* @brief Contains the customization points for buffer implementations.
*/
#include <cstddef>
#include <iterator>
#include <string>
namespace protozero {
// Implementation of buffer customizations points for std::string
/// @cond INTERNAL
template <typename T>
struct buffer_customization {
/**
* Get the number of bytes currently used in the buffer.
*
* @param buffer Pointer to the buffer.
* @returns number of bytes used in the buffer.
*/
static std::size_t size(const std::string* buffer);
/**
* Append count bytes from data to the buffer.
*
* @param buffer Pointer to the buffer.
* @param data Pointer to the data.
* @param count Number of bytes to be added to the buffer.
*/
static void append(std::string* buffer, const char* data, std::size_t count);
/**
* Append count zero bytes to the buffer.
*
* @param buffer Pointer to the buffer.
* @param count Number of bytes to be added to the buffer.
*/
static void append_zeros(std::string* buffer, std::size_t count);
/**
* Shrink the buffer to the specified size. The new size will always be
* smaller than the current size.
*
* @param buffer Pointer to the buffer.
* @param size New size of the buffer.
*
* @pre size < current size of buffer
*/
static void resize(std::string* buffer, std::size_t size);
/**
* Reserve an additional size bytes for use in the buffer. This is used for
* variable-sized buffers to tell the buffer implementation that soon more
* memory will be used. The implementation can ignore this.
*
* @param buffer Pointer to the buffer.
* @param size Number of bytes to reserve.
*/
static void reserve_additional(std::string* buffer, std::size_t size);
/**
* Delete data from the buffer. This must move back the data after the
* part being deleted and resize the buffer accordingly.
*
* @param buffer Pointer to the buffer.
* @param from Offset into the buffer where we want to erase from.
* @param to Offset into the buffer one past the last byte we want to erase.
*
* @pre from, to <= size of the buffer, from < to
*/
static void erase_range(std::string* buffer, std::size_t from, std::size_t to);
/**
* Return a pointer to the memory at the specified position in the buffer.
*
* @param buffer Pointer to the buffer.
* @param pos The position in the buffer.
* @returns pointer to the memory in the buffer at the specified position.
*
* @pre pos <= size of the buffer
*/
static char* at_pos(std::string* buffer, std::size_t pos);
/**
* Add a char to the buffer incrementing the number of chars in the buffer.
*
* @param buffer Pointer to the buffer.
* @param ch The character to add.
*/
static void push_back(std::string* buffer, char ch);
};
/// @endcond
} // namespace protozero
#endif // PROTOZERO_BUFFER_TMPL_HPP

View File

@ -0,0 +1,78 @@
#ifndef PROTOZERO_BUFFER_VECTOR_HPP
#define PROTOZERO_BUFFER_VECTOR_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
/**
* @file buffer_vector.hpp
*
* @brief Contains the customization points for buffer implementation based
* on std::vector<char>
*/
#include "buffer_tmpl.hpp"
#include "config.hpp"
#include <cstddef>
#include <iterator>
#include <vector>
namespace protozero {
// Implementation of buffer customizations points for std::vector<char>
/// @cond INTERNAL
template <>
struct buffer_customization<std::vector<char>> {
static std::size_t size(const std::vector<char>* buffer) noexcept {
return buffer->size();
}
static void append(std::vector<char>* buffer, const char* data, std::size_t count) {
buffer->insert(buffer->end(), data, data + count);
}
static void append_zeros(std::vector<char>* buffer, std::size_t count) {
buffer->insert(buffer->end(), count, '\0');
}
static void resize(std::vector<char>* buffer, std::size_t size) {
protozero_assert(size < buffer->size());
buffer->resize(size);
}
static void reserve_additional(std::vector<char>* buffer, std::size_t size) {
buffer->reserve(buffer->size() + size);
}
static void erase_range(std::vector<char>* buffer, std::size_t from, std::size_t to) {
protozero_assert(from <= buffer->size());
protozero_assert(to <= buffer->size());
protozero_assert(from <= to);
buffer->erase(std::next(buffer->begin(), static_cast<std::string::iterator::difference_type>(from)),
std::next(buffer->begin(), static_cast<std::string::iterator::difference_type>(to)));
}
static char* at_pos(std::vector<char>* buffer, std::size_t pos) {
protozero_assert(pos <= buffer->size());
return (&*buffer->begin()) + pos;
}
static void push_back(std::vector<char>* buffer, char ch) {
buffer->push_back(ch);
}
};
/// @endcond
} // namespace protozero
#endif // PROTOZERO_BUFFER_VECTOR_HPP

View File

@ -16,10 +16,10 @@ documentation.
* @brief Contains functions to swap bytes in values (for different endianness).
*/
#include <cassert>
#include <cstdint>
#include "config.hpp"
#include <protozero/config.hpp>
#include <cstdint>
#include <cstring>
namespace protozero {
namespace detail {
@ -28,10 +28,10 @@ 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);
return ((value & 0xff000000U) >> 24U) |
((value & 0x00ff0000U) >> 8U) |
((value & 0x0000ff00U) << 8U) |
((value & 0x000000ffU) << 24U);
#endif
}
@ -39,46 +39,70 @@ 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);
return ((value & 0xff00000000000000ULL) >> 56U) |
((value & 0x00ff000000000000ULL) >> 40U) |
((value & 0x0000ff0000000000ULL) >> 24U) |
((value & 0x000000ff00000000ULL) >> 8U) |
((value & 0x00000000ff000000ULL) << 8U) |
((value & 0x0000000000ff0000ULL) << 24U) |
((value & 0x000000000000ff00ULL) << 40U) |
((value & 0x00000000000000ffULL) << 56U);
#endif
}
} // end namespace detail
/// byteswap the data pointed to by ptr in-place.
inline void byteswap_inplace(uint32_t* ptr) noexcept {
*ptr = byteswap_impl(*ptr);
*ptr = detail::byteswap_impl(*ptr);
}
/// byteswap the data pointed to by ptr in-place.
inline void byteswap_inplace(uint64_t* ptr) noexcept {
*ptr = byteswap_impl(*ptr);
*ptr = detail::byteswap_impl(*ptr);
}
/// byteswap the data pointed to by ptr in-place.
inline void byteswap_inplace(int32_t* ptr) noexcept {
auto bptr = reinterpret_cast<uint32_t*>(ptr);
*bptr = byteswap_impl(*bptr);
auto* bptr = reinterpret_cast<uint32_t*>(ptr);
*bptr = detail::byteswap_impl(*bptr);
}
/// byteswap the data pointed to by ptr in-place.
inline void byteswap_inplace(int64_t* ptr) noexcept {
auto bptr = reinterpret_cast<uint64_t*>(ptr);
*bptr = byteswap_impl(*bptr);
auto* bptr = reinterpret_cast<uint64_t*>(ptr);
*bptr = detail::byteswap_impl(*bptr);
}
/// byteswap the data pointed to by ptr in-place.
inline void byteswap_inplace(float* ptr) noexcept {
auto bptr = reinterpret_cast<uint32_t*>(ptr);
*bptr = byteswap_impl(*bptr);
static_assert(sizeof(float) == 4, "Expecting four byte float");
uint32_t tmp = 0;
std::memcpy(&tmp, ptr, 4);
tmp = detail::byteswap_impl(tmp); // uint32 overload
std::memcpy(ptr, &tmp, 4);
}
/// byteswap the data pointed to by ptr in-place.
inline void byteswap_inplace(double* ptr) noexcept {
auto bptr = reinterpret_cast<uint64_t*>(ptr);
*bptr = byteswap_impl(*bptr);
static_assert(sizeof(double) == 8, "Expecting eight byte double");
uint64_t tmp = 0;
std::memcpy(&tmp, ptr, 8);
tmp = detail::byteswap_impl(tmp); // uint64 overload
std::memcpy(ptr, &tmp, 8);
}
namespace detail {
// Added for backwards compatibility with any code that might use this
// function (even if it shouldn't have). Will be removed in a later
// version of protozero.
using ::protozero::byteswap_inplace;
} // end namespace detail
} // end namespace protozero
#endif // PROTOZERO_BYTESWAP_HPP

236
protozero/data_view.hpp Normal file
View File

@ -0,0 +1,236 @@
#ifndef PROTOZERO_DATA_VIEW_HPP
#define PROTOZERO_DATA_VIEW_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
/**
* @file data_view.hpp
*
* @brief Contains the implementation of the data_view class.
*/
#include "config.hpp"
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <string>
#include <utility>
namespace protozero {
#ifdef PROTOZERO_USE_VIEW
using data_view = PROTOZERO_USE_VIEW;
#else
/**
* Holds a pointer to some data and a length.
*
* This class is supposed to be compatible with the std::string_view
* that will be available in C++17.
*/
class data_view {
const char* m_data = nullptr;
std::size_t m_size = 0;
public:
/**
* Default constructor. Construct an empty data_view.
*/
constexpr data_view() noexcept = default;
/**
* Create data_view from pointer and size.
*
* @param ptr Pointer to the data.
* @param length Length of the data.
*/
constexpr data_view(const char* ptr, std::size_t length) noexcept
: m_data{ptr},
m_size{length} {
}
/**
* Create data_view from string.
*
* @param str String with the data.
*/
data_view(const std::string& str) noexcept // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
: m_data{str.data()},
m_size{str.size()} {
}
/**
* Create data_view from zero-terminated string.
*
* @param ptr Pointer to the data.
*/
data_view(const char* ptr) noexcept // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
: m_data{ptr},
m_size{std::strlen(ptr)} {
}
/**
* Swap the contents of this object with the other.
*
* @param other Other object to swap data with.
*/
void swap(data_view& other) noexcept {
using std::swap;
swap(m_data, other.m_data);
swap(m_size, other.m_size);
}
/// Return pointer to data.
constexpr const char* data() const noexcept {
return m_data;
}
/// Return length of data in bytes.
constexpr std::size_t size() const noexcept {
return m_size;
}
/// Returns true if size is 0.
constexpr bool empty() const noexcept {
return m_size == 0;
}
#ifndef PROTOZERO_STRICT_API
/**
* Convert data view to string.
*
* @pre Must not be default constructed data_view.
*
* @deprecated to_string() is not available in C++17 string_view so it
* should not be used to make conversion to that class easier
* in the future.
*/
std::string to_string() const {
protozero_assert(m_data);
return {m_data, m_size};
}
#endif
/**
* Convert data view to string.
*
* @pre Must not be default constructed data_view.
*/
explicit operator std::string() const {
protozero_assert(m_data);
return {m_data, m_size};
}
/**
* Compares the contents of this object with the given other object.
*
* @returns 0 if they are the same, <0 if this object is smaller than
* the other or >0 if it is larger. If both objects have the
* same size returns <0 if this object is lexicographically
* before the other, >0 otherwise.
*
* @pre Must not be default constructed data_view.
*/
int compare(data_view other) const noexcept {
assert(m_data && other.m_data);
const int cmp = std::memcmp(data(), other.data(),
std::min(size(), other.size()));
if (cmp == 0) {
if (size() == other.size()) {
return 0;
}
return size() < other.size() ? -1 : 1;
}
return cmp;
}
}; // class data_view
/**
* Swap two data_view objects.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline void swap(data_view& lhs, data_view& rhs) noexcept {
lhs.swap(rhs);
}
/**
* Two data_view instances are equal if they have the same size and the
* same content.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline constexpr bool operator==(const data_view lhs, const data_view rhs) noexcept {
return lhs.size() == rhs.size() &&
std::equal(lhs.data(), lhs.data() + lhs.size(), rhs.data());
}
/**
* Two data_view instances are not equal if they have different sizes or the
* content differs.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline constexpr bool operator!=(const data_view lhs, const data_view rhs) noexcept {
return !(lhs == rhs);
}
/**
* Returns true if lhs.compare(rhs) < 0.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline bool operator<(const data_view lhs, const data_view rhs) noexcept {
return lhs.compare(rhs) < 0;
}
/**
* Returns true if lhs.compare(rhs) <= 0.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline bool operator<=(const data_view lhs, const data_view rhs) noexcept {
return lhs.compare(rhs) <= 0;
}
/**
* Returns true if lhs.compare(rhs) > 0.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline bool operator>(const data_view lhs, const data_view rhs) noexcept {
return lhs.compare(rhs) > 0;
}
/**
* Returns true if lhs.compare(rhs) >= 0.
*
* @param lhs First object.
* @param rhs Second object.
*/
inline bool operator>=(const data_view lhs, const data_view rhs) noexcept {
return lhs.compare(rhs) >= 0;
}
#endif
} // end namespace protozero
#endif // PROTOZERO_DATA_VIEW_HPP

View File

@ -29,7 +29,9 @@ namespace protozero {
*/
struct exception : std::exception {
/// Returns the explanatory string.
const char* what() const noexcept override { return "pbf exception"; }
const char* what() const noexcept override {
return "pbf exception";
}
};
/**
@ -38,7 +40,9 @@ struct exception : std::exception {
*/
struct varint_too_long_exception : exception {
/// 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 +51,9 @@ struct varint_too_long_exception : exception {
*/
struct unknown_pbf_wire_type_exception : exception {
/// 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 +66,34 @@ struct unknown_pbf_wire_type_exception : exception {
*/
struct end_of_buffer_exception : exception {
/// 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";
}
};
/**
* This exception is thrown when a tag has an invalid value. Tags must be
* unsigned integers between 1 and 2^29-1. Tags between 19000 and 19999 are
* not allowed. See
* https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
*/
struct invalid_tag_exception : exception {
/// Returns the explanatory string.
const char* what() const noexcept override {
return "invalid tag exception";
}
};
/**
* This exception is thrown when a length field of a packed repeated field is
* invalid. For fixed size types the length must be a multiple of the size of
* the type.
*/
struct invalid_length_exception : exception {
/// Returns the explanatory string.
const char* what() const noexcept override {
return "invalid length exception";
}
};
} // end namespace protozero

View File

@ -16,17 +16,18 @@ documentation.
* @brief Contains the iterators for access to packed repeated fields.
*/
#include <cstring>
#include <iterator>
#include <utility>
#include <protozero/config.hpp>
#include <protozero/varint.hpp>
#include "config.hpp"
#include "varint.hpp"
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
# include <protozero/byteswap.hpp>
#endif
#include <algorithm>
#include <cstring>
#include <iterator>
#include <utility>
namespace protozero {
/**
@ -55,18 +56,18 @@ public:
* Default constructor. Create empty iterator_range.
*/
constexpr iterator_range() :
P(iterator{}, iterator{}) {
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.
* @param first_iterator Iterator to beginning of range.
* @param last_iterator Iterator to end of range.
*/
constexpr iterator_range(iterator&& first_iterator, iterator&& last_iterator) :
P(std::forward<iterator>(first_iterator),
std::forward<iterator>(last_iterator)) {
P{std::forward<iterator>(first_iterator),
std::forward<iterator>(last_iterator)} {
}
/// Return iterator to beginning of range.
@ -89,11 +90,24 @@ public:
return this->second;
}
/// Return true if this range is empty.
constexpr std::size_t empty() const noexcept {
/**
* Return true if this range is empty.
*
* Complexity: Constant.
*/
constexpr bool empty() const noexcept {
return begin() == end();
}
/**
* Get the size of the range, ie the number of elements it contains.
*
* Complexity: Constant or linear depending on the underlaying iterator.
*/
std::size_t size() const noexcept {
return static_cast<size_t>(std::distance(begin(), end()));
}
/**
* Get element at the beginning of the range.
*
@ -146,27 +160,22 @@ 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;
const char* m_data = nullptr;
public:
using iterator_category = std::forward_iterator_tag;
/// @cond usual iterator functions not documented
using iterator_category = std::random_access_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
const_fixed_iterator() noexcept :
m_data(nullptr),
m_end(nullptr) {
}
const_fixed_iterator() noexcept = default;
const_fixed_iterator(const char* data, const char* end) noexcept :
m_data(data),
m_end(end) {
explicit const_fixed_iterator(const char* data) noexcept :
m_data{data} {
}
const_fixed_iterator(const const_fixed_iterator&) noexcept = default;
@ -177,34 +186,99 @@ public:
~const_fixed_iterator() noexcept = default;
value_type operator*() const {
value_type operator*() const noexcept {
value_type result;
std::memcpy(&result, m_data, sizeof(value_type));
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
detail::byteswap_inplace(&result);
byteswap_inplace(&result);
#endif
return result;
}
const_fixed_iterator& operator++() {
const_fixed_iterator& operator++() noexcept {
m_data += sizeof(value_type);
return *this;
}
const_fixed_iterator operator++(int) {
const const_fixed_iterator tmp(*this);
const_fixed_iterator operator++(int) noexcept {
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;
const_fixed_iterator& operator--() noexcept {
m_data -= sizeof(value_type);
return *this;
}
bool operator!=(const const_fixed_iterator& rhs) const noexcept {
return !(*this == rhs);
const_fixed_iterator operator--(int) noexcept {
const const_fixed_iterator tmp{*this};
--(*this);
return tmp;
}
friend bool operator==(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return lhs.m_data == rhs.m_data;
}
friend bool operator!=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return !(lhs == rhs);
}
friend bool operator<(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return lhs.m_data < rhs.m_data;
}
friend bool operator>(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return rhs < lhs;
}
friend bool operator<=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return !(lhs > rhs);
}
friend bool operator>=(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return !(lhs < rhs);
}
const_fixed_iterator& operator+=(difference_type val) noexcept {
m_data += (sizeof(value_type) * val);
return *this;
}
friend const_fixed_iterator operator+(const_fixed_iterator lhs, difference_type rhs) noexcept {
const_fixed_iterator tmp{lhs};
tmp.m_data += (sizeof(value_type) * rhs);
return tmp;
}
friend const_fixed_iterator operator+(difference_type lhs, const_fixed_iterator rhs) noexcept {
const_fixed_iterator tmp{rhs};
tmp.m_data += (sizeof(value_type) * lhs);
return tmp;
}
const_fixed_iterator& operator-=(difference_type val) noexcept {
m_data -= (sizeof(value_type) * val);
return *this;
}
friend const_fixed_iterator operator-(const_fixed_iterator lhs, difference_type rhs) noexcept {
const_fixed_iterator tmp{lhs};
tmp.m_data -= (sizeof(value_type) * rhs);
return tmp;
}
friend difference_type operator-(const_fixed_iterator lhs, const_fixed_iterator rhs) noexcept {
return static_cast<difference_type>(lhs.m_data - rhs.m_data) / static_cast<difference_type>(sizeof(T));
}
value_type operator[](difference_type n) const noexcept {
return *(*this + n);
}
/// @endcond
}; // class const_fixed_iterator
/**
@ -217,27 +291,40 @@ class const_varint_iterator {
protected:
/// Pointer to current iterator position
const char* m_data;
const char* m_data = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors)
/// Pointer to end iterator position
const char* m_end;
const char* m_end = nullptr; // NOLINT(misc-non-private-member-variables-in-classes, cppcoreguidelines-non-private-member-variables-in-classes,-warnings-as-errors)
public:
/// @cond usual iterator functions not documented
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = value_type*;
using reference = value_type&;
const_varint_iterator() noexcept :
m_data(nullptr),
m_end(nullptr) {
static difference_type distance(const_varint_iterator begin, const_varint_iterator end) noexcept {
// The "distance" between default initialized const_varint_iterator's
// is always 0.
if (!begin.m_data) {
return 0;
}
// We know that each varint contains exactly one byte with the most
// significant bit not set. We can use this to quickly figure out
// how many varints there are without actually decoding the varints.
return std::count_if(begin.m_data, end.m_data, [](char c) noexcept {
return (static_cast<unsigned char>(c) & 0x80U) == 0;
});
}
const_varint_iterator() noexcept = default;
const_varint_iterator(const char* data, const char* end) noexcept :
m_data(data),
m_end(end) {
m_data{data},
m_end{end} {
}
const_varint_iterator(const const_varint_iterator&) noexcept = default;
@ -249,17 +336,20 @@ public:
~const_varint_iterator() noexcept = default;
value_type operator*() const {
protozero_assert(m_data);
const char* d = m_data; // will be thrown away
return static_cast<value_type>(decode_varint(&d, m_end));
}
const_varint_iterator& operator++() {
protozero_assert(m_data);
skip_varint(&m_data, m_end);
return *this;
}
const_varint_iterator operator++(int) {
const const_varint_iterator tmp(*this);
protozero_assert(m_data);
const const_varint_iterator tmp{*this};
++(*this);
return tmp;
}
@ -272,6 +362,8 @@ public:
return !(*this == rhs);
}
/// @endcond
}; // class const_varint_iterator
/**
@ -283,6 +375,8 @@ class const_svarint_iterator : public const_varint_iterator<T> {
public:
/// @cond usual iterator functions not documented
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
@ -290,39 +384,98 @@ public:
using reference = value_type&;
const_svarint_iterator() noexcept :
const_varint_iterator<T>() {
const_varint_iterator<T>{} {
}
const_svarint_iterator(const char* data, const char* end) noexcept :
const_varint_iterator<T>(data, end) {
const_varint_iterator<T>{data, end} {
}
const_svarint_iterator(const const_svarint_iterator&) = default;
const_svarint_iterator(const_svarint_iterator&&) = default;
const_svarint_iterator(const_svarint_iterator&&) noexcept = default;
const_svarint_iterator& operator=(const const_svarint_iterator&) = default;
const_svarint_iterator& operator=(const_svarint_iterator&&) = default;
const_svarint_iterator& operator=(const_svarint_iterator&&) noexcept = default;
~const_svarint_iterator() = default;
value_type operator*() const {
protozero_assert(this->m_data);
const char* d = this->m_data; // will be thrown away
return static_cast<value_type>(decode_zigzag64(decode_varint(&d, this->m_end)));
}
const_svarint_iterator& operator++() {
protozero_assert(this->m_data);
skip_varint(&this->m_data, this->m_end);
return *this;
}
const_svarint_iterator operator++(int) {
const const_svarint_iterator tmp(*this);
protozero_assert(this->m_data);
const const_svarint_iterator tmp{*this};
++(*this);
return tmp;
}
/// @endcond
}; // class const_svarint_iterator
} // end namespace protozero
namespace std {
// Specialize std::distance for all the protozero iterators. Because
// functions can't be partially specialized, we have to do this for
// every value_type we are using.
/// @cond individual overloads do not need to be documented
template <>
inline typename protozero::const_varint_iterator<int32_t>::difference_type
distance<protozero::const_varint_iterator<int32_t>>(protozero::const_varint_iterator<int32_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
protozero::const_varint_iterator<int32_t> last) {
return protozero::const_varint_iterator<int32_t>::distance(first, last);
}
template <>
inline typename protozero::const_varint_iterator<int64_t>::difference_type
distance<protozero::const_varint_iterator<int64_t>>(protozero::const_varint_iterator<int64_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
protozero::const_varint_iterator<int64_t> last) {
return protozero::const_varint_iterator<int64_t>::distance(first, last);
}
template <>
inline typename protozero::const_varint_iterator<uint32_t>::difference_type
distance<protozero::const_varint_iterator<uint32_t>>(protozero::const_varint_iterator<uint32_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
protozero::const_varint_iterator<uint32_t> last) {
return protozero::const_varint_iterator<uint32_t>::distance(first, last);
}
template <>
inline typename protozero::const_varint_iterator<uint64_t>::difference_type
distance<protozero::const_varint_iterator<uint64_t>>(protozero::const_varint_iterator<uint64_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
protozero::const_varint_iterator<uint64_t> last) {
return protozero::const_varint_iterator<uint64_t>::distance(first, last);
}
template <>
inline typename protozero::const_svarint_iterator<int32_t>::difference_type
distance<protozero::const_svarint_iterator<int32_t>>(protozero::const_svarint_iterator<int32_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
protozero::const_svarint_iterator<int32_t> last) {
return protozero::const_svarint_iterator<int32_t>::distance(first, last);
}
template <>
inline typename protozero::const_svarint_iterator<int64_t>::difference_type
distance<protozero::const_svarint_iterator<int64_t>>(protozero::const_svarint_iterator<int64_t> first, // NOLINT(readability-inconsistent-declaration-parameter-name)
protozero::const_svarint_iterator<int64_t> last) {
return protozero::const_svarint_iterator<int64_t>::distance(first, last);
}
/// @endcond
} // end namespace std
#endif // PROTOZERO_ITERATORS_HPP

View File

@ -16,144 +16,16 @@ documentation.
* @brief Contains the pbf_builder template class.
*/
#include <type_traits>
#include "basic_pbf_builder.hpp"
#include "pbf_writer.hpp"
#include <protozero/pbf_writer.hpp>
#include <protozero/types.hpp>
#include <string>
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.
*/
/// Specialization of basic_pbf_builder using std::string as buffer type.
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
using pbf_builder = basic_pbf_builder<std::string, T>;
} // end namespace protozero

View File

@ -16,10 +16,10 @@ documentation.
* @brief Contains the pbf_message template class.
*/
#include <type_traits>
#include "pbf_reader.hpp"
#include "types.hpp"
#include <protozero/pbf_reader.hpp>
#include <protozero/types.hpp>
#include <type_traits>
namespace protozero {
@ -35,7 +35,7 @@ namespace protozero {
*
* std::string buffer;
* // fill buffer...
* pbf_message<Message> message(buffer.data(), buffer.size());
* pbf_message<Message> message{buffer.data(), buffer.size()};
* @endcode
*
* Sub-messages are created using get_message():
@ -45,7 +45,7 @@ namespace protozero {
* ...
* };
*
* pbf_message<Message> message(...);
* pbf_message<Message> message{...};
* message.next();
* pbf_message<SubMessage> submessage = message.get_message();
* @endcode
@ -64,29 +64,115 @@ namespace protozero {
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");
static_assert(std::is_same<pbf_tag_type, typename std::underlying_type<T>::type>::value,
"T must be enum with underlying type protozero::pbf_tag_type");
public:
/// The type of messages this class will read.
using enum_type = T;
/**
* Construct a pbf_message. All arguments are forwarded to the pbf_reader
* parent class.
*/
template <typename... Args>
pbf_message(Args&&... args) noexcept :
pbf_reader(std::forward<Args>(args)...) {
pbf_message(Args&&... args) noexcept : // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
pbf_reader{std::forward<Args>(args)...} {
}
/**
* Set next field in the message as the current field. This is usually
* called in a while loop:
*
* @code
* pbf_message<...> message(...);
* while (message.next()) {
* // handle field
* }
* @endcode
*
* @returns `true` if there is a next field, `false` if not.
* @pre There must be no current field.
* @post If it returns `true` there is a current field now.
*/
bool next() {
return pbf_reader::next();
}
/**
* Set next field with given tag in the message as the current field.
* Fields with other tags are skipped. This is usually called in a while
* loop for repeated fields:
*
* @code
* pbf_message<Example1> message{...};
* while (message.next(Example1::repeated_fixed64_r)) {
* // handle field
* }
* @endcode
*
* or you can call it just once to get the one field with this tag:
*
* @code
* pbf_message<Example1> message{...};
* if (message.next(Example1::required_uint32_x)) {
* // handle field
* }
* @endcode
*
* Note that this will not check the wire type. The two-argument version
* of this function will also check the wire type.
*
* @returns `true` if there is a next field with this tag.
* @pre There must be no current field.
* @post If it returns `true` there is a current field now with the given tag.
*/
bool next(T next_tag) {
return pbf_reader::next(pbf_tag_type(next_tag));
}
/**
* Set next field with given tag and wire type in the message as the
* current field. Fields with other tags are skipped. This is usually
* called in a while loop for repeated fields:
*
* @code
* pbf_message<Example1> message{...};
* while (message.next(Example1::repeated_fixed64_r, pbf_wire_type::varint)) {
* // handle field
* }
* @endcode
*
* or you can call it just once to get the one field with this tag:
*
* @code
* pbf_message<Example1> message{...};
* if (message.next(Example1::required_uint32_x, pbf_wire_type::varint)) {
* // handle field
* }
* @endcode
*
* Note that this will also check the wire type. The one-argument version
* of this function will not check the wire type.
*
* @returns `true` if there is a next field with this tag.
* @pre There must be no current field.
* @post If it returns `true` there is a current field now with the given tag.
*/
bool next(T next_tag, pbf_wire_type type) {
return pbf_reader::next(pbf_tag_type(next_tag), type);
}
/**
* The tag of the current field. The tag is the enum value for the field
* number from the description in the .proto file.
*
* Call next() before calling this function to set the current field.
*
* @returns tag of the current field.
* @pre There must be a current field (ie. next() must have returned `true`).
*/
T tag() const noexcept {
return T(pbf_reader::tag());
}

View File

@ -16,21 +16,23 @@ documentation.
* @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>
#include "config.hpp"
#include "data_view.hpp"
#include "exception.hpp"
#include "iterators.hpp"
#include "types.hpp"
#include "varint.hpp"
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
# include <protozero/byteswap.hpp>
#endif
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <utility>
namespace protozero {
/**
@ -41,13 +43,13 @@ namespace protozero {
* @code
* std::string buffer;
* // fill buffer...
* pbf_reader message(buffer.data(), buffer.size());
* pbf_reader message{buffer.data(), buffer.size()};
* @endcode
*
* Sub-messages are created using get_message():
*
* @code
* pbf_reader message(...);
* pbf_reader message{...};
* message.next();
* pbf_reader submessage = message.get_message();
* @endcode
@ -74,10 +76,11 @@ class pbf_reader {
template <typename T>
T get_fixed() {
T result;
const char* data = m_data;
skip_bytes(sizeof(T));
std::memcpy(&result, m_data - sizeof(T), sizeof(T));
std::memcpy(&result, data, sizeof(T));
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
detail::byteswap_inplace(&result);
byteswap_inplace(&result);
#endif
return result;
}
@ -86,14 +89,17 @@ class pbf_reader {
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)};
if (len % sizeof(T) != 0) {
throw invalid_length_exception{};
}
return {const_fixed_iterator<T>(m_data - len),
const_fixed_iterator<T>(m_data)};
}
template <typename T>
T get_varint() {
return static_cast<T>(decode_varint(&m_data, m_end));
const auto val = static_cast<T>(decode_varint(&m_data, m_end));
return val;
}
template <typename T>
@ -107,14 +113,14 @@ class pbf_reader {
}
void skip_bytes(pbf_length_type len) {
if (m_data + len > m_end) {
throw end_of_buffer_exception();
if (m_end - m_data < static_cast<ptrdiff_t>(len)) {
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
// In debug builds reset the tag to zero so that we can detect (some)
// wrong code.
m_tag = 0;
#endif
}
@ -129,8 +135,8 @@ class pbf_reader {
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}};
return {T{m_data - len, m_data},
T{m_data, m_data}};
}
public:
@ -146,10 +152,8 @@ public:
* @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) {
: m_data{view.data()},
m_end{view.data() + view.size()} {
}
/**
@ -163,12 +167,11 @@ public:
* @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) {
: m_data{data},
m_end{data + size} {
}
#ifndef PROTOZERO_STRICT_API
/**
* Construct a pbf_reader message from a data pointer and a length. The
* pointer will be stored inside the pbf_reader object, no data is copied.
@ -178,13 +181,13 @@ public:
* The buffer must contain a complete protobuf message.
*
* @post There is no current field.
* @deprecated Use one of the other constructors.
*/
explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
: m_data(data.first),
m_end(data.first + data.second),
m_wire_type(pbf_wire_type::unknown),
m_tag(0) {
: m_data{data.first},
m_end{data.first + data.second} {
}
#endif
/**
* Construct a pbf_reader message from a std::string. A pointer to the
@ -197,10 +200,8 @@ public:
* @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) {
: m_data{data.data()},
m_end{data.data() + data.size()} {
}
/**
@ -241,8 +242,15 @@ public:
* are still fields available and to `false` if the last field has been
* read.
*/
operator bool() const noexcept {
return m_data < m_end;
operator bool() const noexcept { // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
return m_data != m_end;
}
/**
* Get a view of the not yet read data.
*/
data_view data() const noexcept {
return {m_data, static_cast<std::size_t>(m_end - m_data)};
}
/**
@ -279,14 +287,15 @@ public:
}
const auto value = get_varint<uint32_t>();
m_tag = pbf_tag_type(value >> 3);
m_tag = pbf_tag_type(value >> 3U);
// tags 0 and 19000 to 19999 are not allowed as per
// https://developers.google.com/protocol-buffers/docs/proto
protozero_assert(((m_tag > 0 && m_tag < 19000) ||
(m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
// https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
if (m_tag == 0 || (m_tag >= 19000 && m_tag <= 19999)) {
throw invalid_tag_exception{};
}
m_wire_type = pbf_wire_type(value & 0x07);
m_wire_type = pbf_wire_type(value & 0x07U);
switch (m_wire_type) {
case pbf_wire_type::varint:
case pbf_wire_type::fixed64:
@ -294,7 +303,7 @@ public:
case pbf_wire_type::fixed32:
break;
default:
throw unknown_pbf_wire_type_exception();
throw unknown_pbf_wire_type_exception{};
}
return true;
@ -306,7 +315,7 @@ public:
* loop for repeated fields:
*
* @code
* pbf_reader message(...);
* pbf_reader message{...};
* while (message.next(17)) {
* // handle field
* }
@ -315,7 +324,7 @@ public:
* or you can call it just once to get the one field with this tag:
*
* @code
* pbf_reader message(...);
* pbf_reader message{...};
* if (message.next(17)) {
* // handle field
* }
@ -332,9 +341,8 @@ public:
while (next()) {
if (m_tag == next_tag) {
return true;
} else {
skip();
}
skip();
}
return false;
}
@ -345,7 +353,7 @@ public:
* called in a while loop for repeated fields:
*
* @code
* pbf_reader message(...);
* pbf_reader message{...};
* while (message.next(17, pbf_wire_type::varint)) {
* // handle field
* }
@ -354,7 +362,7 @@ public:
* or you can call it just once to get the one field with this tag:
*
* @code
* pbf_reader message(...);
* pbf_reader message{...};
* if (message.next(17, pbf_wire_type::varint)) {
* // handle field
* }
@ -371,9 +379,8 @@ public:
while (next()) {
if (m_tag == next_tag && m_wire_type == type) {
return true;
} else {
skip();
}
skip();
}
return false;
}
@ -417,7 +424,7 @@ public:
* Use it like this:
*
* @code
* pbf_reader message(...);
* pbf_reader message{...};
* while (message.next()) {
* switch (message.tag_and_type()) {
* case tag_and_type(17, pbf_wire_type::length_delimited):
@ -468,7 +475,7 @@ public:
skip_bytes(4);
break;
default:
protozero_assert(false && "can not be here because next() should have thrown already");
break;
}
}
@ -487,9 +494,9 @@ public:
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
const bool result = m_data[0] != 0;
skip_varint(&m_data, m_end);
return result;
}
/**
@ -667,7 +674,7 @@ public:
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};
return {m_data - len, len};
}
#ifndef PROTOZERO_STRICT_API
@ -683,7 +690,7 @@ public:
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);
return {m_data - len, len};
}
#endif
@ -717,7 +724,7 @@ public:
* @post The current field was consumed and there is no current field now.
*/
pbf_reader get_message() {
return pbf_reader(get_view());
return pbf_reader{get_view()};
}
///@}
@ -746,6 +753,24 @@ public:
/// Forward iterator for iterating over uint64 (varint) values.
using const_uint64_iterator = const_varint_iterator<uint64_t>;
/// Forward iterator for iterating over fixed32 values.
using const_fixed32_iterator = const_fixed_iterator<uint32_t>;
/// Forward iterator for iterating over sfixed32 values.
using const_sfixed32_iterator = const_fixed_iterator<int32_t>;
/// Forward iterator for iterating over fixed64 values.
using const_fixed64_iterator = const_fixed_iterator<uint64_t>;
/// Forward iterator for iterating over sfixed64 values.
using const_sfixed64_iterator = const_fixed_iterator<int64_t>;
/// Forward iterator for iterating over float values.
using const_float_iterator = const_fixed_iterator<float>;
/// Forward iterator for iterating over double values.
using const_double_iterator = const_fixed_iterator<double>;
///@{
/**
* @name Repeated packed field accessor functions
@ -864,7 +889,7 @@ public:
* @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>()) {
iterator_range<pbf_reader::const_fixed32_iterator> get_packed_fixed32() {
return packed_fixed<uint32_t>();
}
@ -877,7 +902,7 @@ public:
* @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>()) {
iterator_range<pbf_reader::const_sfixed32_iterator> get_packed_sfixed32() {
return packed_fixed<int32_t>();
}
@ -890,7 +915,7 @@ public:
* @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>()) {
iterator_range<pbf_reader::const_fixed64_iterator> get_packed_fixed64() {
return packed_fixed<uint64_t>();
}
@ -903,7 +928,7 @@ public:
* @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>()) {
iterator_range<pbf_reader::const_sfixed64_iterator> get_packed_sfixed64() {
return packed_fixed<int64_t>();
}
@ -916,7 +941,7 @@ public:
* @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>()) {
iterator_range<pbf_reader::const_float_iterator> get_packed_float() {
return packed_fixed<float>();
}
@ -929,7 +954,7 @@ public:
* @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>()) {
iterator_range<pbf_reader::const_double_iterator> get_packed_double() {
return packed_fixed<double>();
}

View File

@ -16,959 +16,60 @@ documentation.
* @brief Contains the pbf_writer class.
*/
#include <cstddef>
#include "basic_pbf_writer.hpp"
#include "buffer_string.hpp"
#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.
* Specialization of basic_pbf_writer using std::string as buffer type.
*/
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
using pbf_writer = basic_pbf_writer<std::string>;
/// Class for generating packed repeated bool fields.
using packed_field_bool = detail::packed_field_varint<bool>;
using packed_field_bool = detail::packed_field_varint<std::string, bool>;
/// Class for generating packed repeated enum fields.
using packed_field_enum = detail::packed_field_varint<int32_t>;
using packed_field_enum = detail::packed_field_varint<std::string, int32_t>;
/// Class for generating packed repeated int32 fields.
using packed_field_int32 = detail::packed_field_varint<int32_t>;
using packed_field_int32 = detail::packed_field_varint<std::string, int32_t>;
/// Class for generating packed repeated sint32 fields.
using packed_field_sint32 = detail::packed_field_svarint<int32_t>;
using packed_field_sint32 = detail::packed_field_svarint<std::string, int32_t>;
/// Class for generating packed repeated uint32 fields.
using packed_field_uint32 = detail::packed_field_varint<uint32_t>;
using packed_field_uint32 = detail::packed_field_varint<std::string, uint32_t>;
/// Class for generating packed repeated int64 fields.
using packed_field_int64 = detail::packed_field_varint<int64_t>;
using packed_field_int64 = detail::packed_field_varint<std::string, int64_t>;
/// Class for generating packed repeated sint64 fields.
using packed_field_sint64 = detail::packed_field_svarint<int64_t>;
using packed_field_sint64 = detail::packed_field_svarint<std::string, int64_t>;
/// Class for generating packed repeated uint64 fields.
using packed_field_uint64 = detail::packed_field_varint<uint64_t>;
using packed_field_uint64 = detail::packed_field_varint<std::string, uint64_t>;
/// Class for generating packed repeated fixed32 fields.
using packed_field_fixed32 = detail::packed_field_fixed<uint32_t>;
using packed_field_fixed32 = detail::packed_field_fixed<std::string, uint32_t>;
/// Class for generating packed repeated sfixed32 fields.
using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
using packed_field_sfixed32 = detail::packed_field_fixed<std::string, int32_t>;
/// Class for generating packed repeated fixed64 fields.
using packed_field_fixed64 = detail::packed_field_fixed<uint64_t>;
using packed_field_fixed64 = detail::packed_field_fixed<std::string, uint64_t>;
/// Class for generating packed repeated sfixed64 fields.
using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
using packed_field_sfixed64 = detail::packed_field_fixed<std::string, int64_t>;
/// Class for generating packed repeated float fields.
using packed_field_float = detail::packed_field_fixed<float>;
using packed_field_float = detail::packed_field_fixed<std::string, float>;
/// Class for generating packed repeated double fields.
using packed_field_double = detail::packed_field_fixed<double>;
using packed_field_double = detail::packed_field_fixed<std::string, double>;
} // end namespace protozero

View File

@ -16,6 +16,8 @@ documentation.
* @brief Contains the declaration of low-level types used in the pbf format.
*/
#include "config.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
@ -23,8 +25,6 @@ documentation.
#include <string>
#include <utility>
#include <protozero/config.hpp>
namespace protozero {
/**
@ -40,8 +40,7 @@ using pbf_tag_type = uint32_t;
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
length_delimited = 2, // string, bytes, nested messages, packed repeated fields
fixed32 = 5, // fixed32, sfixed32, float
unknown = 99 // used for default setting in this library
};
@ -54,7 +53,7 @@ enum class pbf_wire_type : uint32_t {
*/
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);
return (static_cast<uint32_t>(static_cast<pbf_tag_type>(tag)) << 3U) | static_cast<uint32_t>(wire_type);
}
/**
@ -62,145 +61,6 @@ constexpr inline uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept
*/
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

View File

@ -16,9 +16,10 @@ documentation.
* @brief Contains low-level varint and zigzag encoding and decoding functions.
*/
#include <cstdint>
#include "buffer_tmpl.hpp"
#include "exception.hpp"
#include <protozero/exception.hpp>
#include <cstdint>
namespace protozero {
@ -31,34 +32,34 @@ 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 auto* begin = reinterpret_cast<const int8_t*>(*data);
const auto* iend = reinterpret_cast<const int8_t*>(end);
const int8_t* p = begin;
uint64_t val = 0;
if (iend - begin >= max_varint_length) { // fast path
do {
int64_t b;
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();
int64_t b = *p++;
val = ((uint64_t(b) & 0x7fU) ); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 7U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 14U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 21U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 28U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 35U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 42U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 49U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x7fU) << 56U); if (b >= 0) { break; }
b = *p++; val |= ((uint64_t(b) & 0x01U) << 63U); if (b >= 0) { break; }
throw varint_too_long_exception{};
} while (false);
} else {
int shift = 0;
unsigned int shift = 0;
while (p != iend && *p < 0) {
val |= uint64_t(*p++ & 0x7f) << shift;
val |= (uint64_t(*p++) & 0x7fU) << shift;
shift += 7;
}
if (p == iend) {
throw end_of_buffer_exception();
throw end_of_buffer_exception{};
}
val |= uint64_t(*p++) << shift;
}
@ -88,8 +89,8 @@ namespace detail {
*/
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);
if (end != *data && ((static_cast<uint64_t>(**data) & 0x80U) == 0)) {
const auto val = static_cast<uint64_t>(**data);
++(*data);
return val;
}
@ -110,20 +111,20 @@ inline uint64_t decode_varint(const char** data, const char* end) {
* 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 auto* begin = reinterpret_cast<const int8_t*>(*data);
const auto* iend = reinterpret_cast<const int8_t*>(end);
const int8_t* p = begin;
while (p != iend && *p < 0) {
++p;
}
if (p >= begin + max_varint_length) {
throw varint_too_long_exception();
if (p - begin >= max_varint_length) {
throw varint_too_long_exception{};
}
if (p == iend) {
throw end_of_buffer_exception();
throw end_of_buffer_exception{};
}
++p;
@ -138,18 +139,75 @@ inline void skip_varint(const char** data, const char* end) {
* @param data Output iterator the varint encoded value will be written to
* byte by byte.
* @param value The integer that will be encoded.
* @returns the number of bytes written
* @throws Any exception thrown by increment or dereference operator on data.
* @deprecated Use add_varint_to_buffer() instead.
*/
template <typename T>
inline int write_varint(T data, uint64_t value) {
int n = 1;
while (value >= 0x80) {
*data++ = char((value & 0x7f) | 0x80);
value >>= 7;
while (value >= 0x80U) {
*data++ = char((value & 0x7fU) | 0x80U);
value >>= 7U;
++n;
}
*data = char(value);
return n;
}
/**
* Varint encode a 64 bit integer.
*
* @tparam TBuffer A buffer type.
* @param buffer Output buffer the varint will be written to.
* @param value The integer that will be encoded.
* @returns the number of bytes written
* @throws Any exception thrown by calling the buffer_push_back() function.
*/
template <typename TBuffer>
inline void add_varint_to_buffer(TBuffer* buffer, uint64_t value) {
while (value >= 0x80U) {
buffer_customization<TBuffer>::push_back(buffer, char((value & 0x7fU) | 0x80U));
value >>= 7U;
}
buffer_customization<TBuffer>::push_back(buffer, char(value));
}
/**
* Varint encode a 64 bit integer.
*
* @param data Where to add the varint. There must be enough space available!
* @param value The integer that will be encoded.
* @returns the number of bytes written
*/
inline int add_varint_to_buffer(char* data, uint64_t value) noexcept {
int n = 1;
while (value >= 0x80U) {
*data++ = char((value & 0x7fU) | 0x80U);
value >>= 7U;
++n;
}
*data = char(value);
return n;
}
/**
* Get the length of the varint the specified value would produce.
*
* @param value The integer to be encoded.
* @returns the number of bytes the varint would have if we created it.
*/
inline int length_of_varint(uint64_t value) noexcept {
int n = 1;
while (value >= 0x80U) {
value >>= 7U;
++n;
}
*data++ = char(value);
return n;
}
@ -158,28 +216,28 @@ inline int write_varint(T data, uint64_t value) {
* 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));
return (static_cast<uint32_t>(value) << 1U) ^ static_cast<uint32_t>(-static_cast<int32_t>(static_cast<uint32_t>(value) >> 31U));
}
/**
* ZigZag encodes a 64 bit integer.
*/
inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept {
return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
return (static_cast<uint64_t>(value) << 1U) ^ static_cast<uint64_t>(-static_cast<int64_t>(static_cast<uint64_t>(value) >> 63U));
}
/**
* Decodes a 32 bit ZigZag-encoded integer.
*/
inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept {
return static_cast<int32_t>(value >> 1) ^ -static_cast<int32_t>(value & 1);
return static_cast<int32_t>((value >> 1U) ^ static_cast<uint32_t>(-static_cast<int32_t>(value & 1U)));
}
/**
* Decodes a 64 bit ZigZag-encoded integer.
*/
inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept {
return static_cast<int64_t>(value >> 1) ^ -static_cast<int64_t>(value & 1);
return static_cast<int64_t>((value >> 1U) ^ static_cast<uint64_t>(-static_cast<int64_t>(value & 1U)));
}
} // end namespace protozero

View File

@ -20,15 +20,15 @@ documentation.
#define PROTOZERO_VERSION_MAJOR 1
/// The minor version number
#define PROTOZERO_VERSION_MINOR 5
#define PROTOZERO_VERSION_MINOR 7
/// The patch number
#define PROTOZERO_VERSION_PATCH 2
#define PROTOZERO_VERSION_PATCH 1
/// 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"
#define PROTOZERO_VERSION_STRING "1.7.1"
#endif // PROTOZERO_VERSION_HPP

View File

@ -1,6 +1,6 @@
#ifndef VERSION_HPP
#define VERSION_HPP
#define VERSION "v2.6.0"
#define VERSION "v2.6.1"
#endif