bitdeque.cpp raw

   1  // Copyright (c) 2022-present 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>
   6  #include <test/fuzz/FuzzedDataProvider.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  
  20  //! Deterministic random vector of bools, for begin/end insertions to draw from.
  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  }
 542