test/zserio/BitStreamWriterTest.cpp
Line | Count | Source |
1 | | #include <array> |
2 | | #include <cstring> |
3 | | |
4 | | #include "gtest/gtest.h" |
5 | | #include "zserio/BitStreamWriter.h" |
6 | | #include "zserio/CppRuntimeException.h" |
7 | | |
8 | | namespace zserio |
9 | | { |
10 | | |
11 | | class BitStreamWriterTest : public ::testing::Test |
12 | | { |
13 | | public: |
14 | | BitStreamWriterTest() : |
15 | | m_externalBuffer(), |
16 | | m_externalBufferWriter(m_externalBuffer.data(), m_externalBuffer.size()), |
17 | | m_dummyBufferWriter(nullptr, 0) |
18 | 24 | { |
19 | 24 | m_externalBuffer.fill(0); |
20 | 24 | } |
21 | | |
22 | | protected: |
23 | | std::array<uint8_t, 512> m_externalBuffer; |
24 | | BitStreamWriter m_externalBufferWriter; |
25 | | BitStreamWriter m_dummyBufferWriter; |
26 | | }; |
27 | | |
28 | | TEST_F(BitStreamWriterTest, rawConstructor) |
29 | 1 | { |
30 | 1 | std::array<uint8_t, 2> data = {0x00, 0x00}; |
31 | 1 | BitStreamWriter writer(data.data(), data.size()); |
32 | | |
33 | 1 | ASSERT_EQ(data.data(), writer.getBuffer().data()); |
34 | 1 | ASSERT_EQ(data.size() * 8, writer.getBufferBitSize()); |
35 | | |
36 | 1 | writer.writeBits(0x1F, 5); |
37 | 1 | writer.writeBits(0x07, 3); |
38 | 1 | ASSERT_EQ(0xFF, writer.getBuffer()[0]); |
39 | 1 | ASSERT_THROW(writer.writeBits(0xFFFF, 16), CppRuntimeException); |
40 | 1 | writer.writeBits(0x07, 3); |
41 | 1 | writer.writeBits(0x00, 5); |
42 | 1 | ASSERT_EQ(0xE0, writer.getBuffer()[1]); |
43 | 1 | } |
44 | | |
45 | | TEST_F(BitStreamWriterTest, rawConstructorWithBitSize) |
46 | 1 | { |
47 | 1 | std::array<uint8_t, 2> data = {0x00, 0x00}; |
48 | 1 | BitStreamWriter writer(data.data(), 15, BitsTag()); |
49 | | |
50 | 1 | ASSERT_EQ(data.data(), writer.getBuffer().data()); |
51 | 1 | ASSERT_EQ(15, writer.getBufferBitSize()); |
52 | | |
53 | 1 | writer.writeBits(0x1F, 5); |
54 | 1 | writer.writeBits(0x07, 3); |
55 | 1 | ASSERT_EQ(0xFF, writer.getBuffer()[0]); |
56 | 1 | ASSERT_THROW(writer.writeBits(0xFF, 8), CppRuntimeException); |
57 | 1 | writer.writeBits(0x07, 3); |
58 | 1 | writer.writeBits(0x00, 4); |
59 | 1 | ASSERT_EQ(0xE0, writer.getBuffer()[1]); |
60 | 1 | } |
61 | | |
62 | | TEST_F(BitStreamWriterTest, spanConstructor) |
63 | 1 | { |
64 | 1 | std::array<uint8_t, 2> data = {0x00, 0x00}; |
65 | 1 | const Span<uint8_t> span(data); |
66 | 1 | BitStreamWriter writer(span); |
67 | | |
68 | 1 | ASSERT_EQ(span.data(), writer.getBuffer().data()); |
69 | 1 | ASSERT_EQ(span.size() * 8, writer.getBufferBitSize()); |
70 | | |
71 | 1 | writer.writeBits(0x1F, 5); |
72 | 1 | writer.writeBits(0x07, 3); |
73 | 1 | ASSERT_EQ(0xFF, writer.getBuffer()[0]); |
74 | 1 | ASSERT_THROW(writer.writeBits(0xFFFF, 16), CppRuntimeException); |
75 | 1 | writer.writeBits(0x07, 3); |
76 | 1 | writer.writeBits(0x00, 5); |
77 | 1 | ASSERT_EQ(0xE0, writer.getBuffer()[1]); |
78 | 1 | } |
79 | | |
80 | | TEST_F(BitStreamWriterTest, spanConstructorWithBitSize) |
81 | 1 | { |
82 | 1 | std::array<uint8_t, 2> data = {0x00, 0x00}; |
83 | 1 | const Span<uint8_t> span(data); |
84 | 1 | BitStreamWriter writer(span, 15); |
85 | 1 | ASSERT_THROW(BitStreamWriter wrongWriter(span, 17), CppRuntimeException); |
86 | | |
87 | 1 | ASSERT_EQ(span.data(), writer.getBuffer().data()); |
88 | 1 | ASSERT_EQ(15, writer.getBufferBitSize()); |
89 | | |
90 | 1 | writer.writeBits(0x1F, 5); |
91 | 1 | writer.writeBits(0x07, 3); |
92 | 1 | ASSERT_EQ(0xFF, writer.getBuffer()[0]); |
93 | 1 | ASSERT_THROW(writer.writeBits(0xFF, 8), CppRuntimeException); |
94 | 1 | writer.writeBits(0x07, 3); |
95 | 1 | writer.writeBits(0x00, 4); |
96 | 1 | ASSERT_EQ(0xE0, writer.getBuffer()[1]); |
97 | 1 | } |
98 | | |
99 | | TEST_F(BitStreamWriterTest, bitBufferConstructor) |
100 | 1 | { |
101 | 1 | BitBuffer bitBuffer(11); |
102 | 1 | BitStreamWriter writer(bitBuffer); |
103 | | |
104 | 1 | ASSERT_EQ(bitBuffer.getBuffer(), writer.getBuffer().data()); |
105 | 1 | ASSERT_EQ(bitBuffer.getBitSize(), writer.getBufferBitSize()); |
106 | | |
107 | 1 | writer.writeBits(0x1F, 5); |
108 | 1 | writer.writeBits(0x07, 3); |
109 | 1 | ASSERT_EQ(0xFF, writer.getBuffer()[0]); |
110 | 1 | ASSERT_THROW(writer.writeBits(0x0F, 4), CppRuntimeException); |
111 | 1 | writer.writeBits(0x07, 3); |
112 | 1 | ASSERT_EQ(0xE0, writer.getBuffer()[1]); |
113 | 1 | } |
114 | | |
115 | | TEST_F(BitStreamWriterTest, writeUnalignedData) |
116 | 1 | { |
117 | | // number expected to be written at offset |
118 | 1 | const uint8_t testValue = 123; |
119 | | |
120 | 66 | for (uint8_t offset = 0; offset <= 64; ++offset65 ) |
121 | 65 | { |
122 | 65 | BitBuffer bitBuffer(8 + offset); |
123 | | // fill the buffer with 1s to check proper masking |
124 | 65 | std::memset(bitBuffer.getBuffer(), 0xFF, bitBuffer.getByteSize()); |
125 | | |
126 | 65 | BitStreamWriter writer(bitBuffer); |
127 | | |
128 | 65 | writer.writeBits64(0, offset); |
129 | 65 | writer.writeBits(testValue, 8); |
130 | | |
131 | | // check eof |
132 | 65 | ASSERT_THROW(writer.writeBits64(0, 1), CppRuntimeException); |
133 | | |
134 | | // check written value |
135 | 65 | uint8_t writtenTestValue = static_cast<uint8_t>(bitBuffer.getData()[offset / 8U] << (offset % 8U)); |
136 | 65 | if (offset % 8 != 0) |
137 | 56 | { |
138 | 56 | const uint8_t val = |
139 | 56 | static_cast<uint8_t>(bitBuffer.getData()[offset / 8U + 1U] >> (8U - (offset % 8U))); |
140 | 56 | writtenTestValue = static_cast<uint8_t>(writtenTestValue | val); |
141 | 56 | } |
142 | 130 | ASSERT_EQ(testValue, writtenTestValue) << "Offset: " << offset; |
143 | 65 | } |
144 | 1 | } |
145 | | |
146 | | TEST_F(BitStreamWriterTest, writeBits) |
147 | 1 | { |
148 | | // check invalid bitlength acceptance |
149 | 1 | const std::array<uint8_t, 3> numBitsArray = {255, 0, 33}; |
150 | 1 | for (uint8_t numBits : numBitsArray) |
151 | 3 | { |
152 | 3 | ASSERT_THROW(m_externalBufferWriter.writeBits(1, numBits), CppRuntimeException); |
153 | 3 | } |
154 | | |
155 | | // check value out of range |
156 | 32 | for (int i = 1; 1 i < 32; ++i31 ) |
157 | 31 | { |
158 | 31 | const uint32_t maxUnsigned = static_cast<uint32_t>((UINT64_C(1) << i) - 1); |
159 | 31 | m_externalBufferWriter.writeBits(maxUnsigned, static_cast<uint8_t>(i)); |
160 | | |
161 | 31 | const uint32_t maxUnsignedViolation = maxUnsigned + 1; |
162 | 31 | ASSERT_THROW(m_externalBufferWriter.writeBits(maxUnsignedViolation, static_cast<uint8_t>(i)), |
163 | 31 | CppRuntimeException); |
164 | 31 | } |
165 | 1 | } |
166 | | |
167 | | TEST_F(BitStreamWriterTest, writeBits64) |
168 | 1 | { |
169 | | // check invalid bitlength acceptance |
170 | 1 | const std::array<uint8_t, 3> numBitsArray = {255, 0, 65}; |
171 | 1 | for (uint8_t numBits : numBitsArray) |
172 | 3 | { |
173 | 3 | ASSERT_THROW(m_externalBufferWriter.writeBits64(1, numBits), CppRuntimeException); |
174 | 3 | } |
175 | | |
176 | | // check value out of range |
177 | 64 | for (int i = 1; 1 i < 64; ++i63 ) |
178 | 63 | { |
179 | 63 | const uint64_t maxUnsigned = (UINT64_C(1) << i) - 1; |
180 | 63 | m_externalBufferWriter.writeBits64(maxUnsigned, static_cast<uint8_t>(i)); |
181 | | |
182 | 63 | const uint64_t maxUnsignedViolation = maxUnsigned + 1; |
183 | 63 | ASSERT_THROW(m_externalBufferWriter.writeBits64(maxUnsignedViolation, static_cast<uint8_t>(i)), |
184 | 63 | CppRuntimeException); |
185 | 63 | } |
186 | 1 | } |
187 | | |
188 | | TEST_F(BitStreamWriterTest, writeSignedBits) |
189 | 1 | { |
190 | | // check invalid bitlength acceptance |
191 | 1 | const std::array<uint8_t, 3> numBitsArray = {255, 0, 33}; |
192 | 1 | for (uint8_t numBits : numBitsArray) |
193 | 3 | { |
194 | 3 | ASSERT_THROW(m_externalBufferWriter.writeSignedBits(1, numBits), CppRuntimeException); |
195 | 3 | } |
196 | | |
197 | | // check value out of range |
198 | 32 | for (uint32_t i = 1; 1 i < 32; ++i31 ) |
199 | 31 | { |
200 | 31 | const int32_t minSigned = -static_cast<int32_t>(1U << (i - 1U)); |
201 | 31 | const int32_t maxSigned = static_cast<int32_t>((1U << (i - 1U)) - 1U); |
202 | 31 | m_externalBufferWriter.writeSignedBits(minSigned, static_cast<uint8_t>(i)); |
203 | 31 | m_externalBufferWriter.writeSignedBits(maxSigned, static_cast<uint8_t>(i)); |
204 | | |
205 | 31 | const int32_t minSignedViolation = minSigned - 1; |
206 | 31 | const int32_t maxSignedViolation = maxSigned + 1; |
207 | 31 | ASSERT_THROW(m_externalBufferWriter.writeSignedBits(minSignedViolation, static_cast<uint8_t>(i)), |
208 | 31 | CppRuntimeException); |
209 | 31 | ASSERT_THROW(m_externalBufferWriter.writeSignedBits(maxSignedViolation, static_cast<uint8_t>(i)), |
210 | 31 | CppRuntimeException); |
211 | 31 | } |
212 | 1 | } |
213 | | |
214 | | TEST_F(BitStreamWriterTest, writeSignedBits64) |
215 | 1 | { |
216 | | // check invalid bitlength acceptance |
217 | 1 | const std::array<uint8_t, 3> numBitsArray = {255, 0, 65}; |
218 | 1 | for (uint8_t numBits : numBitsArray) |
219 | 3 | { |
220 | 3 | ASSERT_THROW(m_externalBufferWriter.writeSignedBits64(1, numBits), CppRuntimeException); |
221 | 3 | } |
222 | | |
223 | | // check value out of range |
224 | 64 | for (int i = 1; 1 i < 64; ++i63 ) |
225 | 63 | { |
226 | 63 | const int64_t minSigned = -(INT64_C(1) << (i - 1)); |
227 | 63 | const int64_t maxSigned = (INT64_C(1) << (i - 1)) - 1; |
228 | 63 | m_externalBufferWriter.writeSignedBits64(minSigned, static_cast<uint8_t>(i)); |
229 | 63 | m_externalBufferWriter.writeSignedBits64(maxSigned, static_cast<uint8_t>(i)); |
230 | | |
231 | 63 | const int64_t minSignedViolation = minSigned - 1; |
232 | 63 | const int64_t maxSignedViolation = maxSigned + 1; |
233 | 63 | ASSERT_THROW(m_externalBufferWriter.writeSignedBits64(minSignedViolation, static_cast<uint8_t>(i)), |
234 | 63 | CppRuntimeException); |
235 | 63 | ASSERT_THROW(m_externalBufferWriter.writeSignedBits64(maxSignedViolation, static_cast<uint8_t>(i)), |
236 | 63 | CppRuntimeException); |
237 | 63 | } |
238 | 1 | } |
239 | | |
240 | | TEST_F(BitStreamWriterTest, writeVarInt64) |
241 | 1 | { |
242 | | // check value out of range |
243 | 1 | const int64_t outOfRangeValue = |
244 | 1 | static_cast<int64_t>(UINT64_C(1) << (6U + 7U + 7U + 7U + 7U + 7U + 7U + 8U)); |
245 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarInt64(outOfRangeValue), CppRuntimeException); |
246 | 1 | } |
247 | | |
248 | | TEST_F(BitStreamWriterTest, writeVarInt32) |
249 | 1 | { |
250 | | // check value out of range |
251 | 1 | const int32_t outOfRangeValue = static_cast<int32_t>(1U << (6U + 7U + 7U + 8U)); |
252 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarInt32(outOfRangeValue), CppRuntimeException); |
253 | 1 | } |
254 | | |
255 | | TEST_F(BitStreamWriterTest, writeVarInt16) |
256 | 1 | { |
257 | | // check value out of range |
258 | 1 | const int16_t outOfRangeValue = static_cast<int16_t>(1U << (6U + 8U)); |
259 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarInt16(outOfRangeValue), CppRuntimeException); |
260 | 1 | } |
261 | | |
262 | | TEST_F(BitStreamWriterTest, writeVarUInt64) |
263 | 1 | { |
264 | | // check value out of range |
265 | 1 | const uint64_t outOfRangeValue = UINT64_C(1) << (7U + 7U + 7U + 7U + 7U + 7U + 7U + 8U); |
266 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarUInt64(outOfRangeValue), CppRuntimeException); |
267 | 1 | } |
268 | | |
269 | | TEST_F(BitStreamWriterTest, writeVarUInt32) |
270 | 1 | { |
271 | | // check value out of range |
272 | 1 | const uint32_t outOfRangeValue = UINT32_C(1) << (7U + 7U + 7U + 8U); |
273 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarUInt32(outOfRangeValue), CppRuntimeException); |
274 | 1 | } |
275 | | |
276 | | TEST_F(BitStreamWriterTest, writeVarUInt16) |
277 | 1 | { |
278 | | // check value out of range |
279 | 1 | const uint16_t outOfRangeValue = static_cast<uint16_t>(1U << (7U + 8U)); |
280 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarUInt16(outOfRangeValue), CppRuntimeException); |
281 | 1 | } |
282 | | |
283 | | TEST_F(BitStreamWriterTest, writeVarInt) |
284 | 1 | { |
285 | 1 | ASSERT_NO_THROW(m_externalBufferWriter.writeVarInt(INT64_MIN)); |
286 | 1 | ASSERT_NO_THROW(m_externalBufferWriter.writeVarInt(INT64_MAX)); |
287 | 1 | } |
288 | | |
289 | | TEST_F(BitStreamWriterTest, writeVarUInt) |
290 | 1 | { |
291 | 1 | ASSERT_NO_THROW(m_externalBufferWriter.writeVarUInt(0)); |
292 | 1 | ASSERT_NO_THROW(m_externalBufferWriter.writeVarUInt(UINT64_MAX)); |
293 | 1 | } |
294 | | |
295 | | TEST_F(BitStreamWriterTest, writeVarSize) |
296 | 1 | { |
297 | | // check value out of range |
298 | 1 | const uint32_t outOfRangeValue = UINT32_C(1) << (2U + 7U + 7U + 7U + 8U); |
299 | 1 | ASSERT_THROW(m_externalBufferWriter.writeVarSize(outOfRangeValue), CppRuntimeException); |
300 | 1 | } |
301 | | |
302 | | TEST_F(BitStreamWriterTest, writeBitBuffer) |
303 | 1 | { |
304 | 1 | static const size_t bitBufferBitSize = 24; |
305 | 1 | BitBuffer bitBuffer(std::vector<uint8_t>{0xAB, 0xAB, 0xAB}, bitBufferBitSize); |
306 | | |
307 | 1 | { |
308 | 1 | ASSERT_NO_THROW(m_externalBufferWriter.writeBitBuffer(bitBuffer)); |
309 | 1 | Span<const uint8_t> buffer = m_externalBufferWriter.getBuffer(); |
310 | 1 | BitBuffer readBitBuffer{&buffer[1], bitBufferBitSize}; // first byte is bit buffer size |
311 | 1 | ASSERT_EQ(bitBuffer, readBitBuffer); |
312 | 1 | } |
313 | | |
314 | 1 | ASSERT_NO_THROW(m_dummyBufferWriter.writeBitBuffer(bitBuffer)); |
315 | 1 | ASSERT_EQ(bitBufferBitSize + 8, m_dummyBufferWriter.getBitPosition()); // first byte is bit buffer size |
316 | 1 | } |
317 | | |
318 | | TEST_F(BitStreamWriterTest, hasWriteBuffer) |
319 | 1 | { |
320 | 1 | ASSERT_TRUE(m_externalBufferWriter.hasWriteBuffer()); |
321 | 1 | ASSERT_FALSE(m_dummyBufferWriter.hasWriteBuffer()); |
322 | 1 | } |
323 | | |
324 | | TEST_F(BitStreamWriterTest, getWriteBuffer) |
325 | 1 | { |
326 | 1 | ASSERT_EQ(m_externalBuffer.data(), m_externalBufferWriter.getWriteBuffer()); |
327 | | |
328 | 1 | ASSERT_EQ(nullptr, m_dummyBufferWriter.getWriteBuffer()); |
329 | 1 | } |
330 | | |
331 | | TEST_F(BitStreamWriterTest, getBuffer) |
332 | 1 | { |
333 | 1 | ASSERT_EQ(m_externalBuffer.data(), m_externalBufferWriter.getBuffer().data()); |
334 | | |
335 | 1 | ASSERT_EQ(nullptr, m_dummyBufferWriter.getBuffer().data()); |
336 | 1 | } |
337 | | |
338 | | TEST_F(BitStreamWriterTest, dummyBufferTest) |
339 | 1 | { |
340 | 1 | m_dummyBufferWriter.writeBits(1, 1); |
341 | 1 | m_dummyBufferWriter.alignTo(4); |
342 | 1 | m_dummyBufferWriter.writeBits(1, 1); |
343 | 1 | m_dummyBufferWriter.alignTo(4); |
344 | 1 | m_dummyBufferWriter.writeBits(37, 11); |
345 | 1 | m_dummyBufferWriter.alignTo(8); |
346 | 1 | m_dummyBufferWriter.writeBits(1, 1); |
347 | 1 | ASSERT_EQ(25, m_dummyBufferWriter.getBitPosition()); |
348 | 1 | } |
349 | | |
350 | | } // namespace zserio |