1 #ifndef PROTOZERO_PBF_READER_HPP
2 #define PROTOZERO_PBF_READER_HPP
3
4 /*****************************************************************************
5
6 protozero - Minimalistic protocol buffer decoder and encoder in C++.
7
8 This file is from https://github.com/mapbox/protozero where you can find more
9 documentation.
10
11 *****************************************************************************/
12
13 /**
14 * @file pbf_reader.hpp
15 *
16 * @brief Contains the pbf_reader class.
17 */
18
19 #include <cstddef>
20 #include <cstdint>
21 #include <string>
22 #include <utility>
23
24 #include <protozero/config.hpp>
25 #include <protozero/exception.hpp>
26 #include <protozero/iterators.hpp>
27 #include <protozero/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
36 /**
37 * This class represents a protobuf message. Either a top-level message or
38 * a nested sub-message. Top-level messages can be created from any buffer
39 * with a pointer and length:
40 *
41 * @code
42 * std::string buffer;
43 * // fill buffer...
44 * pbf_reader message(buffer.data(), buffer.size());
45 * @endcode
46 *
47 * Sub-messages are created using get_message():
48 *
49 * @code
50 * pbf_reader message(...);
51 * message.next();
52 * pbf_reader submessage = message.get_message();
53 * @endcode
54 *
55 * All methods of the pbf_reader class except get_bytes() and get_string()
56 * provide the strong exception guarantee, ie they either succeed or do not
57 * change the pbf_reader object they are called on. Use the get_view() method
58 * instead of get_bytes() or get_string(), if you need this guarantee.
59 */
60 class pbf_reader {
61
62 // A pointer to the next unread data.
63 const char* m_data = nullptr;
64
65 // A pointer to one past the end of data.
66 const char* m_end = nullptr;
67
68 // The wire type of the current field.
69 pbf_wire_type m_wire_type = pbf_wire_type::unknown;
70
71 // The tag of the current field.
72 pbf_tag_type m_tag = 0;
73
74 template <typename T>
get_fixed()75 T get_fixed() {
76 T result;
77 skip_bytes(sizeof(T));
78 std::memcpy(&result, m_data - sizeof(T), sizeof(T));
79 #if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
80 detail::byteswap_inplace(&result);
81 #endif
82 return result;
83 }
84
85 template <typename T>
packed_fixed()86 iterator_range<const_fixed_iterator<T>> packed_fixed() {
87 protozero_assert(tag() != 0 && "call next() before accessing field value");
88 const auto len = get_len_and_skip();
89 protozero_assert(len % sizeof(T) == 0);
90 return iterator_range<const_fixed_iterator<T>>{const_fixed_iterator<T>(m_data - len, m_data),
91 const_fixed_iterator<T>(m_data, m_data)};
92 }
93
94 template <typename T>
get_varint()95 T get_varint() {
96 return static_cast<T>(decode_varint(&m_data, m_end));
97 }
98
99 template <typename T>
get_svarint()100 T get_svarint() {
101 protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
102 return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
103 }
104
get_length()105 pbf_length_type get_length() {
106 return get_varint<pbf_length_type>();
107 }
108
skip_bytes(pbf_length_type len)109 void skip_bytes(pbf_length_type len) {
110 if (m_data + len > m_end) {
111 throw end_of_buffer_exception();
112 }
113 m_data += len;
114
115 // In debug builds reset the tag to zero so that we can detect (some)
116 // wrong code.
117 #ifndef NDEBUG
118 m_tag = 0;
119 #endif
120 }
121
get_len_and_skip()122 pbf_length_type get_len_and_skip() {
123 const auto len = get_length();
124 skip_bytes(len);
125 return len;
126 }
127
128 template <typename T>
get_packed()129 iterator_range<T> get_packed() {
130 protozero_assert(tag() != 0 && "call next() before accessing field value");
131 const auto len = get_len_and_skip();
132 return iterator_range<T>{T{m_data - len, m_data},
133 T{m_data, m_data}};
134 }
135
136 public:
137
138 /**
139 * Construct a pbf_reader message from a data_view. The pointer from the
140 * data_view will be stored inside the pbf_reader object, no data is
141 * copied. So you must make sure the view stays valid as long as the
142 * pbf_reader object is used.
143 *
144 * The buffer must contain a complete protobuf message.
145 *
146 * @post There is no current field.
147 */
pbf_reader(const data_view & view)148 explicit pbf_reader(const data_view& view) noexcept
149 : m_data(view.data()),
150 m_end(view.data() + view.size()),
151 m_wire_type(pbf_wire_type::unknown),
152 m_tag(0) {
153 }
154
155 /**
156 * Construct a pbf_reader message from a data pointer and a length. The
157 * pointer will be stored inside the pbf_reader object, no data is copied.
158 * So you must make sure the buffer stays valid as long as the pbf_reader
159 * object is used.
160 *
161 * The buffer must contain a complete protobuf message.
162 *
163 * @post There is no current field.
164 */
pbf_reader(const char * data,std::size_t size)165 pbf_reader(const char* data, std::size_t size) noexcept
166 : m_data(data),
167 m_end(data + size),
168 m_wire_type(pbf_wire_type::unknown),
169 m_tag(0) {
170 }
171
172 /**
173 * Construct a pbf_reader message from a data pointer and a length. The
174 * pointer will be stored inside the pbf_reader object, no data is copied.
175 * So you must make sure the buffer stays valid as long as the pbf_reader
176 * object is used.
177 *
178 * The buffer must contain a complete protobuf message.
179 *
180 * @post There is no current field.
181 */
pbf_reader(const std::pair<const char *,std::size_t> & data)182 explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
183 : m_data(data.first),
184 m_end(data.first + data.second),
185 m_wire_type(pbf_wire_type::unknown),
186 m_tag(0) {
187 }
188
189 /**
190 * Construct a pbf_reader message from a std::string. A pointer to the
191 * string internals will be stored inside the pbf_reader object, no data
192 * is copied. So you must make sure the string is unchanged as long as the
193 * pbf_reader object is used.
194 *
195 * The string must contain a complete protobuf message.
196 *
197 * @post There is no current field.
198 */
pbf_reader(const std::string & data)199 explicit pbf_reader(const std::string& data) noexcept
200 : m_data(data.data()),
201 m_end(data.data() + data.size()),
202 m_wire_type(pbf_wire_type::unknown),
203 m_tag(0) {
204 }
205
206 /**
207 * pbf_reader can be default constructed and behaves like it has an empty
208 * buffer.
209 */
210 pbf_reader() noexcept = default;
211
212 /// pbf_reader messages can be copied trivially.
213 pbf_reader(const pbf_reader&) noexcept = default;
214
215 /// pbf_reader messages can be moved trivially.
216 pbf_reader(pbf_reader&&) noexcept = default;
217
218 /// pbf_reader messages can be copied trivially.
219 pbf_reader& operator=(const pbf_reader& other) noexcept = default;
220
221 /// pbf_reader messages can be moved trivially.
222 pbf_reader& operator=(pbf_reader&& other) noexcept = default;
223
224 ~pbf_reader() = default;
225
226 /**
227 * Swap the contents of this object with the other.
228 *
229 * @param other Other object to swap data with.
230 */
swap(pbf_reader & other)231 void swap(pbf_reader& other) noexcept {
232 using std::swap;
233 swap(m_data, other.m_data);
234 swap(m_end, other.m_end);
235 swap(m_wire_type, other.m_wire_type);
236 swap(m_tag, other.m_tag);
237 }
238
239 /**
240 * In a boolean context the pbf_reader class evaluates to `true` if there
241 * are still fields available and to `false` if the last field has been
242 * read.
243 */
operator bool() const244 operator bool() const noexcept {
245 return m_data < m_end;
246 }
247
248 /**
249 * Return the length in bytes of the current message. If you have
250 * already called next() and/or any of the get_*() functions, this will
251 * return the remaining length.
252 *
253 * This can, for instance, be used to estimate the space needed for a
254 * buffer. Of course you have to know reasonably well what data to expect
255 * and how it is encoded for this number to have any meaning.
256 */
length() const257 std::size_t length() const noexcept {
258 return std::size_t(m_end - m_data);
259 }
260
261 /**
262 * Set next field in the message as the current field. This is usually
263 * called in a while loop:
264 *
265 * @code
266 * pbf_reader message(...);
267 * while (message.next()) {
268 * // handle field
269 * }
270 * @endcode
271 *
272 * @returns `true` if there is a next field, `false` if not.
273 * @pre There must be no current field.
274 * @post If it returns `true` there is a current field now.
275 */
next()276 bool next() {
277 if (m_data == m_end) {
278 return false;
279 }
280
281 const auto value = get_varint<uint32_t>();
282 m_tag = pbf_tag_type(value >> 3);
283
284 // tags 0 and 19000 to 19999 are not allowed as per
285 // https://developers.google.com/protocol-buffers/docs/proto
286 protozero_assert(((m_tag > 0 && m_tag < 19000) ||
287 (m_tag > 19999 && m_tag <= ((1 << 29) - 1))) && "tag out of range");
288
289 m_wire_type = pbf_wire_type(value & 0x07);
290 switch (m_wire_type) {
291 case pbf_wire_type::varint:
292 case pbf_wire_type::fixed64:
293 case pbf_wire_type::length_delimited:
294 case pbf_wire_type::fixed32:
295 break;
296 default:
297 throw unknown_pbf_wire_type_exception();
298 }
299
300 return true;
301 }
302
303 /**
304 * Set next field with given tag in the message as the current field.
305 * Fields with other tags are skipped. This is usually called in a while
306 * loop for repeated fields:
307 *
308 * @code
309 * pbf_reader message(...);
310 * while (message.next(17)) {
311 * // handle field
312 * }
313 * @endcode
314 *
315 * or you can call it just once to get the one field with this tag:
316 *
317 * @code
318 * pbf_reader message(...);
319 * if (message.next(17)) {
320 * // handle field
321 * }
322 * @endcode
323 *
324 * Note that this will not check the wire type. The two-argument version
325 * of this function will also check the wire type.
326 *
327 * @returns `true` if there is a next field with this tag.
328 * @pre There must be no current field.
329 * @post If it returns `true` there is a current field now with the given tag.
330 */
next(pbf_tag_type next_tag)331 bool next(pbf_tag_type next_tag) {
332 while (next()) {
333 if (m_tag == next_tag) {
334 return true;
335 } else {
336 skip();
337 }
338 }
339 return false;
340 }
341
342 /**
343 * Set next field with given tag and wire type in the message as the
344 * current field. Fields with other tags are skipped. This is usually
345 * called in a while loop for repeated fields:
346 *
347 * @code
348 * pbf_reader message(...);
349 * while (message.next(17, pbf_wire_type::varint)) {
350 * // handle field
351 * }
352 * @endcode
353 *
354 * or you can call it just once to get the one field with this tag:
355 *
356 * @code
357 * pbf_reader message(...);
358 * if (message.next(17, pbf_wire_type::varint)) {
359 * // handle field
360 * }
361 * @endcode
362 *
363 * Note that this will also check the wire type. The one-argument version
364 * of this function will not check the wire type.
365 *
366 * @returns `true` if there is a next field with this tag.
367 * @pre There must be no current field.
368 * @post If it returns `true` there is a current field now with the given tag.
369 */
next(pbf_tag_type next_tag,pbf_wire_type type)370 bool next(pbf_tag_type next_tag, pbf_wire_type type) {
371 while (next()) {
372 if (m_tag == next_tag && m_wire_type == type) {
373 return true;
374 } else {
375 skip();
376 }
377 }
378 return false;
379 }
380
381 /**
382 * The tag of the current field. The tag is the field number from the
383 * description in the .proto file.
384 *
385 * Call next() before calling this function to set the current field.
386 *
387 * @returns tag of the current field.
388 * @pre There must be a current field (ie. next() must have returned `true`).
389 */
tag() const390 pbf_tag_type tag() const noexcept {
391 return m_tag;
392 }
393
394 /**
395 * Get the wire type of the current field. The wire types are:
396 *
397 * * 0 - varint
398 * * 1 - 64 bit
399 * * 2 - length-delimited
400 * * 5 - 32 bit
401 *
402 * All other types are illegal.
403 *
404 * Call next() before calling this function to set the current field.
405 *
406 * @returns wire type of the current field.
407 * @pre There must be a current field (ie. next() must have returned `true`).
408 */
wire_type() const409 pbf_wire_type wire_type() const noexcept {
410 return m_wire_type;
411 }
412
413 /**
414 * Get the tag and wire type of the current field in one integer suitable
415 * for comparison with a switch statement.
416 *
417 * Use it like this:
418 *
419 * @code
420 * pbf_reader message(...);
421 * while (message.next()) {
422 * switch (message.tag_and_type()) {
423 * case tag_and_type(17, pbf_wire_type::length_delimited):
424 * ....
425 * break;
426 * case tag_and_type(21, pbf_wire_type::varint):
427 * ....
428 * break;
429 * default:
430 * message.skip();
431 * }
432 * }
433 * @endcode
434 */
tag_and_type() const435 uint32_t tag_and_type() const noexcept {
436 return protozero::tag_and_type(tag(), wire_type());
437 }
438
439 /**
440 * Check the wire type of the current field.
441 *
442 * @returns `true` if the current field has the given wire type.
443 * @pre There must be a current field (ie. next() must have returned `true`).
444 */
has_wire_type(pbf_wire_type type) const445 bool has_wire_type(pbf_wire_type type) const noexcept {
446 return wire_type() == type;
447 }
448
449 /**
450 * Consume the current field.
451 *
452 * @pre There must be a current field (ie. next() must have returned `true`).
453 * @post The current field was consumed and there is no current field now.
454 */
skip()455 void skip() {
456 protozero_assert(tag() != 0 && "call next() before calling skip()");
457 switch (wire_type()) {
458 case pbf_wire_type::varint:
459 skip_varint(&m_data, m_end);
460 break;
461 case pbf_wire_type::fixed64:
462 skip_bytes(8);
463 break;
464 case pbf_wire_type::length_delimited:
465 skip_bytes(get_length());
466 break;
467 case pbf_wire_type::fixed32:
468 skip_bytes(4);
469 break;
470 default:
471 protozero_assert(false && "can not be here because next() should have thrown already");
472 }
473 }
474
475 ///@{
476 /**
477 * @name Scalar field accessor functions
478 */
479
480 /**
481 * Consume and return value of current "bool" field.
482 *
483 * @pre There must be a current field (ie. next() must have returned `true`).
484 * @pre The current field must be of type "bool".
485 * @post The current field was consumed and there is no current field now.
486 */
get_bool()487 bool get_bool() {
488 protozero_assert(tag() != 0 && "call next() before accessing field value");
489 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
490 protozero_assert((*m_data & 0x80) == 0 && "not a 1 byte varint");
491 skip_bytes(1);
492 return m_data[-1] != 0; // -1 okay because we incremented m_data the line before
493 }
494
495 /**
496 * Consume and return value of current "enum" field.
497 *
498 * @pre There must be a current field (ie. next() must have returned `true`).
499 * @pre The current field must be of type "enum".
500 * @post The current field was consumed and there is no current field now.
501 */
get_enum()502 int32_t get_enum() {
503 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
504 return get_varint<int32_t>();
505 }
506
507 /**
508 * Consume and return value of current "int32" varint field.
509 *
510 * @pre There must be a current field (ie. next() must have returned `true`).
511 * @pre The current field must be of type "int32".
512 * @post The current field was consumed and there is no current field now.
513 */
get_int32()514 int32_t get_int32() {
515 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
516 return get_varint<int32_t>();
517 }
518
519 /**
520 * Consume and return value of current "sint32" varint field.
521 *
522 * @pre There must be a current field (ie. next() must have returned `true`).
523 * @pre The current field must be of type "sint32".
524 * @post The current field was consumed and there is no current field now.
525 */
get_sint32()526 int32_t get_sint32() {
527 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
528 return get_svarint<int32_t>();
529 }
530
531 /**
532 * Consume and return value of current "uint32" varint field.
533 *
534 * @pre There must be a current field (ie. next() must have returned `true`).
535 * @pre The current field must be of type "uint32".
536 * @post The current field was consumed and there is no current field now.
537 */
get_uint32()538 uint32_t get_uint32() {
539 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
540 return get_varint<uint32_t>();
541 }
542
543 /**
544 * Consume and return value of current "int64" varint field.
545 *
546 * @pre There must be a current field (ie. next() must have returned `true`).
547 * @pre The current field must be of type "int64".
548 * @post The current field was consumed and there is no current field now.
549 */
get_int64()550 int64_t get_int64() {
551 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
552 return get_varint<int64_t>();
553 }
554
555 /**
556 * Consume and return value of current "sint64" varint field.
557 *
558 * @pre There must be a current field (ie. next() must have returned `true`).
559 * @pre The current field must be of type "sint64".
560 * @post The current field was consumed and there is no current field now.
561 */
get_sint64()562 int64_t get_sint64() {
563 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
564 return get_svarint<int64_t>();
565 }
566
567 /**
568 * Consume and return value of current "uint64" varint field.
569 *
570 * @pre There must be a current field (ie. next() must have returned `true`).
571 * @pre The current field must be of type "uint64".
572 * @post The current field was consumed and there is no current field now.
573 */
get_uint64()574 uint64_t get_uint64() {
575 protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
576 return get_varint<uint64_t>();
577 }
578
579 /**
580 * Consume and return value of current "fixed32" field.
581 *
582 * @pre There must be a current field (ie. next() must have returned `true`).
583 * @pre The current field must be of type "fixed32".
584 * @post The current field was consumed and there is no current field now.
585 */
get_fixed32()586 uint32_t get_fixed32() {
587 protozero_assert(tag() != 0 && "call next() before accessing field value");
588 protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
589 return get_fixed<uint32_t>();
590 }
591
592 /**
593 * Consume and return value of current "sfixed32" field.
594 *
595 * @pre There must be a current field (ie. next() must have returned `true`).
596 * @pre The current field must be of type "sfixed32".
597 * @post The current field was consumed and there is no current field now.
598 */
get_sfixed32()599 int32_t get_sfixed32() {
600 protozero_assert(tag() != 0 && "call next() before accessing field value");
601 protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
602 return get_fixed<int32_t>();
603 }
604
605 /**
606 * Consume and return value of current "fixed64" field.
607 *
608 * @pre There must be a current field (ie. next() must have returned `true`).
609 * @pre The current field must be of type "fixed64".
610 * @post The current field was consumed and there is no current field now.
611 */
get_fixed64()612 uint64_t get_fixed64() {
613 protozero_assert(tag() != 0 && "call next() before accessing field value");
614 protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
615 return get_fixed<uint64_t>();
616 }
617
618 /**
619 * Consume and return value of current "sfixed64" field.
620 *
621 * @pre There must be a current field (ie. next() must have returned `true`).
622 * @pre The current field must be of type "sfixed64".
623 * @post The current field was consumed and there is no current field now.
624 */
get_sfixed64()625 int64_t get_sfixed64() {
626 protozero_assert(tag() != 0 && "call next() before accessing field value");
627 protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
628 return get_fixed<int64_t>();
629 }
630
631 /**
632 * Consume and return value of current "float" field.
633 *
634 * @pre There must be a current field (ie. next() must have returned `true`).
635 * @pre The current field must be of type "float".
636 * @post The current field was consumed and there is no current field now.
637 */
get_float()638 float get_float() {
639 protozero_assert(tag() != 0 && "call next() before accessing field value");
640 protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
641 return get_fixed<float>();
642 }
643
644 /**
645 * Consume and return value of current "double" field.
646 *
647 * @pre There must be a current field (ie. next() must have returned `true`).
648 * @pre The current field must be of type "double".
649 * @post The current field was consumed and there is no current field now.
650 */
get_double()651 double get_double() {
652 protozero_assert(tag() != 0 && "call next() before accessing field value");
653 protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
654 return get_fixed<double>();
655 }
656
657 /**
658 * Consume and return value of current "bytes", "string", or "message"
659 * field.
660 *
661 * @returns A data_view object.
662 * @pre There must be a current field (ie. next() must have returned `true`).
663 * @pre The current field must be of type "bytes", "string", or "message".
664 * @post The current field was consumed and there is no current field now.
665 */
get_view()666 data_view get_view() {
667 protozero_assert(tag() != 0 && "call next() before accessing field value");
668 protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
669 const auto len = get_len_and_skip();
670 return data_view{m_data - len, len};
671 }
672
673 #ifndef PROTOZERO_STRICT_API
674 /**
675 * Consume and return value of current "bytes" or "string" field.
676 *
677 * @returns A pair with a pointer to the data and the length of the data.
678 * @pre There must be a current field (ie. next() must have returned `true`).
679 * @pre The current field must be of type "bytes" or "string".
680 * @post The current field was consumed and there is no current field now.
681 */
get_data()682 std::pair<const char*, pbf_length_type> get_data() {
683 protozero_assert(tag() != 0 && "call next() before accessing field value");
684 protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
685 const auto len = get_len_and_skip();
686 return std::make_pair(m_data - len, len);
687 }
688 #endif
689
690 /**
691 * Consume and return value of current "bytes" field.
692 *
693 * @pre There must be a current field (ie. next() must have returned `true`).
694 * @pre The current field must be of type "bytes".
695 * @post The current field was consumed and there is no current field now.
696 */
get_bytes()697 std::string get_bytes() {
698 return std::string(get_view());
699 }
700
701 /**
702 * Consume and return value of current "string" field.
703 *
704 * @pre There must be a current field (ie. next() must have returned `true`).
705 * @pre The current field must be of type "string".
706 * @post The current field was consumed and there is no current field now.
707 */
get_string()708 std::string get_string() {
709 return std::string(get_view());
710 }
711
712 /**
713 * Consume and return value of current "message" field.
714 *
715 * @pre There must be a current field (ie. next() must have returned `true`).
716 * @pre The current field must be of type "message".
717 * @post The current field was consumed and there is no current field now.
718 */
get_message()719 pbf_reader get_message() {
720 return pbf_reader(get_view());
721 }
722
723 ///@}
724
725 /// Forward iterator for iterating over bool (int32 varint) values.
726 using const_bool_iterator = const_varint_iterator< int32_t>;
727
728 /// Forward iterator for iterating over enum (int32 varint) values.
729 using const_enum_iterator = const_varint_iterator< int32_t>;
730
731 /// Forward iterator for iterating over int32 (varint) values.
732 using const_int32_iterator = const_varint_iterator< int32_t>;
733
734 /// Forward iterator for iterating over sint32 (varint) values.
735 using const_sint32_iterator = const_svarint_iterator<int32_t>;
736
737 /// Forward iterator for iterating over uint32 (varint) values.
738 using const_uint32_iterator = const_varint_iterator<uint32_t>;
739
740 /// Forward iterator for iterating over int64 (varint) values.
741 using const_int64_iterator = const_varint_iterator< int64_t>;
742
743 /// Forward iterator for iterating over sint64 (varint) values.
744 using const_sint64_iterator = const_svarint_iterator<int64_t>;
745
746 /// Forward iterator for iterating over uint64 (varint) values.
747 using const_uint64_iterator = const_varint_iterator<uint64_t>;
748
749 ///@{
750 /**
751 * @name Repeated packed field accessor functions
752 */
753
754 /**
755 * Consume current "repeated packed bool" field.
756 *
757 * @returns a pair of iterators to the beginning and one past the end of
758 * the data.
759 * @pre There must be a current field (ie. next() must have returned `true`).
760 * @pre The current field must be of type "repeated packed bool".
761 * @post The current field was consumed and there is no current field now.
762 */
get_packed_bool()763 iterator_range<pbf_reader::const_bool_iterator> get_packed_bool() {
764 return get_packed<pbf_reader::const_bool_iterator>();
765 }
766
767 /**
768 * Consume current "repeated packed enum" field.
769 *
770 * @returns a pair of iterators to the beginning and one past the end of
771 * the data.
772 * @pre There must be a current field (ie. next() must have returned `true`).
773 * @pre The current field must be of type "repeated packed enum".
774 * @post The current field was consumed and there is no current field now.
775 */
get_packed_enum()776 iterator_range<pbf_reader::const_enum_iterator> get_packed_enum() {
777 return get_packed<pbf_reader::const_enum_iterator>();
778 }
779
780 /**
781 * Consume current "repeated packed int32" field.
782 *
783 * @returns a pair of iterators to the beginning and one past the end of
784 * the data.
785 * @pre There must be a current field (ie. next() must have returned `true`).
786 * @pre The current field must be of type "repeated packed int32".
787 * @post The current field was consumed and there is no current field now.
788 */
get_packed_int32()789 iterator_range<pbf_reader::const_int32_iterator> get_packed_int32() {
790 return get_packed<pbf_reader::const_int32_iterator>();
791 }
792
793 /**
794 * Consume current "repeated packed sint32" field.
795 *
796 * @returns a pair of iterators to the beginning and one past the end of
797 * the data.
798 * @pre There must be a current field (ie. next() must have returned `true`).
799 * @pre The current field must be of type "repeated packed sint32".
800 * @post The current field was consumed and there is no current field now.
801 */
get_packed_sint32()802 iterator_range<pbf_reader::const_sint32_iterator> get_packed_sint32() {
803 return get_packed<pbf_reader::const_sint32_iterator>();
804 }
805
806 /**
807 * Consume current "repeated packed uint32" field.
808 *
809 * @returns a pair of iterators to the beginning and one past the end of
810 * the data.
811 * @pre There must be a current field (ie. next() must have returned `true`).
812 * @pre The current field must be of type "repeated packed uint32".
813 * @post The current field was consumed and there is no current field now.
814 */
get_packed_uint32()815 iterator_range<pbf_reader::const_uint32_iterator> get_packed_uint32() {
816 return get_packed<pbf_reader::const_uint32_iterator>();
817 }
818
819 /**
820 * Consume current "repeated packed int64" field.
821 *
822 * @returns a pair of iterators to the beginning and one past the end of
823 * the data.
824 * @pre There must be a current field (ie. next() must have returned `true`).
825 * @pre The current field must be of type "repeated packed int64".
826 * @post The current field was consumed and there is no current field now.
827 */
get_packed_int64()828 iterator_range<pbf_reader::const_int64_iterator> get_packed_int64() {
829 return get_packed<pbf_reader::const_int64_iterator>();
830 }
831
832 /**
833 * Consume current "repeated packed sint64" field.
834 *
835 * @returns a pair of iterators to the beginning and one past the end of
836 * the data.
837 * @pre There must be a current field (ie. next() must have returned `true`).
838 * @pre The current field must be of type "repeated packed sint64".
839 * @post The current field was consumed and there is no current field now.
840 */
get_packed_sint64()841 iterator_range<pbf_reader::const_sint64_iterator> get_packed_sint64() {
842 return get_packed<pbf_reader::const_sint64_iterator>();
843 }
844
845 /**
846 * Consume current "repeated packed uint64" field.
847 *
848 * @returns a pair of iterators to the beginning and one past the end of
849 * the data.
850 * @pre There must be a current field (ie. next() must have returned `true`).
851 * @pre The current field must be of type "repeated packed uint64".
852 * @post The current field was consumed and there is no current field now.
853 */
get_packed_uint64()854 iterator_range<pbf_reader::const_uint64_iterator> get_packed_uint64() {
855 return get_packed<pbf_reader::const_uint64_iterator>();
856 }
857
858 /**
859 * Consume current "repeated packed fixed32" field.
860 *
861 * @returns a pair of iterators to the beginning and one past the end of
862 * the data.
863 * @pre There must be a current field (ie. next() must have returned `true`).
864 * @pre The current field must be of type "repeated packed fixed32".
865 * @post The current field was consumed and there is no current field now.
866 */
get_packed_fixed32()867 auto get_packed_fixed32() -> decltype(packed_fixed<uint32_t>()) {
868 return packed_fixed<uint32_t>();
869 }
870
871 /**
872 * Consume current "repeated packed sfixed32" field.
873 *
874 * @returns a pair of iterators to the beginning and one past the end of
875 * the data.
876 * @pre There must be a current field (ie. next() must have returned `true`).
877 * @pre The current field must be of type "repeated packed sfixed32".
878 * @post The current field was consumed and there is no current field now.
879 */
get_packed_sfixed32()880 auto get_packed_sfixed32() -> decltype(packed_fixed<int32_t>()) {
881 return packed_fixed<int32_t>();
882 }
883
884 /**
885 * Consume current "repeated packed fixed64" field.
886 *
887 * @returns a pair of iterators to the beginning and one past the end of
888 * the data.
889 * @pre There must be a current field (ie. next() must have returned `true`).
890 * @pre The current field must be of type "repeated packed fixed64".
891 * @post The current field was consumed and there is no current field now.
892 */
get_packed_fixed64()893 auto get_packed_fixed64() -> decltype(packed_fixed<uint64_t>()) {
894 return packed_fixed<uint64_t>();
895 }
896
897 /**
898 * Consume current "repeated packed sfixed64" field.
899 *
900 * @returns a pair of iterators to the beginning and one past the end of
901 * the data.
902 * @pre There must be a current field (ie. next() must have returned `true`).
903 * @pre The current field must be of type "repeated packed sfixed64".
904 * @post The current field was consumed and there is no current field now.
905 */
get_packed_sfixed64()906 auto get_packed_sfixed64() -> decltype(packed_fixed<int64_t>()) {
907 return packed_fixed<int64_t>();
908 }
909
910 /**
911 * Consume current "repeated packed float" field.
912 *
913 * @returns a pair of iterators to the beginning and one past the end of
914 * the data.
915 * @pre There must be a current field (ie. next() must have returned `true`).
916 * @pre The current field must be of type "repeated packed float".
917 * @post The current field was consumed and there is no current field now.
918 */
get_packed_float()919 auto get_packed_float() -> decltype(packed_fixed<float>()) {
920 return packed_fixed<float>();
921 }
922
923 /**
924 * Consume current "repeated packed double" field.
925 *
926 * @returns a pair of iterators to the beginning and one past the end of
927 * the data.
928 * @pre There must be a current field (ie. next() must have returned `true`).
929 * @pre The current field must be of type "repeated packed double".
930 * @post The current field was consumed and there is no current field now.
931 */
get_packed_double()932 auto get_packed_double() -> decltype(packed_fixed<double>()) {
933 return packed_fixed<double>();
934 }
935
936 ///@}
937
938 }; // class pbf_reader
939
940 /**
941 * Swap two pbf_reader objects.
942 *
943 * @param lhs First object.
944 * @param rhs Second object.
945 */
swap(pbf_reader & lhs,pbf_reader & rhs)946 inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
947 lhs.swap(rhs);
948 }
949
950 } // end namespace protozero
951
952 #endif // PROTOZERO_PBF_READER_HPP
953