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

PowerDNS / pdns / 15920880335

26 Jun 2025 03:30PM UTC coverage: 61.923% (-3.7%) from 65.652%
15920880335

push

github

web-flow
Merge pull request #15669 from miodvallat/serial_keyer

Increase zone serial number after zone key operations

38311 of 91850 branches covered (41.71%)

Branch coverage included in aggregate %.

27 of 29 new or added lines in 1 file covered. (93.1%)

6308 existing lines in 78 files now uncovered.

120482 of 164587 relevant lines covered (73.2%)

5965233.22 hits per line

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

76.5
/ext/protozero/include/protozero/basic_pbf_writer.hpp
1
#ifndef PROTOZERO_BASIC_PBF_WRITER_HPP
2
#define PROTOZERO_BASIC_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 basic_pbf_writer.hpp
15
 *
16
 * @brief Contains the basic_pbf_writer template class.
17
 */
18

19
#include "buffer_tmpl.hpp"
20
#include "config.hpp"
21
#include "data_view.hpp"
22
#include "types.hpp"
23
#include "varint.hpp"
24

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

29
#include <cstddef>
30
#include <cstdint>
31
#include <cstring>
32
#include <initializer_list>
33
#include <iterator>
34
#include <limits>
35
#include <string>
36
#include <utility>
37

38
namespace protozero {
39

40
namespace detail {
41

42
    template <typename B, typename T> class packed_field_varint;
43
    template <typename B, typename T> class packed_field_svarint;
44
    template <typename B, typename T> class packed_field_fixed;
45

46
} // end namespace detail
47

48
/**
49
 * The basic_pbf_writer is used to write PBF formatted messages into a buffer.
50
 *
51
 * This uses TBuffer as the type for the underlaying buffer. In typical uses
52
 * this is std::string, but you can use a different type that must support
53
 * the right interface. Please see the documentation for details.
54
 *
55
 * Almost all methods in this class can throw an std::bad_alloc exception if
56
 * the underlying buffer class wants to resize.
57
 */
58
template <typename TBuffer>
59
class basic_pbf_writer {
60

61
    // A pointer to a buffer holding the data already written to the PBF
62
    // message. For default constructed writers or writers that have been
63
    // rolled back, this is a nullptr.
64
    TBuffer* m_data = nullptr;
65

66
    // A pointer to a parent writer object if this is a submessage. If this
67
    // is a top-level writer, it is a nullptr.
68
    basic_pbf_writer* m_parent_writer = nullptr;
69

70
    // This is usually 0. If there is an open submessage, this is set in the
71
    // parent to the rollback position, ie. the last position before the
72
    // submessage was started. This is the position where the header of the
73
    // submessage starts.
74
    std::size_t m_rollback_pos = 0;
75

76
    // This is usually 0. If there is an open submessage, this is set in the
77
    // parent to the position where the data of the submessage is written to.
78
    std::size_t m_pos = 0;
79

80
    void add_varint(uint64_t value) {
16,250✔
81
        protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
16,250!
82
        protozero_assert(m_data);
16,250!
83
        add_varint_to_buffer(m_data, value);
3,190✔
84
    }
16,250✔
85

86
    void add_field(pbf_tag_type tag, pbf_wire_type type) {
9,636✔
87
        protozero_assert(((tag > 0 && tag < 19000) || (tag > 19999 && tag <= ((1U << 29U) - 1))) && "tag out of range");
9,636!
88
        const uint32_t b = (tag << 3U) | static_cast<uint32_t>(type);
1,782✔
89
        add_varint(b);
9,636✔
90
    }
9,636✔
91

92
    void add_tagged_varint(pbf_tag_type tag, uint64_t value) {
3,445✔
93
        add_field(tag, pbf_wire_type::varint);
3,445✔
94
        add_varint(value);
3,445✔
95
    }
3,445✔
96

97
    template <typename T>
98
    void add_fixed(T value) {
334✔
99
        protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
334!
100
        protozero_assert(m_data);
334!
101
#if PROTOZERO_BYTE_ORDER != PROTOZERO_LITTLE_ENDIAN
102
        byteswap_inplace(&value);
103
#endif
104
        buffer_customization<TBuffer>::append(m_data, reinterpret_cast<const char*>(&value), sizeof(T));
38✔
105
    }
334✔
106

107
    template <typename T, typename It>
108
    void add_packed_fixed(pbf_tag_type tag, It first, It last, std::input_iterator_tag /*unused*/) { // NOLINT(performance-unnecessary-value-param)
109
        if (first == last) {
110
            return;
111
        }
112

113
        basic_pbf_writer sw{*this, tag};
114

115
        while (first != last) {
116
            sw.add_fixed<T>(*first++);
117
        }
118
    }
119

120
    template <typename T, typename It>
121
    void add_packed_fixed(pbf_tag_type tag, It first, It last, std::forward_iterator_tag /*unused*/) { // NOLINT(performance-unnecessary-value-param)
122
        if (first == last) {
123
            return;
124
        }
125

126
        const auto length = std::distance(first, last);
127
        add_length_varint(tag, sizeof(T) * pbf_length_type(length));
128
        reserve(sizeof(T) * std::size_t(length));
129

130
        while (first != last) {
131
            add_fixed<T>(*first++);
132
        }
133
    }
134

135
    template <typename It>
136
    void add_packed_varint(pbf_tag_type tag, It first, It last) { // NOLINT(performance-unnecessary-value-param)
137
        if (first == last) {
138
            return;
139
        }
140

141
        basic_pbf_writer sw{*this, tag};
142

143
        while (first != last) {
144
            sw.add_varint(uint64_t(*first++));
145
        }
146
    }
147

148
    template <typename It>
149
    void add_packed_svarint(pbf_tag_type tag, It first, It last) { // NOLINT(performance-unnecessary-value-param)
150
        if (first == last) {
151
            return;
152
        }
153

154
        basic_pbf_writer sw{*this, tag};
155

156
        while (first != last) {
157
            sw.add_varint(encode_zigzag64(*first++));
158
        }
159
    }
160

161
    // The number of bytes to reserve for the varint holding the length of
162
    // a length-delimited field. The length has to fit into pbf_length_type,
163
    // and a varint needs 8 bit for every 7 bit.
164
    enum : int {
165
        reserve_bytes = (sizeof(pbf_length_type) * 8 / 7) + 1
166
    };
167

168
    // If m_rollpack_pos is set to this special value, it means that when
169
    // the submessage is closed, nothing needs to be done, because the length
170
    // of the submessage has already been written correctly.
171
    enum : std::size_t {
172
        size_is_known = std::numeric_limits<std::size_t>::max()
173
    };
174

175
    void open_submessage(pbf_tag_type tag, std::size_t size) {
1,984✔
176
        protozero_assert(m_pos == 0);
1,984!
177
        protozero_assert(m_data);
1,984!
178
        if (size == 0) {
1,984!
179
            m_rollback_pos = buffer_customization<TBuffer>::size(m_data);
1,984✔
180
            add_field(tag, pbf_wire_type::length_delimited);
1,984✔
181
            buffer_customization<TBuffer>::append_zeros(m_data, std::size_t(reserve_bytes));
1,984✔
182
        } else {
1,984✔
183
            m_rollback_pos = size_is_known;
×
184
            add_length_varint(tag, static_cast<pbf_length_type>(size));
×
185
            reserve(size);
×
186
        }
×
187
        m_pos = buffer_customization<TBuffer>::size(m_data);
1,984✔
188
    }
1,984✔
189

190
    void rollback_submessage() {
4✔
191
        protozero_assert(m_pos != 0);
4!
192
        protozero_assert(m_rollback_pos != size_is_known);
4!
193
        protozero_assert(m_data);
4!
194
        buffer_customization<TBuffer>::resize(m_data, m_rollback_pos);
×
195
        m_pos = 0;
4✔
196
    }
4✔
197

198
    void commit_submessage() {
1,980✔
199
        protozero_assert(m_pos != 0);
1,980!
200
        protozero_assert(m_rollback_pos != size_is_known);
1,980!
201
        protozero_assert(m_data);
1,980!
202
        const auto length = pbf_length_type(buffer_customization<TBuffer>::size(m_data) - m_pos);
316✔
203

204
        protozero_assert(buffer_customization<TBuffer>::size(m_data) >= m_pos - reserve_bytes);
1,980!
205
        const auto n = add_varint_to_buffer(buffer_customization<TBuffer>::at_pos(m_data, m_pos - reserve_bytes), length);
316✔
206

207
        buffer_customization<TBuffer>::erase_range(m_data, m_pos - reserve_bytes + n, m_pos);
1,980✔
208
        m_pos = 0;
1,980✔
209
    }
1,980✔
210

211
    void close_submessage() {
1,984✔
212
        protozero_assert(m_data);
1,984!
213
        if (m_pos == 0 || m_rollback_pos == size_is_known) {
1,984!
214
            return;
×
215
        }
×
216
        if (buffer_customization<TBuffer>::size(m_data) - m_pos == 0) {
1,984✔
217
            rollback_submessage();
4✔
218
        } else {
1,980✔
219
            commit_submessage();
1,980✔
220
        }
1,980✔
221
    }
1,984✔
222

223
    void add_length_varint(pbf_tag_type tag, pbf_length_type length) {
3,169✔
224
        add_field(tag, pbf_wire_type::length_delimited);
3,169✔
225
        add_varint(length);
3,169✔
226
    }
3,169✔
227

228
public:
229

230
    /**
231
     * Create a writer using the specified buffer as a data store. The
232
     * basic_pbf_writer stores a pointer to that buffer and adds all data to
233
     * it. The buffer doesn't have to be empty. The basic_pbf_writer will just
234
     * append data.
235
     */
236
    explicit basic_pbf_writer(TBuffer& buffer) noexcept :
237
        m_data{&buffer} {
3,548✔
238
    }
3,548✔
239

240
    /**
241
     * Create a writer without a data store. In this form the writer can not
242
     * be used!
243
     */
244
    basic_pbf_writer() noexcept = default;
2,317✔
245

246
    /**
247
     * Construct a basic_pbf_writer for a submessage from the basic_pbf_writer
248
     * of the parent message.
249
     *
250
     * @param parent_writer The basic_pbf_writer
251
     * @param tag Tag (field number) of the field that will be written
252
     * @param size Optional size of the submessage in bytes (use 0 for unknown).
253
     *        Setting this allows some optimizations but is only possible in
254
     *        a few very specific cases.
255
     */
256
    basic_pbf_writer(basic_pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size = 0) :
257
        m_data{parent_writer.m_data},
316✔
258
        m_parent_writer{&parent_writer} {
1,984✔
259
        m_parent_writer->open_submessage(tag, size);
1,984✔
260
    }
1,984✔
261

262
    /// A basic_pbf_writer object can not be copied
263
    basic_pbf_writer(const basic_pbf_writer&) = delete;
264

265
    /// A basic_pbf_writer object can not be copied
266
    basic_pbf_writer& operator=(const basic_pbf_writer&) = delete;
267

268
    /**
269
     * A basic_pbf_writer object can be moved. After this the other
270
     * basic_pbf_writer will be invalid.
271
     */
272
    basic_pbf_writer(basic_pbf_writer&& other) noexcept :
273
        m_data{other.m_data},
274
        m_parent_writer{other.m_parent_writer},
275
        m_rollback_pos{other.m_rollback_pos},
276
        m_pos{other.m_pos} {
277
        other.m_data = nullptr;
278
        other.m_parent_writer = nullptr;
279
        other.m_rollback_pos = 0;
280
        other.m_pos = 0;
281
    }
282

283
    /**
284
     * A basic_pbf_writer object can be moved. After this the other
285
     * basic_pbf_writer will be invalid.
286
     */
287
    basic_pbf_writer& operator=(basic_pbf_writer&& other) noexcept {
1,757✔
288
        m_data = other.m_data;
1,757✔
289
        m_parent_writer = other.m_parent_writer;
1,757✔
290
        m_rollback_pos = other.m_rollback_pos;
1,757✔
291
        m_pos = other.m_pos;
1,757✔
292
        other.m_data = nullptr;
1,757✔
293
        other.m_parent_writer = nullptr;
1,757✔
294
        other.m_rollback_pos = 0;
1,757✔
295
        other.m_pos = 0;
1,757✔
296
        return *this;
1,757✔
297
    }
1,757✔
298

299
    ~basic_pbf_writer() noexcept {
7,846✔
300
        try {
7,846✔
301
            if (m_parent_writer != nullptr) {
7,846✔
302
                m_parent_writer->close_submessage();
1,906✔
303
            }
1,906✔
304
        } catch (...) {
7,846✔
305
            // This try/catch is used to make the destructor formally noexcept.
306
            // close_submessage() is not noexcept, but will not throw the way
307
            // it is called here, so we are good. But to be paranoid, call...
308
            std::terminate();
×
309
        }
×
310
    }
7,846✔
311

312
    /**
313
     * Check if this writer is valid. A writer is invalid if it was default
314
     * constructed, moved from, or if commit() has been called on it.
315
     * Otherwise it is valid.
316
     */
317
    bool valid() const noexcept {
18✔
318
        return m_data != nullptr;
18✔
319
    }
18✔
320

321
    /**
322
     * Swap the contents of this object with the other.
323
     *
324
     * @param other Other object to swap data with.
325
     */
326
    void swap(basic_pbf_writer& other) noexcept {
327
        using std::swap;
328
        swap(m_data, other.m_data);
329
        swap(m_parent_writer, other.m_parent_writer);
330
        swap(m_rollback_pos, other.m_rollback_pos);
331
        swap(m_pos, other.m_pos);
332
    }
333

334
    /**
335
     * Reserve size bytes in the underlying message store in addition to
336
     * whatever the message store already holds. So unlike
337
     * the `std::string::reserve()` method this is not an absolute size,
338
     * but additional memory that should be reserved.
339
     *
340
     * @param size Number of bytes to reserve in underlying message store.
341
     */
UNCOV
342
    void reserve(std::size_t size) {
×
UNCOV
343
        protozero_assert(m_data);
×
344
        buffer_customization<TBuffer>::reserve_additional(m_data, size);
×
UNCOV
345
    }
×
346

347
    /**
348
     * Commit this submessage. This does the same as when the basic_pbf_writer
349
     * goes out of scope and is destructed.
350
     *
351
     * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the
352
     *      basic_pbf_writer constructor taking a parent message.
353
     * @post The basic_pbf_writer is invalid and can't be used any more.
354
     */
355
    void commit() {
78✔
356
        protozero_assert(m_parent_writer && "you can't call commit() on a basic_pbf_writer without a parent");
78!
357
        protozero_assert(m_pos == 0 && "you can't call commit() on a basic_pbf_writer that has an open nested submessage");
78!
358
        m_parent_writer->close_submessage();
78✔
359
        m_parent_writer = nullptr;
78✔
360
        m_data = nullptr;
78✔
361
    }
78✔
362

363
    /**
364
     * Cancel writing of this submessage. The complete submessage will be
365
     * removed as if it was never created and no fields were added.
366
     *
367
     * @pre Must be a basic_pbf_writer of a submessage, ie one opened with the
368
     *      basic_pbf_writer constructor taking a parent message.
369
     * @post The basic_pbf_writer is invalid and can't be used any more.
370
     */
371
    void rollback() {
372
        protozero_assert(m_parent_writer && "you can't call rollback() on a basic_pbf_writer without a parent");
373
        protozero_assert(m_pos == 0 && "you can't call rollback() on a basic_pbf_writer that has an open nested submessage");
374
        m_parent_writer->rollback_submessage();
375
        m_parent_writer = nullptr;
376
        m_data = nullptr;
377
    }
378

379
    ///@{
380
    /**
381
     * @name Scalar field writer functions
382
     */
383

384
    /**
385
     * Add "bool" field to data.
386
     *
387
     * @param tag Tag (field number) of the field
388
     * @param value Value to be written
389
     */
390
    void add_bool(pbf_tag_type tag, bool value) {
704✔
391
        add_field(tag, pbf_wire_type::varint);
704✔
392
        protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
704!
393
        protozero_assert(m_data);
704!
394
        m_data->push_back(static_cast<char>(value));
20✔
395
    }
704✔
396

397
    /**
398
     * Add "enum" field to data.
399
     *
400
     * @param tag Tag (field number) of the field
401
     * @param value Value to be written
402
     */
403
    void add_enum(pbf_tag_type tag, int32_t value) {
241✔
404
        add_tagged_varint(tag, static_cast<uint64_t>(value));
241✔
405
    }
241✔
406

407
    /**
408
     * Add "int32" field to data.
409
     *
410
     * @param tag Tag (field number) of the field
411
     * @param value Value to be written
412
     */
413
    void add_int32(pbf_tag_type tag, int32_t value) {
414
        add_tagged_varint(tag, static_cast<uint64_t>(value));
415
    }
416

417
    /**
418
     * Add "sint32" field to data.
419
     *
420
     * @param tag Tag (field number) of the field
421
     * @param value Value to be written
422
     */
423
    void add_sint32(pbf_tag_type tag, int32_t value) {
424
        add_tagged_varint(tag, encode_zigzag32(value));
425
    }
426

427
    /**
428
     * Add "uint32" field to data.
429
     *
430
     * @param tag Tag (field number) of the field
431
     * @param value Value to be written
432
     */
433
    void add_uint32(pbf_tag_type tag, uint32_t value) {
2,907✔
434
        add_tagged_varint(tag, value);
2,907✔
435
    }
2,907✔
436

437
    /**
438
     * Add "int64" field to data.
439
     *
440
     * @param tag Tag (field number) of the field
441
     * @param value Value to be written
442
     */
443
    void add_int64(pbf_tag_type tag, int64_t value) {
212✔
444
        add_tagged_varint(tag, static_cast<uint64_t>(value));
212✔
445
    }
212✔
446

447
    /**
448
     * Add "sint64" field to data.
449
     *
450
     * @param tag Tag (field number) of the field
451
     * @param value Value to be written
452
     */
453
    void add_sint64(pbf_tag_type tag, int64_t value) {
454
        add_tagged_varint(tag, encode_zigzag64(value));
455
    }
456

457
    /**
458
     * Add "uint64" field to data.
459
     *
460
     * @param tag Tag (field number) of the field
461
     * @param value Value to be written
462
     */
463
    void add_uint64(pbf_tag_type tag, uint64_t value) {
85✔
464
        add_tagged_varint(tag, value);
85✔
465
    }
85✔
466

467
    /**
468
     * Add "fixed32" field to data.
469
     *
470
     * @param tag Tag (field number) of the field
471
     * @param value Value to be written
472
     */
473
    void add_fixed32(pbf_tag_type tag, uint32_t value) {
134✔
474
        add_field(tag, pbf_wire_type::fixed32);
134✔
475
        add_fixed<uint32_t>(value);
134✔
476
    }
134✔
477

478
    /**
479
     * Add "sfixed32" field to data.
480
     *
481
     * @param tag Tag (field number) of the field
482
     * @param value Value to be written
483
     */
484
    void add_sfixed32(pbf_tag_type tag, int32_t value) {
485
        add_field(tag, pbf_wire_type::fixed32);
486
        add_fixed<int32_t>(value);
487
    }
488

489
    /**
490
     * Add "fixed64" field to data.
491
     *
492
     * @param tag Tag (field number) of the field
493
     * @param value Value to be written
494
     */
495
    void add_fixed64(pbf_tag_type tag, uint64_t value) {
136✔
496
        add_field(tag, pbf_wire_type::fixed64);
136✔
497
        add_fixed<uint64_t>(value);
136✔
498
    }
136✔
499

500
    /**
501
     * Add "sfixed64" field to data.
502
     *
503
     * @param tag Tag (field number) of the field
504
     * @param value Value to be written
505
     */
506
    void add_sfixed64(pbf_tag_type tag, int64_t value) {
507
        add_field(tag, pbf_wire_type::fixed64);
508
        add_fixed<int64_t>(value);
509
    }
510

511
    /**
512
     * Add "float" field to data.
513
     *
514
     * @param tag Tag (field number) of the field
515
     * @param value Value to be written
516
     */
517
    void add_float(pbf_tag_type tag, float value) {
518
        add_field(tag, pbf_wire_type::fixed32);
519
        add_fixed<float>(value);
520
    }
521

522
    /**
523
     * Add "double" field to data.
524
     *
525
     * @param tag Tag (field number) of the field
526
     * @param value Value to be written
527
     */
528
    void add_double(pbf_tag_type tag, double value) {
64✔
529
        add_field(tag, pbf_wire_type::fixed64);
64✔
530
        add_fixed<double>(value);
64✔
531
    }
64✔
532

533
    /**
534
     * Add "bytes" field to data.
535
     *
536
     * @param tag Tag (field number) of the field
537
     * @param value Pointer to value to be written
538
     * @param size Number of bytes to be written
539
     */
540
    void add_bytes(pbf_tag_type tag, const char* value, std::size_t size) {
3,169✔
541
        protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
3,169!
542
        protozero_assert(m_data);
3,169!
543
        protozero_assert(size <= std::numeric_limits<pbf_length_type>::max());
3,169!
544
        add_length_varint(tag, static_cast<pbf_length_type>(size));
495✔
545
        buffer_customization<TBuffer>::append(m_data, value, size);
3,169✔
546
    }
3,169✔
547

548
    /**
549
     * Add "bytes" field to data.
550
     *
551
     * @param tag Tag (field number) of the field
552
     * @param value Value to be written
553
     */
554
    void add_bytes(pbf_tag_type tag, const data_view& value) {
555
        add_bytes(tag, value.data(), value.size());
556
    }
557

558
    /**
559
     * Add "bytes" field to data.
560
     *
561
     * @param tag Tag (field number) of the field
562
     * @param value Value to be written
563
     */
564
    void add_bytes(pbf_tag_type tag, const std::string& value) {
1,238✔
565
        add_bytes(tag, value.data(), value.size());
1,238✔
566
    }
1,238✔
567

568
    /**
569
     * Add "bytes" field to data. Bytes from the value are written until
570
     * a null byte is encountered. The null byte is not added.
571
     *
572
     * @param tag Tag (field number) of the field
573
     * @param value Pointer to zero-delimited value to be written
574
     */
575
    void add_bytes(pbf_tag_type tag, const char* value) {
30✔
576
        add_bytes(tag, value, std::strlen(value));
30✔
577
    }
30✔
578

579
    /**
580
     * Add "bytes" field to data using vectored input. All the data in the
581
     * 2nd and further arguments is "concatenated" with only a single copy
582
     * into the final buffer.
583
     *
584
     * This will work with objects of any type supporting the data() and
585
     * size() methods like std::string or protozero::data_view.
586
     *
587
     * Example:
588
     * @code
589
     * std::string data1 = "abc";
590
     * std::string data2 = "xyz";
591
     * writer.add_bytes_vectored(1, data1, data2);
592
     * @endcode
593
     *
594
     * @tparam Ts List of types supporting data() and size() methods.
595
     * @param tag Tag (field number) of the field
596
     * @param values List of objects of types Ts with data to be appended.
597
     */
598
    template <typename... Ts>
599
    void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) {
600
        protozero_assert(m_pos == 0 && "you can't add fields to a parent basic_pbf_writer if there is an existing basic_pbf_writer for a submessage");
601
        protozero_assert(m_data);
602
        size_t sum_size = 0;
603
        (void)std::initializer_list<size_t>{sum_size += values.size()...};
604
        protozero_assert(sum_size <= std::numeric_limits<pbf_length_type>::max());
605
        add_length_varint(tag, static_cast<pbf_length_type>(sum_size));
606
        buffer_customization<TBuffer>::reserve_additional(m_data, sum_size);
607
        (void)std::initializer_list<int>{(buffer_customization<TBuffer>::append(m_data, values.data(), values.size()), 0)...};
608
    }
609

610
    /**
611
     * Add "string" field to data.
612
     *
613
     * @param tag Tag (field number) of the field
614
     * @param value Pointer to value to be written
615
     * @param size Number of bytes to be written
616
     */
617
    void add_string(pbf_tag_type tag, const char* value, std::size_t size) {
618
        add_bytes(tag, value, size);
619
    }
620

621
    /**
622
     * Add "string" field to data.
623
     *
624
     * @param tag Tag (field number) of the field
625
     * @param value Value to be written
626
     */
627
    void add_string(pbf_tag_type tag, const data_view& value) {
9✔
628
        add_bytes(tag, value.data(), value.size());
9✔
629
    }
9✔
630

631
    /**
632
     * Add "string" field to data.
633
     *
634
     * @param tag Tag (field number) of the field
635
     * @param value Value to be written
636
     */
637
    void add_string(pbf_tag_type tag, const std::string& value) {
1,208✔
638
        add_bytes(tag, value.data(), value.size());
1,208✔
639
    }
1,208✔
640

641
    /**
642
     * Add "string" field to data. Bytes from the value are written until
643
     * a null byte is encountered. The null byte is not added.
644
     *
645
     * @param tag Tag (field number) of the field
646
     * @param value Pointer to value to be written
647
     */
648
    void add_string(pbf_tag_type tag, const char* value) {
2✔
649
        add_bytes(tag, value, std::strlen(value));
2✔
650
    }
2✔
651

652
    /**
653
     * Add "message" field to data.
654
     *
655
     * @param tag Tag (field number) of the field
656
     * @param value Pointer to message to be written
657
     * @param size Length of the message
658
     */
659
    void add_message(pbf_tag_type tag, const char* value, std::size_t size) {
660
        add_bytes(tag, value, size);
661
    }
662

663
    /**
664
     * Add "message" field to data.
665
     *
666
     * @param tag Tag (field number) of the field
667
     * @param value Value to be written. The value must be a complete message.
668
     */
669
    void add_message(pbf_tag_type tag, const data_view& value) {
670
        add_bytes(tag, value.data(), value.size());
671
    }
672

673
    /**
674
     * Add "message" field to data.
675
     *
676
     * @param tag Tag (field number) of the field
677
     * @param value Value to be written. The value must be a complete message.
678
     */
679
    void add_message(pbf_tag_type tag, const std::string& value) {
680
        add_bytes(tag, value.data(), value.size());
681
    }
682

683
    ///@}
684

685
    ///@{
686
    /**
687
     * @name Repeated packed field writer functions
688
     */
689

690
    /**
691
     * Add "repeated packed bool" field to data.
692
     *
693
     * @tparam InputIterator A type satisfying the InputIterator concept.
694
     *         Dereferencing the iterator must yield a type assignable to bool.
695
     * @param tag Tag (field number) of the field
696
     * @param first Iterator pointing to the beginning of the data
697
     * @param last Iterator pointing one past the end of data
698
     */
699
    template <typename InputIterator>
700
    void add_packed_bool(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
701
        add_packed_varint(tag, first, last);
702
    }
703

704
    /**
705
     * Add "repeated packed enum" field to data.
706
     *
707
     * @tparam InputIterator A type satisfying the InputIterator concept.
708
     *         Dereferencing the iterator must yield a type assignable to int32_t.
709
     * @param tag Tag (field number) of the field
710
     * @param first Iterator pointing to the beginning of the data
711
     * @param last Iterator pointing one past the end of data
712
     */
713
    template <typename InputIterator>
714
    void add_packed_enum(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
715
        add_packed_varint(tag, first, last);
716
    }
717

718
    /**
719
     * Add "repeated packed int32" field to data.
720
     *
721
     * @tparam InputIterator A type satisfying the InputIterator concept.
722
     *         Dereferencing the iterator must yield a type assignable to int32_t.
723
     * @param tag Tag (field number) of the field
724
     * @param first Iterator pointing to the beginning of the data
725
     * @param last Iterator pointing one past the end of data
726
     */
727
    template <typename InputIterator>
728
    void add_packed_int32(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
729
        add_packed_varint(tag, first, last);
730
    }
731

732
    /**
733
     * Add "repeated packed sint32" field to data.
734
     *
735
     * @tparam InputIterator A type satisfying the InputIterator concept.
736
     *         Dereferencing the iterator must yield a type assignable to int32_t.
737
     * @param tag Tag (field number) of the field
738
     * @param first Iterator pointing to the beginning of the data
739
     * @param last Iterator pointing one past the end of data
740
     */
741
    template <typename InputIterator>
742
    void add_packed_sint32(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
743
        add_packed_svarint(tag, first, last);
744
    }
745

746
    /**
747
     * Add "repeated packed uint32" field to data.
748
     *
749
     * @tparam InputIterator A type satisfying the InputIterator concept.
750
     *         Dereferencing the iterator must yield a type assignable to uint32_t.
751
     * @param tag Tag (field number) of the field
752
     * @param first Iterator pointing to the beginning of the data
753
     * @param last Iterator pointing one past the end of data
754
     */
755
    template <typename InputIterator>
756
    void add_packed_uint32(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
757
        add_packed_varint(tag, first, last);
758
    }
759

760
    /**
761
     * Add "repeated packed int64" field to data.
762
     *
763
     * @tparam InputIterator A type satisfying the InputIterator concept.
764
     *         Dereferencing the iterator must yield a type assignable to int64_t.
765
     * @param tag Tag (field number) of the field
766
     * @param first Iterator pointing to the beginning of the data
767
     * @param last Iterator pointing one past the end of data
768
     */
769
    template <typename InputIterator>
770
    void add_packed_int64(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
771
        add_packed_varint(tag, first, last);
772
    }
773

774
    /**
775
     * Add "repeated packed sint64" field to data.
776
     *
777
     * @tparam InputIterator A type satisfying the InputIterator concept.
778
     *         Dereferencing the iterator must yield a type assignable to int64_t.
779
     * @param tag Tag (field number) of the field
780
     * @param first Iterator pointing to the beginning of the data
781
     * @param last Iterator pointing one past the end of data
782
     */
783
    template <typename InputIterator>
784
    void add_packed_sint64(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
785
        add_packed_svarint(tag, first, last);
786
    }
787

788
    /**
789
     * Add "repeated packed uint64" field to data.
790
     *
791
     * @tparam InputIterator A type satisfying the InputIterator concept.
792
     *         Dereferencing the iterator must yield a type assignable to uint64_t.
793
     * @param tag Tag (field number) of the field
794
     * @param first Iterator pointing to the beginning of the data
795
     * @param last Iterator pointing one past the end of data
796
     */
797
    template <typename InputIterator>
798
    void add_packed_uint64(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
799
        add_packed_varint(tag, first, last);
800
    }
801

802
    /**
803
     * Add a "repeated packed" fixed-size field to data. The following
804
     * fixed-size fields are available:
805
     *
806
     * uint32_t -> repeated packed fixed32
807
     * int32_t  -> repeated packed sfixed32
808
     * uint64_t -> repeated packed fixed64
809
     * int64_t  -> repeated packed sfixed64
810
     * double   -> repeated packed double
811
     * float    -> repeated packed float
812
     *
813
     * @tparam ValueType One of the following types: (u)int32/64_t, double, float.
814
     * @tparam InputIterator A type satisfying the InputIterator concept.
815
     * @param tag Tag (field number) of the field
816
     * @param first Iterator pointing to the beginning of the data
817
     * @param last Iterator pointing one past the end of data
818
     */
819
    template <typename ValueType, typename InputIterator>
820
    void add_packed_fixed(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
821
        static_assert(std::is_same<ValueType, uint32_t>::value ||
822
                      std::is_same<ValueType, int32_t>::value ||
823
                      std::is_same<ValueType, int64_t>::value ||
824
                      std::is_same<ValueType, uint64_t>::value ||
825
                      std::is_same<ValueType, double>::value ||
826
                      std::is_same<ValueType, float>::value, "Only some types are allowed");
827
        add_packed_fixed<ValueType, InputIterator>(tag, first, last,
828
            typename std::iterator_traits<InputIterator>::iterator_category{});
829
    }
830

831
    /**
832
     * Add "repeated packed fixed32" field to data.
833
     *
834
     * @tparam InputIterator A type satisfying the InputIterator concept.
835
     *         Dereferencing the iterator must yield a type assignable to uint32_t.
836
     * @param tag Tag (field number) of the field
837
     * @param first Iterator pointing to the beginning of the data
838
     * @param last Iterator pointing one past the end of data
839
     */
840
    template <typename InputIterator>
841
    void add_packed_fixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
842
        add_packed_fixed<uint32_t, InputIterator>(tag, first, last,
843
            typename std::iterator_traits<InputIterator>::iterator_category{});
844
    }
845

846
    /**
847
     * Add "repeated packed sfixed32" field to data.
848
     *
849
     * @tparam InputIterator A type satisfying the InputIterator concept.
850
     *         Dereferencing the iterator must yield a type assignable to int32_t.
851
     * @param tag Tag (field number) of the field
852
     * @param first Iterator pointing to the beginning of the data
853
     * @param last Iterator pointing one past the end of data
854
     */
855
    template <typename InputIterator>
856
    void add_packed_sfixed32(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
857
        add_packed_fixed<int32_t, InputIterator>(tag, first, last,
858
            typename std::iterator_traits<InputIterator>::iterator_category{});
859
    }
860

861
    /**
862
     * Add "repeated packed fixed64" field to data.
863
     *
864
     * @tparam InputIterator A type satisfying the InputIterator concept.
865
     *         Dereferencing the iterator must yield a type assignable to uint64_t.
866
     * @param tag Tag (field number) of the field
867
     * @param first Iterator pointing to the beginning of the data
868
     * @param last Iterator pointing one past the end of data
869
     */
870
    template <typename InputIterator>
871
    void add_packed_fixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
872
        add_packed_fixed<uint64_t, InputIterator>(tag, first, last,
873
            typename std::iterator_traits<InputIterator>::iterator_category{});
874
    }
875

876
    /**
877
     * Add "repeated packed sfixed64" field to data.
878
     *
879
     * @tparam InputIterator A type satisfying the InputIterator concept.
880
     *         Dereferencing the iterator must yield a type assignable to int64_t.
881
     * @param tag Tag (field number) of the field
882
     * @param first Iterator pointing to the beginning of the data
883
     * @param last Iterator pointing one past the end of data
884
     */
885
    template <typename InputIterator>
886
    void add_packed_sfixed64(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
887
        add_packed_fixed<int64_t, InputIterator>(tag, first, last,
888
            typename std::iterator_traits<InputIterator>::iterator_category{});
889
    }
890

891
    /**
892
     * Add "repeated packed float" field to data.
893
     *
894
     * @tparam InputIterator A type satisfying the InputIterator concept.
895
     *         Dereferencing the iterator must yield a type assignable to float.
896
     * @param tag Tag (field number) of the field
897
     * @param first Iterator pointing to the beginning of the data
898
     * @param last Iterator pointing one past the end of data
899
     */
900
    template <typename InputIterator>
901
    void add_packed_float(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
902
        add_packed_fixed<float, InputIterator>(tag, first, last,
903
            typename std::iterator_traits<InputIterator>::iterator_category{});
904
    }
905

906
    /**
907
     * Add "repeated packed double" field to data.
908
     *
909
     * @tparam InputIterator A type satisfying the InputIterator concept.
910
     *         Dereferencing the iterator must yield a type assignable to double.
911
     * @param tag Tag (field number) of the field
912
     * @param first Iterator pointing to the beginning of the data
913
     * @param last Iterator pointing one past the end of data
914
     */
915
    template <typename InputIterator>
916
    void add_packed_double(pbf_tag_type tag, InputIterator first, InputIterator last) { // NOLINT(performance-unnecessary-value-param)
917
        add_packed_fixed<double, InputIterator>(tag, first, last,
918
            typename std::iterator_traits<InputIterator>::iterator_category{});
919
    }
920

921
    ///@}
922

923
    template <typename B, typename T> friend class detail::packed_field_varint;
924
    template <typename B, typename T> friend class detail::packed_field_svarint;
925
    template <typename B, typename T> friend class detail::packed_field_fixed;
926

927
}; // class basic_pbf_writer
928

929
/**
930
 * Swap two basic_pbf_writer objects.
931
 *
932
 * @param lhs First object.
933
 * @param rhs Second object.
934
 */
935
template <typename TBuffer>
936
inline void swap(basic_pbf_writer<TBuffer>& lhs, basic_pbf_writer<TBuffer>& rhs) noexcept {
937
    lhs.swap(rhs);
938
}
939

940
namespace detail {
941

942
    template <typename TBuffer>
943
    class packed_field {
944

945
        basic_pbf_writer<TBuffer> m_writer{};
946

947
    public:
948

949
        packed_field(const packed_field&) = delete;
950
        packed_field& operator=(const packed_field&) = delete;
951

952
        packed_field(packed_field&&) noexcept = default;
953
        packed_field& operator=(packed_field&&) noexcept = default;
954

955
        packed_field() = default;
956

957
        packed_field(basic_pbf_writer<TBuffer>& parent_writer, pbf_tag_type tag) :
958
            m_writer{parent_writer, tag} {
959
        }
960

961
        packed_field(basic_pbf_writer<TBuffer>& parent_writer, pbf_tag_type tag, std::size_t size) :
962
            m_writer{parent_writer, tag, size} {
963
        }
964

965
        ~packed_field() noexcept = default;
966

967
        bool valid() const noexcept {
968
            return m_writer.valid();
969
        }
970

971
        void commit() {
972
            m_writer.commit();
973
        }
974

975
        void rollback() {
976
            m_writer.rollback();
977
        }
978

979
        basic_pbf_writer<TBuffer>& writer() noexcept {
980
            return m_writer;
981
        }
982

983
    }; // class packed_field
984

985
    template <typename TBuffer, typename T>
986
    class packed_field_fixed : public packed_field<TBuffer> {
987

988
    public:
989

990
        packed_field_fixed() :
991
            packed_field<TBuffer>{} {
992
        }
993

994
        template <typename P>
995
        packed_field_fixed(basic_pbf_writer<TBuffer>& parent_writer, P tag) :
996
            packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag)} {
997
        }
998

999
        template <typename P>
1000
        packed_field_fixed(basic_pbf_writer<TBuffer>& parent_writer, P tag, std::size_t size) :
1001
            packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag), size * sizeof(T)} {
1002
        }
1003

1004
        void add_element(T value) {
1005
            this->writer().template add_fixed<T>(value);
1006
        }
1007

1008
    }; // class packed_field_fixed
1009

1010
    template <typename TBuffer, typename T>
1011
    class packed_field_varint : public packed_field<TBuffer> {
1012

1013
    public:
1014

1015
        packed_field_varint() :
1016
            packed_field<TBuffer>{} {
1017
        }
1018

1019
        template <typename P>
1020
        packed_field_varint(basic_pbf_writer<TBuffer>& parent_writer, P tag) :
1021
            packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag)} {
1022
        }
1023

1024
        void add_element(T value) {
1025
            this->writer().add_varint(uint64_t(value));
1026
        }
1027

1028
    }; // class packed_field_varint
1029

1030
    template <typename TBuffer, typename T>
1031
    class packed_field_svarint : public packed_field<TBuffer> {
1032

1033
    public:
1034

1035
        packed_field_svarint() :
1036
            packed_field<TBuffer>{} {
1037
        }
1038

1039
        template <typename P>
1040
        packed_field_svarint(basic_pbf_writer<TBuffer>& parent_writer, P tag) :
1041
            packed_field<TBuffer>{parent_writer, static_cast<pbf_tag_type>(tag)} {
1042
        }
1043

1044
        void add_element(T value) {
1045
            this->writer().add_varint(encode_zigzag64(value));
1046
        }
1047

1048
    }; // class packed_field_svarint
1049

1050
} // end namespace detail
1051

1052
} // end namespace protozero
1053

1054
#endif // PROTOZERO_BASIC_PBF_WRITER_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

© 2025 Coveralls, Inc