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 <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <iterator>
23 #include <limits>
24 #include <string>
25 
26 #include <protozero/config.hpp>
27 #include <protozero/pbf_types.hpp>
28 #include <protozero/varint.hpp>
29 
30 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
31 # include <protozero/byteswap.hpp>
32 #endif
33 
34 namespace protozero {
35 
42 class pbf_writer {
43 
44  std::string* m_data;
45  pbf_writer* m_parent_writer;
46  size_t m_pos = 0;
47 
48  inline void add_varint(uint64_t value) {
49  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");
50  protozero_assert(m_data);
51  write_varint(std::back_inserter(*m_data), value);
52  }
53 
54  inline void add_field(pbf_tag_type tag, pbf_wire_type type) {
55  protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
56  uint32_t b = (tag << 3) | uint32_t(type);
57  add_varint(b);
58  }
59 
60  inline void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
61  add_field(tag, pbf_wire_type::varint);
62  add_varint(value);
63  }
64 
65  template <typename T>
66  inline void add_fixed(T value) {
67  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");
68  protozero_assert(m_data);
69 #if PROTOZERO_BYTE_ORDER == PROTOZERO_LITTLE_ENDIAN
70  m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
71 #else
72  auto size = m_data->size();
73  m_data->resize(size + sizeof(T));
74  byteswap<sizeof(T)>(reinterpret_cast<const char*>(&value), const_cast<char*>(m_data->data() + size));
75 #endif
76  }
77 
78  template <typename T, typename It>
79  inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
80  if (first == last) {
81  return;
82  }
83 
84  pbf_writer sw(*this, tag);
85 
86  while (first != last) {
87  sw.add_fixed<T>(*first++);
88  }
89  }
90 
91  template <typename T, typename It>
92  inline void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
93  if (first == last) {
94  return;
95  }
96 
97  add_length_varint(tag, sizeof(T) * pbf_length_type(std::distance(first, last)));
98 
99  while (first != last) {
100  add_fixed<T>(*first++);
101  }
102  }
103 
104  template <typename It>
105  inline void add_packed_varint(pbf_tag_type tag, It first, It last) {
106  if (first == last) {
107  return;
108  }
109 
110  pbf_writer sw(*this, tag);
111 
112  while (first != last) {
113  sw.add_varint(uint64_t(*first++));
114  }
115  }
116 
117  template <typename It>
118  inline void add_packed_svarint(pbf_tag_type tag, It first, It last) {
119  if (first == last) {
120  return;
121  }
122 
123  pbf_writer sw(*this, tag);
124 
125  while (first != last) {
126  sw.add_varint(encode_zigzag64(*first++));
127  }
128  }
129 
130  // The number of bytes to reserve for the varint holding the length of
131  // a length-delimited field. The length has to fit into pbf_length_type,
132  // and a varint needs 8 bit for every 7 bit.
133  static const int reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1;
134 
135  inline void open_submessage(pbf_tag_type tag) {
136  protozero_assert(m_pos == 0);
137  protozero_assert(m_data);
138  add_field(tag, pbf_wire_type::length_delimited);
139  m_data->append(size_t(reserve_bytes), '\0');
140  m_pos = m_data->size();
141  }
142 
143  inline void close_submessage() {
144  protozero_assert(m_pos != 0);
145  protozero_assert(m_data);
146  auto length = pbf_length_type(m_data->size() - m_pos);
147 
148  protozero_assert(m_data->size() >= m_pos - reserve_bytes);
149  auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
150 
151  m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
152  m_pos = 0;
153  }
154 
155  inline void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
156  add_field(tag, pbf_wire_type::length_delimited);
157  add_varint(length);
158  }
159 
160 public:
161 
166  inline explicit pbf_writer(std::string& data) noexcept :
167  m_data(&data),
168  m_parent_writer(nullptr),
169  m_pos(0) {
170  }
171 
176  inline pbf_writer() noexcept :
177  m_data(nullptr),
178  m_parent_writer(nullptr),
179  m_pos(0) {
180  }
181 
189  inline pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag) :
190  m_data(parent_writer.m_data),
191  m_parent_writer(&parent_writer),
192  m_pos(0) {
193  m_parent_writer->open_submessage(tag);
194  }
195 
197  pbf_writer(const pbf_writer&) noexcept = default;
198 
200  pbf_writer& operator=(const pbf_writer&) noexcept = default;
201 
203  inline pbf_writer(pbf_writer&&) noexcept = default;
204 
206  inline pbf_writer& operator=(pbf_writer&&) noexcept = default;
207 
208  inline ~pbf_writer() {
209  if (m_parent_writer) {
210  m_parent_writer->close_submessage();
211  }
212  }
213 
215 
225  inline void add_bool(pbf_tag_type tag, bool value) {
226  add_field(tag, pbf_wire_type::varint);
227  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");
228  protozero_assert(m_data);
229  m_data->append(1, value);
230  }
231 
238  inline void add_enum(pbf_tag_type tag, int32_t value) {
239  add_tagged_varint(tag, uint64_t(value));
240  }
241 
248  inline void add_int32(pbf_tag_type tag, int32_t value) {
249  add_tagged_varint(tag, uint64_t(value));
250  }
251 
258  inline void add_sint32(pbf_tag_type tag, int32_t value) {
259  add_tagged_varint(tag, encode_zigzag32(value));
260  }
261 
268  inline void add_uint32(pbf_tag_type tag, uint32_t value) {
269  add_tagged_varint(tag, value);
270  }
271 
278  inline void add_int64(pbf_tag_type tag, int64_t value) {
279  add_tagged_varint(tag, uint64_t(value));
280  }
281 
288  inline void add_sint64(pbf_tag_type tag, int64_t value) {
289  add_tagged_varint(tag, encode_zigzag64(value));
290  }
291 
298  inline void add_uint64(pbf_tag_type tag, uint64_t value) {
299  add_tagged_varint(tag, value);
300  }
301 
308  inline void add_fixed32(pbf_tag_type tag, uint32_t value) {
309  add_field(tag, pbf_wire_type::fixed32);
310  add_fixed<uint32_t>(value);
311  }
312 
319  inline void add_sfixed32(pbf_tag_type tag, int32_t value) {
320  add_field(tag, pbf_wire_type::fixed32);
321  add_fixed<int32_t>(value);
322  }
323 
330  inline void add_fixed64(pbf_tag_type tag, uint64_t value) {
331  add_field(tag, pbf_wire_type::fixed64);
332  add_fixed<uint64_t>(value);
333  }
334 
341  inline void add_sfixed64(pbf_tag_type tag, int64_t value) {
342  add_field(tag, pbf_wire_type::fixed64);
343  add_fixed<int64_t>(value);
344  }
345 
352  inline void add_float(pbf_tag_type tag, float value) {
353  add_field(tag, pbf_wire_type::fixed32);
354  add_fixed<float>(value);
355  }
356 
363  inline void add_double(pbf_tag_type tag, double value) {
364  add_field(tag, pbf_wire_type::fixed64);
365  add_fixed<double>(value);
366  }
367 
375  inline void add_bytes(pbf_tag_type tag, const char* value, size_t size) {
376  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");
377  protozero_assert(m_data);
378  protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
379  add_length_varint(tag, pbf_length_type(size));
380  m_data->append(value, size);
381  }
382 
389  inline void add_bytes(pbf_tag_type tag, const std::string& value) {
390  add_bytes(tag, value.data(), value.size());
391  }
392 
400  inline void add_string(pbf_tag_type tag, const char* value, size_t size) {
401  add_bytes(tag, value, size);
402  }
403 
410  inline void add_string(pbf_tag_type tag, const std::string& value) {
411  add_bytes(tag, value.data(), value.size());
412  }
413 
421  inline void add_string(pbf_tag_type tag, const char* value) {
422  add_bytes(tag, value, std::strlen(value));
423  }
424 
432  inline void add_message(pbf_tag_type tag, const char* value, size_t size) {
433  add_bytes(tag, value, size);
434  }
435 
442  inline void add_message(pbf_tag_type tag, const std::string& value) {
443  add_bytes(tag, value.data(), value.size());
444  }
445 
447 
449 
462  template <typename InputIterator>
463  inline void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
464  add_packed_varint(tag, first, last);
465  }
466 
476  template <typename InputIterator>
477  inline void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
478  add_packed_varint(tag, first, last);
479  }
480 
490  template <typename InputIterator>
491  inline void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
492  add_packed_varint(tag, first, last);
493  }
494 
504  template <typename InputIterator>
505  inline void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
506  add_packed_svarint(tag, first, last);
507  }
508 
518  template <typename InputIterator>
519  inline void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
520  add_packed_varint(tag, first, last);
521  }
522 
532  template <typename InputIterator>
533  inline void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
534  add_packed_varint(tag, first, last);
535  }
536 
546  template <typename InputIterator>
547  inline void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
548  add_packed_svarint(tag, first, last);
549  }
550 
560  template <typename InputIterator>
561  inline void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
562  add_packed_varint(tag, first, last);
563  }
564 
574  template <typename InputIterator>
575  inline void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
576  add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
577  typename std::iterator_traits<InputIterator>::iterator_category());
578  }
579 
589  template <typename InputIterator>
590  inline void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
591  add_packed_fixed<int32_t, InputIterator>(tag, first, last,
592  typename std::iterator_traits<InputIterator>::iterator_category());
593  }
594 
604  template <typename InputIterator>
605  inline void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
606  add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
607  typename std::iterator_traits<InputIterator>::iterator_category());
608  }
609 
619  template <typename InputIterator>
620  inline void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
621  add_packed_fixed<int64_t, InputIterator>(tag, first, last,
622  typename std::iterator_traits<InputIterator>::iterator_category());
623  }
624 
634  template <typename InputIterator>
635  inline void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
636  add_packed_fixed<float, InputIterator>(tag, first, last,
637  typename std::iterator_traits<InputIterator>::iterator_category());
638  }
639 
649  template <typename InputIterator>
650  inline void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
651  add_packed_fixed<double, InputIterator>(tag, first, last,
652  typename std::iterator_traits<InputIterator>::iterator_category());
653  }
654 
656 
657 }; // class pbf_writer
658 
659 } // end namespace protozero
660 
661 #endif // PROTOZERO_PBF_WRITER_HPP
void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:605
void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:505
void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:547
void add_string(pbf_tag_type tag, const char *value)
Definition: pbf_writer.hpp:421
void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:620
void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:590
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:288
void add_sfixed64(pbf_tag_type tag, int64_t value)
Definition: pbf_writer.hpp:341
void add_uint32(pbf_tag_type tag, uint32_t value)
Definition: pbf_writer.hpp:268
void add_bytes(pbf_tag_type tag, const std::string &value)
Definition: pbf_writer.hpp:389
void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:477
Definition: pbf_writer.hpp:42
void add_int64(pbf_tag_type tag, int64_t value)
Definition: pbf_writer.hpp:278
Contains macro checks for different configurations.
void add_int32(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:248
void add_string(pbf_tag_type tag, const std::string &value)
Definition: pbf_writer.hpp:410
void add_uint64(pbf_tag_type tag, uint64_t value)
Definition: pbf_writer.hpp:298
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:442
void add_float(pbf_tag_type tag, float value)
Definition: pbf_writer.hpp:352
uint32_t pbf_tag_type
Definition: pbf_types.hpp:26
void add_enum(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:238
void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:561
pbf_writer() noexcept
Definition: pbf_writer.hpp:176
void add_bytes(pbf_tag_type tag, const char *value, size_t size)
Definition: pbf_writer.hpp:375
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:519
uint32_t encode_zigzag32(int32_t value) noexcept
Definition: varint.hpp:105
pbf_writer & operator=(const pbf_writer &) noexcept=default
A pbf_writer object can be copied.
void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:533
void add_string(pbf_tag_type tag, const char *value, size_t size)
Definition: pbf_writer.hpp:400
void add_fixed64(pbf_tag_type tag, uint64_t value)
Definition: pbf_writer.hpp:330
void add_bool(pbf_tag_type tag, bool value)
Definition: pbf_writer.hpp:225
void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:463
void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:650
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:635
pbf_writer(pbf_writer &parent_writer, pbf_tag_type tag)
Definition: pbf_writer.hpp:189
void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:491
void add_sfixed32(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:319
pbf_writer(std::string &data) noexcept
Definition: pbf_writer.hpp:166
Contains low-level varint and zigzag encoding and decoding functions.
void add_sint32(pbf_tag_type tag, int32_t value)
Definition: pbf_writer.hpp:258
void add_fixed32(pbf_tag_type tag, uint32_t value)
Definition: pbf_writer.hpp:308
void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last)
Definition: pbf_writer.hpp:575
void add_message(pbf_tag_type tag, const char *value, size_t size)
Definition: pbf_writer.hpp:432
void add_double(pbf_tag_type tag, double value)
Definition: pbf_writer.hpp:363
All parts of the protozero header-only library are in this namespace.
Definition: byteswap.hpp:24