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