Coverage Report

Created: 2026-04-23 10:02

src/zserio/BitStreamReader.h
Line
Count
Source
1
#ifndef ZSERIO_BIT_STREAM_READER_H_INC
2
#define ZSERIO_BIT_STREAM_READER_H_INC
3
4
#include <algorithm>
5
#include <cstring>
6
#include <string>
7
8
#include "zserio/BitBuffer.h"
9
#include "zserio/RebindAlloc.h"
10
#include "zserio/Span.h"
11
#include "zserio/String.h"
12
#include "zserio/Types.h"
13
#include "zserio/Vector.h"
14
15
namespace zserio
16
{
17
18
/**
19
 * Wrapper class to specify maximum initial array allocation.
20
 */
21
class ArrayPreallocation
22
{
23
public:
24
    static constexpr size_t MAX_INITIAL_ARRAY_ALLOCATION = 128 * 1024;
25
26
    /**
27
     * Default constructor sets default array preallocation.
28
     */
29
    ArrayPreallocation() :
30
            allocation(MAX_INITIAL_ARRAY_ALLOCATION)
31
4.64k
    {}
32
    /**
33
     * Constructor.
34
     *
35
     * \param alloc Sets custom maximum preallocation for arrays.
36
     */
37
    explicit ArrayPreallocation(size_t alloc) :
38
            allocation(alloc)
39
12
    {}
40
    /**
41
     * Gets the value for maximum array preallocation.
42
     */
43
    operator size_t() const
44
4.65k
    {
45
4.65k
        return allocation;
46
4.65k
    }
47
48
private:
49
    size_t allocation;
50
};
51
52
/**
53
 * Reader class which allows to read various data from the bit stream.
54
 */
55
class BitStreamReader
56
{
57
public:
58
    /** Type for bit position. */
59
    using BitPosType = size_t;
60
61
    /**
62
     * Context of the reader defining its state.
63
     */
64
    struct ReaderContext
65
    {
66
        /**
67
         * Constructor.
68
         *
69
         * \param readBuffer Span to the buffer to read.
70
         * \param readBufferBitSize Size of the buffer in bits.
71
         */
72
        explicit ReaderContext(
73
                Span<const uint8_t> readBuffer, size_t readBufferBitSize, size_t maxArrayPrealloc);
74
75
        /**
76
         * Destructor.
77
         */
78
        ~ReaderContext() = default;
79
80
        /**
81
         * Copying and moving is disallowed!
82
         * \{
83
         */
84
        ReaderContext(const ReaderContext&) = delete;
85
        ReaderContext& operator=(const ReaderContext&) = delete;
86
87
        ReaderContext(const ReaderContext&&) = delete;
88
        ReaderContext& operator=(const ReaderContext&&) = delete;
89
        /**
90
         * \}
91
         */
92
93
        Span<const uint8_t> buffer; /**< Buffer to read from. */
94
        const BitPosType bufferBitSize; /**< Size of the buffer in bits. */
95
96
        uintptr_t cache; /**< Bit cache to optimize bit reading. */
97
        uint8_t cacheNumBits; /**< Num bits available in the bit cache. */
98
99
        BitPosType bitIndex; /**< Current bit index. */
100
101
        const size_t maxArrayPreallocation; /**< Maximum initial array allocation. */
102
    };
103
104
    /**
105
     * Constructor from raw buffer.
106
     *
107
     * \param buffer Pointer to the buffer to read.
108
     * \param bufferByteSize Size of the buffer in bytes.
109
     * \param maxArrayPrealloc Maximum preallocation for arrays.
110
     */
111
    explicit BitStreamReader(
112
            const uint8_t* buffer, size_t bufferByteSize, ArrayPreallocation maxArrayPrealloc = {});
113
114
    /**
115
     * Constructor from buffer passed as a Span.
116
     *
117
     * \param buffer Buffer to read.
118
     * \param maxArrayPrealloc Maximum preallocation for arrays.
119
     */
120
    explicit BitStreamReader(Span<const uint8_t> buffer, ArrayPreallocation maxArrayPrealloc = {});
121
122
    /**
123
     * Constructor from buffer passed as a Span with exact bit size.
124
     *
125
     * \param buffer Buffer to read.
126
     * \param bufferBitSize Size of the buffer in bits.
127
     * \param maxArrayPrealloc Maximum preallocation for arrays.
128
     */
129
    explicit BitStreamReader(
130
            Span<const uint8_t> buffer, size_t bufferBitSize, ArrayPreallocation maxArrayPrealloc = {});
131
132
    /**
133
     * Constructor from raw buffer with exact bit size.
134
     *
135
     * \param buffer Pointer to buffer to read.
136
     * \param bufferBitSize Size of the buffer in bits.
137
     * \param maxArrayPrealloc Maximum preallocation for arrays.
138
     */
139
    explicit BitStreamReader(
140
            const uint8_t* buffer, size_t bufferBitSize, BitsTag, ArrayPreallocation maxArrayPrealloc = {});
141
142
    /**
143
     * Constructor from bit buffer.
144
     *
145
     * \param bitBuffer Bit buffer to read from.
146
     * \param maxArrayPrealloc Maximum preallocation for arrays.
147
     */
148
    template <typename ALLOC>
149
    explicit BitStreamReader(const BasicBitBuffer<ALLOC>& bitBuffer, ArrayPreallocation maxArrayPrealloc = {}) :
150
            BitStreamReader(bitBuffer.getData(), bitBuffer.getBitSize(), maxArrayPrealloc)
151
396
    {}
152
153
    /**
154
     * Destructor.
155
     */
156
    ~BitStreamReader() = default;
157
158
    /**
159
     * Reads unsigned bits up to 32-bits.
160
     *
161
     * \param numBits Number of bits to read.
162
     *
163
     * \return Read bits.
164
     */
165
    uint32_t readBits(uint8_t numBits = 32);
166
167
    /**
168
     * Reads unsigned bits up to 64-bits.
169
     *
170
     * \param numBits Number of bits to read.
171
     *
172
     * \return Read bits.
173
     */
174
    uint64_t readBits64(uint8_t numBits = 64);
175
176
    /**
177
     * Reads signed bits up to 32-bits.
178
     *
179
     * \param numBits Number of bits to read.
180
     *
181
     * \return Read bits.
182
     */
183
    int32_t readSignedBits(uint8_t numBits = 32);
184
185
    /**
186
     * Reads signed bits up to 64-bits.
187
     *
188
     * \param numBits Number of bits to read.
189
     *
190
     * \return Read bits.
191
     */
192
    int64_t readSignedBits64(uint8_t numBits = 64);
193
194
    /**
195
     * Reads signed variable integer up to 64 bits.
196
     *
197
     * \return Read varint64.
198
     */
199
    int64_t readVarInt64();
200
201
    /**
202
     * Reads signed variable integer up to 32 bits.
203
     *
204
     * \return Read varint32.
205
     */
206
    int32_t readVarInt32();
207
208
    /**
209
     * Reads signed variable integer up to 16 bits.
210
     *
211
     * \return Read varint16.
212
     */
213
    int16_t readVarInt16();
214
215
    /**
216
     * Read unsigned variable integer up to 64 bits.
217
     *
218
     * \return Read varuint64.
219
     */
220
    uint64_t readVarUInt64();
221
222
    /**
223
     * Read unsigned variable integer up to 32 bits.
224
     *
225
     * \return Read varuint32.
226
     */
227
    uint32_t readVarUInt32();
228
229
    /**
230
     * Read unsigned variable integer up to 16 bits.
231
     *
232
     * \return Read varuint16.
233
     */
234
    uint16_t readVarUInt16();
235
236
    /**
237
     * Reads signed variable integer up to 72 bits.
238
     *
239
     * \return Read varint.
240
     */
241
    int64_t readVarInt();
242
243
    /**
244
     * Read unsigned variable integer up to 72 bits.
245
     *
246
     * \return Read varuint.
247
     */
248
    uint64_t readVarUInt();
249
250
    /**
251
     * Read variable size integer up to 40 bits.
252
     *
253
     * \return Read varsize.
254
     */
255
    uint32_t readVarSize();
256
257
    /**
258
     * Reads 16-bit float.
259
     *
260
     * \return Read float16.
261
     */
262
    float readFloat16();
263
264
    /**
265
     * Reads 32-bit float.
266
     *
267
     * \return Read float32.
268
     */
269
    float readFloat32();
270
271
    /**
272
     * Reads 64-bit float double.
273
     *
274
     * \return Read float64.
275
     */
276
    double readFloat64();
277
278
    /**
279
     * Reads bytes.
280
     *
281
     * \param alloc Allocator to use.
282
     *
283
     * \return Read bytes as a vector.
284
     */
285
    template <typename ALLOC = std::allocator<uint8_t>>
286
    vector<uint8_t, ALLOC> readBytes(const ALLOC& alloc = ALLOC())
287
478
    {
288
478
        const size_t len = static_cast<size_t>(readVarSize());
289
478
        const BitPosType beginBitPosition = getBitPosition();
290
478
        if (beginBitPosition + 8ULL * len > getBufferBitSize())
291
5
        {
292
5
            throw CppRuntimeException("BitStreamReader: Byte array size exceeds available buffer!");
293
5
        }
294
473
        if ((beginBitPosition & 0x07U) != 0)
295
40
        {
296
            // we are not aligned to byte
297
40
            vector<uint8_t, ALLOC> value{alloc};
298
40
            value.reserve(len);
299
132
            for (size_t i = 0; i < len; 
++i92
)
300
92
            {
301
92
                value.push_back(readByte());
302
92
            }
303
40
            return value;
304
40
        }
305
433
        else
306
433
        {
307
            // we are aligned to byte
308
433
            setBitPosition(beginBitPosition + len * 8);
309
433
            Span<const uint8_t>::iterator beginIt = m_context.buffer.begin() + beginBitPosition / 8;
310
433
            return vector<uint8_t, ALLOC>(beginIt, beginIt + len, alloc);
311
433
        }
312
473
    }
313
314
    /**
315
     * Reads an UTF-8 string.
316
     *
317
     * \param alloc Allocator to use.
318
     *
319
     * \return Read string.
320
     */
321
    template <typename ALLOC = std::allocator<char>>
322
    string<ALLOC> readString(const ALLOC& alloc = ALLOC())
323
532
    {
324
532
        const size_t len = static_cast<size_t>(readVarSize());
325
532
        const BitPosType beginBitPosition = getBitPosition();
326
532
        if (beginBitPosition + 8ULL * len > getBufferBitSize())
327
5
        {
328
5
            throw CppRuntimeException("BitStreamReader: String size exceeds available buffer!");
329
5
        }
330
527
        if ((beginBitPosition & 0x07U) != 0)
331
60
        {
332
            // we are not aligned to byte
333
60
            string<ALLOC> value{alloc};
334
60
            value.reserve(len);
335
942
            for (size_t i = 0; i < len; 
++i882
)
336
882
            {
337
882
                using char_traits = std::char_traits<char>;
338
882
                const char readCharacter =
339
882
                        char_traits::to_char_type(static_cast<char_traits::int_type>(readByte()));
340
882
                value.push_back(readCharacter);
341
882
            }
342
60
            return value;
343
60
        }
344
467
        else
345
467
        {
346
            // we are aligned to byte
347
467
            setBitPosition(beginBitPosition + len * 8);
348
467
            Span<const uint8_t>::iterator beginIt = m_context.buffer.begin() + beginBitPosition / 8;
349
467
            return string<ALLOC>(beginIt, beginIt + len, alloc);
350
467
        }
351
527
    }
352
353
    /**
354
     * Reads bool as a single bit.
355
     *
356
     * \return Read bool value.
357
     */
358
    bool readBool();
359
360
    /**
361
     * Reads a bit buffer.
362
     *
363
     * \param alloc Allocator to use.
364
     *
365
     * \return Read bit buffer.
366
     */
367
    template <typename ALLOC = std::allocator<uint8_t>>
368
    BasicBitBuffer<RebindAlloc<ALLOC, uint8_t>> readBitBuffer(const ALLOC& allocator = ALLOC())
369
510
    {
370
510
        const size_t bitSize = static_cast<size_t>(readVarSize());
371
510
        const BitPosType beginBitPosition = getBitPosition();
372
510
        if (beginBitPosition + static_cast<uint64_t>(bitSize) > getBufferBitSize())
373
5
        {
374
5
            throw CppRuntimeException("BitStreamReader: Bit buffer size exceeds available buffer!");
375
5
        }
376
377
505
        const size_t numBytesToRead = bitSize / 8;
378
505
        const uint8_t numRestBits = static_cast<uint8_t>(bitSize - numBytesToRead * 8);
379
505
        BasicBitBuffer<RebindAlloc<ALLOC, uint8_t>> bitBuffer(bitSize, allocator);
380
505
        Span<uint8_t> buffer = bitBuffer.getData();
381
505
        const Span<uint8_t>::iterator itEnd = buffer.begin() + numBytesToRead;
382
505
        if ((beginBitPosition & 0x07U) != 0)
383
54
        {
384
            // we are not aligned to byte
385
114
            for (Span<uint8_t>::iterator it = buffer.begin(); it != itEnd; 
++it60
)
386
60
            {
387
60
                *it = static_cast<uint8_t>(readBits(8));
388
60
            }
389
54
        }
390
451
        else
391
451
        {
392
            // we are aligned to byte
393
451
            setBitPosition(beginBitPosition + numBytesToRead * 8);
394
451
            Span<const uint8_t>::const_iterator sourceIt = m_context.buffer.begin() + beginBitPosition / 8;
395
451
            (void)std::copy(sourceIt, sourceIt + numBytesToRead, buffer.begin());
396
451
        }
397
398
505
        if (numRestBits > 0)
399
114
        {
400
114
            *itEnd = static_cast<uint8_t>(readBits(numRestBits) << (8U - numRestBits));
401
114
        }
402
403
505
        return bitBuffer;
404
510
    }
405
406
    /**
407
     * Gets current bit position.
408
     *
409
     * \return Current bit position.
410
     */
411
    BitPosType getBitPosition() const
412
27.4k
    {
413
27.4k
        return m_context.bitIndex;
414
27.4k
    }
415
416
    /**
417
     * Sets current bit position. Use with caution!
418
     *
419
     * \param position New bit position.
420
     */
421
    void setBitPosition(BitPosType position);
422
423
    /**
424
     * Moves current bit position to perform the requested bit alignment.
425
     *
426
     * \param alignment Size of the alignment in bits.
427
     */
428
    void alignTo(size_t alignment);
429
430
    /**
431
     * Gets size of the underlying buffer in bits.
432
     *
433
     * \return Buffer bit size.
434
     */
435
    size_t getBufferBitSize() const
436
3.24k
    {
437
3.24k
        return m_context.bufferBitSize;
438
3.24k
    }
439
440
    /**
441
     * Gets the maximum initial array capacity in bytes.
442
     *
443
     * \return Maximum array preallocation.
444
     */
445
    size_t getMaxArrayPreallocation() const
446
3.78k
    {
447
3.78k
        return m_context.maxArrayPreallocation;
448
3.78k
    }
449
450
private:
451
    uint8_t readByte();
452
453
    ReaderContext m_context;
454
};
455
456
} // namespace zserio
457
458
#endif // ifndef ZSERIO_BIT_STREAM_READER_H_INC