protozero
Minimalistic protocol buffer decoder and encoder in C++.
pbf_writer.hpp
Go to the documentation of this file.
1 #ifndef PROTOZERO_PBF_WRITER_HPP
2 #define PROTOZERO_PBF_WRITER_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 <cassert>
20 #include <cstddef>
21 #include <cstdint>
22 #include <cstring>
23 #include <iterator>
24 #include <limits>
25 #include <string>
26 
27 #include <protozero/pbf_types.hpp>
28 #include <protozero/varint.hpp>
29 
30 #if __BYTE_ORDER != __LITTLE_ENDIAN
31 # include <protozero/byteswap.hpp>
32 #endif
33 
35 #ifndef protozero_assert
36 # define protozero_assert(x) assert(x)
37 #endif
38 
39 namespace protozero {
40 
47 class pbf_writer {
48 
49  std::string* m_data;
50  pbf_writer* m_parent_writer;
51  size_t m_pos = 0;
52 
53  inline void add_varint(uint64_t value) {
54  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");
55  protozero_assert(m_data);
56  write_varint(std::back_inserter(*m_data), value);
57  }
58 
59  inline void add_field(pbf_tag_type tag, pbf_wire_type type) {
60  protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
61  uint32_t b = (tag << 3) | uint32_t(type);
62  add_varint(b);
63  }
64 
65  inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
66  add_field(tag, pbf_wire_type::varint);
67  add_varint(value);
68  }
69 
70  template <typename T>
71  inline void add_fixed(T value) {
72  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");
73  protozero_assert(m_data);
74 #if __BYTE_ORDER == __LITTLE_ENDIAN
75  m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
76 #else
77  auto size = m_data->size();
78  m_data->resize(size + sizeof(T));
79  byteswap<sizeof(T)>(reinterpret_cast<const char*>(&value), const_cast<char*>(m_data->data() + size));
80 #endif
81  }
82 
83  template <typename T, typename It>
84  inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
85  if (first == last) {
86  return;
87  }
88 
89  pbf_writer sw(*this, tag);
90 
91  while (first != last) {
92  sw.add_fixed<T>(*first++);
93  }
94  }
95 
96  template <typename T, typename It>
97  inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
98  if (first == last) {
99  return;
100  }
101 
102  add_length_varint(tag, sizeof(T) * pbf_length_type(std::distance(first, last)));
103 
104  while (first != last) {
105  add_fixed<T>(*first++);
106  }
107  }
108 
109  template <typename It>
110  inline void add_packed_varint(pbf_tag_type tag, It first, It last) {
111  if (first == last) {
112  return;
113  }
114 
115  pbf_writer sw(*this, tag);
116 
117  while (first != last) {
118  sw.add_varint(uint64_t(*first++));
119  }
120  }
121 
122  template <typename It>
123  inline void add_packed_svarint(pbf_tag_type tag, It first, It last) {
124  if (first == last) {
125  return;
126  }
127 
128  pbf_writer sw(*this, tag);
129 
130  while (first != last) {
131  sw.add_varint(encode_zigzag64(*first++));
132  }
133  }
134 
135  // The number of bytes to reserve for the varint holding the length of
136  // a length-delimited field. The length has to fit into pbf_length_type,
137  // and a varint needs 8 bit for every 7 bit.
138  static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
139 
140  inline void open_submessage(pbf_tag_type tag) {
141  protozero_assert(m_pos == 0);
142  protozero_assert(m_data);
143  add_field(tag, pbf_wire_type::length_delimited);
144  m_data->append(size_t(reserve_bytes), '\0');
145  m_pos = m_data->size();
146  }
147 
148  inline void close_submessage() {
149  protozero_assert(m_pos != 0);
150  protozero_assert(m_data);
151  auto length = pbf_length_type(m_data->size() - m_pos);
152 
153  protozero_assert(m_data->size() >= m_pos - reserve_bytes);
154  auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
155 
156  m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
157  m_pos = 0;
158  }
159 
160  inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
161  add_field(tag, pbf_wire_type::length_delimited);
162  add_varint(length);
163  }
164 
165 public:
166 
171  inline explicit pbf_writer(std::string& data) noexcept :
172  m_data(&data),
173  m_parent_writer(nullptr),
174  m_pos(0) {
175  }
176 
181  inline pbf_writer() noexcept :
182  m_data(nullptr),
183  m_parent_writer(nullptr),
184  m_pos(0) {
185  }
186 
194  inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag) :
195  m_data(parent_writer.m_data),
196  m_parent_writer(&parent_writer),
197  m_pos(0) {
198  m_parent_writer->open_submessage(tag);
199  }
200 
202  pbf_writer(const pbf_writer&) noexcept = default;
203 
205  pbf_writer& operator=(const pbf_writer&) noexcept = default;
206 
208  inline pbf_writer(pbf_writer&&) noexcept = default;
209 
211  inline pbf_writer& operator=(pbf_writer&&) noexcept = default;
212 
213  inline ~pbf_writer() {
214  if (m_parent_writer) {
215  m_parent_writer->close_submessage();
216  }
217  }
218 
220 
230  inline void add_bool(pbf_tag_type tag, bool value) {
231  add_field(tag, pbf_wire_type::varint);
232  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");
233  protozero_assert(m_data);
234  m_data->append(1, value);
235  }
236 
243  inline void add_enum(pbf_tag_type tag, int32_t value) {
244  add_tagged_varint(tag, uint64_t(value));
245  }
246 
253  inline void add_int32(pbf_tag_type tag, int32_t value) {
254  add_tagged_varint(tag, uint64_t(value));
255  }
256 
263  inline void add_sint32(pbf_tag_type tag, int32_t value) {
264  add_tagged_varint(tag, encode_zigzag32(value));
265  }
266 
273  inline void add_uint32(pbf_tag_type tag, uint32_t value) {
274  add_tagged_varint(tag, value);
275  }
276 
283  inline void add_int64(pbf_tag_type tag, int64_t value) {
284  add_tagged_varint(tag, uint64_t(value));
285  }
286 
293  inline void add_sint64(pbf_tag_type tag, int64_t value) {
294  add_tagged_varint(tag, encode_zigzag64(value));
295  }
296 
303  inline void add_uint64(pbf_tag_type tag, uint64_t value) {
304  add_tagged_varint(tag, value);
305  }
306 
313  inline void add_fixed32(pbf_tag_type tag, uint32_t value) {
314  add_field(tag, pbf_wire_type::fixed32);
315  add_fixed<uint32_t>(value);
316  }
317 
324  inline void add_sfixed32(pbf_tag_type tag, int32_t value) {
325  add_field(tag, pbf_wire_type::fixed32);
326  add_fixed<int32_t>(value);
327  }
328 
335  inline void add_fixed64(pbf_tag_type tag, uint64_t value) {
336  add_field(tag, pbf_wire_type::fixed64);
337  add_fixed<uint64_t>(value);
338  }
339 
346  inline void add_sfixed64(pbf_tag_type tag, int64_t value) {
347  add_field(tag, pbf_wire_type::fixed64);
348  add_fixed<int64_t>(value);
349  }
350 
357  inline void add_float(pbf_tag_type tag, float value) {
358  add_field(tag, pbf_wire_type::fixed32);
359  add_fixed<float>(value);
360  }
361 
368  inline void add_double(pbf_tag_type tag, double value) {
369  add_field(tag, pbf_wire_type::fixed64);
370  add_fixed<double>(value);
371  }
372 
380  inline void add_bytes(pbf_tag_type tag, const char* value, size_t size) {
381  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");
382  protozero_assert(m_data);
383  assert(size <= std::numeric_limits<pbf_length_type>::max());
384  add_length_varint(tag, pbf_length_type(size));
385  m_data->append(value, size);
386  }
387 
394  inline void add_bytes(pbf_tag_type tag, const std::string& value) {
395  add_bytes(tag, value.data(), value.size());
396  }
397 
405  inline void add_string(pbf_tag_type tag, const char* value, size_t size) {
406  add_bytes(tag, value, size);
407  }
408 
415  inline void add_string(pbf_tag_type tag, const std::string& value) {
416  add_bytes(tag, value.data(), value.size());
417  }
418 
426  inline void add_string(pbf_tag_type tag, const char* value) {
427  add_bytes(tag, value, std::strlen(value));
428  }
429 
437  inline void add_message(pbf_tag_type tag, const char* value, size_t size) {
438  add_bytes(tag, value, size);
439  }
440 
447  inline void add_message(pbf_tag_type tag, const std::string& value) {
448  add_bytes(tag, value.data(), value.size());
449  }
450 
452 
454 
467  template <typename InputIterator>
468  inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
469  add_packed_varint(tag, first, last);
470  }
471 
481  template <typename InputIterator>
482  inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
483  add_packed_varint(tag, first, last);
484  }
485 
495  template <typename InputIterator>
496  inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
497  add_packed_varint(tag, first, last);
498  }
499 
509  template <typename InputIterator>
510  inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
511  add_packed_svarint(tag, first, last);
512  }
513 
523  template <typename InputIterator>
524  inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
525  add_packed_varint(tag, first, last);
526  }
527 
537  template <typename InputIterator>
538  inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
539  add_packed_varint(tag, first, last);
540  }
541 
551  template <typename InputIterator>
552  inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
553  add_packed_svarint(tag, first, last);
554  }
555 
565  template <typename InputIterator>
566  inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
567  add_packed_varint(tag, first, last);
568  }
569 
579  template <typename InputIterator>
580  inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
581  add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
582  typename std::iterator_traits<InputIterator>::iterator_category());
583  }
584 
594  template <typename InputIterator>
595  inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
596  add_packed_fixed<int32_t, InputIterator>(tag, first, last,
597  typename std::iterator_traits<InputIterator>::iterator_category());
598  }
599 
609  template <typename InputIterator>
610  inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
611  add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
612  typename std::iterator_traits<InputIterator>::iterator_category());
613  }
614 
624  template <typename InputIterator>
625  inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
626  add_packed_fixed<int64_t, InputIterator>(tag, first, last,
627  typename std::iterator_traits<InputIterator>::iterator_category());
628  }
629 
639  template <typename InputIterator>
640  inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
641  add_packed_fixed<float, InputIterator>(tag, first, last,
642  typename std::iterator_traits<InputIterator>::iterator_category());
643  }
644 
654  template <typename InputIterator>
655  inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
656  add_packed_fixed<double, InputIterator>(tag, first, last,
657  typename std::iterator_traits<InputIterator>::iterator_category());
658  }
659 
661 
662 }; // class pbf_writer
663 
664 } // end namespace protozero
665 
666 #endif // PROTOZERO_PBF_WRITER_HPP
void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:610
void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:510
void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:552
void add_string(pbf_tag_type tag, const char *value)
Definition: pbf_writer.hpp:426
void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:625
void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:595
uint32_t pbf_length_type
Definition: pbf_types.hpp:45
uint64_t encode_zigzag64(int64_t value) noexcept
Definition: varint.hpp:112
void add_sint64(pbf_tag_type tag, int64_t value)
Definition: pbf_writer.hpp:293
void add_sfixed64(pbf_tag_type tag, int64_t value)
Definition: pbf_writer.hpp:346
void add_uint32(pbf_tag_type tag, uint32_t value)
Definition: pbf_writer.hpp:273
void add_bytes(pbf_tag_type tag, const std::string &value)
Definition: pbf_writer.hpp:394
#define protozero_assert(x)
Wrapper for assert() used for testing.
Definition: pbf_writer.hpp:36
void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:482
Definition: pbf_writer.hpp:47
void add_int64(pbf_tag_type tag, int64_t value)
Definition: pbf_writer.hpp:283
void add_int32(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:253
void add_string(pbf_tag_type tag, const std::string &value)
Definition: pbf_writer.hpp:415
void add_uint64(pbf_tag_type tag, uint64_t value)
Definition: pbf_writer.hpp:303
pbf_wire_type
Definition: pbf_types.hpp:33
Contains the declaration of low-level types used in the pbf format.
void add_message(pbf_tag_type tag, const std::string &value)
Definition: pbf_writer.hpp:447
void add_float(pbf_tag_type tag, float value)
Definition: pbf_writer.hpp:357
uint32_t pbf_tag_type
Definition: pbf_types.hpp:26
void add_enum(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:243
void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:566
pbf_writer() noexcept
Definition: pbf_writer.hpp:181
void add_bytes(pbf_tag_type tag, const char *value, size_t size)
Definition: pbf_writer.hpp:380
Contains functions to swap bytes in values (for different endianness).
void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:524
uint32_t encode_zigzag32(int32_t value) noexcept
Definition: varint.hpp:105
void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:538
void add_string(pbf_tag_type tag, const char *value, size_t size)
Definition: pbf_writer.hpp:405
void add_fixed64(pbf_tag_type tag, uint64_t value)
Definition: pbf_writer.hpp:335
void add_bool(pbf_tag_type tag, bool value)
Definition: pbf_writer.hpp:230
void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:468
void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:655
int write_varint(OutputIterator data, uint64_t value)
Definition: varint.hpp:89
void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:640
pbf_writer(pbf_writer &parent_writer, pbf_tag_type tag)
Definition: pbf_writer.hpp:194
void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:496
void add_sfixed32(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:324
pbf_writer(std::string &data) noexcept
Definition: pbf_writer.hpp:171
Contains low-level varint and zigzag encoding and decoding functions.
void add_sint32(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:263
void add_fixed32(pbf_tag_type tag, uint32_t value)
Definition: pbf_writer.hpp:313
void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:580
void add_message(pbf_tag_type tag, const char *value, size_t size)
Definition: pbf_writer.hpp:437
void add_double(pbf_tag_type tag, double value)
Definition: pbf_writer.hpp:368
All parts of the protozero header-only library are in this namespace.
Definition: byteswap.hpp:22