Bitcoin Core 22.99.0
P2P Digital Currency
streams_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2012-2020 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <streams.h>
7
8#include <boost/test/unit_test.hpp>
9
11
12BOOST_AUTO_TEST_CASE(streams_vector_writer)
13{
14 unsigned char a(1);
15 unsigned char b(2);
16 unsigned char bytes[] = { 3, 4, 5, 6 };
17 std::vector<unsigned char> vch;
18
19 // Each test runs twice. Serializing a second time at the same starting
20 // point should yield the same results, even if the first test grew the
21 // vector.
22
24 BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
26 BOOST_CHECK((vch == std::vector<unsigned char>{{1, 2}}));
27 vch.clear();
28
30 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
32 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2}}));
33 vch.clear();
34
35 vch.resize(5, 0);
37 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
39 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 1, 2, 0}}));
40 vch.clear();
41
42 vch.resize(4, 0);
44 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
46 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 1, 2}}));
47 vch.clear();
48
49 vch.resize(4, 0);
51 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
53 BOOST_CHECK((vch == std::vector<unsigned char>{{0, 0, 0, 0, 1, 2}}));
54 vch.clear();
55
57 BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
59 BOOST_CHECK((vch == std::vector<unsigned char>{{3, 4, 5, 6}}));
60 vch.clear();
61
62 vch.resize(4, 8);
63 CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
64 BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
65 CVectorWriter(SER_NETWORK, INIT_PROTO_VERSION, vch, 2, a, bytes, b);
66 BOOST_CHECK((vch == std::vector<unsigned char>{{8, 8, 1, 3, 4, 5, 6, 2}}));
67 vch.clear();
68}
69
70BOOST_AUTO_TEST_CASE(streams_vector_reader)
71{
72 std::vector<unsigned char> vch = {1, 255, 3, 4, 5, 6};
73
75 BOOST_CHECK_EQUAL(reader.size(), 6U);
76 BOOST_CHECK(!reader.empty());
77
78 // Read a single byte as an unsigned char.
79 unsigned char a;
80 reader >> a;
82 BOOST_CHECK_EQUAL(reader.size(), 5U);
83 BOOST_CHECK(!reader.empty());
84
85 // Read a single byte as a signed char.
86 signed char b;
87 reader >> b;
88 BOOST_CHECK_EQUAL(b, -1);
89 BOOST_CHECK_EQUAL(reader.size(), 4U);
90 BOOST_CHECK(!reader.empty());
91
92 // Read a 4 bytes as an unsigned int.
93 unsigned int c;
94 reader >> c;
95 BOOST_CHECK_EQUAL(c, 100992003U); // 3,4,5,6 in little-endian base-256
96 BOOST_CHECK_EQUAL(reader.size(), 0U);
97 BOOST_CHECK(reader.empty());
98
99 // Reading after end of byte vector throws an error.
100 signed int d;
101 BOOST_CHECK_THROW(reader >> d, std::ios_base::failure);
102
103 // Read a 4 bytes as a signed int from the beginning of the buffer.
104 VectorReader new_reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0);
105 new_reader >> d;
106 BOOST_CHECK_EQUAL(d, 67370753); // 1,255,3,4 in little-endian base-256
107 BOOST_CHECK_EQUAL(new_reader.size(), 2U);
108 BOOST_CHECK(!new_reader.empty());
109
110 // Reading after end of byte vector throws an error even if the reader is
111 // not totally empty.
112 BOOST_CHECK_THROW(new_reader >> d, std::ios_base::failure);
113}
114
115BOOST_AUTO_TEST_CASE(streams_vector_reader_rvalue)
116{
117 std::vector<uint8_t> data{0x82, 0xa7, 0x31};
118 VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, data, /* pos= */ 0);
119 uint32_t varint = 0;
120 // Deserialize into r-value
121 reader >> VARINT(varint);
122 BOOST_CHECK_EQUAL(varint, 54321U);
123 BOOST_CHECK(reader.empty());
124}
125
126BOOST_AUTO_TEST_CASE(bitstream_reader_writer)
127{
129
130 BitStreamWriter<CDataStream> bit_writer(data);
131 bit_writer.Write(0, 1);
132 bit_writer.Write(2, 2);
133 bit_writer.Write(6, 3);
134 bit_writer.Write(11, 4);
135 bit_writer.Write(1, 5);
136 bit_writer.Write(32, 6);
137 bit_writer.Write(7, 7);
138 bit_writer.Write(30497, 16);
139 bit_writer.Flush();
140
141 CDataStream data_copy(data);
142 uint32_t serialized_int1;
143 data >> serialized_int1;
144 BOOST_CHECK_EQUAL(serialized_int1, (uint32_t)0x7700C35A); // NOTE: Serialized as LE
145 uint16_t serialized_int2;
146 data >> serialized_int2;
147 BOOST_CHECK_EQUAL(serialized_int2, (uint16_t)0x1072); // NOTE: Serialized as LE
148
149 BitStreamReader<CDataStream> bit_reader(data_copy);
150 BOOST_CHECK_EQUAL(bit_reader.Read(1), 0U);
151 BOOST_CHECK_EQUAL(bit_reader.Read(2), 2U);
152 BOOST_CHECK_EQUAL(bit_reader.Read(3), 6U);
153 BOOST_CHECK_EQUAL(bit_reader.Read(4), 11U);
154 BOOST_CHECK_EQUAL(bit_reader.Read(5), 1U);
155 BOOST_CHECK_EQUAL(bit_reader.Read(6), 32U);
156 BOOST_CHECK_EQUAL(bit_reader.Read(7), 7U);
157 BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497U);
158 BOOST_CHECK_THROW(bit_reader.Read(8), std::ios_base::failure);
159}
160
161BOOST_AUTO_TEST_CASE(streams_serializedata_xor)
162{
163 std::vector<uint8_t> in;
164 std::vector<char> expected_xor;
165 std::vector<unsigned char> key;
166 CDataStream ds(in, 0, 0);
167
168 // Degenerate case
169
170 key.push_back('\x00');
171 key.push_back('\x00');
172 ds.Xor(key);
174 std::string(expected_xor.begin(), expected_xor.end()),
175 std::string(ds.begin(), ds.end()));
176
177 in.push_back('\x0f');
178 in.push_back('\xf0');
179 expected_xor.push_back('\xf0');
180 expected_xor.push_back('\x0f');
181
182 // Single character key
183
184 ds.clear();
185 ds.insert(ds.begin(), in.begin(), in.end());
186 key.clear();
187
188 key.push_back('\xff');
189 ds.Xor(key);
191 std::string(expected_xor.begin(), expected_xor.end()),
192 std::string(ds.begin(), ds.end()));
193
194 // Multi character key
195
196 in.clear();
197 expected_xor.clear();
198 in.push_back('\xf0');
199 in.push_back('\x0f');
200 expected_xor.push_back('\x0f');
201 expected_xor.push_back('\x00');
202
203 ds.clear();
204 ds.insert(ds.begin(), in.begin(), in.end());
205
206 key.clear();
207 key.push_back('\xff');
208 key.push_back('\x0f');
209
210 ds.Xor(key);
212 std::string(expected_xor.begin(), expected_xor.end()),
213 std::string(ds.begin(), ds.end()));
214}
215
216BOOST_AUTO_TEST_CASE(streams_buffered_file)
217{
218 fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
219 FILE* file = fsbridge::fopen(streams_test_filename, "w+b");
220
221 // The value at each offset is the offset.
222 for (uint8_t j = 0; j < 40; ++j) {
223 fwrite(&j, 1, 1, file);
224 }
225 rewind(file);
226
227 // The buffer size (second arg) must be greater than the rewind
228 // amount (third arg).
229 try {
230 CBufferedFile bfbad(file, 25, 25, 222, 333);
231 BOOST_CHECK(false);
232 } catch (const std::exception& e) {
233 BOOST_CHECK(strstr(e.what(),
234 "Rewind limit must be less than buffer size") != nullptr);
235 }
236
237 // The buffer is 25 bytes, allow rewinding 10 bytes.
238 CBufferedFile bf(file, 25, 10, 222, 333);
239 BOOST_CHECK(!bf.eof());
240
241 // These two members have no functional effect.
242 BOOST_CHECK_EQUAL(bf.GetType(), 222);
243 BOOST_CHECK_EQUAL(bf.GetVersion(), 333);
244
245 uint8_t i;
246 bf >> i;
247 BOOST_CHECK_EQUAL(i, 0);
248 bf >> i;
249 BOOST_CHECK_EQUAL(i, 1);
250
251 // After reading bytes 0 and 1, we're positioned at 2.
252 BOOST_CHECK_EQUAL(bf.GetPos(), 2U);
253
254 // Rewind to offset 0, ok (within the 10 byte window).
255 BOOST_CHECK(bf.SetPos(0));
256 bf >> i;
257 BOOST_CHECK_EQUAL(i, 0);
258
259 // We can go forward to where we've been, but beyond may fail.
260 BOOST_CHECK(bf.SetPos(2));
261 bf >> i;
262 BOOST_CHECK_EQUAL(i, 2);
263
264 // If you know the maximum number of bytes that should be
265 // read to deserialize the variable, you can limit the read
266 // extent. The current file offset is 3, so the following
267 // SetLimit() allows zero bytes to be read.
268 BOOST_CHECK(bf.SetLimit(3));
269 try {
270 bf >> i;
271 BOOST_CHECK(false);
272 } catch (const std::exception& e) {
273 BOOST_CHECK(strstr(e.what(),
274 "Read attempted past buffer limit") != nullptr);
275 }
276 // The default argument removes the limit completely.
277 BOOST_CHECK(bf.SetLimit());
278 // The read position should still be at 3 (no change).
279 BOOST_CHECK_EQUAL(bf.GetPos(), 3U);
280
281 // Read from current offset, 3, forward until position 10.
282 for (uint8_t j = 3; j < 10; ++j) {
283 bf >> i;
284 BOOST_CHECK_EQUAL(i, j);
285 }
286 BOOST_CHECK_EQUAL(bf.GetPos(), 10U);
287
288 // We're guaranteed (just barely) to be able to rewind to zero.
289 BOOST_CHECK(bf.SetPos(0));
290 BOOST_CHECK_EQUAL(bf.GetPos(), 0U);
291 bf >> i;
292 BOOST_CHECK_EQUAL(i, 0);
293
294 // We can set the position forward again up to the farthest
295 // into the stream we've been, but no farther. (Attempting
296 // to go farther may succeed, but it's not guaranteed.)
297 BOOST_CHECK(bf.SetPos(10));
298 bf >> i;
299 BOOST_CHECK_EQUAL(i, 10);
300 BOOST_CHECK_EQUAL(bf.GetPos(), 11U);
301
302 // Now it's only guaranteed that we can rewind to offset 1
303 // (current read position, 11, minus rewind amount, 10).
304 BOOST_CHECK(bf.SetPos(1));
305 BOOST_CHECK_EQUAL(bf.GetPos(), 1U);
306 bf >> i;
307 BOOST_CHECK_EQUAL(i, 1);
308
309 // We can stream into large variables, even larger than
310 // the buffer size.
311 BOOST_CHECK(bf.SetPos(11));
312 {
313 uint8_t a[40 - 11];
314 bf >> a;
315 for (uint8_t j = 0; j < sizeof(a); ++j) {
316 BOOST_CHECK_EQUAL(a[j], 11 + j);
317 }
318 }
319 BOOST_CHECK_EQUAL(bf.GetPos(), 40U);
320
321 // We've read the entire file, the next read should throw.
322 try {
323 bf >> i;
324 BOOST_CHECK(false);
325 } catch (const std::exception& e) {
326 BOOST_CHECK(strstr(e.what(),
327 "CBufferedFile::Fill: end of file") != nullptr);
328 }
329 // Attempting to read beyond the end sets the EOF indicator.
330 BOOST_CHECK(bf.eof());
331
332 // Still at offset 40, we can go back 10, to 30.
333 BOOST_CHECK_EQUAL(bf.GetPos(), 40U);
334 BOOST_CHECK(bf.SetPos(30));
335 bf >> i;
336 BOOST_CHECK_EQUAL(i, 30);
337 BOOST_CHECK_EQUAL(bf.GetPos(), 31U);
338
339 // We're too far to rewind to position zero.
340 BOOST_CHECK(!bf.SetPos(0));
341 // But we should now be positioned at least as far back as allowed
342 // by the rewind window (relative to our farthest read position, 40).
343 BOOST_CHECK(bf.GetPos() <= 30);
344
345 // We can explicitly close the file, or the destructor will do it.
346 bf.fclose();
347
348 fs::remove(streams_test_filename);
349}
350
351BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
352{
353 // Make this test deterministic.
355
356 fs::path streams_test_filename = m_args.GetDataDirBase() / "streams_test_tmp";
357 for (int rep = 0; rep < 50; ++rep) {
358 FILE* file = fsbridge::fopen(streams_test_filename, "w+b");
359 size_t fileSize = InsecureRandRange(256);
360 for (uint8_t i = 0; i < fileSize; ++i) {
361 fwrite(&i, 1, 1, file);
362 }
363 rewind(file);
364
365 size_t bufSize = InsecureRandRange(300) + 1;
366 size_t rewindSize = InsecureRandRange(bufSize);
367 CBufferedFile bf(file, bufSize, rewindSize, 222, 333);
368 size_t currentPos = 0;
369 size_t maxPos = 0;
370 for (int step = 0; step < 100; ++step) {
371 if (currentPos >= fileSize)
372 break;
373
374 // We haven't read to the end of the file yet.
375 BOOST_CHECK(!bf.eof());
376 BOOST_CHECK_EQUAL(bf.GetPos(), currentPos);
377
378 // Pretend the file consists of a series of objects of varying
379 // sizes; the boundaries of the objects can interact arbitrarily
380 // with the CBufferFile's internal buffer. These first three
381 // cases simulate objects of various sizes (1, 2, 5 bytes).
382 switch (InsecureRandRange(5)) {
383 case 0: {
384 uint8_t a[1];
385 if (currentPos + 1 > fileSize)
386 continue;
387 bf.SetLimit(currentPos + 1);
388 bf >> a;
389 for (uint8_t i = 0; i < 1; ++i) {
390 BOOST_CHECK_EQUAL(a[i], currentPos);
391 currentPos++;
392 }
393 break;
394 }
395 case 1: {
396 uint8_t a[2];
397 if (currentPos + 2 > fileSize)
398 continue;
399 bf.SetLimit(currentPos + 2);
400 bf >> a;
401 for (uint8_t i = 0; i < 2; ++i) {
402 BOOST_CHECK_EQUAL(a[i], currentPos);
403 currentPos++;
404 }
405 break;
406 }
407 case 2: {
408 uint8_t a[5];
409 if (currentPos + 5 > fileSize)
410 continue;
411 bf.SetLimit(currentPos + 5);
412 bf >> a;
413 for (uint8_t i = 0; i < 5; ++i) {
414 BOOST_CHECK_EQUAL(a[i], currentPos);
415 currentPos++;
416 }
417 break;
418 }
419 case 3: {
420 // Find a byte value (that is at or ahead of the current position).
421 size_t find = currentPos + InsecureRandRange(8);
422 if (find >= fileSize)
423 find = fileSize - 1;
424 bf.FindByte(static_cast<char>(find));
425 // The value at each offset is the offset.
426 BOOST_CHECK_EQUAL(bf.GetPos(), find);
427 currentPos = find;
428
429 bf.SetLimit(currentPos + 1);
430 uint8_t i;
431 bf >> i;
432 BOOST_CHECK_EQUAL(i, currentPos);
433 currentPos++;
434 break;
435 }
436 case 4: {
437 size_t requestPos = InsecureRandRange(maxPos + 4);
438 bool okay = bf.SetPos(requestPos);
439 // The new position may differ from the requested position
440 // because we may not be able to rewind beyond the rewind
441 // window, and we may not be able to move forward beyond the
442 // farthest position we've reached so far.
443 currentPos = bf.GetPos();
444 BOOST_CHECK_EQUAL(okay, currentPos == requestPos);
445 // Check that we can position within the rewind window.
446 if (requestPos <= maxPos &&
447 maxPos > rewindSize &&
448 requestPos >= maxPos - rewindSize) {
449 // We requested a position within the rewind window.
450 BOOST_CHECK(okay);
451 }
452 break;
453 }
454 }
455 if (maxPos < currentPos)
456 maxPos = currentPos;
457 }
458 }
459 fs::remove(streams_test_filename);
460}
461
uint64_t Read(int nbits)
Read the specified number of bits from the stream.
Definition: streams.h:477
void Write(uint64_t data, int nbits)
Write the nbits least significant bits of a 64-bit int to the output stream.
Definition: streams.h:525
void Flush()
Flush any unwritten bits to the output stream, padding with 0's to the next byte boundary.
Definition: streams.h:545
Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to deserialize from.
Definition: streams.h:674
bool SetLimit(uint64_t nPos=std::numeric_limits< uint64_t >::max())
prevent reading beyond a certain position no argument removes the limit
Definition: streams.h:782
void FindByte(char ch)
search for a given byte in the stream, and remain positioned on it
Definition: streams.h:797
int GetVersion() const
Definition: streams.h:722
uint64_t GetPos() const
return the current reading position
Definition: streams.h:759
int GetType() const
Definition: streams.h:723
bool SetPos(uint64_t nPos)
rewind to a given reading position
Definition: streams.h:764
void fclose()
Definition: streams.h:725
bool eof() const
check whether we're at the end of the source file
Definition: streams.h:734
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:205
const_iterator begin() const
Definition: streams.h:251
iterator insert(iterator it, const uint8_t x)
Definition: streams.h:262
void Xor(const std::vector< unsigned char > &key)
XOR the contents of this stream with a certain key.
Definition: streams.h:437
const_iterator end() const
Definition: streams.h:253
void clear()
Definition: streams.h:261
Minimal stream for reading from an existing vector by reference.
Definition: streams.h:134
bool empty() const
Definition: streams.h:181
size_t size() const
Definition: streams.h:180
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:34
BOOST_AUTO_TEST_SUITE_END()
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
#define BOOST_CHECK_THROW(stmt, excMatch)
Definition: object.cpp:19
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
#define VARINT(obj)
Definition: serialize.h:443
@ SER_NETWORK
Definition: serialize.h:138
@ ZEROS
Seed with a compile time constant of zeros.
static uint64_t InsecureRandRange(uint64_t range)
Definition: setup_common.h:68
static void SeedInsecureRand(SeedRand seed=SeedRand::SEED)
Definition: setup_common.h:56
BOOST_AUTO_TEST_CASE(streams_vector_writer)
Basic testing setup.
Definition: setup_common.h:76
static const int INIT_PROTO_VERSION
initial proto version, to be increased after version/verack negotiation
Definition: version.h:15