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