Bitcoin Core  27.99.0
P2P Digital Currency
bitdeque.cpp
Go to the documentation of this file.
1 // Copyright (c) 2022 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 <random.h>
7 #include <test/fuzz/util.h>
8 #include <util/bitdeque.h>
9 
10 #include <deque>
11 #include <vector>
12 
13 namespace {
14 
15 constexpr int LEN_BITS = 16;
16 constexpr int RANDDATA_BITS = 20;
17 
18 using bitdeque_type = bitdeque<128>;
19 
21 std::vector<bool> RANDDATA;
22 
23 void InitRandData()
24 {
25  FastRandomContext ctx(true);
26  RANDDATA.clear();
27  for (size_t i = 0; i < (1U << RANDDATA_BITS) + (1U << LEN_BITS); ++i) {
28  RANDDATA.push_back(ctx.randbool());
29  }
30 }
31 
32 } // namespace
33 
34 FUZZ_TARGET(bitdeque, .init = InitRandData)
35 {
36  FuzzedDataProvider provider(buffer.data(), buffer.size());
37  FastRandomContext ctx(true);
38 
39  size_t maxlen = (1U << provider.ConsumeIntegralInRange<size_t>(0, LEN_BITS)) - 1;
40  size_t limitlen = 4 * maxlen;
41 
42  std::deque<bool> deq;
43  bitdeque_type bitdeq;
44 
45  const auto& cdeq = deq;
46  const auto& cbitdeq = bitdeq;
47 
48  size_t initlen = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
49  while (initlen) {
50  bool val = ctx.randbool();
51  deq.push_back(val);
52  bitdeq.push_back(val);
53  --initlen;
54  }
55 
56  const auto iter_limit{maxlen > 6000 ? 90U : 900U};
57  LIMITED_WHILE(provider.remaining_bytes() > 0, iter_limit)
58  {
59  CallOneOf(
60  provider,
61  [&] {
62  // constructor()
63  deq = std::deque<bool>{};
64  bitdeq = bitdeque_type{};
65  },
66  [&] {
67  // clear()
68  deq.clear();
69  bitdeq.clear();
70  },
71  [&] {
72  // resize()
73  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
74  deq.resize(count);
75  bitdeq.resize(count);
76  },
77  [&] {
78  // assign(count, val)
79  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
80  bool val = ctx.randbool();
81  deq.assign(count, val);
82  bitdeq.assign(count, val);
83  },
84  [&] {
85  // constructor(count, val)
86  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
87  bool val = ctx.randbool();
88  deq = std::deque<bool>(count, val);
89  bitdeq = bitdeque_type(count, val);
90  },
91  [&] {
92  // constructor(count)
93  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
94  deq = std::deque<bool>(count);
95  bitdeq = bitdeque_type(count);
96  },
97  [&] {
98  // construct(begin, end)
99  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
100  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
101  auto rand_end = rand_begin + count;
102  deq = std::deque<bool>(rand_begin, rand_end);
103  bitdeq = bitdeque_type(rand_begin, rand_end);
104  },
105  [&] {
106  // assign(begin, end)
107  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
108  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
109  auto rand_end = rand_begin + count;
110  deq.assign(rand_begin, rand_end);
111  bitdeq.assign(rand_begin, rand_end);
112  },
113  [&] {
114  // construct(initializer_list)
115  std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool(), ctx.randbool(), ctx.randbool()};
116  deq = std::deque<bool>(ilist);
117  bitdeq = bitdeque_type(ilist);
118  },
119  [&] {
120  // assign(initializer_list)
121  std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool()};
122  deq.assign(ilist);
123  bitdeq.assign(ilist);
124  },
125  [&] {
126  // operator=(const&)
127  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
128  bool val = ctx.randbool();
129  const std::deque<bool> deq2(count, val);
130  deq = deq2;
131  const bitdeque_type bitdeq2(count, val);
132  bitdeq = bitdeq2;
133  },
134  [&] {
135  // operator=(&&)
136  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
137  bool val = ctx.randbool();
138  std::deque<bool> deq2(count, val);
139  deq = std::move(deq2);
140  bitdeque_type bitdeq2(count, val);
141  bitdeq = std::move(bitdeq2);
142  },
143  [&] {
144  // deque swap
145  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
146  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
147  auto rand_end = rand_begin + count;
148  std::deque<bool> deq2(rand_begin, rand_end);
149  bitdeque_type bitdeq2(rand_begin, rand_end);
150  using std::swap;
151  assert(deq.size() == bitdeq.size());
152  assert(deq2.size() == bitdeq2.size());
153  swap(deq, deq2);
154  swap(bitdeq, bitdeq2);
155  assert(deq.size() == bitdeq.size());
156  assert(deq2.size() == bitdeq2.size());
157  },
158  [&] {
159  // deque.swap
160  auto count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
161  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
162  auto rand_end = rand_begin + count;
163  std::deque<bool> deq2(rand_begin, rand_end);
164  bitdeque_type bitdeq2(rand_begin, rand_end);
165  assert(deq.size() == bitdeq.size());
166  assert(deq2.size() == bitdeq2.size());
167  deq.swap(deq2);
168  bitdeq.swap(bitdeq2);
169  assert(deq.size() == bitdeq.size());
170  assert(deq2.size() == bitdeq2.size());
171  },
172  [&] {
173  // operator=(initializer_list)
174  std::initializer_list<bool> ilist{ctx.randbool(), ctx.randbool(), ctx.randbool()};
175  deq = ilist;
176  bitdeq = ilist;
177  },
178  [&] {
179  // iterator arithmetic
180  auto pos1 = provider.ConsumeIntegralInRange<long>(0, cdeq.size());
181  auto pos2 = provider.ConsumeIntegralInRange<long>(0, cdeq.size());
182  auto it = deq.begin() + pos1;
183  auto bitit = bitdeq.begin() + pos1;
184  if ((size_t)pos1 != cdeq.size()) assert(*it == *bitit);
185  assert(it - deq.begin() == pos1);
186  assert(bitit - bitdeq.begin() == pos1);
187  if (provider.ConsumeBool()) {
188  it += pos2 - pos1;
189  bitit += pos2 - pos1;
190  } else {
191  it -= pos1 - pos2;
192  bitit -= pos1 - pos2;
193  }
194  if ((size_t)pos2 != cdeq.size()) assert(*it == *bitit);
195  assert(deq.end() - it == bitdeq.end() - bitit);
196  if (provider.ConsumeBool()) {
197  if ((size_t)pos2 != cdeq.size()) {
198  ++it;
199  ++bitit;
200  }
201  } else {
202  if (pos2 != 0) {
203  --it;
204  --bitit;
205  }
206  }
207  assert(deq.end() - it == bitdeq.end() - bitit);
208  },
209  [&] {
210  // begin() and end()
211  assert(deq.end() - deq.begin() == bitdeq.end() - bitdeq.begin());
212  },
213  [&] {
214  // begin() and end() (const)
215  assert(cdeq.end() - cdeq.begin() == cbitdeq.end() - cbitdeq.begin());
216  },
217  [&] {
218  // rbegin() and rend()
219  assert(deq.rend() - deq.rbegin() == bitdeq.rend() - bitdeq.rbegin());
220  },
221  [&] {
222  // rbegin() and rend() (const)
223  assert(cdeq.rend() - cdeq.rbegin() == cbitdeq.rend() - cbitdeq.rbegin());
224  },
225  [&] {
226  // cbegin() and cend()
227  assert(cdeq.cend() - cdeq.cbegin() == cbitdeq.cend() - cbitdeq.cbegin());
228  },
229  [&] {
230  // crbegin() and crend()
231  assert(cdeq.crend() - cdeq.crbegin() == cbitdeq.crend() - cbitdeq.crbegin());
232  },
233  [&] {
234  // size() and maxsize()
235  assert(cdeq.size() == cbitdeq.size());
236  assert(cbitdeq.size() <= cbitdeq.max_size());
237  },
238  [&] {
239  // empty
240  assert(cdeq.empty() == cbitdeq.empty());
241  },
242  [&] {
243  // at (in range) and flip
244  if (!cdeq.empty()) {
245  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
246  auto& ref = deq.at(pos);
247  auto bitref = bitdeq.at(pos);
248  assert(ref == bitref);
249  if (ctx.randbool()) {
250  ref = !ref;
251  bitref.flip();
252  }
253  }
254  },
255  [&] {
256  // at (maybe out of range) and bit assign
257  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() + maxlen);
258  bool newval = ctx.randbool();
259  bool throw_deq{false}, throw_bitdeq{false};
260  bool val_deq{false}, val_bitdeq{false};
261  try {
262  auto& ref = deq.at(pos);
263  val_deq = ref;
264  ref = newval;
265  } catch (const std::out_of_range&) {
266  throw_deq = true;
267  }
268  try {
269  auto ref = bitdeq.at(pos);
270  val_bitdeq = ref;
271  ref = newval;
272  } catch (const std::out_of_range&) {
273  throw_bitdeq = true;
274  }
275  assert(throw_deq == throw_bitdeq);
276  assert(throw_bitdeq == (pos >= cdeq.size()));
277  if (!throw_deq) assert(val_deq == val_bitdeq);
278  },
279  [&] {
280  // at (maybe out of range) (const)
281  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() + maxlen);
282  bool throw_deq{false}, throw_bitdeq{false};
283  bool val_deq{false}, val_bitdeq{false};
284  try {
285  auto& ref = cdeq.at(pos);
286  val_deq = ref;
287  } catch (const std::out_of_range&) {
288  throw_deq = true;
289  }
290  try {
291  auto ref = cbitdeq.at(pos);
292  val_bitdeq = ref;
293  } catch (const std::out_of_range&) {
294  throw_bitdeq = true;
295  }
296  assert(throw_deq == throw_bitdeq);
297  assert(throw_bitdeq == (pos >= cdeq.size()));
298  if (!throw_deq) assert(val_deq == val_bitdeq);
299  },
300  [&] {
301  // operator[]
302  if (!cdeq.empty()) {
303  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
304  assert(deq[pos] == bitdeq[pos]);
305  if (ctx.randbool()) {
306  deq[pos] = !deq[pos];
307  bitdeq[pos].flip();
308  }
309  }
310  },
311  [&] {
312  // operator[] const
313  if (!cdeq.empty()) {
314  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
315  assert(deq[pos] == bitdeq[pos]);
316  }
317  },
318  [&] {
319  // front()
320  if (!cdeq.empty()) {
321  auto& ref = deq.front();
322  auto bitref = bitdeq.front();
323  assert(ref == bitref);
324  if (ctx.randbool()) {
325  ref = !ref;
326  bitref = !bitref;
327  }
328  }
329  },
330  [&] {
331  // front() const
332  if (!cdeq.empty()) {
333  auto& ref = cdeq.front();
334  auto bitref = cbitdeq.front();
335  assert(ref == bitref);
336  }
337  },
338  [&] {
339  // back() and swap(bool, ref)
340  if (!cdeq.empty()) {
341  auto& ref = deq.back();
342  auto bitref = bitdeq.back();
343  assert(ref == bitref);
344  if (ctx.randbool()) {
345  ref = !ref;
346  bitref.flip();
347  }
348  }
349  },
350  [&] {
351  // back() const
352  if (!cdeq.empty()) {
353  const auto& cdeq = deq;
354  const auto& cbitdeq = bitdeq;
355  auto& ref = cdeq.back();
356  auto bitref = cbitdeq.back();
357  assert(ref == bitref);
358  }
359  },
360  [&] {
361  // push_back()
362  if (cdeq.size() < limitlen) {
363  bool val = ctx.randbool();
364  if (cdeq.empty()) {
365  deq.push_back(val);
366  bitdeq.push_back(val);
367  } else {
368  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
369  auto& ref = deq[pos];
370  auto bitref = bitdeq[pos];
371  assert(ref == bitref);
372  deq.push_back(val);
373  bitdeq.push_back(val);
374  assert(ref == bitref); // references are not invalidated
375  }
376  }
377  },
378  [&] {
379  // push_front()
380  if (cdeq.size() < limitlen) {
381  bool val = ctx.randbool();
382  if (cdeq.empty()) {
383  deq.push_front(val);
384  bitdeq.push_front(val);
385  } else {
386  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
387  auto& ref = deq[pos];
388  auto bitref = bitdeq[pos];
389  assert(ref == bitref);
390  deq.push_front(val);
391  bitdeq.push_front(val);
392  assert(ref == bitref); // references are not invalidated
393  }
394  }
395  },
396  [&] {
397  // pop_back()
398  if (!cdeq.empty()) {
399  if (cdeq.size() == 1) {
400  deq.pop_back();
401  bitdeq.pop_back();
402  } else {
403  size_t pos = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 2);
404  auto& ref = deq[pos];
405  auto bitref = bitdeq[pos];
406  assert(ref == bitref);
407  deq.pop_back();
408  bitdeq.pop_back();
409  assert(ref == bitref); // references to other elements are not invalidated
410  }
411  }
412  },
413  [&] {
414  // pop_front()
415  if (!cdeq.empty()) {
416  if (cdeq.size() == 1) {
417  deq.pop_front();
418  bitdeq.pop_front();
419  } else {
420  size_t pos = provider.ConsumeIntegralInRange<size_t>(1, cdeq.size() - 1);
421  auto& ref = deq[pos];
422  auto bitref = bitdeq[pos];
423  assert(ref == bitref);
424  deq.pop_front();
425  bitdeq.pop_front();
426  assert(ref == bitref); // references to other elements are not invalidated
427  }
428  }
429  },
430  [&] {
431  // erase (in middle, single)
432  if (!cdeq.empty()) {
433  size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - 1);
434  size_t after = cdeq.size() - 1 - before;
435  auto it = deq.erase(cdeq.begin() + before);
436  auto bitit = bitdeq.erase(cbitdeq.begin() + before);
437  assert(it == cdeq.begin() + before && it == cdeq.end() - after);
438  assert(bitit == cbitdeq.begin() + before && bitit == cbitdeq.end() - after);
439  }
440  },
441  [&] {
442  // erase (at front, range)
443  size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
444  auto it = deq.erase(cdeq.begin(), cdeq.begin() + count);
445  auto bitit = bitdeq.erase(cbitdeq.begin(), cbitdeq.begin() + count);
446  assert(it == deq.begin());
447  assert(bitit == bitdeq.begin());
448  },
449  [&] {
450  // erase (at back, range)
451  size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
452  auto it = deq.erase(cdeq.end() - count, cdeq.end());
453  auto bitit = bitdeq.erase(cbitdeq.end() - count, cbitdeq.end());
454  assert(it == deq.end());
455  assert(bitit == bitdeq.end());
456  },
457  [&] {
458  // erase (in middle, range)
459  size_t count = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
460  size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size() - count);
461  size_t after = cdeq.size() - count - before;
462  auto it = deq.erase(cdeq.begin() + before, cdeq.end() - after);
463  auto bitit = bitdeq.erase(cbitdeq.begin() + before, cbitdeq.end() - after);
464  assert(it == cdeq.begin() + before && it == cdeq.end() - after);
465  assert(bitit == cbitdeq.begin() + before && bitit == cbitdeq.end() - after);
466  },
467  [&] {
468  // insert/emplace (in middle, single)
469  if (cdeq.size() < limitlen) {
470  size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
471  bool val = ctx.randbool();
472  bool do_emplace = provider.ConsumeBool();
473  auto it = deq.insert(cdeq.begin() + before, val);
474  auto bitit = do_emplace ? bitdeq.emplace(cbitdeq.begin() + before, val)
475  : bitdeq.insert(cbitdeq.begin() + before, val);
476  assert(it == deq.begin() + before);
477  assert(bitit == bitdeq.begin() + before);
478  }
479  },
480  [&] {
481  // insert (at front, begin/end)
482  if (cdeq.size() < limitlen) {
483  size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
484  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
485  auto rand_end = rand_begin + count;
486  auto it = deq.insert(cdeq.begin(), rand_begin, rand_end);
487  auto bitit = bitdeq.insert(cbitdeq.begin(), rand_begin, rand_end);
488  assert(it == cdeq.begin());
489  assert(bitit == cbitdeq.begin());
490  }
491  },
492  [&] {
493  // insert (at back, begin/end)
494  if (cdeq.size() < limitlen) {
495  size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
496  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
497  auto rand_end = rand_begin + count;
498  auto it = deq.insert(cdeq.end(), rand_begin, rand_end);
499  auto bitit = bitdeq.insert(cbitdeq.end(), rand_begin, rand_end);
500  assert(it == cdeq.end() - count);
501  assert(bitit == cbitdeq.end() - count);
502  }
503  },
504  [&] {
505  // insert (in middle, range)
506  if (cdeq.size() < limitlen) {
507  size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
508  size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
509  bool val = ctx.randbool();
510  auto it = deq.insert(cdeq.begin() + before, count, val);
511  auto bitit = bitdeq.insert(cbitdeq.begin() + before, count, val);
512  assert(it == deq.begin() + before);
513  assert(bitit == bitdeq.begin() + before);
514  }
515  },
516  [&] {
517  // insert (in middle, begin/end)
518  if (cdeq.size() < limitlen) {
519  size_t count = provider.ConsumeIntegralInRange<size_t>(0, maxlen);
520  size_t before = provider.ConsumeIntegralInRange<size_t>(0, cdeq.size());
521  auto rand_begin = RANDDATA.begin() + ctx.randbits(RANDDATA_BITS);
522  auto rand_end = rand_begin + count;
523  auto it = deq.insert(cdeq.begin() + before, rand_begin, rand_end);
524  auto bitit = bitdeq.insert(cbitdeq.begin() + before, rand_begin, rand_end);
525  assert(it == deq.begin() + before);
526  assert(bitit == bitdeq.begin() + before);
527  }
528  });
529  }
530  {
531  assert(deq.size() == bitdeq.size());
532  auto it = deq.begin();
533  auto bitit = bitdeq.begin();
534  auto itend = deq.end();
535  while (it != itend) {
536  assert(*it == *bitit);
537  ++it;
538  ++bitit;
539  }
540  }
541 }
FUZZ_TARGET(bitdeque,.init=InitRandData)
Definition: bitdeque.cpp:34
Fast randomness source.
Definition: random.h:145
bool randbool() noexcept
Generate a random boolean.
Definition: random.h:228
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
Definition: random.h:185
T ConsumeIntegralInRange(T min, T max)
Class that mimics std::deque<bool>, but with std::vector<bool>'s bit packing.
Definition: bitdeque.h:23
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:23
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
static int count
assert(!tx.IsCoinBase())