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
13 /**
14 * @file pbf_writer.hpp
15 *
16 * @brief Contains the pbf_writer class.
17 */
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <iterator>
23 #include <limits>
24 #include <string>
25 #include <utility>
26
27 #include <protozero/config.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
37 namespace detail {
38
39 template <typename T> class packed_field_varint;
40 template <typename T> class packed_field_svarint;
41 template <typename T> class packed_field_fixed;
42
43 } // end namespace detail
44
45 /**
46 * The pbf_writer is used to write PBF formatted messages into a buffer.
47 *
48 * Almost all methods in this class can throw an std::bad_alloc exception if
49 * the std::string used as a buffer wants to resize.
50 */
51 class pbf_writer {
52
53 // A pointer to a string buffer holding the data already written to the
54 // PBF message. For default constructed writers or writers that have been
55 // rolled back, this is a nullptr.
56 std::string* m_data;
57
58 // A pointer to a parent writer object if this is a submessage. If this
59 // is a top-level writer, it is a nullptr.
60 pbf_writer* m_parent_writer;
61
62 // This is usually 0. If there is an open submessage, this is set in the
63 // parent to the rollback position, ie. the last position before the
64 // submessage was started. This is the position where the header of the
65 // submessage starts.
66 std::size_t m_rollback_pos = 0;
67
68 // This is usually 0. If there is an open submessage, this is set in the
69 // parent to the position where the data of the submessage is written to.
70 std::size_t m_pos = 0;
71
add_varint(uint64_t value)72 void add_varint(uint64_t value) {
73 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");
74 protozero_assert(m_data);
75 write_varint(std::back_inserter(*m_data), value);
76 }
77
add_field(pbf_tag_type tag,pbf_wire_type type)78 void add_field(pbf_tag_type tag, pbf_wire_type type) {
79 protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1 << 29) - 1))) && "tag out of range");
80 const uint32_t b = (tag << 3) | uint32_t(type);
81 add_varint(b);
82 }
83
add_tagged_varint(pbf_tag_type tag,uint64_t value)84 void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
85 add_field(tag, pbf_wire_type::varint);
86 add_varint(value);
87 }
88
89 template <typename T>
add_fixed(T value)90 void add_fixed(T value) {
91 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");
92 protozero_assert(m_data);
93 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
94 detail::byteswap_inplace(&value);
95 #endif
96 m_data->append(reinterpret_cast<const char*>(&value), sizeof(T));
97 }
98
99 template <typename T, typename It>
add_packed_fixed(pbf_tag_type tag,It first,It last,std::input_iterator_tag)100 void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag) {
101 if (first == last) {
102 return;
103 }
104
105 pbf_writer sw(*this, tag);
106
107 while (first != last) {
108 sw.add_fixed<T>(*first++);
109 }
110 }
111
112 template <typename T, typename It>
add_packed_fixed(pbf_tag_type tag,It first,It last,std::forward_iterator_tag)113 void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag) {
114 if (first == last) {
115 return;
116 }
117
118 const auto length = std::distance(first, last);
119 add_length_varint(tag, sizeof(T) * pbf_length_type(length));
120 reserve(sizeof(T) * std::size_t(length));
121
122 while (first != last) {
123 add_fixed<T>(*first++);
124 }
125 }
126
127 template <typename It>
add_packed_varint(pbf_tag_type tag,It first,It last)128 void add_packed_varint(pbf_tag_type tag, It first, It last) {
129 if (first == last) {
130 return;
131 }
132
133 pbf_writer sw(*this, tag);
134
135 while (first != last) {
136 sw.add_varint(uint64_t(*first++));
137 }
138 }
139
140 template <typename It>
add_packed_svarint(pbf_tag_type tag,It first,It last)141 void add_packed_svarint(pbf_tag_type tag, It first, It last) {
142 if (first == last) {
143 return;
144 }
145
146 pbf_writer sw(*this, tag);
147
148 while (first != last) {
149 sw.add_varint(encode_zigzag64(*first++));
150 }
151 }
152
153 // The number of bytes to reserve for the varint holding the length of
154 // a length-delimited field. The length has to fit into pbf_length_type,
155 // and a varint needs 8 bit for every 7 bit.
156 enum constant_reserve_bytes : int {
157 reserve_bytes = sizeof(pbf_length_type) * 8 / 7 + 1
158 };
159
160 // If m_rollpack_pos is set to this special value, it means that when
161 // the submessage is closed, nothing needs to be done, because the length
162 // of the submessage has already been written correctly.
163 enum constant_size_is_known : std::size_t {
164 size_is_known = std::numeric_limits<std::size_t>::max()
165 };
166
open_submessage(pbf_tag_type tag,std::size_t size)167 void open_submessage(pbf_tag_type tag, std::size_t size) {
168 protozero_assert(m_pos == 0);
169 protozero_assert(m_data);
170 if (size == 0) {
171 m_rollback_pos = m_data->size();
172 add_field(tag, pbf_wire_type::length_delimited);
173 m_data->append(std::size_t(reserve_bytes), '\0');
174 } else {
175 m_rollback_pos = size_is_known;
176 add_length_varint(tag, pbf_length_type(size));
177 reserve(size);
178 }
179 m_pos = m_data->size();
180 }
181
rollback_submessage()182 void rollback_submessage() {
183 protozero_assert(m_pos != 0);
184 protozero_assert(m_rollback_pos != size_is_known);
185 protozero_assert(m_data);
186 m_data->resize(m_rollback_pos);
187 m_pos = 0;
188 }
189
commit_submessage()190 void commit_submessage() {
191 protozero_assert(m_pos != 0);
192 protozero_assert(m_rollback_pos != size_is_known);
193 protozero_assert(m_data);
194 const auto length = pbf_length_type(m_data->size() - m_pos);
195
196 protozero_assert(m_data->size() >= m_pos - reserve_bytes);
197 const auto n = write_varint(m_data->begin() + long(m_pos) - reserve_bytes, length);
198
199 m_data->erase(m_data->begin() + long(m_pos) - reserve_bytes + n, m_data->begin() + long(m_pos));
200 m_pos = 0;
201 }
202
close_submessage()203 void close_submessage() {
204 protozero_assert(m_data);
205 if (m_pos == 0 || m_rollback_pos == size_is_known) {
206 return;
207 }
208 if (m_data->size() - m_pos == 0) {
209 rollback_submessage();
210 } else {
211 commit_submessage();
212 }
213 }
214
add_length_varint(pbf_tag_type tag,pbf_length_type length)215 void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
216 add_field(tag, pbf_wire_type::length_delimited);
217 add_varint(length);
218 }
219
220 public:
221
222 /**
223 * Create a writer using the given string as a data store. The pbf_writer
224 * stores a reference to that string and adds all data to it. The string
225 * doesn't have to be empty. The pbf_writer will just append data.
226 */
pbf_writer(std::string & data)227 explicit pbf_writer(std::string& data) noexcept :
228 m_data(&data),
229 m_parent_writer(nullptr) {
230 }
231
232 /**
233 * Create a writer without a data store. In this form the writer can not
234 * be used!
235 */
pbf_writer()236 pbf_writer() noexcept :
237 m_data(nullptr),
238 m_parent_writer(nullptr) {
239 }
240
241 /**
242 * Construct a pbf_writer for a submessage from the pbf_writer of the
243 * parent message.
244 *
245 * @param parent_writer The pbf_writer
246 * @param tag Tag (field number) of the field that will be written
247 * @param size Optional size of the submessage in bytes (use 0 for unknown).
248 * Setting this allows some optimizations but is only possible in
249 * a few very specific cases.
250 */
pbf_writer(pbf_writer & parent_writer,pbf_tag_type tag,std::size_t size=0)251 pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
252 m_data(parent_writer.m_data),
253 m_parent_writer(&parent_writer) {
254 m_parent_writer->open_submessage(tag, size);
255 }
256
257 /// A pbf_writer object can be copied
258 pbf_writer(const pbf_writer&) noexcept = default;
259
260 /// A pbf_writer object can be copied
261 pbf_writer& operator=(const pbf_writer&) noexcept = default;
262
263 /// A pbf_writer object can be moved
264 pbf_writer(pbf_writer&&) noexcept = default;
265
266 /// A pbf_writer object can be moved
267 pbf_writer& operator=(pbf_writer&&) noexcept = default;
268
~pbf_writer()269 ~pbf_writer() {
270 if (m_parent_writer) {
271 m_parent_writer->close_submessage();
272 }
273 }
274
275 /**
276 * Swap the contents of this object with the other.
277 *
278 * @param other Other object to swap data with.
279 */
swap(pbf_writer & other)280 void swap(pbf_writer& other) noexcept {
281 using std::swap;
282 swap(m_data, other.m_data);
283 swap(m_parent_writer, other.m_parent_writer);
284 swap(m_rollback_pos, other.m_rollback_pos);
285 swap(m_pos, other.m_pos);
286 }
287
288 /**
289 * Reserve size bytes in the underlying message store in addition to
290 * whatever the message store already holds. So unlike
291 * the `std::string::reserve()` method this is not an absolute size,
292 * but additional memory that should be reserved.
293 *
294 * @param size Number of bytes to reserve in underlying message store.
295 */
reserve(std::size_t size)296 void reserve(std::size_t size) {
297 protozero_assert(m_data);
298 m_data->reserve(m_data->size() + size);
299 }
300
301 /**
302 * Cancel writing of this submessage. The complete submessage will be
303 * removed as if it was never created and no fields were added.
304 *
305 * @pre Must be a pbf_writer of a submessage, ie one opened with the
306 * pbf_writer constructor taking a parent message.
307 */
rollback()308 void rollback() {
309 protozero_assert(m_parent_writer && "you can't call rollback() on a pbf_writer without a parent");
310 protozero_assert(m_pos == 0 && "you can't call rollback() on a pbf_writer that has an open nested submessage");
311 m_parent_writer->rollback_submessage();
312 m_data = nullptr;
313 }
314
315 ///@{
316 /**
317 * @name Scalar field writer functions
318 */
319
320 /**
321 * Add "bool" field to data.
322 *
323 * @param tag Tag (field number) of the field
324 * @param value Value to be written
325 */
add_bool(pbf_tag_type tag,bool value)326 void add_bool(pbf_tag_type tag, bool value) {
327 add_field(tag, pbf_wire_type::varint);
328 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");
329 protozero_assert(m_data);
330 m_data->append(1, value);
331 }
332
333 /**
334 * Add "enum" field to data.
335 *
336 * @param tag Tag (field number) of the field
337 * @param value Value to be written
338 */
add_enum(pbf_tag_type tag,int32_t value)339 void add_enum(pbf_tag_type tag, int32_t value) {
340 add_tagged_varint(tag, uint64_t(value));
341 }
342
343 /**
344 * Add "int32" field to data.
345 *
346 * @param tag Tag (field number) of the field
347 * @param value Value to be written
348 */
add_int32(pbf_tag_type tag,int32_t value)349 void add_int32(pbf_tag_type tag, int32_t value) {
350 add_tagged_varint(tag, uint64_t(value));
351 }
352
353 /**
354 * Add "sint32" field to data.
355 *
356 * @param tag Tag (field number) of the field
357 * @param value Value to be written
358 */
add_sint32(pbf_tag_type tag,int32_t value)359 void add_sint32(pbf_tag_type tag, int32_t value) {
360 add_tagged_varint(tag, encode_zigzag32(value));
361 }
362
363 /**
364 * Add "uint32" field to data.
365 *
366 * @param tag Tag (field number) of the field
367 * @param value Value to be written
368 */
add_uint32(pbf_tag_type tag,uint32_t value)369 void add_uint32(pbf_tag_type tag, uint32_t value) {
370 add_tagged_varint(tag, value);
371 }
372
373 /**
374 * Add "int64" field to data.
375 *
376 * @param tag Tag (field number) of the field
377 * @param value Value to be written
378 */
add_int64(pbf_tag_type tag,int64_t value)379 void add_int64(pbf_tag_type tag, int64_t value) {
380 add_tagged_varint(tag, uint64_t(value));
381 }
382
383 /**
384 * Add "sint64" field to data.
385 *
386 * @param tag Tag (field number) of the field
387 * @param value Value to be written
388 */
add_sint64(pbf_tag_type tag,int64_t value)389 void add_sint64(pbf_tag_type tag, int64_t value) {
390 add_tagged_varint(tag, encode_zigzag64(value));
391 }
392
393 /**
394 * Add "uint64" field to data.
395 *
396 * @param tag Tag (field number) of the field
397 * @param value Value to be written
398 */
add_uint64(pbf_tag_type tag,uint64_t value)399 void add_uint64(pbf_tag_type tag, uint64_t value) {
400 add_tagged_varint(tag, value);
401 }
402
403 /**
404 * Add "fixed32" field to data.
405 *
406 * @param tag Tag (field number) of the field
407 * @param value Value to be written
408 */
add_fixed32(pbf_tag_type tag,uint32_t value)409 void add_fixed32(pbf_tag_type tag, uint32_t value) {
410 add_field(tag, pbf_wire_type::fixed32);
411 add_fixed<uint32_t>(value);
412 }
413
414 /**
415 * Add "sfixed32" field to data.
416 *
417 * @param tag Tag (field number) of the field
418 * @param value Value to be written
419 */
add_sfixed32(pbf_tag_type tag,int32_t value)420 void add_sfixed32(pbf_tag_type tag, int32_t value) {
421 add_field(tag, pbf_wire_type::fixed32);
422 add_fixed<int32_t>(value);
423 }
424
425 /**
426 * Add "fixed64" field to data.
427 *
428 * @param tag Tag (field number) of the field
429 * @param value Value to be written
430 */
add_fixed64(pbf_tag_type tag,uint64_t value)431 void add_fixed64(pbf_tag_type tag, uint64_t value) {
432 add_field(tag, pbf_wire_type::fixed64);
433 add_fixed<uint64_t>(value);
434 }
435
436 /**
437 * Add "sfixed64" field to data.
438 *
439 * @param tag Tag (field number) of the field
440 * @param value Value to be written
441 */
add_sfixed64(pbf_tag_type tag,int64_t value)442 void add_sfixed64(pbf_tag_type tag, int64_t value) {
443 add_field(tag, pbf_wire_type::fixed64);
444 add_fixed<int64_t>(value);
445 }
446
447 /**
448 * Add "float" field to data.
449 *
450 * @param tag Tag (field number) of the field
451 * @param value Value to be written
452 */
add_float(pbf_tag_type tag,float value)453 void add_float(pbf_tag_type tag, float value) {
454 add_field(tag, pbf_wire_type::fixed32);
455 add_fixed<float>(value);
456 }
457
458 /**
459 * Add "double" field to data.
460 *
461 * @param tag Tag (field number) of the field
462 * @param value Value to be written
463 */
add_double(pbf_tag_type tag,double value)464 void add_double(pbf_tag_type tag, double value) {
465 add_field(tag, pbf_wire_type::fixed64);
466 add_fixed<double>(value);
467 }
468
469 /**
470 * Add "bytes" field to data.
471 *
472 * @param tag Tag (field number) of the field
473 * @param value Pointer to value to be written
474 * @param size Number of bytes to be written
475 */
add_bytes(pbf_tag_type tag,const char * value,std::size_t size)476 void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
477 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");
478 protozero_assert(m_data);
479 protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
480 add_length_varint(tag, pbf_length_type(size));
481 m_data->append(value, size);
482 }
483
484 /**
485 * Add "bytes" field to data.
486 *
487 * @param tag Tag (field number) of the field
488 * @param value Value to be written
489 */
add_bytes(pbf_tag_type tag,const data_view & value)490 void add_bytes(pbf_tag_type tag, const data_view& value) {
491 add_bytes(tag, value.data(), value.size());
492 }
493
494 /**
495 * Add "bytes" field to data.
496 *
497 * @param tag Tag (field number) of the field
498 * @param value Value to be written
499 */
add_bytes(pbf_tag_type tag,const std::string & value)500 void add_bytes(pbf_tag_type tag, const std::string& value) {
501 add_bytes(tag, value.data(), value.size());
502 }
503
504 /**
505 * Add "bytes" field to data. Bytes from the value are written until
506 * a null byte is encountered. The null byte is not added.
507 *
508 * @param tag Tag (field number) of the field
509 * @param value Pointer to zero-delimited value to be written
510 */
add_bytes(pbf_tag_type tag,const char * value)511 void add_bytes(pbf_tag_type tag, const char* value) {
512 add_bytes(tag, value, std::strlen(value));
513 }
514
515 /**
516 * Add "bytes" field to data using vectored input. All the data in the
517 * 2nd and further arguments is "concatenated" with only a single copy
518 * into the final buffer.
519 *
520 * This will work with objects of any type supporting the data() and
521 * size() methods like std::string or protozero::data_view.
522 *
523 * Example:
524 * @code
525 * std::string data1 = "abc";
526 * std::string data2 = "xyz";
527 * writer.add_bytes_vectored(1, data1, data2);
528 * @endcode
529 *
530 * @tparam Ts List of types supporting data() and size() methods.
531 * @param tag Tag (field number) of the field
532 * @param values List of objects of types Ts with data to be appended.
533 */
534 template <typename... Ts>
add_bytes_vectored(pbf_tag_type tag,Ts &&...values)535 void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) {
536 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");
537 protozero_assert(m_data);
538 size_t sum_size = 0;
539 (void)std::initializer_list<size_t>{sum_size += values.size()...};
540 protozero_assert(sum_size <= std::numeric_limits<pbf_length_type>::max());
541 add_length_varint(tag, pbf_length_type(sum_size));
542 m_data->reserve(m_data->size() + sum_size);
543 (void)std::initializer_list<int>{(m_data->append(values.data(), values.size()), 0)...};
544 }
545
546 /**
547 * Add "string" field to data.
548 *
549 * @param tag Tag (field number) of the field
550 * @param value Pointer to value to be written
551 * @param size Number of bytes to be written
552 */
add_string(pbf_tag_type tag,const char * value,std::size_t size)553 void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
554 add_bytes(tag, value, size);
555 }
556
557 /**
558 * Add "string" field to data.
559 *
560 * @param tag Tag (field number) of the field
561 * @param value Value to be written
562 */
add_string(pbf_tag_type tag,const data_view & value)563 void add_string(pbf_tag_type tag, const data_view& value) {
564 add_bytes(tag, value.data(), value.size());
565 }
566
567 /**
568 * Add "string" field to data.
569 *
570 * @param tag Tag (field number) of the field
571 * @param value Value to be written
572 */
add_string(pbf_tag_type tag,const std::string & value)573 void add_string(pbf_tag_type tag, const std::string& value) {
574 add_bytes(tag, value.data(), value.size());
575 }
576
577 /**
578 * Add "string" field to data. Bytes from the value are written until
579 * a null byte is encountered. The null byte is not added.
580 *
581 * @param tag Tag (field number) of the field
582 * @param value Pointer to value to be written
583 */
add_string(pbf_tag_type tag,const char * value)584 void add_string(pbf_tag_type tag, const char* value) {
585 add_bytes(tag, value, std::strlen(value));
586 }
587
588 /**
589 * Add "message" field to data.
590 *
591 * @param tag Tag (field number) of the field
592 * @param value Pointer to message to be written
593 * @param size Length of the message
594 */
add_message(pbf_tag_type tag,const char * value,std::size_t size)595 void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
596 add_bytes(tag, value, size);
597 }
598
599 /**
600 * Add "message" field to data.
601 *
602 * @param tag Tag (field number) of the field
603 * @param value Value to be written. The value must be a complete message.
604 */
add_message(pbf_tag_type tag,const data_view & value)605 void add_message(pbf_tag_type tag, const data_view& value) {
606 add_bytes(tag, value.data(), value.size());
607 }
608
609 /**
610 * Add "message" field to data.
611 *
612 * @param tag Tag (field number) of the field
613 * @param value Value to be written. The value must be a complete message.
614 */
add_message(pbf_tag_type tag,const std::string & value)615 void add_message(pbf_tag_type tag, const std::string& value) {
616 add_bytes(tag, value.data(), value.size());
617 }
618
619 ///@}
620
621 ///@{
622 /**
623 * @name Repeated packed field writer functions
624 */
625
626 /**
627 * Add "repeated packed bool" field to data.
628 *
629 * @tparam InputIterator A type satisfying the InputIterator concept.
630 * Dereferencing the iterator must yield a type assignable to bool.
631 * @param tag Tag (field number) of the field
632 * @param first Iterator pointing to the beginning of the data
633 * @param last Iterator pointing one past the end of data
634 */
635 template <typename InputIterator>
add_packed_bool(pbf_tag_type tag,InputIterator first,InputIterator last)636 void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) {
637 add_packed_varint(tag, first, last);
638 }
639
640 /**
641 * Add "repeated packed enum" field to data.
642 *
643 * @tparam InputIterator A type satisfying the InputIterator concept.
644 * Dereferencing the iterator must yield a type assignable to int32_t.
645 * @param tag Tag (field number) of the field
646 * @param first Iterator pointing to the beginning of the data
647 * @param last Iterator pointing one past the end of data
648 */
649 template <typename InputIterator>
add_packed_enum(pbf_tag_type tag,InputIterator first,InputIterator last)650 void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) {
651 add_packed_varint(tag, first, last);
652 }
653
654 /**
655 * Add "repeated packed int32" field to data.
656 *
657 * @tparam InputIterator A type satisfying the InputIterator concept.
658 * Dereferencing the iterator must yield a type assignable to int32_t.
659 * @param tag Tag (field number) of the field
660 * @param first Iterator pointing to the beginning of the data
661 * @param last Iterator pointing one past the end of data
662 */
663 template <typename InputIterator>
add_packed_int32(pbf_tag_type tag,InputIterator first,InputIterator last)664 void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) {
665 add_packed_varint(tag, first, last);
666 }
667
668 /**
669 * Add "repeated packed sint32" field to data.
670 *
671 * @tparam InputIterator A type satisfying the InputIterator concept.
672 * Dereferencing the iterator must yield a type assignable to int32_t.
673 * @param tag Tag (field number) of the field
674 * @param first Iterator pointing to the beginning of the data
675 * @param last Iterator pointing one past the end of data
676 */
677 template <typename InputIterator>
add_packed_sint32(pbf_tag_type tag,InputIterator first,InputIterator last)678 void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
679 add_packed_svarint(tag, first, last);
680 }
681
682 /**
683 * Add "repeated packed uint32" field to data.
684 *
685 * @tparam InputIterator A type satisfying the InputIterator concept.
686 * Dereferencing the iterator must yield a type assignable to uint32_t.
687 * @param tag Tag (field number) of the field
688 * @param first Iterator pointing to the beginning of the data
689 * @param last Iterator pointing one past the end of data
690 */
691 template <typename InputIterator>
add_packed_uint32(pbf_tag_type tag,InputIterator first,InputIterator last)692 void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) {
693 add_packed_varint(tag, first, last);
694 }
695
696 /**
697 * Add "repeated packed int64" field to data.
698 *
699 * @tparam InputIterator A type satisfying the InputIterator concept.
700 * Dereferencing the iterator must yield a type assignable to int64_t.
701 * @param tag Tag (field number) of the field
702 * @param first Iterator pointing to the beginning of the data
703 * @param last Iterator pointing one past the end of data
704 */
705 template <typename InputIterator>
add_packed_int64(pbf_tag_type tag,InputIterator first,InputIterator last)706 void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) {
707 add_packed_varint(tag, first, last);
708 }
709
710 /**
711 * Add "repeated packed sint64" field to data.
712 *
713 * @tparam InputIterator A type satisfying the InputIterator concept.
714 * Dereferencing the iterator must yield a type assignable to int64_t.
715 * @param tag Tag (field number) of the field
716 * @param first Iterator pointing to the beginning of the data
717 * @param last Iterator pointing one past the end of data
718 */
719 template <typename InputIterator>
add_packed_sint64(pbf_tag_type tag,InputIterator first,InputIterator last)720 void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
721 add_packed_svarint(tag, first, last);
722 }
723
724 /**
725 * Add "repeated packed uint64" field to data.
726 *
727 * @tparam InputIterator A type satisfying the InputIterator concept.
728 * Dereferencing the iterator must yield a type assignable to uint64_t.
729 * @param tag Tag (field number) of the field
730 * @param first Iterator pointing to the beginning of the data
731 * @param last Iterator pointing one past the end of data
732 */
733 template <typename InputIterator>
add_packed_uint64(pbf_tag_type tag,InputIterator first,InputIterator last)734 void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) {
735 add_packed_varint(tag, first, last);
736 }
737
738 /**
739 * Add "repeated packed fixed32" field to data.
740 *
741 * @tparam InputIterator A type satisfying the InputIterator concept.
742 * Dereferencing the iterator must yield a type assignable to uint32_t.
743 * @param tag Tag (field number) of the field
744 * @param first Iterator pointing to the beginning of the data
745 * @param last Iterator pointing one past the end of data
746 */
747 template <typename InputIterator>
add_packed_fixed32(pbf_tag_type tag,InputIterator first,InputIterator last)748 void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
749 add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
750 typename std::iterator_traits<InputIterator>::iterator_category());
751 }
752
753 /**
754 * Add "repeated packed sfixed32" field to data.
755 *
756 * @tparam InputIterator A type satisfying the InputIterator concept.
757 * Dereferencing the iterator must yield a type assignable to int32_t.
758 * @param tag Tag (field number) of the field
759 * @param first Iterator pointing to the beginning of the data
760 * @param last Iterator pointing one past the end of data
761 */
762 template <typename InputIterator>
add_packed_sfixed32(pbf_tag_type tag,InputIterator first,InputIterator last)763 void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) {
764 add_packed_fixed<int32_t, InputIterator>(tag, first, last,
765 typename std::iterator_traits<InputIterator>::iterator_category());
766 }
767
768 /**
769 * Add "repeated packed fixed64" field to data.
770 *
771 * @tparam InputIterator A type satisfying the InputIterator concept.
772 * Dereferencing the iterator must yield a type assignable to uint64_t.
773 * @param tag Tag (field number) of the field
774 * @param first Iterator pointing to the beginning of the data
775 * @param last Iterator pointing one past the end of data
776 */
777 template <typename InputIterator>
add_packed_fixed64(pbf_tag_type tag,InputIterator first,InputIterator last)778 void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
779 add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
780 typename std::iterator_traits<InputIterator>::iterator_category());
781 }
782
783 /**
784 * Add "repeated packed sfixed64" field to data.
785 *
786 * @tparam InputIterator A type satisfying the InputIterator concept.
787 * Dereferencing the iterator must yield a type assignable to int64_t.
788 * @param tag Tag (field number) of the field
789 * @param first Iterator pointing to the beginning of the data
790 * @param last Iterator pointing one past the end of data
791 */
792 template <typename InputIterator>
add_packed_sfixed64(pbf_tag_type tag,InputIterator first,InputIterator last)793 void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) {
794 add_packed_fixed<int64_t, InputIterator>(tag, first, last,
795 typename std::iterator_traits<InputIterator>::iterator_category());
796 }
797
798 /**
799 * Add "repeated packed float" field to data.
800 *
801 * @tparam InputIterator A type satisfying the InputIterator concept.
802 * Dereferencing the iterator must yield a type assignable to float.
803 * @param tag Tag (field number) of the field
804 * @param first Iterator pointing to the beginning of the data
805 * @param last Iterator pointing one past the end of data
806 */
807 template <typename InputIterator>
add_packed_float(pbf_tag_type tag,InputIterator first,InputIterator last)808 void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) {
809 add_packed_fixed<float, InputIterator>(tag, first, last,
810 typename std::iterator_traits<InputIterator>::iterator_category());
811 }
812
813 /**
814 * Add "repeated packed double" field to data.
815 *
816 * @tparam InputIterator A type satisfying the InputIterator concept.
817 * Dereferencing the iterator must yield a type assignable to double.
818 * @param tag Tag (field number) of the field
819 * @param first Iterator pointing to the beginning of the data
820 * @param last Iterator pointing one past the end of data
821 */
822 template <typename InputIterator>
add_packed_double(pbf_tag_type tag,InputIterator first,InputIterator last)823 void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) {
824 add_packed_fixed<double, InputIterator>(tag, first, last,
825 typename std::iterator_traits<InputIterator>::iterator_category());
826 }
827
828 ///@}
829
830 template <typename T> friend class detail::packed_field_varint;
831 template <typename T> friend class detail::packed_field_svarint;
832 template <typename T> friend class detail::packed_field_fixed;
833
834 }; // class pbf_writer
835
836 /**
837 * Swap two pbf_writer objects.
838 *
839 * @param lhs First object.
840 * @param rhs Second object.
841 */
swap(pbf_writer & lhs,pbf_writer & rhs)842 inline void swap(pbf_writer& lhs, pbf_writer& rhs) noexcept {
843 lhs.swap(rhs);
844 }
845
846 namespace detail {
847
848 class packed_field {
849
850 protected:
851
852 pbf_writer m_writer;
853
854 public:
855
856 packed_field(const packed_field&) = delete;
857 packed_field& operator=(const packed_field&) = delete;
858
859 packed_field(packed_field&&) = default;
860 packed_field& operator=(packed_field&&) = default;
861
packed_field(pbf_writer & parent_writer,pbf_tag_type tag)862 packed_field(pbf_writer& parent_writer, pbf_tag_type tag) :
863 m_writer(parent_writer, tag) {
864 }
865
packed_field(pbf_writer & parent_writer,pbf_tag_type tag,std::size_t size)866 packed_field(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
867 m_writer(parent_writer, tag, size) {
868 }
869
rollback()870 void rollback() {
871 m_writer.rollback();
872 }
873
874 }; // class packed_field
875
876 template <typename T>
877 class packed_field_fixed : public packed_field {
878
879 public:
880
881 template <typename P>
packed_field_fixed(pbf_writer & parent_writer,P tag)882 packed_field_fixed(pbf_writer& parent_writer, P tag) :
883 packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
884 }
885
886 template <typename P>
packed_field_fixed(pbf_writer & parent_writer,P tag,std::size_t size)887 packed_field_fixed(pbf_writer& parent_writer, P tag, std::size_t size) :
888 packed_field(parent_writer, static_cast<pbf_tag_type>(tag), size * sizeof(T)) {
889 }
890
add_element(T value)891 void add_element(T value) {
892 m_writer.add_fixed<T>(value);
893 }
894
895 }; // class packed_field_fixed
896
897 template <typename T>
898 class packed_field_varint : public packed_field {
899
900 public:
901
902 template <typename P>
packed_field_varint(pbf_writer & parent_writer,P tag)903 packed_field_varint(pbf_writer& parent_writer, P tag) :
904 packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
905 }
906
add_element(T value)907 void add_element(T value) {
908 m_writer.add_varint(uint64_t(value));
909 }
910
911 }; // class packed_field_varint
912
913 template <typename T>
914 class packed_field_svarint : public packed_field {
915
916 public:
917
918 template <typename P>
packed_field_svarint(pbf_writer & parent_writer,P tag)919 packed_field_svarint(pbf_writer& parent_writer, P tag) :
920 packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
921 }
922
add_element(T value)923 void add_element(T value) {
924 m_writer.add_varint(encode_zigzag64(value));
925 }
926
927 }; // class packed_field_svarint
928
929 } // end namespace detail
930
931 /// Class for generating packed repeated bool fields.
932 using packed_field_bool = detail::packed_field_varint<bool>;
933
934 /// Class for generating packed repeated enum fields.
935 using packed_field_enum = detail::packed_field_varint<int32_t>;
936
937 /// Class for generating packed repeated int32 fields.
938 using packed_field_int32 = detail::packed_field_varint<int32_t>;
939
940 /// Class for generating packed repeated sint32 fields.
941 using packed_field_sint32 = detail::packed_field_svarint<int32_t>;
942
943 /// Class for generating packed repeated uint32 fields.
944 using packed_field_uint32 = detail::packed_field_varint<uint32_t>;
945
946 /// Class for generating packed repeated int64 fields.
947 using packed_field_int64 = detail::packed_field_varint<int64_t>;
948
949 /// Class for generating packed repeated sint64 fields.
950 using packed_field_sint64 = detail::packed_field_svarint<int64_t>;
951
952 /// Class for generating packed repeated uint64 fields.
953 using packed_field_uint64 = detail::packed_field_varint<uint64_t>;
954
955 /// Class for generating packed repeated fixed32 fields.
956 using packed_field_fixed32 = detail::packed_field_fixed<uint32_t>;
957
958 /// Class for generating packed repeated sfixed32 fields.
959 using packed_field_sfixed32 = detail::packed_field_fixed<int32_t>;
960
961 /// Class for generating packed repeated fixed64 fields.
962 using packed_field_fixed64 = detail::packed_field_fixed<uint64_t>;
963
964 /// Class for generating packed repeated sfixed64 fields.
965 using packed_field_sfixed64 = detail::packed_field_fixed<int64_t>;
966
967 /// Class for generating packed repeated float fields.
968 using packed_field_float = detail::packed_field_fixed<float>;
969
970 /// Class for generating packed repeated double fields.
971 using packed_field_double = detail::packed_field_fixed<double>;
972
973 } // end namespace protozero
974
975 #endif // PROTOZERO_PBF_WRITER_HPP
976