pbf_reader.hpp
Go to the documentation of this file.
#ifndef PROTOZERO_PBF_READER_HPP
#define PROTOZERO_PBF_READER_HPP
/*****************************************************************************
protozero - Minimalistic protocol buffer decoder and encoder in C++.
This file is from https://github.com/mapbox/protozero where you can find more
documentation.
*****************************************************************************/
#include <protozero/config.hpp>
#include <protozero/data_view.hpp>
#include <protozero/exception.hpp>
#include <protozero/iterators.hpp>
#include <protozero/types.hpp>
#include <protozero/varint.hpp>
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
# include <protozero/byteswap.hpp>
#endif
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <string>
#include <utility>
namespace protozero {
class pbf_reader {
// A pointer to the next unread data.
const char* m_data = nullptr;
// A pointer to one past the end of data.
const char* m_end = nullptr;
// The wire type of the current field.
pbf_wire_type m_wire_type = pbf_wire_type::unknown;
// The tag of the current field.
pbf_tag_type m_tag = 0;
template <typename T>
T get_fixed() {
T result;
const char* data = m_data;
skip_bytes(sizeof(T));
std::memcpy(&result, data, sizeof(T));
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
byteswap_inplace(&result);
#endif
return result;
}
template <typename T>
iterator_range<const_fixed_iterator<T>> packed_fixed() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
const auto len = get_len_and_skip();
if (len % sizeof(T) != 0) {
throw invalid_length_exception{};
}
return {const_fixed_iterator<T>(m_data - len),
const_fixed_iterator<T>(m_data)};
}
template <typename T>
T get_varint() {
const auto val = static_cast<T>(decode_varint(&m_data, m_end));
return val;
}
template <typename T>
T get_svarint() {
protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
}
pbf_length_type get_length() {
return get_varint<pbf_length_type>();
}
void skip_bytes(pbf_length_type len) {
if (m_end - m_data < static_cast<ptrdiff_t>(len)) {
throw end_of_buffer_exception{};
}
m_data += len;
#ifndef NDEBUG
// In debug builds reset the tag to zero so that we can detect (some)
// wrong code.
m_tag = 0;
#endif
}
pbf_length_type get_len_and_skip() {
const auto len = get_length();
skip_bytes(len);
return len;
}
template <typename T>
iterator_range<T> get_packed() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
const auto len = get_len_and_skip();
return {T{m_data - len, m_data},
T{m_data, m_data}};
}
public:
explicit pbf_reader(const data_view& view) noexcept
: m_data{view.data()},
m_end{view.data() + view.size()} {
}
pbf_reader(const char* data, std::size_t size) noexcept
: m_data{data},
m_end{data + size} {
}
#ifndef PROTOZERO_STRICT_API
explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
: m_data{data.first},
m_end{data.first + data.second} {
}
#endif
explicit pbf_reader(const std::string& data) noexcept
: m_data{data.data()},
m_end{data.data() + data.size()} {
}
pbf_reader() noexcept = default;
pbf_reader(const pbf_reader&) noexcept = default;
pbf_reader(pbf_reader&&) noexcept = default;
pbf_reader& operator=(const pbf_reader& other) noexcept = default;
pbf_reader& operator=(pbf_reader&& other) noexcept = default;
~pbf_reader() = default;
void swap(pbf_reader& other) noexcept {
using std::swap;
swap(m_data, other.m_data);
swap(m_end, other.m_end);
swap(m_wire_type, other.m_wire_type);
swap(m_tag, other.m_tag);
}
operator bool() const noexcept { // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
return m_data != m_end;
}
data_view data() const noexcept {
return {m_data, static_cast<std::size_t>(m_end - m_data)};
}
std::size_t length() const noexcept {
return std::size_t(m_end - m_data);
}
bool next() {
if (m_data == m_end) {
return false;
}
const auto value = get_varint<uint32_t>();
m_tag = pbf_tag_type(value >> 3u);
// tags 0 and 19000 to 19999 are not allowed as per
// https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
if (m_tag == 0 || (m_tag >= 19000 && m_tag <= 19999)) {
throw invalid_tag_exception{};
}
m_wire_type = pbf_wire_type(value & 0x07u);
switch (m_wire_type) {
case pbf_wire_type::varint:
case pbf_wire_type::fixed64:
case pbf_wire_type::length_delimited:
case pbf_wire_type::fixed32:
break;
default:
throw unknown_pbf_wire_type_exception{};
}
return true;
}
bool next(pbf_tag_type next_tag) {
while (next()) {
if (m_tag == next_tag) {
return true;
}
skip();
}
return false;
}
bool next(pbf_tag_type next_tag, pbf_wire_type type) {
while (next()) {
if (m_tag == next_tag && m_wire_type == type) {
return true;
}
skip();
}
return false;
}
pbf_tag_type tag() const noexcept {
return m_tag;
}
pbf_wire_type wire_type() const noexcept {
return m_wire_type;
}
uint32_t tag_and_type() const noexcept {
return protozero::tag_and_type(tag(), wire_type());
}
bool has_wire_type(pbf_wire_type type) const noexcept {
return wire_type() == type;
}
void skip() {
protozero_assert(tag() != 0 && "call next() before calling skip()");
switch (wire_type()) {
case pbf_wire_type::varint:
skip_varint(&m_data, m_end);
break;
case pbf_wire_type::fixed64:
skip_bytes(8);
break;
case pbf_wire_type::length_delimited:
skip_bytes(get_length());
break;
case pbf_wire_type::fixed32:
skip_bytes(4);
break;
default:
break;
}
}
bool get_bool() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
const auto data = m_data;
skip_varint(&m_data, m_end);
return data[0] != 0;
}
int32_t get_enum() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int32_t>();
}
int32_t get_int32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int32_t>();
}
int32_t get_sint32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_svarint<int32_t>();
}
uint32_t get_uint32() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<uint32_t>();
}
int64_t get_int64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<int64_t>();
}
int64_t get_sint64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_svarint<int64_t>();
}
uint64_t get_uint64() {
protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
return get_varint<uint64_t>();
}
uint32_t get_fixed32() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
return get_fixed<uint32_t>();
}
int32_t get_sfixed32() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
return get_fixed<int32_t>();
}
uint64_t get_fixed64() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
return get_fixed<uint64_t>();
}
int64_t get_sfixed64() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
return get_fixed<int64_t>();
}
float get_float() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
return get_fixed<float>();
}
double get_double() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
return get_fixed<double>();
}
data_view get_view() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
const auto len = get_len_and_skip();
return {m_data - len, len};
}
#ifndef PROTOZERO_STRICT_API
std::pair<const char*, pbf_length_type> get_data() {
protozero_assert(tag() != 0 && "call next() before accessing field value");
protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
const auto len = get_len_and_skip();
return {m_data - len, len};
}
#endif
std::string get_bytes() {
return std::string(get_view());
}
std::string get_string() {
return std::string(get_view());
}
pbf_reader get_message() {
return pbf_reader{get_view()};
}
using const_bool_iterator = const_varint_iterator< int32_t>;
using const_enum_iterator = const_varint_iterator< int32_t>;
using const_int32_iterator = const_varint_iterator< int32_t>;
using const_sint32_iterator = const_svarint_iterator<int32_t>;
using const_uint32_iterator = const_varint_iterator<uint32_t>;
using const_int64_iterator = const_varint_iterator< int64_t>;
using const_sint64_iterator = const_svarint_iterator<int64_t>;
using const_uint64_iterator = const_varint_iterator<uint64_t>;
using const_fixed32_iterator = const_fixed_iterator<uint32_t>;
using const_sfixed32_iterator = const_fixed_iterator<int32_t>;
using const_fixed64_iterator = const_fixed_iterator<uint64_t>;
using const_sfixed64_iterator = const_fixed_iterator<int64_t>;
using const_float_iterator = const_fixed_iterator<float>;
using const_double_iterator = const_fixed_iterator<double>;
iterator_range<pbf_reader::const_bool_iterator> get_packed_bool() {
return get_packed<pbf_reader::const_bool_iterator>();
}
iterator_range<pbf_reader::const_enum_iterator> get_packed_enum() {
return get_packed<pbf_reader::const_enum_iterator>();
}
iterator_range<pbf_reader::const_int32_iterator> get_packed_int32() {
return get_packed<pbf_reader::const_int32_iterator>();
}
iterator_range<pbf_reader::const_sint32_iterator> get_packed_sint32() {
return get_packed<pbf_reader::const_sint32_iterator>();
}
iterator_range<pbf_reader::const_uint32_iterator> get_packed_uint32() {
return get_packed<pbf_reader::const_uint32_iterator>();
}
iterator_range<pbf_reader::const_int64_iterator> get_packed_int64() {
return get_packed<pbf_reader::const_int64_iterator>();
}
iterator_range<pbf_reader::const_sint64_iterator> get_packed_sint64() {
return get_packed<pbf_reader::const_sint64_iterator>();
}
iterator_range<pbf_reader::const_uint64_iterator> get_packed_uint64() {
return get_packed<pbf_reader::const_uint64_iterator>();
}
iterator_range<pbf_reader::const_fixed32_iterator> get_packed_fixed32() {
return packed_fixed<uint32_t>();
}
iterator_range<pbf_reader::const_sfixed32_iterator> get_packed_sfixed32() {
return packed_fixed<int32_t>();
}
iterator_range<pbf_reader::const_fixed64_iterator> get_packed_fixed64() {
return packed_fixed<uint64_t>();
}
iterator_range<pbf_reader::const_sfixed64_iterator> get_packed_sfixed64() {
return packed_fixed<int64_t>();
}
iterator_range<pbf_reader::const_float_iterator> get_packed_float() {
return packed_fixed<float>();
}
iterator_range<pbf_reader::const_double_iterator> get_packed_double() {
return packed_fixed<double>();
}
}; // class pbf_reader
inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
lhs.swap(rhs);
}
} // end namespace protozero
#endif // PROTOZERO_PBF_READER_HPP
protozero::pbf_reader::pbf_reader
pbf_reader(const data_view &view) noexcept
Definition: pbf_reader.hpp:154
protozero::pbf_reader::get_sfixed64
int64_t get_sfixed64()
Definition: pbf_reader.hpp:632
protozero::pbf_reader::get_uint32
uint32_t get_uint32()
Definition: pbf_reader.hpp:545
protozero::pbf_reader::get_fixed64
uint64_t get_fixed64()
Definition: pbf_reader.hpp:619
protozero::pbf_reader::get_sfixed32
int32_t get_sfixed32()
Definition: pbf_reader.hpp:606
protozero::unknown_pbf_wire_type_exception
Definition: exception.hpp:52
protozero::pbf_reader::get_uint64
uint64_t get_uint64()
Definition: pbf_reader.hpp:581
protozero::decode_zigzag64
constexpr int64_t decode_zigzag64(uint64_t value) noexcept
Definition: varint.hpp:199
protozero::const_fixed_iterator
Definition: iterators.hpp:160
protozero::pbf_reader::get_int32
int32_t get_int32()
Definition: pbf_reader.hpp:521
protozero::invalid_length_exception
Definition: exception.hpp:92
protozero::pbf_reader::pbf_reader
pbf_reader(const std::string &data) noexcept
Definition: pbf_reader.hpp:202
protozero::pbf_reader::get_packed_sint64
iterator_range< pbf_reader::const_sint64_iterator > get_packed_sint64()
Definition: pbf_reader.hpp:866
protozero::tag_and_type
constexpr uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept
Definition: types.hpp:55
protozero::swap
void swap(pbf_reader &lhs, pbf_reader &rhs) noexcept
Definition: pbf_reader.hpp:971
config.hpp
Contains macro checks for different configurations.
protozero::pbf_reader::get_packed_float
iterator_range< pbf_reader::const_float_iterator > get_packed_float()
Definition: pbf_reader.hpp:944
types.hpp
Contains the declaration of low-level types used in the pbf format.
protozero::pbf_reader::has_wire_type
bool has_wire_type(pbf_wire_type type) const noexcept
Definition: pbf_reader.hpp:452
protozero::pbf_reader::length
std::size_t length() const noexcept
Definition: pbf_reader.hpp:265
protozero::pbf_reader::skip
void skip()
Definition: pbf_reader.hpp:462
protozero::pbf_reader::get_packed_int32
iterator_range< pbf_reader::const_int32_iterator > get_packed_int32()
Definition: pbf_reader.hpp:814
protozero::pbf_reader::get_message
pbf_reader get_message()
Definition: pbf_reader.hpp:726
protozero::pbf_reader::pbf_reader
pbf_reader() noexcept=default
protozero::pbf_reader::get_packed_int64
iterator_range< pbf_reader::const_int64_iterator > get_packed_int64()
Definition: pbf_reader.hpp:853
protozero::skip_varint
void skip_varint(const char **data, const char *end)
Definition: varint.hpp:112
iterators.hpp
Contains the iterators for access to packed repeated fields.
protozero::data_view::size
constexpr std::size_t size() const noexcept
Return length of data in bytes.
Definition: data_view.hpp:99
protozero::pbf_wire_type
pbf_wire_type
Definition: types.hpp:40
protozero::pbf_reader::get_packed_uint64
iterator_range< pbf_reader::const_uint64_iterator > get_packed_uint64()
Definition: pbf_reader.hpp:879
protozero::pbf_reader::get_packed_sint32
iterator_range< pbf_reader::const_sint32_iterator > get_packed_sint32()
Definition: pbf_reader.hpp:827
protozero::pbf_reader::get_packed_sfixed32
iterator_range< pbf_reader::const_sfixed32_iterator > get_packed_sfixed32()
Definition: pbf_reader.hpp:905
protozero::pbf_reader::wire_type
pbf_wire_type wire_type() const noexcept
Definition: pbf_reader.hpp:416
data_view.hpp
Contains the implementation of the data_view class.
protozero::invalid_tag_exception
Definition: exception.hpp:80
protozero::pbf_reader::swap
void swap(pbf_reader &other) noexcept
Definition: pbf_reader.hpp:232
protozero::pbf_reader::data
data_view data() const noexcept
Definition: pbf_reader.hpp:252
protozero::pbf_reader::get_sint64
int64_t get_sint64()
Definition: pbf_reader.hpp:569
protozero::pbf_reader::next
bool next(pbf_tag_type next_tag)
Definition: pbf_reader.hpp:340
protozero::pbf_reader::tag_and_type
uint32_t tag_and_type() const noexcept
Definition: pbf_reader.hpp:442
protozero::pbf_reader::get_data
std::pair< const char *, pbf_length_type > get_data()
Definition: pbf_reader.hpp:689
protozero::pbf_reader::get_packed_bool
iterator_range< pbf_reader::const_bool_iterator > get_packed_bool()
Definition: pbf_reader.hpp:788
byteswap.hpp
Contains functions to swap bytes in values (for different endianness).
protozero::pbf_reader::get_sint32
int32_t get_sint32()
Definition: pbf_reader.hpp:533
protozero::pbf_reader::get_bytes
std::string get_bytes()
Definition: pbf_reader.hpp:704
protozero::pbf_reader::get_packed_double
iterator_range< pbf_reader::const_double_iterator > get_packed_double()
Definition: pbf_reader.hpp:957
protozero::pbf_reader::get_double
double get_double()
Definition: pbf_reader.hpp:658
protozero::pbf_reader::operator=
pbf_reader & operator=(const pbf_reader &other) noexcept=default
pbf_reader messages can be copied trivially.
protozero::pbf_reader::get_bool
bool get_bool()
Definition: pbf_reader.hpp:494
protozero::pbf_reader::get_string
std::string get_string()
Definition: pbf_reader.hpp:715
protozero::pbf_length_type
uint32_t pbf_length_type
Definition: types.hpp:62
exception.hpp
Contains the exceptions used in the protozero library.
protozero::pbf_reader::pbf_reader
pbf_reader(const std::pair< const char *, std::size_t > &data) noexcept
Definition: pbf_reader.hpp:186
protozero::pbf_reader::get_view
data_view get_view()
Definition: pbf_reader.hpp:673
protozero::pbf_reader::get_packed_fixed32
iterator_range< pbf_reader::const_fixed32_iterator > get_packed_fixed32()
Definition: pbf_reader.hpp:892
protozero::pbf_reader::pbf_reader
pbf_reader(const char *data, std::size_t size) noexcept
Definition: pbf_reader.hpp:169
protozero::pbf_tag_type
uint32_t pbf_tag_type
Definition: types.hpp:33
protozero::pbf_reader::get_fixed32
uint32_t get_fixed32()
Definition: pbf_reader.hpp:593
protozero::pbf_reader::get_packed_enum
iterator_range< pbf_reader::const_enum_iterator > get_packed_enum()
Definition: pbf_reader.hpp:801
protozero::data_view
Definition: data_view.hpp:39
protozero::pbf_reader::tag
pbf_tag_type tag() const noexcept
Definition: pbf_reader.hpp:397
protozero::const_svarint_iterator
Definition: iterators.hpp:374
protozero::pbf_reader::get_packed_sfixed64
iterator_range< pbf_reader::const_sfixed64_iterator > get_packed_sfixed64()
Definition: pbf_reader.hpp:931
protozero::byteswap_inplace
void byteswap_inplace(uint32_t *ptr) noexcept
byteswap the data pointed to by ptr in-place.
Definition: byteswap.hpp:55
protozero::const_varint_iterator
Definition: iterators.hpp:289
protozero::pbf_reader::get_enum
int32_t get_enum()
Definition: pbf_reader.hpp:509
protozero::pbf_reader
Definition: pbf_reader.hpp:62
protozero::pbf_reader::next
bool next(pbf_tag_type next_tag, pbf_wire_type type)
Definition: pbf_reader.hpp:378
protozero::data_view::data
constexpr const char * data() const noexcept
Return pointer to data.
Definition: data_view.hpp:94
protozero::iterator_range
Definition: iterators.hpp:39
protozero::pbf_reader::get_float
float get_float()
Definition: pbf_reader.hpp:645
protozero::end_of_buffer_exception
Definition: exception.hpp:67
varint.hpp
Contains low-level varint and zigzag encoding and decoding functions.
protozero::decode_varint
uint64_t decode_varint(const char **data, const char *end)
Definition: varint.hpp:89
protozero::pbf_reader::next
bool next()
Definition: pbf_reader.hpp:284
protozero::pbf_reader::get_packed_fixed64
iterator_range< pbf_reader::const_fixed64_iterator > get_packed_fixed64()
Definition: pbf_reader.hpp:918
protozero::pbf_reader::get_int64
int64_t get_int64()
Definition: pbf_reader.hpp:557
protozero::pbf_reader::get_packed_uint32
iterator_range< pbf_reader::const_uint32_iterator > get_packed_uint32()
Definition: pbf_reader.hpp:840
protozero
All parts of the protozero header-only library are in this namespace.
Definition: byteswap.hpp:23