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 
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  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 
115 BOOST_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 
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  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 
216 BOOST_AUTO_TEST_CASE(streams_buffered_file)
217 {
218  FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
219  // The value at each offset is the offset.
220  for (uint8_t j = 0; j < 40; ++j) {
221  fwrite(&j, 1, 1, file);
222  }
223  rewind(file);
224 
225  // The buffer size (second arg) must be greater than the rewind
226  // amount (third arg).
227  try {
228  CBufferedFile bfbad(file, 25, 25, 222, 333);
229  BOOST_CHECK(false);
230  } catch (const std::exception& e) {
231  BOOST_CHECK(strstr(e.what(),
232  "Rewind limit must be less than buffer size") != nullptr);
233  }
234 
235  // The buffer is 25 bytes, allow rewinding 10 bytes.
236  CBufferedFile bf(file, 25, 10, 222, 333);
237  BOOST_CHECK(!bf.eof());
238 
239  // These two members have no functional effect.
240  BOOST_CHECK_EQUAL(bf.GetType(), 222);
241  BOOST_CHECK_EQUAL(bf.GetVersion(), 333);
242 
243  uint8_t i;
244  bf >> i;
245  BOOST_CHECK_EQUAL(i, 0);
246  bf >> i;
247  BOOST_CHECK_EQUAL(i, 1);
248 
249  // After reading bytes 0 and 1, we're positioned at 2.
250  BOOST_CHECK_EQUAL(bf.GetPos(), 2U);
251 
252  // Rewind to offset 0, ok (within the 10 byte window).
253  BOOST_CHECK(bf.SetPos(0));
254  bf >> i;
255  BOOST_CHECK_EQUAL(i, 0);
256 
257  // We can go forward to where we've been, but beyond may fail.
258  BOOST_CHECK(bf.SetPos(2));
259  bf >> i;
260  BOOST_CHECK_EQUAL(i, 2);
261 
262  // If you know the maximum number of bytes that should be
263  // read to deserialize the variable, you can limit the read
264  // extent. The current file offset is 3, so the following
265  // SetLimit() allows zero bytes to be read.
266  BOOST_CHECK(bf.SetLimit(3));
267  try {
268  bf >> i;
269  BOOST_CHECK(false);
270  } catch (const std::exception& e) {
271  BOOST_CHECK(strstr(e.what(),
272  "Read attempted past buffer limit") != nullptr);
273  }
274  // The default argument removes the limit completely.
275  BOOST_CHECK(bf.SetLimit());
276  // The read position should still be at 3 (no change).
277  BOOST_CHECK_EQUAL(bf.GetPos(), 3U);
278 
279  // Read from current offset, 3, forward until position 10.
280  for (uint8_t j = 3; j < 10; ++j) {
281  bf >> i;
282  BOOST_CHECK_EQUAL(i, j);
283  }
284  BOOST_CHECK_EQUAL(bf.GetPos(), 10U);
285 
286  // We're guaranteed (just barely) to be able to rewind to zero.
287  BOOST_CHECK(bf.SetPos(0));
288  BOOST_CHECK_EQUAL(bf.GetPos(), 0U);
289  bf >> i;
290  BOOST_CHECK_EQUAL(i, 0);
291 
292  // We can set the position forward again up to the farthest
293  // into the stream we've been, but no farther. (Attempting
294  // to go farther may succeed, but it's not guaranteed.)
295  BOOST_CHECK(bf.SetPos(10));
296  bf >> i;
297  BOOST_CHECK_EQUAL(i, 10);
298  BOOST_CHECK_EQUAL(bf.GetPos(), 11U);
299 
300  // Now it's only guaranteed that we can rewind to offset 1
301  // (current read position, 11, minus rewind amount, 10).
302  BOOST_CHECK(bf.SetPos(1));
303  BOOST_CHECK_EQUAL(bf.GetPos(), 1U);
304  bf >> i;
305  BOOST_CHECK_EQUAL(i, 1);
306 
307  // We can stream into large variables, even larger than
308  // the buffer size.
309  BOOST_CHECK(bf.SetPos(11));
310  {
311  uint8_t a[40 - 11];
312  bf >> a;
313  for (uint8_t j = 0; j < sizeof(a); ++j) {
314  BOOST_CHECK_EQUAL(a[j], 11 + j);
315  }
316  }
317  BOOST_CHECK_EQUAL(bf.GetPos(), 40U);
318 
319  // We've read the entire file, the next read should throw.
320  try {
321  bf >> i;
322  BOOST_CHECK(false);
323  } catch (const std::exception& e) {
324  BOOST_CHECK(strstr(e.what(),
325  "CBufferedFile::Fill: end of file") != nullptr);
326  }
327  // Attempting to read beyond the end sets the EOF indicator.
328  BOOST_CHECK(bf.eof());
329 
330  // Still at offset 40, we can go back 10, to 30.
331  BOOST_CHECK_EQUAL(bf.GetPos(), 40U);
332  BOOST_CHECK(bf.SetPos(30));
333  bf >> i;
334  BOOST_CHECK_EQUAL(i, 30);
335  BOOST_CHECK_EQUAL(bf.GetPos(), 31U);
336 
337  // We're too far to rewind to position zero.
338  BOOST_CHECK(!bf.SetPos(0));
339  // But we should now be positioned at least as far back as allowed
340  // by the rewind window (relative to our farthest read position, 40).
341  BOOST_CHECK(bf.GetPos() <= 30);
342 
343  // We can explicitly close the file, or the destructor will do it.
344  bf.fclose();
345 
346  fs::remove("streams_test_tmp");
347 }
348 
349 BOOST_AUTO_TEST_CASE(streams_buffered_file_rand)
350 {
351  // Make this test deterministic.
353 
354  for (int rep = 0; rep < 50; ++rep) {
355  FILE* file = fsbridge::fopen("streams_test_tmp", "w+b");
356  size_t fileSize = InsecureRandRange(256);
357  for (uint8_t i = 0; i < fileSize; ++i) {
358  fwrite(&i, 1, 1, file);
359  }
360  rewind(file);
361 
362  size_t bufSize = InsecureRandRange(300) + 1;
363  size_t rewindSize = InsecureRandRange(bufSize);
364  CBufferedFile bf(file, bufSize, rewindSize, 222, 333);
365  size_t currentPos = 0;
366  size_t maxPos = 0;
367  for (int step = 0; step < 100; ++step) {
368  if (currentPos >= fileSize)
369  break;
370 
371  // We haven't read to the end of the file yet.
372  BOOST_CHECK(!bf.eof());
373  BOOST_CHECK_EQUAL(bf.GetPos(), currentPos);
374 
375  // Pretend the file consists of a series of objects of varying
376  // sizes; the boundaries of the objects can interact arbitrarily
377  // with the CBufferFile's internal buffer. These first three
378  // cases simulate objects of various sizes (1, 2, 5 bytes).
379  switch (InsecureRandRange(5)) {
380  case 0: {
381  uint8_t a[1];
382  if (currentPos + 1 > fileSize)
383  continue;
384  bf.SetLimit(currentPos + 1);
385  bf >> a;
386  for (uint8_t i = 0; i < 1; ++i) {
387  BOOST_CHECK_EQUAL(a[i], currentPos);
388  currentPos++;
389  }
390  break;
391  }
392  case 1: {
393  uint8_t a[2];
394  if (currentPos + 2 > fileSize)
395  continue;
396  bf.SetLimit(currentPos + 2);
397  bf >> a;
398  for (uint8_t i = 0; i < 2; ++i) {
399  BOOST_CHECK_EQUAL(a[i], currentPos);
400  currentPos++;
401  }
402  break;
403  }
404  case 2: {
405  uint8_t a[5];
406  if (currentPos + 5 > fileSize)
407  continue;
408  bf.SetLimit(currentPos + 5);
409  bf >> a;
410  for (uint8_t i = 0; i < 5; ++i) {
411  BOOST_CHECK_EQUAL(a[i], currentPos);
412  currentPos++;
413  }
414  break;
415  }
416  case 3: {
417  // Find a byte value (that is at or ahead of the current position).
418  size_t find = currentPos + InsecureRandRange(8);
419  if (find >= fileSize)
420  find = fileSize - 1;
421  bf.FindByte(static_cast<char>(find));
422  // The value at each offset is the offset.
423  BOOST_CHECK_EQUAL(bf.GetPos(), find);
424  currentPos = find;
425 
426  bf.SetLimit(currentPos + 1);
427  uint8_t i;
428  bf >> i;
429  BOOST_CHECK_EQUAL(i, currentPos);
430  currentPos++;
431  break;
432  }
433  case 4: {
434  size_t requestPos = InsecureRandRange(maxPos + 4);
435  bool okay = bf.SetPos(requestPos);
436  // The new position may differ from the requested position
437  // because we may not be able to rewind beyond the rewind
438  // window, and we may not be able to move forward beyond the
439  // farthest position we've reached so far.
440  currentPos = bf.GetPos();
441  BOOST_CHECK_EQUAL(okay, currentPos == requestPos);
442  // Check that we can position within the rewind window.
443  if (requestPos <= maxPos &&
444  maxPos > rewindSize &&
445  requestPos >= maxPos - rewindSize) {
446  // We requested a position within the rewind window.
447  BOOST_CHECK(okay);
448  }
449  break;
450  }
451  }
452  if (maxPos < currentPos)
453  maxPos = currentPos;
454  }
455  }
456  fs::remove("streams_test_tmp");
457 }
458 
CDataStream::insert
iterator insert(iterator it, const uint8_t x)
Definition: streams.h:262
BitStreamReader
Definition: streams.h:457
SeedInsecureRand
static void SeedInsecureRand(SeedRand seed=SeedRand::SEED)
Definition: setup_common.h:56
BitStreamWriter::Flush
void Flush()
Flush any unwritten bits to the output stream, padding with 0's to the next byte boundary.
Definition: streams.h:545
CVectorWriter
Definition: streams.h:72
fsbridge::fopen
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:24
streams.h
CDataStream::begin
const_iterator begin() const
Definition: streams.h:251
CBufferedFile::SetPos
bool SetPos(uint64_t nPos)
rewind to a given reading position
Definition: streams.h:764
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:437
InsecureRandRange
static uint64_t InsecureRandRange(uint64_t range)
Definition: setup_common.h:68
CBufferedFile::fclose
void fclose()
Definition: streams.h:725
CBufferedFile::FindByte
void FindByte(char ch)
search for a given byte in the stream, and remain positioned on it
Definition: streams.h:797
BOOST_FIXTURE_TEST_SUITE
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
VectorReader
Minimal stream for reading from an existing vector by reference.
Definition: streams.h:133
BitStreamWriter
Definition: streams.h:500
BOOST_AUTO_TEST_SUITE_END
BOOST_AUTO_TEST_SUITE_END()
VectorReader::empty
bool empty() const
Definition: streams.h:181
BitStreamReader::Read
uint64_t Read(int nbits)
Read the specified number of bits from the stream.
Definition: streams.h:477
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:673
CBufferedFile::GetVersion
int GetVersion() const
Definition: streams.h:722
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:525
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:734
CDataStream::end
const_iterator end() const
Definition: streams.h:253
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:782
BasicTestingSetup
Basic testing setup.
Definition: setup_common.h:76
BOOST_AUTO_TEST_CASE
BOOST_AUTO_TEST_CASE(streams_vector_writer)
Definition: streams_tests.cpp:12
VectorReader::size
size_t size() const
Definition: streams.h:180
CDataStream::clear
void clear()
Definition: streams.h:261
CBufferedFile::GetType
int GetType() const
Definition: streams.h:723
CDataStream
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:204
SER_NETWORK
@ SER_NETWORK
Definition: serialize.h:138
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:759
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