• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 17094357417

20 Aug 2025 09:28AM UTC coverage: 30.381% (-34.3%) from 64.683%
17094357417

Pull #15994

github

web-flow
Merge de3b7cf17 into b46c65b05
Pull Request #15994: REST API: normalize record contents received

9841 of 44300 branches covered (22.21%)

Branch coverage included in aggregate %.

0 of 30 new or added lines in 1 file covered. (0.0%)

27380 existing lines in 238 files now uncovered.

28140 of 80715 relevant lines covered (34.86%)

860897.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/ext/protozero/include/protozero/pbf_reader.hpp
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 "config.hpp"
20
#include "data_view.hpp"
21
#include "exception.hpp"
22
#include "iterators.hpp"
23
#include "types.hpp"
24
#include "varint.hpp"
25

26
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
27
# include <protozero/byteswap.hpp>
28
#endif
29

30
#include <cstddef>
31
#include <cstdint>
32
#include <cstring>
33
#include <string>
34
#include <utility>
35

36
namespace protozero {
37

38
/**
39
 * This class represents a protobuf message. Either a top-level message or
40
 * a nested sub-message. Top-level messages can be created from any buffer
41
 * with a pointer and length:
42
 *
43
 * @code
44
 *    std::string buffer;
45
 *    // fill buffer...
46
 *    pbf_reader message{buffer.data(), buffer.size()};
47
 * @endcode
48
 *
49
 * Sub-messages are created using get_message():
50
 *
51
 * @code
52
 *    pbf_reader message{...};
53
 *    message.next();
54
 *    pbf_reader submessage = message.get_message();
55
 * @endcode
56
 *
57
 * All methods of the pbf_reader class except get_bytes() and get_string()
58
 * provide the strong exception guarantee, ie they either succeed or do not
59
 * change the pbf_reader object they are called on. Use the get_view() method
60
 * instead of get_bytes() or get_string(), if you need this guarantee.
61
 */
62
class pbf_reader {
63

64
    // A pointer to the next unread data.
65
    const char* m_data = nullptr;
66

67
    // A pointer to one past the end of data.
68
    const char* m_end = nullptr;
69

70
    // The wire type of the current field.
71
    pbf_wire_type m_wire_type = pbf_wire_type::unknown;
72

73
    // The tag of the current field.
74
    pbf_tag_type m_tag = 0;
75

76
    template <typename T>
UNCOV
77
    T get_fixed() {
×
UNCOV
78
        T result;
×
UNCOV
79
        const char* tmp_data = m_data;
×
UNCOV
80
        skip_bytes(sizeof(T));
×
UNCOV
81
        std::memcpy(&result, tmp_data, sizeof(T));
×
82
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
83
        byteswap_inplace(&result);
84
#endif
UNCOV
85
        return result;
×
UNCOV
86
    }
×
87

88
    template <typename T>
89
    iterator_range<const_fixed_iterator<T>> packed_fixed() {
×
90
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
91
        const auto len = get_len_and_skip();
×
92
        if (len % sizeof(T) != 0) {
×
93
            throw invalid_length_exception{};
×
94
        }
×
95
        return {const_fixed_iterator<T>(m_data - len),
×
96
                const_fixed_iterator<T>(m_data)};
×
97
    }
×
98

99
    template <typename T>
UNCOV
100
    T get_varint() {
×
UNCOV
101
        const auto val = static_cast<T>(decode_varint(&m_data, m_end));
×
UNCOV
102
        return val;
×
UNCOV
103
    }
×
104

105
    template <typename T>
106
    T get_svarint() {
×
107
        protozero_assert((has_wire_type(pbf_wire_type::varint) || has_wire_type(pbf_wire_type::length_delimited)) && "not a varint");
×
108
        return static_cast<T>(decode_zigzag64(decode_varint(&m_data, m_end)));
×
109
    }
×
110

UNCOV
111
    pbf_length_type get_length() {
×
UNCOV
112
        return get_varint<pbf_length_type>();
×
UNCOV
113
    }
×
114

UNCOV
115
    void skip_bytes(pbf_length_type len) {
×
UNCOV
116
        if (m_end - m_data < static_cast<ptrdiff_t>(len)) {
×
117
            throw end_of_buffer_exception{};
×
118
        }
×
UNCOV
119
        m_data += len;
×
120

UNCOV
121
#ifndef NDEBUG
×
122
        // In debug builds reset the tag to zero so that we can detect (some)
123
        // wrong code.
UNCOV
124
        m_tag = 0;
×
UNCOV
125
#endif
×
UNCOV
126
    }
×
127

UNCOV
128
    pbf_length_type get_len_and_skip() {
×
UNCOV
129
        const auto len = get_length();
×
UNCOV
130
        skip_bytes(len);
×
UNCOV
131
        return len;
×
UNCOV
132
    }
×
133

134
    template <typename T>
135
    iterator_range<T> get_packed() {
×
136
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
137
        const auto len = get_len_and_skip();
×
138
        return {T{m_data - len, m_data},
×
139
                T{m_data, m_data}};
×
140
    }
×
141

142
public:
143

144
    /**
145
     * Construct a pbf_reader message from a data_view. The pointer from the
146
     * data_view will be stored inside the pbf_reader object, no data is
147
     * copied. So you must make sure the view stays valid as long as the
148
     * pbf_reader object is used.
149
     *
150
     * The buffer must contain a complete protobuf message.
151
     *
152
     * @post There is no current field.
153
     */
154
    explicit pbf_reader(const data_view& view) noexcept
155
        : m_data{view.data()},
UNCOV
156
          m_end{view.data() + view.size()} {
×
UNCOV
157
    }
×
158

159
    /**
160
     * Construct a pbf_reader message from a data pointer and a length. The
161
     * pointer will be stored inside the pbf_reader object, no data is copied.
162
     * So you must make sure the buffer stays valid as long as the pbf_reader
163
     * object is used.
164
     *
165
     * The buffer must contain a complete protobuf message.
166
     *
167
     * @post There is no current field.
168
     */
169
    pbf_reader(const char* data, std::size_t size) noexcept
170
        : m_data{data},
171
          m_end{data + size} {
×
172
    }
×
173

174
#ifndef PROTOZERO_STRICT_API
175
    /**
176
     * Construct a pbf_reader message from a data pointer and a length. The
177
     * pointer will be stored inside the pbf_reader object, no data is copied.
178
     * So you must make sure the buffer stays valid as long as the pbf_reader
179
     * object is used.
180
     *
181
     * The buffer must contain a complete protobuf message.
182
     *
183
     * @post There is no current field.
184
     * @deprecated Use one of the other constructors.
185
     */
186
    explicit pbf_reader(const std::pair<const char*, std::size_t>& data) noexcept
187
        : m_data{data.first},
188
          m_end{data.first + data.second} {
×
189
    }
×
190
#endif
191

192
    /**
193
     * Construct a pbf_reader message from a std::string. A pointer to the
194
     * string internals will be stored inside the pbf_reader object, no data
195
     * is copied. So you must make sure the string is unchanged as long as the
196
     * pbf_reader object is used.
197
     *
198
     * The string must contain a complete protobuf message.
199
     *
200
     * @post There is no current field.
201
     */
202
    explicit pbf_reader(const std::string& data) noexcept
203
        : m_data{data.data()},
UNCOV
204
          m_end{data.data() + data.size()} {
×
UNCOV
205
    }
×
206

207
    /**
208
     * pbf_reader can be default constructed and behaves like it has an empty
209
     * buffer.
210
     */
211
    pbf_reader() noexcept = default;
212

213
    /// pbf_reader messages can be copied trivially.
214
    pbf_reader(const pbf_reader&) noexcept = default;
215

216
    /// pbf_reader messages can be moved trivially.
217
    pbf_reader(pbf_reader&&) noexcept = default;
218

219
    /// pbf_reader messages can be copied trivially.
220
    pbf_reader& operator=(const pbf_reader& other) noexcept = default;
221

222
    /// pbf_reader messages can be moved trivially.
223
    pbf_reader& operator=(pbf_reader&& other) noexcept = default;
224

225
    ~pbf_reader() = default;
226

227
    /**
228
     * Swap the contents of this object with the other.
229
     *
230
     * @param other Other object to swap data with.
231
     */
232
    void swap(pbf_reader& other) noexcept {
×
233
        using std::swap;
×
234
        swap(m_data, other.m_data);
×
235
        swap(m_end, other.m_end);
×
236
        swap(m_wire_type, other.m_wire_type);
×
237
        swap(m_tag, other.m_tag);
×
238
    }
×
239

240
    /**
241
     * In a boolean context the pbf_reader class evaluates to `true` if there
242
     * are still fields available and to `false` if the last field has been
243
     * read.
244
     */
245
    operator bool() const noexcept { // NOLINT(google-explicit-constructor, hicpp-explicit-conversions)
×
246
        return m_data != m_end;
×
247
    }
×
248

249
    /**
250
     * Get a view of the not yet read data.
251
     */
252
    data_view data() const noexcept {
×
253
        return {m_data, static_cast<std::size_t>(m_end - m_data)};
×
254
    }
×
255

256
    /**
257
     * Return the length in bytes of the current message. If you have
258
     * already called next() and/or any of the get_*() functions, this will
259
     * return the remaining length.
260
     *
261
     * This can, for instance, be used to estimate the space needed for a
262
     * buffer. Of course you have to know reasonably well what data to expect
263
     * and how it is encoded for this number to have any meaning.
264
     */
265
    std::size_t length() const noexcept {
×
266
        return static_cast<std::size_t>(m_end - m_data);
×
267
    }
×
268

269
    /**
270
     * Set next field in the message as the current field. This is usually
271
     * called in a while loop:
272
     *
273
     * @code
274
     *    pbf_reader message(...);
275
     *    while (message.next()) {
276
     *        // handle field
277
     *    }
278
     * @endcode
279
     *
280
     * @returns `true` if there is a next field, `false` if not.
281
     * @pre There must be no current field.
282
     * @post If it returns `true` there is a current field now.
283
     */
UNCOV
284
    bool next() {
×
UNCOV
285
        if (m_data == m_end) {
×
UNCOV
286
            return false;
×
UNCOV
287
        }
×
288

UNCOV
289
        const auto value = get_varint<uint32_t>();
×
UNCOV
290
        m_tag = static_cast<pbf_tag_type>(value >> 3U);
×
291

292
        // tags 0 and 19000 to 19999 are not allowed as per
293
        // https://developers.google.com/protocol-buffers/docs/proto#assigning-tags
UNCOV
294
        if (m_tag == 0 || (m_tag >= 19000 && m_tag <= 19999)) {
×
295
            throw invalid_tag_exception{};
×
296
        }
×
297

UNCOV
298
        m_wire_type = pbf_wire_type(value & 0x07U);
×
UNCOV
299
        switch (m_wire_type) {
×
UNCOV
300
            case pbf_wire_type::varint:
×
UNCOV
301
            case pbf_wire_type::fixed64:
×
UNCOV
302
            case pbf_wire_type::length_delimited:
×
UNCOV
303
            case pbf_wire_type::fixed32:
×
UNCOV
304
                break;
×
305
            default:
×
306
                throw unknown_pbf_wire_type_exception{};
×
UNCOV
307
        }
×
308

UNCOV
309
        return true;
×
UNCOV
310
    }
×
311

312
    /**
313
     * Set next field with given tag in the message as the current field.
314
     * Fields with other tags are skipped. This is usually called in a while
315
     * loop for repeated fields:
316
     *
317
     * @code
318
     *    pbf_reader message{...};
319
     *    while (message.next(17)) {
320
     *        // handle field
321
     *    }
322
     * @endcode
323
     *
324
     * or you can call it just once to get the one field with this tag:
325
     *
326
     * @code
327
     *    pbf_reader message{...};
328
     *    if (message.next(17)) {
329
     *        // handle field
330
     *    }
331
     * @endcode
332
     *
333
     * Note that this will not check the wire type. The two-argument version
334
     * of this function will also check the wire type.
335
     *
336
     * @returns `true` if there is a next field with this tag.
337
     * @pre There must be no current field.
338
     * @post If it returns `true` there is a current field now with the given tag.
339
     */
UNCOV
340
    bool next(pbf_tag_type next_tag) {
×
UNCOV
341
        while (next()) {
×
UNCOV
342
            if (m_tag == next_tag) {
×
UNCOV
343
                return true;
×
UNCOV
344
            }
×
345
            skip();
×
346
        }
×
347
        return false;
×
UNCOV
348
    }
×
349

350
    /**
351
     * Set next field with given tag and wire type in the message as the
352
     * current field. Fields with other tags are skipped. This is usually
353
     * called in a while loop for repeated fields:
354
     *
355
     * @code
356
     *    pbf_reader message{...};
357
     *    while (message.next(17, pbf_wire_type::varint)) {
358
     *        // handle field
359
     *    }
360
     * @endcode
361
     *
362
     * or you can call it just once to get the one field with this tag:
363
     *
364
     * @code
365
     *    pbf_reader message{...};
366
     *    if (message.next(17, pbf_wire_type::varint)) {
367
     *        // handle field
368
     *    }
369
     * @endcode
370
     *
371
     * Note that this will also check the wire type. The one-argument version
372
     * of this function will not check the wire type.
373
     *
374
     * @returns `true` if there is a next field with this tag.
375
     * @pre There must be no current field.
376
     * @post If it returns `true` there is a current field now with the given tag.
377
     */
378
    bool next(pbf_tag_type next_tag, pbf_wire_type type) {
×
379
        while (next()) {
×
380
            if (m_tag == next_tag && m_wire_type == type) {
×
381
                return true;
×
382
            }
×
383
            skip();
×
384
        }
×
385
        return false;
×
386
    }
×
387

388
    /**
389
     * The tag of the current field. The tag is the field number from the
390
     * description in the .proto file.
391
     *
392
     * Call next() before calling this function to set the current field.
393
     *
394
     * @returns tag of the current field.
395
     * @pre There must be a current field (ie. next() must have returned `true`).
396
     */
UNCOV
397
    pbf_tag_type tag() const noexcept {
×
UNCOV
398
        return m_tag;
×
UNCOV
399
    }
×
400

401
    /**
402
     * Get the wire type of the current field. The wire types are:
403
     *
404
     * * 0 - varint
405
     * * 1 - 64 bit
406
     * * 2 - length-delimited
407
     * * 5 - 32 bit
408
     *
409
     * All other types are illegal.
410
     *
411
     * Call next() before calling this function to set the current field.
412
     *
413
     * @returns wire type of the current field.
414
     * @pre There must be a current field (ie. next() must have returned `true`).
415
     */
UNCOV
416
    pbf_wire_type wire_type() const noexcept {
×
UNCOV
417
        return m_wire_type;
×
UNCOV
418
    }
×
419

420
    /**
421
     * Get the tag and wire type of the current field in one integer suitable
422
     * for comparison with a switch statement.
423
     *
424
     * Use it like this:
425
     *
426
     * @code
427
     *    pbf_reader message{...};
428
     *    while (message.next()) {
429
     *        switch (message.tag_and_type()) {
430
     *            case tag_and_type(17, pbf_wire_type::length_delimited):
431
     *                ....
432
     *                break;
433
     *            case tag_and_type(21, pbf_wire_type::varint):
434
     *                ....
435
     *                break;
436
     *            default:
437
     *                message.skip();
438
     *        }
439
     *    }
440
     * @endcode
441
     */
442
    uint32_t tag_and_type() const noexcept {
×
443
        return protozero::tag_and_type(tag(), wire_type());
×
444
    }
×
445

446
    /**
447
     * Check the wire type of the current field.
448
     *
449
     * @returns `true` if the current field has the given wire type.
450
     * @pre There must be a current field (ie. next() must have returned `true`).
451
     */
UNCOV
452
    bool has_wire_type(pbf_wire_type type) const noexcept {
×
UNCOV
453
        return wire_type() == type;
×
UNCOV
454
    }
×
455

456
    /**
457
     * Consume the current field.
458
     *
459
     * @pre There must be a current field (ie. next() must have returned `true`).
460
     * @post The current field was consumed and there is no current field now.
461
     */
462
    void skip() {
×
463
        protozero_assert(tag() != 0 && "call next() before calling skip()");
×
464
        switch (wire_type()) {
×
465
            case pbf_wire_type::varint:
×
466
                skip_varint(&m_data, m_end);
×
467
                break;
×
468
            case pbf_wire_type::fixed64:
×
469
                skip_bytes(8);
×
470
                break;
×
471
            case pbf_wire_type::length_delimited:
×
472
                skip_bytes(get_length());
×
473
                break;
×
474
            case pbf_wire_type::fixed32:
×
475
                skip_bytes(4);
×
476
                break;
×
477
            default:
×
478
                break;
×
479
        }
×
480
    }
×
481

482
    ///@{
483
    /**
484
     * @name Scalar field accessor functions
485
     */
486

487
    /**
488
     * Consume and return value of current "bool" field.
489
     *
490
     * @pre There must be a current field (ie. next() must have returned `true`).
491
     * @pre The current field must be of type "bool".
492
     * @post The current field was consumed and there is no current field now.
493
     */
UNCOV
494
    bool get_bool() {
×
UNCOV
495
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
UNCOV
496
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
497
        const bool result = m_data[0] != 0;
×
UNCOV
498
        skip_varint(&m_data, m_end);
×
UNCOV
499
        return result;
×
UNCOV
500
    }
×
501

502
    /**
503
     * Consume and return value of current "enum" field.
504
     *
505
     * @pre There must be a current field (ie. next() must have returned `true`).
506
     * @pre The current field must be of type "enum".
507
     * @post The current field was consumed and there is no current field now.
508
     */
509
    int32_t get_enum() {
×
510
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
511
        return get_varint<int32_t>();
×
512
    }
×
513

514
    /**
515
     * Consume and return value of current "int32" varint field.
516
     *
517
     * @pre There must be a current field (ie. next() must have returned `true`).
518
     * @pre The current field must be of type "int32".
519
     * @post The current field was consumed and there is no current field now.
520
     */
UNCOV
521
    int32_t get_int32() {
×
UNCOV
522
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
523
        return get_varint<int32_t>();
×
UNCOV
524
    }
×
525

526
    /**
527
     * Consume and return value of current "sint32" varint field.
528
     *
529
     * @pre There must be a current field (ie. next() must have returned `true`).
530
     * @pre The current field must be of type "sint32".
531
     * @post The current field was consumed and there is no current field now.
532
     */
533
    int32_t get_sint32() {
×
534
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
535
        return get_svarint<int32_t>();
×
536
    }
×
537

538
    /**
539
     * Consume and return value of current "uint32" varint field.
540
     *
541
     * @pre There must be a current field (ie. next() must have returned `true`).
542
     * @pre The current field must be of type "uint32".
543
     * @post The current field was consumed and there is no current field now.
544
     */
UNCOV
545
    uint32_t get_uint32() {
×
UNCOV
546
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
547
        return get_varint<uint32_t>();
×
UNCOV
548
    }
×
549

550
    /**
551
     * Consume and return value of current "int64" varint field.
552
     *
553
     * @pre There must be a current field (ie. next() must have returned `true`).
554
     * @pre The current field must be of type "int64".
555
     * @post The current field was consumed and there is no current field now.
556
     */
UNCOV
557
    int64_t get_int64() {
×
UNCOV
558
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
559
        return get_varint<int64_t>();
×
UNCOV
560
    }
×
561

562
    /**
563
     * Consume and return value of current "sint64" varint field.
564
     *
565
     * @pre There must be a current field (ie. next() must have returned `true`).
566
     * @pre The current field must be of type "sint64".
567
     * @post The current field was consumed and there is no current field now.
568
     */
569
    int64_t get_sint64() {
×
570
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
571
        return get_svarint<int64_t>();
×
572
    }
×
573

574
    /**
575
     * Consume and return value of current "uint64" varint field.
576
     *
577
     * @pre There must be a current field (ie. next() must have returned `true`).
578
     * @pre The current field must be of type "uint64".
579
     * @post The current field was consumed and there is no current field now.
580
     */
UNCOV
581
    uint64_t get_uint64() {
×
UNCOV
582
        protozero_assert(has_wire_type(pbf_wire_type::varint) && "not a varint");
×
583
        return get_varint<uint64_t>();
×
UNCOV
584
    }
×
585

586
    /**
587
     * Consume and return value of current "fixed32" field.
588
     *
589
     * @pre There must be a current field (ie. next() must have returned `true`).
590
     * @pre The current field must be of type "fixed32".
591
     * @post The current field was consumed and there is no current field now.
592
     */
UNCOV
593
    uint32_t get_fixed32() {
×
UNCOV
594
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
UNCOV
595
        protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
×
596
        return get_fixed<uint32_t>();
×
UNCOV
597
    }
×
598

599
    /**
600
     * Consume and return value of current "sfixed32" field.
601
     *
602
     * @pre There must be a current field (ie. next() must have returned `true`).
603
     * @pre The current field must be of type "sfixed32".
604
     * @post The current field was consumed and there is no current field now.
605
     */
606
    int32_t get_sfixed32() {
×
607
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
608
        protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
×
609
        return get_fixed<int32_t>();
×
610
    }
×
611

612
    /**
613
     * Consume and return value of current "fixed64" field.
614
     *
615
     * @pre There must be a current field (ie. next() must have returned `true`).
616
     * @pre The current field must be of type "fixed64".
617
     * @post The current field was consumed and there is no current field now.
618
     */
UNCOV
619
    uint64_t get_fixed64() {
×
UNCOV
620
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
UNCOV
621
        protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
×
622
        return get_fixed<uint64_t>();
×
UNCOV
623
    }
×
624

625
    /**
626
     * Consume and return value of current "sfixed64" field.
627
     *
628
     * @pre There must be a current field (ie. next() must have returned `true`).
629
     * @pre The current field must be of type "sfixed64".
630
     * @post The current field was consumed and there is no current field now.
631
     */
632
    int64_t get_sfixed64() {
×
633
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
634
        protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
×
635
        return get_fixed<int64_t>();
×
636
    }
×
637

638
    /**
639
     * Consume and return value of current "float" field.
640
     *
641
     * @pre There must be a current field (ie. next() must have returned `true`).
642
     * @pre The current field must be of type "float".
643
     * @post The current field was consumed and there is no current field now.
644
     */
UNCOV
645
    float get_float() {
×
UNCOV
646
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
UNCOV
647
        protozero_assert(has_wire_type(pbf_wire_type::fixed32) && "not a 32-bit fixed");
×
648
        return get_fixed<float>();
×
UNCOV
649
    }
×
650

651
    /**
652
     * Consume and return value of current "double" field.
653
     *
654
     * @pre There must be a current field (ie. next() must have returned `true`).
655
     * @pre The current field must be of type "double".
656
     * @post The current field was consumed and there is no current field now.
657
     */
UNCOV
658
    double get_double() {
×
UNCOV
659
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
UNCOV
660
        protozero_assert(has_wire_type(pbf_wire_type::fixed64) && "not a 64-bit fixed");
×
661
        return get_fixed<double>();
×
UNCOV
662
    }
×
663

664
    /**
665
     * Consume and return value of current "bytes", "string", or "message"
666
     * field.
667
     *
668
     * @returns A data_view object.
669
     * @pre There must be a current field (ie. next() must have returned `true`).
670
     * @pre The current field must be of type "bytes", "string", or "message".
671
     * @post The current field was consumed and there is no current field now.
672
     */
UNCOV
673
    data_view get_view() {
×
UNCOV
674
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
UNCOV
675
        protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
×
676
        const auto len = get_len_and_skip();
×
UNCOV
677
        return {m_data - len, len};
×
UNCOV
678
    }
×
679

680
#ifndef PROTOZERO_STRICT_API
681
    /**
682
     * Consume and return value of current "bytes" or "string" field.
683
     *
684
     * @returns A pair with a pointer to the data and the length of the data.
685
     * @pre There must be a current field (ie. next() must have returned `true`).
686
     * @pre The current field must be of type "bytes" or "string".
687
     * @post The current field was consumed and there is no current field now.
688
     */
689
    std::pair<const char*, pbf_length_type> get_data() {
×
690
        protozero_assert(tag() != 0 && "call next() before accessing field value");
×
691
        protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
×
692
        const auto len = get_len_and_skip();
×
693
        return {m_data - len, len};
×
694
    }
×
695
#endif
696

697
    /**
698
     * Consume and return value of current "bytes" field.
699
     *
700
     * @pre There must be a current field (ie. next() must have returned `true`).
701
     * @pre The current field must be of type "bytes".
702
     * @post The current field was consumed and there is no current field now.
703
     */
UNCOV
704
    std::string get_bytes() {
×
UNCOV
705
        return std::string(get_view());
×
UNCOV
706
    }
×
707

708
    /**
709
     * Consume and return value of current "string" field.
710
     *
711
     * @pre There must be a current field (ie. next() must have returned `true`).
712
     * @pre The current field must be of type "string".
713
     * @post The current field was consumed and there is no current field now.
714
     */
UNCOV
715
    std::string get_string() {
×
UNCOV
716
        return std::string(get_view());
×
UNCOV
717
    }
×
718

719
    /**
720
     * Consume and return value of current "message" field.
721
     *
722
     * @pre There must be a current field (ie. next() must have returned `true`).
723
     * @pre The current field must be of type "message".
724
     * @post The current field was consumed and there is no current field now.
725
     */
UNCOV
726
    pbf_reader get_message() {
×
UNCOV
727
        return pbf_reader{get_view()};
×
UNCOV
728
    }
×
729

730
    ///@}
731

732
    /// Forward iterator for iterating over bool (int32 varint) values.
733
    using const_bool_iterator   = const_varint_iterator< int32_t>;
734

735
    /// Forward iterator for iterating over enum (int32 varint) values.
736
    using const_enum_iterator   = const_varint_iterator< int32_t>;
737

738
    /// Forward iterator for iterating over int32 (varint) values.
739
    using const_int32_iterator  = const_varint_iterator< int32_t>;
740

741
    /// Forward iterator for iterating over sint32 (varint) values.
742
    using const_sint32_iterator = const_svarint_iterator<int32_t>;
743

744
    /// Forward iterator for iterating over uint32 (varint) values.
745
    using const_uint32_iterator = const_varint_iterator<uint32_t>;
746

747
    /// Forward iterator for iterating over int64 (varint) values.
748
    using const_int64_iterator  = const_varint_iterator< int64_t>;
749

750
    /// Forward iterator for iterating over sint64 (varint) values.
751
    using const_sint64_iterator = const_svarint_iterator<int64_t>;
752

753
    /// Forward iterator for iterating over uint64 (varint) values.
754
    using const_uint64_iterator = const_varint_iterator<uint64_t>;
755

756
    /// Forward iterator for iterating over fixed32 values.
757
    using const_fixed32_iterator = const_fixed_iterator<uint32_t>;
758

759
    /// Forward iterator for iterating over sfixed32 values.
760
    using const_sfixed32_iterator = const_fixed_iterator<int32_t>;
761

762
    /// Forward iterator for iterating over fixed64 values.
763
    using const_fixed64_iterator = const_fixed_iterator<uint64_t>;
764

765
    /// Forward iterator for iterating over sfixed64 values.
766
    using const_sfixed64_iterator = const_fixed_iterator<int64_t>;
767

768
    /// Forward iterator for iterating over float values.
769
    using const_float_iterator = const_fixed_iterator<float>;
770

771
    /// Forward iterator for iterating over double values.
772
    using const_double_iterator = const_fixed_iterator<double>;
773

774
    ///@{
775
    /**
776
     * @name Repeated packed field accessor functions
777
     */
778

779
    /**
780
     * Consume current "repeated packed bool" field.
781
     *
782
     * @returns a pair of iterators to the beginning and one past the end of
783
     *          the data.
784
     * @pre There must be a current field (ie. next() must have returned `true`).
785
     * @pre The current field must be of type "repeated packed bool".
786
     * @post The current field was consumed and there is no current field now.
787
     */
788
    iterator_range<pbf_reader::const_bool_iterator> get_packed_bool() {
×
789
        return get_packed<pbf_reader::const_bool_iterator>();
×
790
    }
×
791

792
    /**
793
     * Consume current "repeated packed enum" field.
794
     *
795
     * @returns a pair of iterators to the beginning and one past the end of
796
     *          the data.
797
     * @pre There must be a current field (ie. next() must have returned `true`).
798
     * @pre The current field must be of type "repeated packed enum".
799
     * @post The current field was consumed and there is no current field now.
800
     */
801
    iterator_range<pbf_reader::const_enum_iterator> get_packed_enum() {
×
802
        return get_packed<pbf_reader::const_enum_iterator>();
×
803
    }
×
804

805
    /**
806
     * Consume current "repeated packed int32" field.
807
     *
808
     * @returns a pair of iterators to the beginning and one past the end of
809
     *          the data.
810
     * @pre There must be a current field (ie. next() must have returned `true`).
811
     * @pre The current field must be of type "repeated packed int32".
812
     * @post The current field was consumed and there is no current field now.
813
     */
814
    iterator_range<pbf_reader::const_int32_iterator> get_packed_int32() {
×
815
        return get_packed<pbf_reader::const_int32_iterator>();
×
816
    }
×
817

818
    /**
819
     * Consume current "repeated packed sint32" field.
820
     *
821
     * @returns a pair of iterators to the beginning and one past the end of
822
     *          the data.
823
     * @pre There must be a current field (ie. next() must have returned `true`).
824
     * @pre The current field must be of type "repeated packed sint32".
825
     * @post The current field was consumed and there is no current field now.
826
     */
827
    iterator_range<pbf_reader::const_sint32_iterator> get_packed_sint32() {
×
828
        return get_packed<pbf_reader::const_sint32_iterator>();
×
829
    }
×
830

831
    /**
832
     * Consume current "repeated packed uint32" field.
833
     *
834
     * @returns a pair of iterators to the beginning and one past the end of
835
     *          the data.
836
     * @pre There must be a current field (ie. next() must have returned `true`).
837
     * @pre The current field must be of type "repeated packed uint32".
838
     * @post The current field was consumed and there is no current field now.
839
     */
840
    iterator_range<pbf_reader::const_uint32_iterator> get_packed_uint32() {
×
841
        return get_packed<pbf_reader::const_uint32_iterator>();
×
842
    }
×
843

844
    /**
845
     * Consume current "repeated packed int64" field.
846
     *
847
     * @returns a pair of iterators to the beginning and one past the end of
848
     *          the data.
849
     * @pre There must be a current field (ie. next() must have returned `true`).
850
     * @pre The current field must be of type "repeated packed int64".
851
     * @post The current field was consumed and there is no current field now.
852
     */
853
    iterator_range<pbf_reader::const_int64_iterator> get_packed_int64() {
×
854
        return get_packed<pbf_reader::const_int64_iterator>();
×
855
    }
×
856

857
    /**
858
     * Consume current "repeated packed sint64" field.
859
     *
860
     * @returns a pair of iterators to the beginning and one past the end of
861
     *          the data.
862
     * @pre There must be a current field (ie. next() must have returned `true`).
863
     * @pre The current field must be of type "repeated packed sint64".
864
     * @post The current field was consumed and there is no current field now.
865
     */
866
    iterator_range<pbf_reader::const_sint64_iterator> get_packed_sint64() {
×
867
        return get_packed<pbf_reader::const_sint64_iterator>();
×
868
    }
×
869

870
    /**
871
     * Consume current "repeated packed uint64" field.
872
     *
873
     * @returns a pair of iterators to the beginning and one past the end of
874
     *          the data.
875
     * @pre There must be a current field (ie. next() must have returned `true`).
876
     * @pre The current field must be of type "repeated packed uint64".
877
     * @post The current field was consumed and there is no current field now.
878
     */
879
    iterator_range<pbf_reader::const_uint64_iterator> get_packed_uint64() {
×
880
        return get_packed<pbf_reader::const_uint64_iterator>();
×
881
    }
×
882

883
    /**
884
     * Consume current "repeated packed fixed32" field.
885
     *
886
     * @returns a pair of iterators to the beginning and one past the end of
887
     *          the data.
888
     * @pre There must be a current field (ie. next() must have returned `true`).
889
     * @pre The current field must be of type "repeated packed fixed32".
890
     * @post The current field was consumed and there is no current field now.
891
     */
892
    iterator_range<pbf_reader::const_fixed32_iterator> get_packed_fixed32() {
×
893
        return packed_fixed<uint32_t>();
×
894
    }
×
895

896
    /**
897
     * Consume current "repeated packed sfixed32" field.
898
     *
899
     * @returns a pair of iterators to the beginning and one past the end of
900
     *          the data.
901
     * @pre There must be a current field (ie. next() must have returned `true`).
902
     * @pre The current field must be of type "repeated packed sfixed32".
903
     * @post The current field was consumed and there is no current field now.
904
     */
905
    iterator_range<pbf_reader::const_sfixed32_iterator> get_packed_sfixed32() {
×
906
        return packed_fixed<int32_t>();
×
907
    }
×
908

909
    /**
910
     * Consume current "repeated packed fixed64" field.
911
     *
912
     * @returns a pair of iterators to the beginning and one past the end of
913
     *          the data.
914
     * @pre There must be a current field (ie. next() must have returned `true`).
915
     * @pre The current field must be of type "repeated packed fixed64".
916
     * @post The current field was consumed and there is no current field now.
917
     */
918
    iterator_range<pbf_reader::const_fixed64_iterator> get_packed_fixed64() {
×
919
        return packed_fixed<uint64_t>();
×
920
    }
×
921

922
    /**
923
     * Consume current "repeated packed sfixed64" field.
924
     *
925
     * @returns a pair of iterators to the beginning and one past the end of
926
     *          the data.
927
     * @pre There must be a current field (ie. next() must have returned `true`).
928
     * @pre The current field must be of type "repeated packed sfixed64".
929
     * @post The current field was consumed and there is no current field now.
930
     */
931
    iterator_range<pbf_reader::const_sfixed64_iterator> get_packed_sfixed64() {
×
932
        return packed_fixed<int64_t>();
×
933
    }
×
934

935
    /**
936
     * Consume current "repeated packed float" field.
937
     *
938
     * @returns a pair of iterators to the beginning and one past the end of
939
     *          the data.
940
     * @pre There must be a current field (ie. next() must have returned `true`).
941
     * @pre The current field must be of type "repeated packed float".
942
     * @post The current field was consumed and there is no current field now.
943
     */
944
    iterator_range<pbf_reader::const_float_iterator> get_packed_float() {
×
945
        return packed_fixed<float>();
×
946
    }
×
947

948
    /**
949
     * Consume current "repeated packed double" field.
950
     *
951
     * @returns a pair of iterators to the beginning and one past the end of
952
     *          the data.
953
     * @pre There must be a current field (ie. next() must have returned `true`).
954
     * @pre The current field must be of type "repeated packed double".
955
     * @post The current field was consumed and there is no current field now.
956
     */
957
    iterator_range<pbf_reader::const_double_iterator> get_packed_double() {
×
958
        return packed_fixed<double>();
×
959
    }
×
960

961
    ///@}
962

963
}; // class pbf_reader
964

965
/**
966
 * Swap two pbf_reader objects.
967
 *
968
 * @param lhs First object.
969
 * @param rhs Second object.
970
 */
971
inline void swap(pbf_reader& lhs, pbf_reader& rhs) noexcept {
×
972
    lhs.swap(rhs);
×
973
}
×
974

975
} // end namespace protozero
976

977
#endif // PROTOZERO_PBF_READER_HPP
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc