mirror of
https://github.com/mapbox/tippecanoe.git
synced 2025-04-08 11:34:12 +00:00
Upgrade to protozero 1.7.1 (#5)
* Upgrade to protozero 1.7.1 * Bump version number
This commit is contained in:
parent
073700aa38
commit
8e46650da7
@ -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
|
||||
|
266
protozero/basic_pbf_builder.hpp
Normal file
266
protozero/basic_pbf_builder.hpp
Normal 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
|
1054
protozero/basic_pbf_writer.hpp
Normal file
1054
protozero/basic_pbf_writer.hpp
Normal file
File diff suppressed because it is too large
Load Diff
222
protozero/buffer_fixed.hpp
Normal file
222
protozero/buffer_fixed.hpp
Normal 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
|
78
protozero/buffer_string.hpp
Normal file
78
protozero/buffer_string.hpp
Normal 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
113
protozero/buffer_tmpl.hpp
Normal 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
|
78
protozero/buffer_vector.hpp
Normal file
78
protozero/buffer_vector.hpp
Normal 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
|
@ -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
236
protozero/data_view.hpp
Normal 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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -1,6 +1,6 @@
|
||||
#ifndef VERSION_HPP
|
||||
#define VERSION_HPP
|
||||
|
||||
#define VERSION "v2.6.0"
|
||||
#define VERSION "v2.6.1"
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user