Bitcoin Core  22.99.0
P2P Digital Currency
streams_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2012-2021 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 
12 BOOST_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 
70 BOOST_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;
81  BOOST_CHECK_EQUAL(a, 1);
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  SpanReader new_reader{SER_NETWORK, INIT_PROTO_VERSION, vch};
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 
115 BOOST_AUTO_TEST_CASE(streams_vector_reader_rvalue)
116 {
117  std::vector<uint8_t> data{0x82, 0xa7, 0x31};
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 
126 BOOST_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 
161 BOOST_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  ds.str());
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  ds.str());
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  ds.str());
214 }
215 
216 BOOST_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 
351 BOOST_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 
BitStreamReader
Definition: streams.h:437
SeedInsecureRand
static void SeedInsecureRand(SeedRand seed=SeedRand::SEED)
Definition: setup_common.h:63
BitStreamWriter::Flush
void Flush()
Flush any unwritten bits to the output stream, padding with 0's to the next byte boundary.
Definition: streams.h:525
CVectorWriter
Definition: streams.h:72
fsbridge::fopen
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:25
streams.h
CDataStream::begin
const_iterator begin() const
Definition: streams.h:231
CBufferedFile::SetPos
bool SetPos(uint64_t nPos)
rewind to a given reading position
Definition: streams.h:744
setup_common.h
CDataStream::Xor
void Xor(const std::vector< unsigned char > &key)
XOR the contents of this stream with a certain key.
Definition: streams.h:417
InsecureRandRange
static uint64_t InsecureRandRange(uint64_t range)
Definition: setup_common.h:75
CBufferedFile::fclose
void fclose()
Definition: streams.h:705
CBufferedFile::FindByte
void FindByte(char ch)
search for a given byte in the stream, and remain positioned on it
Definition: streams.h:777
BOOST_FIXTURE_TEST_SUITE
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
BitStreamWriter
Definition: streams.h:480
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
BitStreamReader::Read
uint64_t Read(int nbits)
Read the specified number of bits from the stream.
Definition: streams.h:457
SeedRand::ZEROS
@ ZEROS
Seed with a compile time constant of zeros.
CBufferedFile
Non-refcounted RAII wrapper around a FILE* that implements a ring buffer to deserialize from.
Definition: streams.h:653
CBufferedFile::GetVersion
int GetVersion() const
Definition: streams.h:702
BitStreamWriter::Write
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:505
fs::path
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:33
VARINT
#define VARINT(obj)
Definition: serialize.h:443
CBufferedFile::eof
bool eof() const
check whether we're at the end of the source file
Definition: streams.h:714
CBufferedFile::SetLimit
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:762
SpanReader
Minimal stream for reading from an existing byte array by Span.
Definition: streams.h:133
BasicTestingSetup
Basic testing setup.
Definition: setup_common.h:83
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(streams_vector_writer)
Definition: streams_tests.cpp:12
CDataStream::insert
iterator insert(iterator it, const value_type x)
Definition: streams.h:242
CDataStream::clear
void clear()
Definition: streams.h:241
CBufferedFile::GetType
int GetType() const
Definition: streams.h:703
CDataStream
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:184
SER_NETWORK
@ SER_NETWORK
Definition: serialize.h:138
BOOST_CHECK_THROW
#define BOOST_CHECK_THROW(stmt, excMatch)
Definition: object.cpp:19
CDataStream::str
std::string str() const
Definition: streams.h:222
CBufferedFile::GetPos
uint64_t GetPos() const
return the current reading position
Definition: streams.h:739
BOOST_CHECK
#define BOOST_CHECK(expr)
Definition: object.cpp:17
BOOST_CHECK_EQUAL
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
INIT_PROTO_VERSION
static const int INIT_PROTO_VERSION
initial proto version, to be increased after version/verack negotiation
Definition: version.h:15