protozero
Minimalistic protocol buffer decoder and encoder in C++.
pbf_reader.hpp
Go to the documentation of this file.
1 #ifndef PROTOZERO_PBF_READER_HPP
2 #define PROTOZERO_PBF_READER_HPP
3 
4 /*****************************************************************************
5 
6 protozero - Minimalistic protocol buffer decoder and encoder in C++.
7 
8 This file is from https://github.com/mapbox/protozero where you can find more
9 documentation.
10 
11 *****************************************************************************/
12 
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <string>
23 #include <utility>
24 
25 #include <protozero/config.hpp>
26 #include <protozero/exception.hpp>
27 #include <protozero/iterators.hpp>
28 #include <protozero/types.hpp>
29 #include <protozero/varint.hpp>
30 
31 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
32 # include <protozero/byteswap.hpp>
33 #endif
34 
35 namespace protozero {
36 
61 class pbf_reader {
62 
63  // A pointer to the next unread data.
64  const char* m_data = nullptr;
65 
66  // A pointer to one past the end of data.
67  const char* m_end = nullptr;
68 
69  // The wire type of the current field.
70  pbf_wire_type m_wire_type = pbf_wire_type::unknown;
71 
72  // The tag of the current field.
73  pbf_tag_type m_tag = 0;
74 
75  template <typename T>
76  T get_fixed() {
77  T result;
78  skip_bytes(sizeof(T));
79  std::memcpy(&result, m_data - sizeof(T), sizeof(T));
80 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
81  detail::byteswap_inplace(&result);
82 #endif
83  return result;
84  }
85 
86  template <typename T>
88  protozero_assert(tag() != 0 && "call next() before accessing field value");
89  const auto len = get_len_and_skip();
90  protozero_assert(len % sizeof(T) == 0);
91  return {const_fixed_iterator<T>(m_data - len, m_data),
92  const_fixed_iterator<T>(m_data, m_data)};
93  }
94 
95  template <typename T>
96  T get_varint() {
97  return static_cast<T>(decode_varint(&m_data, m_end));
98  }
99 
100  template <typename T>
101  T get_svarint() {
102  protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
103  return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
104  }
105 
106  pbf_length_type get_length() {
107  return get_varint<pbf_length_type>();
108  }
109 
110  void skip_bytes(pbf_length_type len) {
111  if (m_data + len > m_end) {
112  throw end_of_buffer_exception{};
113  }
114  m_data += len;
115 
116  // In debug builds reset the tag to zero so that we can detect (some)
117  // wrong code.
118 #ifndef NDEBUG
119  m_tag = 0;
120 #endif
121  }
122 
123  pbf_length_type get_len_and_skip() {
124  const auto len = get_length();
125  skip_bytes(len);
126  return len;
127  }
128 
129  template <typename T>
130  iterator_range<T> get_packed() {
131  protozero_assert(tag() != 0 && "call next() before accessing field value");
132  const auto len = get_len_and_skip();
133  return {T{m_data - len, m_data},
134  T{m_data, m_data}};
135  }
136 
137 public:
138 
149  explicit pbf_reader(const data_view& view) noexcept
150  : m_data(view.data()),
151  m_end(view.data() + view.size()) {
152  }
153 
164  pbf_reader(const char* data, std::size_t size) noexcept
165  : m_data(data),
166  m_end(data + size) {
167  }
168 
179  explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
180  : m_data(data.first),
181  m_end(data.first + data.second) {
182  }
183 
194  explicit pbf_reader(const std::string& data) noexcept
195  : m_data(data.data()),
196  m_end(data.data() + data.size()) {
197  }
198 
203  pbf_reader() noexcept = default;
204 
206  pbf_reader(const pbf_reader&) noexcept = default;
207 
209  pbf_reader(pbf_reader&&) noexcept = default;
210 
212  pbf_reader& operator=(const pbf_reader& other) noexcept = default;
213 
215  pbf_reader& operator=(pbf_reader&& other) noexcept = default;
216 
217  ~pbf_reader() = default;
218 
224  void swap(pbf_reader& other) noexcept {
225  using std::swap;
226  swap(m_data, other.m_data);
227  swap(m_end, other.m_end);
228  swap(m_wire_type, other.m_wire_type);
229  swap(m_tag, other.m_tag);
230  }
231 
237  operator bool() const noexcept { // NOLINT clang-tidy: google-explicit-constructor
238  return m_data < m_end;
239  }
240 
250  std::size_t length() const noexcept {
251  return std::size_t(m_end - m_data);
252  }
253 
269  bool next() {
270  if (m_data == m_end) {
271  return false;
272  }
273 
274  const auto value = get_varint<uint32_t>();
275  m_tag = pbf_tag_type(value >> 3);
276 
277  // tags 0 and 19000 to 19999 are not allowed as per
278  // https://developers.google.com/protocol-buffers/docs/proto
279  protozero_assert(((m_tag > 0 && m_tag < 19000) ||
280  (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
281 
282  m_wire_type = pbf_wire_type(value & 0x07);
283  switch (m_wire_type) {
284  case pbf_wire_type::varint:
285  case pbf_wire_type::fixed64:
286  case pbf_wire_type::length_delimited:
287  case pbf_wire_type::fixed32:
288  break;
289  default:
291  }
292 
293  return true;
294  }
295 
324  bool next(pbf_tag_type next_tag) {
325  while (next()) {
326  if (m_tag == next_tag) {
327  return true;
328  }
329  skip();
330  }
331  return false;
332  }
333 
362  bool next(pbf_tag_type next_tag, pbf_wire_type type) {
363  while (next()) {
364  if (m_tag == next_tag && m_wire_type == type) {
365  return true;
366  }
367  skip();
368  }
369  return false;
370  }
371 
381  pbf_tag_type tag() const noexcept {
382  return m_tag;
383  }
384 
400  pbf_wire_type wire_type() const noexcept {
401  return m_wire_type;
402  }
403 
426  uint32_t tag_and_type() const noexcept {
428  }
429 
436  bool has_wire_type(pbf_wire_type type) const noexcept {
437  return wire_type() == type;
438  }
439 
446  void skip() {
447  protozero_assert(tag() != 0 && "call next() before calling skip()");
448  switch (wire_type()) {
449  case pbf_wire_type::varint:
450  skip_varint(&m_data, m_end);
451  break;
452  case pbf_wire_type::fixed64:
453  skip_bytes(8);
454  break;
455  case pbf_wire_type::length_delimited:
456  skip_bytes(get_length());
457  break;
458  case pbf_wire_type::fixed32:
459  skip_bytes(4);
460  break;
461  default:
462  protozero_assert(false && "can not be here because next() should have thrown already");
463  }
464  }
465 
467 
478  bool get_bool() {
479  protozero_assert(tag() != 0 && "call next() before accessing field value");
480  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
481  protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
482  skip_bytes(1);
483  return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
484  }
485 
493  int32_t get_enum() {
494  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
495  return get_varint<int32_t>();
496  }
497 
505  int32_t get_int32() {
506  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
507  return get_varint<int32_t>();
508  }
509 
517  int32_t get_sint32() {
518  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
519  return get_svarint<int32_t>();
520  }
521 
529  uint32_t get_uint32() {
530  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
531  return get_varint<uint32_t>();
532  }
533 
541  int64_t get_int64() {
542  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
543  return get_varint<int64_t>();
544  }
545 
553  int64_t get_sint64() {
554  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
555  return get_svarint<int64_t>();
556  }
557 
565  uint64_t get_uint64() {
566  protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
567  return get_varint<uint64_t>();
568  }
569 
577  uint32_t get_fixed32() {
578  protozero_assert(tag() != 0 && "call next() before accessing field value");
579  protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
580  return get_fixed<uint32_t>();
581  }
582 
590  int32_t get_sfixed32() {
591  protozero_assert(tag() != 0 && "call next() before accessing field value");
592  protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
593  return get_fixed<int32_t>();
594  }
595 
603  uint64_t get_fixed64() {
604  protozero_assert(tag() != 0 && "call next() before accessing field value");
605  protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
606  return get_fixed<uint64_t>();
607  }
608 
616  int64_t get_sfixed64() {
617  protozero_assert(tag() != 0 && "call next() before accessing field value");
618  protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
619  return get_fixed<int64_t>();
620  }
621 
629  float get_float() {
630  protozero_assert(tag() != 0 && "call next() before accessing field value");
631  protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
632  return get_fixed<float>();
633  }
634 
642  double get_double() {
643  protozero_assert(tag() != 0 && "call next() before accessing field value");
644  protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
645  return get_fixed<double>();
646  }
647 
658  protozero_assert(tag() != 0 && "call next() before accessing field value");
659  protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
660  const auto len = get_len_and_skip();
661  return {m_data - len, len};
662  }
663 
664 #ifndef PROTOZERO_STRICT_API
665 
673  std::pair<const char*, pbf_length_type> get_data() {
674  protozero_assert(tag() != 0 && "call next() before accessing field value");
675  protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
676  const auto len = get_len_and_skip();
677  return {m_data - len, len};
678  }
679 #endif
680 
688  std::string get_bytes() {
689  return std::string(get_view());
690  }
691 
699  std::string get_string() {
700  return std::string(get_view());
701  }
702 
711  return pbf_reader{get_view()};
712  }
713 
715 
718 
721 
724 
727 
730 
733 
736 
739 
741 
755  return get_packed<pbf_reader::const_bool_iterator>();
756  }
757 
768  return get_packed<pbf_reader::const_enum_iterator>();
769  }
770 
781  return get_packed<pbf_reader::const_int32_iterator>();
782  }
783 
794  return get_packed<pbf_reader::const_sint32_iterator>();
795  }
796 
807  return get_packed<pbf_reader::const_uint32_iterator>();
808  }
809 
820  return get_packed<pbf_reader::const_int64_iterator>();
821  }
822 
833  return get_packed<pbf_reader::const_sint64_iterator>();
834  }
835 
846  return get_packed<pbf_reader::const_uint64_iterator>();
847  }
848 
858  auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
859  return packed_fixed<uint32_t>();
860  }
861 
871  auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
872  return packed_fixed<int32_t>();
873  }
874 
884  auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
885  return packed_fixed<uint64_t>();
886  }
887 
897  auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
898  return packed_fixed<int64_t>();
899  }
900 
910  auto get_packed_float() -> decltype(packed_fixed<float>()) {
911  return packed_fixed<float>();
912  }
913 
923  auto get_packed_double() -> decltype(packed_fixed<double>()) {
924  return packed_fixed<double>();
925  }
926 
928 
929 }; // class pbf_reader
930 
937 inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
938  lhs.swap(rhs);
939 }
940 
941 } // end namespace protozero
942 
943 #endif // PROTOZERO_PBF_READER_HPP
pbf_reader(const data_view &view) noexcept
Definition: pbf_reader.hpp:149
int64_t get_sfixed64()
Definition: pbf_reader.hpp:616
uint32_t get_uint32()
Definition: pbf_reader.hpp:529
uint64_t get_fixed64()
Definition: pbf_reader.hpp:603
int32_t get_sfixed32()
Definition: pbf_reader.hpp:590
Definition: exception.hpp:48
uint64_t get_uint64()
Definition: pbf_reader.hpp:565
auto get_packed_double() -> decltype(packed_fixed< double >())
Definition: pbf_reader.hpp:923
constexpr int64_t decode_zigzag64(uint64_t value) noexcept
Definition: varint.hpp:181
Definition: iterators.hpp:160
int32_t get_int32()
Definition: pbf_reader.hpp:505
pbf_reader(const std::string &data) noexcept
Definition: pbf_reader.hpp:194
iterator_range< pbf_reader::const_sint64_iterator > get_packed_sint64()
Definition: pbf_reader.hpp:832
auto get_packed_float() -> decltype(packed_fixed< float >())
Definition: pbf_reader.hpp:910
auto get_packed_fixed32() -> decltype(packed_fixed< uint32_t >())
Definition: pbf_reader.hpp:858
constexpr uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept
Definition: types.hpp:56
void swap(pbf_reader &lhs, pbf_reader &rhs) noexcept
Definition: pbf_reader.hpp:937
Contains macro checks for different configurations.
Contains the declaration of low-level types used in the pbf format.
bool has_wire_type(pbf_wire_type type) const noexcept
Definition: pbf_reader.hpp:436
std::size_t length() const noexcept
Definition: pbf_reader.hpp:250
void skip()
Definition: pbf_reader.hpp:446
iterator_range< pbf_reader::const_int32_iterator > get_packed_int32()
Definition: pbf_reader.hpp:780
pbf_reader get_message()
Definition: pbf_reader.hpp:710
pbf_reader() noexcept=default
iterator_range< pbf_reader::const_int64_iterator > get_packed_int64()
Definition: pbf_reader.hpp:819
void skip_varint(const char **data, const char *end)
Definition: varint.hpp:112
auto get_packed_sfixed64() -> decltype(packed_fixed< int64_t >())
Definition: pbf_reader.hpp:897
Contains the iterators for access to packed repeated fields.
auto get_packed_sfixed32() -> decltype(packed_fixed< int32_t >())
Definition: pbf_reader.hpp:871
pbf_wire_type
Definition: types.hpp:40
iterator_range< pbf_reader::const_uint64_iterator > get_packed_uint64()
Definition: pbf_reader.hpp:845
iterator_range< pbf_reader::const_sint32_iterator > get_packed_sint32()
Definition: pbf_reader.hpp:793
pbf_wire_type wire_type() const noexcept
Definition: pbf_reader.hpp:400
void swap(pbf_reader &other) noexcept
Definition: pbf_reader.hpp:224
int64_t get_sint64()
Definition: pbf_reader.hpp:553
bool next(pbf_tag_type next_tag)
Definition: pbf_reader.hpp:324
uint32_t tag_and_type() const noexcept
Definition: pbf_reader.hpp:426
std::pair< const char *, pbf_length_type > get_data()
Definition: pbf_reader.hpp:673
auto get_packed_fixed64() -> decltype(packed_fixed< uint64_t >())
Definition: pbf_reader.hpp:884
iterator_range< pbf_reader::const_bool_iterator > get_packed_bool()
Definition: pbf_reader.hpp:754
Contains functions to swap bytes in values (for different endianness).
int32_t get_sint32()
Definition: pbf_reader.hpp:517
std::string get_bytes()
Definition: pbf_reader.hpp:688
double get_double()
Definition: pbf_reader.hpp:642
pbf_reader & operator=(const pbf_reader &other) noexcept=default
pbf_reader messages can be copied trivially.
bool get_bool()
Definition: pbf_reader.hpp:478
std::string get_string()
Definition: pbf_reader.hpp:699
uint32_t pbf_length_type
Definition: types.hpp:63
Contains the exceptions used in the protozero library.
pbf_reader(const std::pair< const char *, std::size_t > &data) noexcept
Definition: pbf_reader.hpp:179
data_view get_view()
Definition: pbf_reader.hpp:657
pbf_reader(const char *data, std::size_t size) noexcept
Definition: pbf_reader.hpp:164
uint32_t pbf_tag_type
Definition: types.hpp:33
uint32_t get_fixed32()
Definition: pbf_reader.hpp:577
iterator_range< pbf_reader::const_enum_iterator > get_packed_enum()
Definition: pbf_reader.hpp:767
Definition: types.hpp:75
pbf_tag_type tag() const noexcept
Definition: pbf_reader.hpp:381
Definition: iterators.hpp:303
Definition: iterators.hpp:230
int32_t get_enum()
Definition: pbf_reader.hpp:493
Definition: pbf_reader.hpp:61
bool next(pbf_tag_type next_tag, pbf_wire_type type)
Definition: pbf_reader.hpp:362
Definition: iterators.hpp:39
float get_float()
Definition: pbf_reader.hpp:629
Definition: exception.hpp:61
Contains low-level varint and zigzag encoding and decoding functions.
uint64_t decode_varint(const char **data, const char *end)
Definition: varint.hpp:89
bool next()
Definition: pbf_reader.hpp:269
int64_t get_int64()
Definition: pbf_reader.hpp:541
iterator_range< pbf_reader::const_uint32_iterator > get_packed_uint32()
Definition: pbf_reader.hpp:806
All parts of the protozero header-only library are in this namespace.
Definition: byteswap.hpp:24