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