Coverage Report

Created: 2024-12-05 10:39

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