Coverage Report

Created: 2023-12-13 14:58

src/zserio/BitStreamWriter.cpp
Line
Count
Source
1
#include <cstring>
2
#include <fstream>
3
#include <array>
4
#include <algorithm>
5
6
#include "zserio/CppRuntimeException.h"
7
#include "zserio/BitSizeOfCalculator.h"
8
#include "zserio/FloatUtil.h"
9
#include "zserio/BitStreamWriter.h"
10
11
namespace zserio
12
{
13
14
static const std::array<uint32_t, 33> MAX_U32_VALUES =
15
{
16
    0x00U,
17
    0x0001U,     0x0003U,     0x0007U,     0x000fU,
18
    0x001fU,     0x003fU,     0x007fU,     0x00ffU,
19
    0x01ffU,     0x03ffU,     0x07ffU,     0x0fffU,
20
    0x1fffU,     0x3fffU,     0x7fffU,     0xffffU,
21
    0x0001ffffU, 0x0003ffffU, 0x0007ffffU, 0x000fffffU,
22
    0x001fffffU, 0x003fffffU, 0x007fffffU, 0x00ffffffU,
23
    0x01ffffffU, 0x03ffffffU, 0x07ffffffU, 0x0fffffffU,
24
    0x1fffffffU, 0x3fffffffU, 0x7fffffffU, 0xffffffffU,
25
};
26
27
static const std::array<int32_t, 33> MIN_I32_VALUES =
28
{
29
    0,
30
   -0x0001,     -0x0002,     -0x0004,     -0x0008,
31
   -0x0010,     -0x0020,     -0x0040,     -0x0080,
32
   -0x0100,     -0x0200,     -0x0400,     -0x0800,
33
   -0x1000,     -0x2000,     -0x4000,     -0x8000,
34
   -0x00010000, -0x00020000, -0x00040000, -0x00080000,
35
   -0x00100000, -0x00200000, -0x00400000, -0x00800000,
36
   -0x01000000, -0x02000000, -0x04000000, -0x08000000,
37
   -0x10000000, -0x20000000, -0x40000000, INT32_MIN
38
};
39
40
static const std::array<int32_t, 33> MAX_I32_VALUES =
41
{
42
    0x00,
43
    0x0000,      0x0001,      0x0003,      0x0007,
44
    0x000f,      0x001f,      0x003f,      0x007f,
45
    0x00ff,      0x01ff,      0x03ff,      0x07ff,
46
    0x0fff,      0x1fff,      0x3fff,      0x7fff,
47
    0x0000ffff,  0x0001ffff,  0x0003ffff,  0x0007ffff,
48
    0x000fffff,  0x001fffff,  0x003fffff,  0x007fffff,
49
    0x00ffffff,  0x01ffffff,  0x03ffffff,  0x07ffffff,
50
    0x0fffffff,  0x1fffffff,  0x3fffffff,  0x7fffffff
51
};
52
53
static const std::array<uint64_t, 65> MAX_U64_VALUES =
54
{
55
    0x00ULL,
56
    0x0001ULL,             0x0003ULL,             0x0007ULL,             0x000fULL,
57
    0x001fULL,             0x003fULL,             0x007fULL,             0x00ffULL,
58
    0x01ffULL,             0x03ffULL,             0x07ffULL,             0x0fffULL,
59
    0x1fffULL,             0x3fffULL,             0x7fffULL,             0xffffULL,
60
    0x0001ffffULL,         0x0003ffffULL,         0x0007ffffULL,         0x000fffffULL,
61
    0x001fffffULL,         0x003fffffULL,         0x007fffffULL,         0x00ffffffULL,
62
    0x01ffffffULL,         0x03ffffffULL,         0x07ffffffULL,         0x0fffffffULL,
63
    0x1fffffffULL,         0x3fffffffULL,         0x7fffffffULL,         0xffffffffULL,
64
    0x0001ffffffffULL,     0x0003ffffffffULL,     0x0007ffffffffULL,     0x000fffffffffULL,
65
    0x001fffffffffULL,     0x003fffffffffULL,     0x007fffffffffULL,     0x00ffffffffffULL,
66
    0x01ffffffffffULL,     0x03ffffffffffULL,     0x07ffffffffffULL,     0x0fffffffffffULL,
67
    0x1fffffffffffULL,     0x3fffffffffffULL,     0x7fffffffffffULL,     0xffffffffffffULL,
68
    0x0001ffffffffffffULL, 0x0003ffffffffffffULL, 0x0007ffffffffffffULL, 0x000fffffffffffffULL,
69
    0x001fffffffffffffULL, 0x003fffffffffffffULL, 0x007fffffffffffffULL, 0x00ffffffffffffffULL,
70
    0x01ffffffffffffffULL, 0x03ffffffffffffffULL, 0x07ffffffffffffffULL, 0x0fffffffffffffffULL,
71
    0x1fffffffffffffffULL, 0x3fffffffffffffffULL, 0x7fffffffffffffffULL, 0xffffffffffffffffULL
72
};
73
74
static const std::array<int64_t, 65> MIN_I64_VALUES =
75
{
76
    0LL,
77
   -0x0001LL,             -0x0002LL,             -0x0004LL,             -0x0008LL,
78
   -0x0010LL,             -0x0020LL,             -0x0040LL,             -0x0080LL,
79
   -0x0100LL,             -0x0200LL,             -0x0400LL,             -0x0800LL,
80
   -0x1000LL,             -0x2000LL,             -0x4000LL,             -0x8000LL,
81
   -0x00010000LL,         -0x00020000LL,         -0x00040000LL,         -0x00080000LL,
82
   -0x00100000LL,         -0x00200000LL,         -0x00400000LL,         -0x00800000LL,
83
   -0x01000000LL,         -0x02000000LL,         -0x04000000LL,         -0x08000000LL,
84
   -0x10000000LL,         -0x20000000LL,         -0x40000000LL,         -0x80000000LL,
85
   -0x000100000000LL,     -0x000200000000LL,     -0x000400000000LL,     -0x000800000000LL,
86
   -0x001000000000LL,     -0x002000000000LL,     -0x004000000000LL,     -0x008000000000LL,
87
   -0x010000000000LL,     -0x020000000000LL,     -0x040000000000LL,     -0x080000000000LL,
88
   -0x100000000000LL,     -0x200000000000LL,     -0x400000000000LL,     -0x800000000000LL,
89
   -0x0001000000000000LL, -0x0002000000000000LL, -0x0004000000000000LL, -0x0008000000000000LL,
90
   -0x0010000000000000LL, -0x0020000000000000LL, -0x0040000000000000LL, -0x0080000000000000LL,
91
   -0x0100000000000000LL, -0x0200000000000000LL, -0x0400000000000000LL, -0x0800000000000000LL,
92
   -0x1000000000000000LL, -0x2000000000000000LL, -0x4000000000000000LL, INT64_MIN
93
};
94
95
static const std::array<int64_t, 65> MAX_I64_VALUES =
96
{
97
    0x00LL,
98
    0x0000LL,              0x0001LL,              0x0003LL,             0x0007LL,
99
    0x000fLL,              0x001fLL,              0x003fLL,             0x007fLL,
100
    0x00ffLL,              0x01ffLL,              0x03ffLL,             0x07ffLL,
101
    0x0fffLL,              0x1fffLL,              0x3fffLL,             0x7fffLL,
102
    0x0000ffffLL,          0x0001ffffLL,          0x0003ffffLL,         0x0007ffffLL,
103
    0x000fffffLL,          0x001fffffLL,          0x003fffffLL,         0x007fffffLL,
104
    0x00ffffffLL,          0x01ffffffLL,          0x03ffffffLL,         0x07ffffffLL,
105
    0x0fffffffLL,          0x1fffffffLL,          0x3fffffffLL,         0x7fffffffLL,
106
    0x0000ffffffffLL,      0x0001ffffffffLL,      0x0003ffffffffLL,     0x0007ffffffffLL,
107
    0x000fffffffffLL,      0x001fffffffffLL,      0x003fffffffffLL,     0x007fffffffffLL,
108
    0x00ffffffffffLL,      0x01ffffffffffLL,      0x03ffffffffffLL,     0x07ffffffffffLL,
109
    0x0fffffffffffLL,      0x1fffffffffffLL,      0x3fffffffffffLL,     0x7fffffffffffLL,
110
    0x0000ffffffffffffLL,  0x0001ffffffffffffLL,  0x0003ffffffffffffLL, 0x0007ffffffffffffLL,
111
    0x000fffffffffffffLL,  0x001fffffffffffffLL,  0x003fffffffffffffLL, 0x007fffffffffffffLL,
112
    0x00ffffffffffffffLL,  0x01ffffffffffffffLL,  0x03ffffffffffffffLL, 0x07ffffffffffffffLL,
113
    0x0fffffffffffffffLL,  0x1fffffffffffffffLL,  0x3fffffffffffffffLL, 0x7fffffffffffffffLL
114
};
115
116
BitStreamWriter::BitStreamWriter(uint8_t* buffer, size_t bufferBitSize, BitsTag) :
117
        m_buffer(buffer, (bufferBitSize + 7) / 8),
118
        m_bitIndex(0),
119
        m_bufferBitSize(bufferBitSize)
120
1
{}
121
122
BitStreamWriter::BitStreamWriter(uint8_t* buffer, size_t bufferByteSize) :
123
        BitStreamWriter(Span<uint8_t>(buffer, bufferByteSize))
124
3.40k
{}
125
126
BitStreamWriter::BitStreamWriter(Span<uint8_t> buffer) :
127
        m_buffer(buffer),
128
        m_bitIndex(0),
129
        m_bufferBitSize(buffer.size() * 8)
130
3.41k
{}
131
132
BitStreamWriter::BitStreamWriter(Span<uint8_t> buffer, size_t bufferBitSize) :
133
        m_buffer(buffer),
134
        m_bitIndex(0),
135
        m_bufferBitSize(bufferBitSize)
136
471
{
137
471
    if (buffer.size() < (bufferBitSize + 7) / 8)
138
1
    {
139
1
        throw CppRuntimeException("BitStreamWriter: Wrong buffer bit size ('") << buffer.size() <<
140
1
                "' < '" << (bufferBitSize + 7) / 8 << "')!";
141
1
    }
142
471
}
143
144
void BitStreamWriter::writeBits(uint32_t data, uint8_t numBits)
145
8.85k
{
146
8.85k
    if (numBits > sizeof(uint32_t) * 8 || 
data > MAX_U32_VALUES[numBits]8.85k
)
147
34
    {
148
34
        throw CppRuntimeException("BitStreamWriter: Writing of ") << numBits << "-bits value '" << data <<
149
34
                "' failed!";
150
34
    }
151
152
8.81k
    writeUnsignedBits(data, numBits);
153
8.81k
}
154
155
void BitStreamWriter::writeBits64(uint64_t data, uint8_t numBits)
156
4.35k
{
157
4.35k
    if (numBits > sizeof(uint64_t) * 8 || 
data > MAX_U64_VALUES[numBits]4.35k
)
158
66
    {
159
66
        throw CppRuntimeException("BitStreamWriter: Writing of ") << numBits << "-bits value '" << data <<
160
66
                "' failed!";
161
66
    }
162
163
4.28k
    writeUnsignedBits64(data, numBits);
164
4.28k
}
165
166
void BitStreamWriter::writeSignedBits(int32_t data, uint8_t numBits)
167
1.62k
{
168
1.62k
    if (numBits > sizeof(int32_t) * 8 || 
data < MIN_I32_VALUES[numBits]1.62k
||
data > MAX_I32_VALUES[numBits]1.59k
)
169
65
    {
170
65
        throw CppRuntimeException("BitStreamWriter: Writing of ") << numBits << "-bits value '" << data <<
171
65
                "' failed!";
172
65
    }
173
174
1.56k
    writeUnsignedBits(static_cast<uint32_t>(data) & MAX_U32_VALUES[numBits], numBits);
175
1.56k
}
176
177
void BitStreamWriter::writeSignedBits64(int64_t data, uint8_t numBits)
178
2.18k
{
179
2.18k
    if (numBits > sizeof(int64_t) * 8 || 
data < MIN_I64_VALUES[numBits]2.18k
||
data > MAX_I64_VALUES[numBits]2.12k
)
180
129
    {
181
129
        throw CppRuntimeException("BitStreamWriter: Writing of ") << numBits << "-bits value '" << data <<
182
129
                "' failed!";
183
129
    }
184
185
2.05k
    writeUnsignedBits64(static_cast<uint64_t>(data) & MAX_U64_VALUES[numBits], numBits);
186
2.05k
}
187
188
void BitStreamWriter::writeVarInt64(int64_t data)
189
4.42k
{
190
4.42k
    writeSignedVarNum(data, 8, zserio::bitSizeOfVarInt64(data) / 8);
191
4.42k
}
192
193
void BitStreamWriter::writeVarInt32(int32_t data)
194
1.19k
{
195
1.19k
    writeSignedVarNum(data, 4, zserio::bitSizeOfVarInt32(data) / 8);
196
1.19k
}
197
198
void BitStreamWriter::writeVarInt16(int16_t data)
199
344
{
200
344
    writeSignedVarNum(data, 2, zserio::bitSizeOfVarInt16(data) / 8);
201
344
}
202
203
void BitStreamWriter::writeVarUInt64(uint64_t data)
204
3.14k
{
205
3.14k
    writeUnsignedVarNum(data, 8, zserio::bitSizeOfVarUInt64(data) / 8);
206
3.14k
}
207
208
void BitStreamWriter::writeVarUInt32(uint32_t data)
209
820
{
210
820
    writeUnsignedVarNum(data, 4, zserio::bitSizeOfVarUInt32(data) / 8);
211
820
}
212
213
void BitStreamWriter::writeVarUInt16(uint16_t data)
214
284
{
215
284
    writeUnsignedVarNum(data, 2, zserio::bitSizeOfVarUInt16(data) / 8);
216
284
}
217
218
void BitStreamWriter::writeVarInt(int64_t data)
219
5.50k
{
220
5.50k
    if (data == INT64_MIN)
221
159
        writeBits(0x80, 8); // INT64_MIN is encoded as -0
222
5.34k
    else
223
5.34k
        writeSignedVarNum(data, 9, zserio::bitSizeOfVarInt(data) / 8);
224
5.50k
}
225
226
void BitStreamWriter::writeVarUInt(uint64_t data)
227
2.75k
{
228
2.75k
    writeUnsignedVarNum(data, 9, zserio::bitSizeOfVarUInt(data) / 8);
229
2.75k
}
230
231
void BitStreamWriter::writeVarSize(uint32_t data)
232
3.11k
{
233
3.11k
    writeUnsignedVarNum(data, 5, zserio::bitSizeOfVarSize(data) / 8);
234
3.11k
}
235
236
void BitStreamWriter::writeFloat16(float data)
237
305
{
238
305
    const uint16_t halfPrecisionFloat = convertFloatToUInt16(data);
239
305
    writeUnsignedBits(halfPrecisionFloat, 16);
240
305
}
241
242
void BitStreamWriter::writeFloat32(float data)
243
499
{
244
499
    const uint32_t singlePrecisionFloat = convertFloatToUInt32(data);
245
499
    writeUnsignedBits(singlePrecisionFloat, 32);
246
499
}
247
248
void BitStreamWriter::writeFloat64(double data)
249
859
{
250
859
    const uint64_t doublePrecisionFloat = convertDoubleToUInt64(data);
251
859
    writeUnsignedBits64(doublePrecisionFloat, 64);
252
859
}
253
254
void BitStreamWriter::writeBytes(Span<const uint8_t> data)
255
99
{
256
99
    const size_t len = data.size();
257
99
    writeVarSize(convertSizeToUInt32(len));
258
259
99
    const BitPosType beginBitPosition = getBitPosition();
260
99
    if ((beginBitPosition & 0x07U) != 0)
261
52
    {
262
        // we are not aligned to byte
263
180
        for (size_t i = 0; i < len; 
++i128
)
264
128
            writeBits(data[i], 8);
265
52
    }
266
47
    else
267
47
    {
268
        // we are aligned to bytes
269
47
        setBitPosition(beginBitPosition + len * 8);
270
47
        if (hasWriteBuffer())
271
45
            std::copy(data.begin(), data.end(), m_buffer.begin() + beginBitPosition / 8);
272
47
    }
273
99
}
274
275
void BitStreamWriter::writeString(StringView data)
276
161
{
277
161
    const size_t len = data.size();
278
161
    writeVarSize(convertSizeToUInt32(len));
279
280
161
    const BitPosType beginBitPosition = getBitPosition();
281
161
    if ((beginBitPosition & 0x07U) != 0)
282
78
    {
283
        // we are not aligned to byte
284
1.54k
        for (size_t i = 0; i < len; 
++i1.47k
)
285
1.47k
            writeBits(static_cast<uint8_t>(data[i]), 8);
286
78
    }
287
83
    else
288
83
    {
289
        // we are aligned to bytes
290
83
        setBitPosition(beginBitPosition + len * 8);
291
83
        if (hasWriteBuffer())
292
80
            std::copy(data.begin(), data.begin() + len, m_buffer.data() + beginBitPosition / 8);
293
83
    }
294
161
}
295
296
void BitStreamWriter::writeBool(bool data)
297
909
{
298
909
    writeBits((data ? 
1391
:
0518
), 1);
299
909
}
300
301
void BitStreamWriter::setBitPosition(BitPosType position)
302
704
{
303
704
    if (hasWriteBuffer())
304
695
        checkCapacity(position);
305
306
704
    m_bitIndex = position;
307
704
}
308
309
void BitStreamWriter::alignTo(size_t alignment)
310
4.73k
{
311
4.73k
    const BitPosType offset = getBitPosition() % alignment;
312
4.73k
    if (offset != 0)
313
2.46k
    {
314
2.46k
        const uint8_t skip = static_cast<uint8_t>(alignment - offset);
315
2.46k
        writeBits64(0, skip);
316
2.46k
    }
317
4.73k
}
318
319
const uint8_t* BitStreamWriter::getWriteBuffer() const
320
515
{
321
515
    return m_buffer.data();
322
515
}
323
324
Span<const uint8_t> BitStreamWriter::getBuffer() const
325
18
{
326
18
    return m_buffer;
327
18
}
328
329
void BitStreamWriter::writeUnsignedBits(uint32_t data, uint8_t numBits)
330
103k
{
331
103k
    if (!hasWriteBuffer())
332
37.3k
    {
333
37.3k
        m_bitIndex += numBits;
334
37.3k
        return;
335
37.3k
    }
336
337
66.4k
    checkCapacity(m_bitIndex + numBits);
338
339
66.4k
    uint8_t restNumBits = numBits;
340
66.4k
    const uint8_t bitsUsed = m_bitIndex & 0x07U;
341
66.4k
    uint8_t bitsFree = 8 - bitsUsed;
342
66.4k
    size_t byteIndex = m_bitIndex / 8;
343
344
66.4k
    if (restNumBits > bitsFree)
345
46.3k
    {
346
        // first part
347
46.3k
        const uint8_t shiftNum = restNumBits - bitsFree;
348
46.3k
        const uint8_t maskedByte = m_buffer[byteIndex] & ~(0xFFU >> bitsUsed);
349
46.3k
        m_buffer[byteIndex++] = maskedByte | static_cast<uint8_t>(data >> shiftNum);
350
46.3k
        restNumBits -= bitsFree;
351
352
        // middle parts
353
64.3k
        while (restNumBits >= 8)
354
18.0k
        {
355
18.0k
            restNumBits -= 8;
356
18.0k
            m_buffer[byteIndex++] = static_cast<uint8_t>((data >> restNumBits) & MAX_U32_VALUES[8]);
357
18.0k
        }
358
359
        // reset bits free
360
46.3k
        bitsFree = 8;
361
46.3k
    }
362
363
    // last part
364
66.4k
    if (restNumBits > 0)
365
64.2k
    {
366
64.2k
        const uint8_t shiftNum = bitsFree - restNumBits;
367
64.2k
        const uint32_t mask = MAX_U32_VALUES[restNumBits];
368
64.2k
        const uint8_t maskedByte = m_buffer[byteIndex] &
369
64.2k
                static_cast<uint8_t>(~static_cast<uint8_t>(mask << shiftNum));
370
64.2k
        m_buffer[byteIndex] = maskedByte | static_cast<uint8_t>((data & mask) << shiftNum);
371
64.2k
    }
372
373
66.4k
    m_bitIndex += numBits;
374
66.4k
}
375
376
inline void BitStreamWriter::writeUnsignedBits64(uint64_t data, uint8_t numBits)
377
7.20k
{
378
7.20k
    if (numBits <= 32)
379
4.04k
    {
380
4.04k
        writeUnsignedBits(static_cast<uint32_t>(data), numBits);
381
4.04k
    }
382
3.16k
    else
383
3.16k
    {
384
3.16k
        writeUnsignedBits(static_cast<uint32_t>(data >> 32U), numBits - 32);
385
3.16k
        writeUnsignedBits(static_cast<uint32_t>(data), 32);
386
3.16k
    }
387
7.20k
}
388
389
inline void BitStreamWriter::writeSignedVarNum(int64_t value, size_t maxVarBytes, size_t numVarBytes)
390
11.3k
{
391
11.3k
    const uint64_t absValue = static_cast<uint64_t>(value < 0 ? 
-value3.92k
:
value7.37k
);
392
11.3k
    writeVarNum(absValue, true, value < 0, maxVarBytes, numVarBytes);
393
11.3k
}
394
395
inline void BitStreamWriter::writeUnsignedVarNum(uint64_t value, size_t maxVarBytes, size_t numVarBytes)
396
10.1k
{
397
10.1k
    writeVarNum(value, false, false, maxVarBytes, numVarBytes);
398
10.1k
}
399
400
inline void BitStreamWriter::writeVarNum(uint64_t value, bool hasSign, bool isNegative, size_t maxVarBytes,
401
        size_t numVarBytes)
402
21.4k
{
403
21.4k
    static const std::array<uint64_t, 8> bitMasks = { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF };
404
21.4k
    const bool hasMaxByteRange = (numVarBytes == maxVarBytes);
405
406
103k
    for (size_t i = 0; i < numVarBytes; 
i++81.9k
)
407
81.9k
    {
408
81.9k
        uint8_t byte = 0x00;
409
81.9k
        uint8_t numBits = 8;
410
81.9k
        const bool hasNextByte = (i < numVarBytes - 1);
411
81.9k
        const bool hasSignBit = (hasSign && 
i == 048.6k
);
412
81.9k
        if (hasSignBit)
413
11.3k
        {
414
11.3k
            if (isNegative)
415
3.92k
                byte |= 0x80U;
416
11.3k
            numBits--;
417
11.3k
        }
418
81.9k
        if (hasNextByte)
419
60.5k
        {
420
60.5k
            numBits--;
421
60.5k
            byte |= (0x01U << numBits); // use bit 6 if signed bit is present, use bit 7 otherwise
422
60.5k
        }
423
21.4k
        else // this is the last byte
424
21.4k
        {
425
21.4k
            if (!hasMaxByteRange) // next byte indicator is not used in last byte in case of max byte range
426
18.8k
                numBits--;
427
21.4k
        }
428
429
81.9k
        const size_t shiftBits = (numVarBytes - (i + 1)) * 7 + ((hasMaxByteRange && 
hasNextByte17.7k
) ?
115.1k
:
066.8k
);
430
81.9k
        byte |= static_cast<uint8_t>((value >> shiftBits) & bitMasks[numBits - 1]);
431
81.9k
        writeUnsignedBits(byte, 8);
432
81.9k
    }
433
21.4k
}
434
435
inline void BitStreamWriter::throwInsufficientCapacityException() const
436
71
{
437
71
    throw InsufficientCapacityException("BitStreamWriter: Reached end of bit buffer!");
438
71
}
439
440
inline void BitStreamWriter::checkCapacity(size_t bitSize) const
441
67.1k
{
442
67.1k
    if (bitSize > m_bufferBitSize)
443
71
        throwInsufficientCapacityException();
444
67.1k
}
445
446
} // namespace zserio