LCOV - code coverage report
Current view: top level - src/test/fuzz - vecdeque.cpp (source / functions) Coverage Total Hit
Test: total_coverage.info Lines: 0.0 % 276 0
Test Date: 2024-07-07 05:05:19 Functions: 0.0 % 58 0
Branches: 0.0 % 576 0

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 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 <span.h>
       7                 :             : #include <test/fuzz/util.h>
       8                 :             : #include <util/vecdeque.h>
       9                 :             : 
      10                 :             : #include <deque>
      11                 :             : #include <stdint.h>
      12                 :             : 
      13                 :             : namespace {
      14                 :             : 
      15                 :             : /** The maximum number of simultaneous buffers kept by the test. */
      16                 :             : static constexpr size_t MAX_BUFFERS{3};
      17                 :             : /** How many elements are kept in a buffer at most. */
      18                 :             : static constexpr size_t MAX_BUFFER_SIZE{48};
      19                 :             : /** How many operations are performed at most on the buffers in one test. */
      20                 :             : static constexpr size_t MAX_OPERATIONS{1024};
      21                 :             : 
      22                 :             : /** Perform a simulation fuzz test on VecDeque type T.
      23                 :             :  *
      24                 :             :  * T must be constructible from a uint64_t seed, comparable to other T, copyable, and movable.
      25                 :             :  */
      26                 :             : template<typename T, bool CheckNoneLeft>
      27                 :           0 : void TestType(Span<const uint8_t> buffer, uint64_t rng_tweak)
      28                 :             : {
      29                 :           0 :     FuzzedDataProvider provider(buffer.data(), buffer.size());
      30                 :             :     // Local RNG, only used for the seeds to initialize T objects with.
      31                 :           0 :     InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>() ^ rng_tweak);
      32                 :             : 
      33                 :             :     // Real circular buffers.
      34                 :           0 :     std::vector<VecDeque<T>> real;
      35         [ #  # ]:           0 :     real.reserve(MAX_BUFFERS);
      36                 :             :     // Simulated circular buffers.
      37                 :           0 :     std::vector<std::deque<T>> sim;
      38         [ #  # ]:           0 :     sim.reserve(MAX_BUFFERS);
      39                 :             :     // Temporary object of type T.
      40                 :           0 :     std::optional<T> tmp;
      41                 :             : 
      42                 :             :     // Compare a real and a simulated buffer.
      43         [ #  # ]:           0 :     auto compare_fn = [](const VecDeque<T>& r, const std::deque<T>& s) {
      44   [ #  #  #  #  :           0 :         assert(r.size() == s.size());
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      45   [ #  #  #  #  :           0 :         assert(r.empty() == s.empty());
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      46   [ #  #  #  #  :           0 :         assert(r.capacity() >= r.size());
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      47   [ #  #  #  #  :           0 :         if (s.size() == 0) return;
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      48   [ #  #  #  #  :           0 :         assert(r.front() == s.front());
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      49   [ #  #  #  #  :           0 :         assert(r.back() == s.back());
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      50   [ #  #  #  #  :           0 :         for (size_t i = 0; i < s.size(); ++i) {
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      51   [ #  #  #  #  :           0 :             assert(r[i] == s[i]);
          #  #  #  #  #  
             #  #  #  #  
                      # ]
      52                 :             :         }
      53                 :             :     };
      54                 :             : 
      55   [ #  #  #  # ]:           0 :     LIMITED_WHILE(provider.remaining_bytes(), MAX_OPERATIONS) {
      56                 :           0 :         int command = provider.ConsumeIntegral<uint8_t>() % 64;
      57   [ #  #  #  # ]:           0 :         unsigned idx = real.empty() ? 0 : provider.ConsumeIntegralInRange<unsigned>(0, real.size() - 1);
      58                 :           0 :         const size_t num_buffers = sim.size();
      59                 :             :         // Pick one operation based on value of command. Not all operations are always applicable.
      60                 :             :         // Loop through the applicable ones until command reaches 0 (which avoids the need to
      61                 :             :         // compute the number of applicable commands ahead of time).
      62                 :           0 :         const bool non_empty{num_buffers != 0};
      63                 :           0 :         const bool non_full{num_buffers < MAX_BUFFERS};
      64                 :           0 :         const bool partially_full{non_empty && non_full};
      65                 :           0 :         const bool multiple_exist{num_buffers > 1};
      66   [ #  #  #  # ]:           0 :         const bool existing_buffer_non_full{non_empty && sim[idx].size() < MAX_BUFFER_SIZE};
      67   [ #  #  #  # ]:           0 :         const bool existing_buffer_non_empty{non_empty && !sim[idx].empty()};
      68                 :             :         assert(non_full || non_empty);
      69                 :             :         while (true) {
      70   [ #  #  #  # ]:           0 :             if (non_full && command-- == 0) {
      71                 :             :                 /* Default construct. */
      72         [ #  # ]:           0 :                 real.emplace_back();
      73         [ #  # ]:           0 :                 sim.emplace_back();
      74                 :             :                 break;
      75                 :             :             }
      76   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
      77                 :             :                 /* resize() */
      78                 :           0 :                 compare_fn(real[idx], sim[idx]);
      79                 :           0 :                 size_t new_size = provider.ConsumeIntegralInRange<size_t>(0, MAX_BUFFER_SIZE);
      80         [ #  # ]:           0 :                 real[idx].resize(new_size);
      81         [ #  # ]:           0 :                 sim[idx].resize(new_size);
      82         [ #  # ]:           0 :                 assert(real[idx].size() == new_size);
      83                 :             :                 break;
      84                 :             :             }
      85   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
      86                 :             :                 /* clear() */
      87                 :           0 :                 compare_fn(real[idx], sim[idx]);
      88                 :           0 :                 real[idx].clear();
      89                 :           0 :                 sim[idx].clear();
      90         [ #  # ]:           0 :                 assert(real[idx].empty());
      91                 :             :                 break;
      92                 :             :             }
      93   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
      94                 :             :                 /* Copy construct default. */
      95                 :           0 :                 compare_fn(real[idx], sim[idx]);
      96                 :           0 :                 real[idx] = VecDeque<T>();
      97                 :           0 :                 sim[idx].clear();
      98         [ #  # ]:           0 :                 assert(real[idx].size() == 0);
      99                 :             :                 break;
     100                 :             :             }
     101   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
     102                 :             :                 /* Destruct. */
     103                 :           0 :                 compare_fn(real.back(), sim.back());
     104                 :           0 :                 real.pop_back();
     105                 :           0 :                 sim.pop_back();
     106                 :             :                 break;
     107                 :             :             }
     108   [ #  #  #  # ]:           0 :             if (partially_full && command-- == 0) {
     109                 :             :                 /* Copy construct. */
     110         [ #  # ]:           0 :                 real.emplace_back(real[idx]);
     111         [ #  # ]:           0 :                 sim.emplace_back(sim[idx]);
     112                 :             :                 break;
     113                 :             :             }
     114   [ #  #  #  # ]:           0 :             if (partially_full && command-- == 0) {
     115                 :             :                 /* Move construct. */
     116         [ #  # ]:           0 :                 VecDeque<T> copy(real[idx]);
     117         [ #  # ]:           0 :                 real.emplace_back(std::move(copy));
     118         [ #  # ]:           0 :                 sim.emplace_back(sim[idx]);
     119                 :             :                 break;
     120                 :           0 :             }
     121   [ #  #  #  # ]:           0 :             if (multiple_exist && command-- == 0) {
     122                 :             :                 /* swap() */
     123                 :           0 :                 swap(real[idx], real[(idx + 1) % num_buffers]);
     124                 :           0 :                 swap(sim[idx], sim[(idx + 1) % num_buffers]);
     125                 :             :                 break;
     126                 :             :             }
     127   [ #  #  #  # ]:           0 :             if (multiple_exist && command-- == 0) {
     128                 :             :                 /* Copy assign. */
     129                 :           0 :                 compare_fn(real[idx], sim[idx]);
     130         [ #  # ]:           0 :                 real[idx] = real[(idx + 1) % num_buffers];
     131         [ #  # ]:           0 :                 sim[idx] = sim[(idx + 1) % num_buffers];
     132                 :             :                 break;
     133                 :             :             }
     134   [ #  #  #  # ]:           0 :             if (multiple_exist && command-- == 0) {
     135                 :             :                 /* Move assign. */
     136         [ #  # ]:           0 :                 VecDeque<T> copy(real[(idx + 1) % num_buffers]);
     137                 :           0 :                 compare_fn(real[idx], sim[idx]);
     138         [ #  # ]:           0 :                 real[idx] = std::move(copy);
     139         [ #  # ]:           0 :                 sim[idx] = sim[(idx + 1) % num_buffers];
     140                 :             :                 break;
     141                 :           0 :             }
     142   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
     143                 :             :                 /* Self swap() */
     144                 :           0 :                 swap(real[idx], real[idx]);
     145                 :             :                 break;
     146                 :             :             }
     147   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
     148                 :             :                 /* Self-copy assign. */
     149         [ #  # ]:           0 :                 real[idx] = real[idx];
     150                 :             :                 break;
     151                 :             :             }
     152   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
     153                 :             :                 /* Self-move assign. */
     154                 :             :                 // Do not use std::move(real[idx]) here: -Wself-move correctly warns about that.
     155                 :           0 :                 real[idx] = static_cast<VecDeque<T>&&>(real[idx]);
     156                 :             :                 break;
     157                 :             :             }
     158   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
     159                 :             :                 /* reserve() */
     160                 :           0 :                 size_t res_size = provider.ConsumeIntegralInRange<size_t>(0, MAX_BUFFER_SIZE);
     161         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     162         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     163         [ #  # ]:           0 :                 real[idx].reserve(res_size);
     164         [ #  # ]:           0 :                 assert(real[idx].size() == old_size);
     165   [ #  #  #  # ]:           0 :                 assert(real[idx].capacity() == std::max(old_cap, res_size));
     166                 :             :                 break;
     167                 :             :             }
     168   [ #  #  #  # ]:           0 :             if (non_empty && command-- == 0) {
     169                 :             :                 /* shrink_to_fit() */
     170         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     171         [ #  # ]:           0 :                 real[idx].shrink_to_fit();
     172         [ #  # ]:           0 :                 assert(real[idx].size() == old_size);
     173         [ #  # ]:           0 :                 assert(real[idx].capacity() == old_size);
     174                 :             :                 break;
     175                 :             :             }
     176   [ #  #  #  # ]:           0 :             if (existing_buffer_non_full && command-- == 0) {
     177                 :             :                 /* push_back() (copying) */
     178   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     179         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     180         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     181         [ #  # ]:           0 :                 real[idx].push_back(*tmp);
     182         [ #  # ]:           0 :                 sim[idx].push_back(*tmp);
     183         [ #  # ]:           0 :                 assert(real[idx].size() == old_size + 1);
     184         [ #  # ]:           0 :                 if (old_cap > old_size) {
     185         [ #  # ]:           0 :                     assert(real[idx].capacity() == old_cap);
     186                 :             :                 } else {
     187         [ #  # ]:           0 :                     assert(real[idx].capacity() > old_cap);
     188         [ #  # ]:           0 :                     assert(real[idx].capacity() <= 2 * (old_cap + 1));
     189                 :             :                 }
     190                 :             :                 break;
     191                 :             :             }
     192   [ #  #  #  # ]:           0 :             if (existing_buffer_non_full && command-- == 0) {
     193                 :             :                 /* push_back() (moving) */
     194   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     195         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     196         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     197         [ #  # ]:           0 :                 sim[idx].push_back(*tmp);
     198         [ #  # ]:           0 :                 real[idx].push_back(std::move(*tmp));
     199         [ #  # ]:           0 :                 assert(real[idx].size() == old_size + 1);
     200         [ #  # ]:           0 :                 if (old_cap > old_size) {
     201         [ #  # ]:           0 :                     assert(real[idx].capacity() == old_cap);
     202                 :             :                 } else {
     203         [ #  # ]:           0 :                     assert(real[idx].capacity() > old_cap);
     204         [ #  # ]:           0 :                     assert(real[idx].capacity() <= 2 * (old_cap + 1));
     205                 :             :                 }
     206                 :             :                 break;
     207                 :             :             }
     208   [ #  #  #  # ]:           0 :             if (existing_buffer_non_full && command-- == 0) {
     209                 :             :                 /* emplace_back() */
     210                 :           0 :                 uint64_t seed{rng.rand64()};
     211         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     212         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     213         [ #  # ]:           0 :                 sim[idx].emplace_back(seed);
     214         [ #  # ]:           0 :                 real[idx].emplace_back(seed);
     215         [ #  # ]:           0 :                 assert(real[idx].size() == old_size + 1);
     216         [ #  # ]:           0 :                 if (old_cap > old_size) {
     217         [ #  # ]:           0 :                     assert(real[idx].capacity() == old_cap);
     218                 :             :                 } else {
     219         [ #  # ]:           0 :                     assert(real[idx].capacity() > old_cap);
     220         [ #  # ]:           0 :                     assert(real[idx].capacity() <= 2 * (old_cap + 1));
     221                 :             :                 }
     222                 :             :                 break;
     223                 :             :             }
     224   [ #  #  #  # ]:           0 :             if (existing_buffer_non_full && command-- == 0) {
     225                 :             :                 /* push_front() (copying) */
     226   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     227         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     228         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     229         [ #  # ]:           0 :                 real[idx].push_front(*tmp);
     230         [ #  # ]:           0 :                 sim[idx].push_front(*tmp);
     231         [ #  # ]:           0 :                 assert(real[idx].size() == old_size + 1);
     232         [ #  # ]:           0 :                 if (old_cap > old_size) {
     233         [ #  # ]:           0 :                     assert(real[idx].capacity() == old_cap);
     234                 :             :                 } else {
     235         [ #  # ]:           0 :                     assert(real[idx].capacity() > old_cap);
     236         [ #  # ]:           0 :                     assert(real[idx].capacity() <= 2 * (old_cap + 1));
     237                 :             :                 }
     238                 :             :                 break;
     239                 :             :             }
     240   [ #  #  #  # ]:           0 :             if (existing_buffer_non_full && command-- == 0) {
     241                 :             :                 /* push_front() (moving) */
     242   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     243         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     244         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     245         [ #  # ]:           0 :                 sim[idx].push_front(*tmp);
     246         [ #  # ]:           0 :                 real[idx].push_front(std::move(*tmp));
     247         [ #  # ]:           0 :                 assert(real[idx].size() == old_size + 1);
     248         [ #  # ]:           0 :                 if (old_cap > old_size) {
     249         [ #  # ]:           0 :                     assert(real[idx].capacity() == old_cap);
     250                 :             :                 } else {
     251         [ #  # ]:           0 :                     assert(real[idx].capacity() > old_cap);
     252         [ #  # ]:           0 :                     assert(real[idx].capacity() <= 2 * (old_cap + 1));
     253                 :             :                 }
     254                 :             :                 break;
     255                 :             :             }
     256   [ #  #  #  # ]:           0 :             if (existing_buffer_non_full && command-- == 0) {
     257                 :             :                 /* emplace_front() */
     258                 :           0 :                 uint64_t seed{rng.rand64()};
     259         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     260         [ #  # ]:           0 :                 size_t old_cap = real[idx].capacity();
     261         [ #  # ]:           0 :                 sim[idx].emplace_front(seed);
     262         [ #  # ]:           0 :                 real[idx].emplace_front(seed);
     263         [ #  # ]:           0 :                 assert(real[idx].size() == old_size + 1);
     264         [ #  # ]:           0 :                 if (old_cap > old_size) {
     265         [ #  # ]:           0 :                     assert(real[idx].capacity() == old_cap);
     266                 :             :                 } else {
     267         [ #  # ]:           0 :                     assert(real[idx].capacity() > old_cap);
     268         [ #  # ]:           0 :                     assert(real[idx].capacity() <= 2 * (old_cap + 1));
     269                 :             :                 }
     270                 :             :                 break;
     271                 :             :             }
     272   [ #  #  #  # ]:           0 :             if (existing_buffer_non_empty && command-- == 0) {
     273                 :             :                 /* front() [modifying] */
     274   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     275         [ #  # ]:           0 :                 size_t old_size = real[idx].size();
     276         [ #  # ]:           0 :                 assert(sim[idx].front() == real[idx].front());
     277         [ #  # ]:           0 :                 sim[idx].front() = *tmp;
     278         [ #  # ]:           0 :                 real[idx].front() = std::move(*tmp);
     279         [ #  # ]:           0 :                 assert(real[idx].size() == old_size);
     280                 :             :                 break;
     281                 :             :             }
     282   [ #  #  #  # ]:           0 :             if (existing_buffer_non_empty && command-- == 0) {
     283                 :             :                 /* back() [modifying] */
     284   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     285                 :           0 :                 size_t old_size = real[idx].size();
     286         [ #  # ]:           0 :                 assert(sim[idx].back() == real[idx].back());
     287         [ #  # ]:           0 :                 sim[idx].back() = *tmp;
     288         [ #  # ]:           0 :                 real[idx].back() = *tmp;
     289         [ #  # ]:           0 :                 assert(real[idx].size() == old_size);
     290                 :             :                 break;
     291                 :             :             }
     292   [ #  #  #  # ]:           0 :             if (existing_buffer_non_empty && command-- == 0) {
     293                 :             :                 /* operator[] [modifying] */
     294   [ #  #  #  # ]:           0 :                 tmp = T(rng.rand64());
     295                 :           0 :                 size_t pos = provider.ConsumeIntegralInRange<size_t>(0, sim[idx].size() - 1);
     296                 :           0 :                 size_t old_size = real[idx].size();
     297         [ #  # ]:           0 :                 assert(sim[idx][pos] == real[idx][pos]);
     298         [ #  # ]:           0 :                 sim[idx][pos] = *tmp;
     299         [ #  # ]:           0 :                 real[idx][pos] = std::move(*tmp);
     300         [ #  # ]:           0 :                 assert(real[idx].size() == old_size);
     301                 :             :                 break;
     302                 :             :             }
     303   [ #  #  #  # ]:           0 :             if (existing_buffer_non_empty && command-- == 0) {
     304                 :             :                 /* pop_front() */
     305         [ #  # ]:           0 :                 assert(sim[idx].front() == real[idx].front());
     306                 :           0 :                 size_t old_size = real[idx].size();
     307                 :           0 :                 sim[idx].pop_front();
     308         [ #  # ]:           0 :                 real[idx].pop_front();
     309         [ #  # ]:           0 :                 assert(real[idx].size() == old_size - 1);
     310                 :             :                 break;
     311                 :             :             }
     312   [ #  #  #  #  :           0 :             if (existing_buffer_non_empty && command-- == 0) {
                   #  # ]
     313                 :             :                 /* pop_back() */
     314         [ #  # ]:           0 :                 assert(sim[idx].back() == real[idx].back());
     315                 :           0 :                 size_t old_size = real[idx].size();
     316                 :           0 :                 sim[idx].pop_back();
     317         [ #  # ]:           0 :                 real[idx].pop_back();
     318         [ #  # ]:           0 :                 assert(real[idx].size() == old_size - 1);
     319                 :             :                 break;
     320                 :             :             }
     321                 :             :         }
     322                 :             :     }
     323                 :             : 
     324                 :             :     /* Fully compare the final state. */
     325         [ #  # ]:           0 :     for (unsigned i = 0; i < sim.size(); ++i) {
     326                 :             :         // Make sure const getters work.
     327                 :           0 :         const VecDeque<T>& realbuf = real[i];
     328                 :           0 :         const std::deque<T>& simbuf = sim[i];
     329                 :           0 :         compare_fn(realbuf, simbuf);
     330         [ #  # ]:           0 :         for (unsigned j = 0; j < sim.size(); ++j) {
     331         [ #  # ]:           0 :             assert((realbuf == real[j]) == (simbuf == sim[j]));
     332         [ #  # ]:           0 :             assert(((realbuf <=> real[j]) >= 0) == (simbuf >= sim[j]));
     333         [ #  # ]:           0 :             assert(((realbuf <=> real[j]) <= 0) == (simbuf <= sim[j]));
     334                 :             :         }
     335                 :             :         // Clear out the buffers so we can check below that no objects exist anymore.
     336                 :           0 :         sim[i].clear();
     337                 :           0 :         real[i].clear();
     338                 :             :     }
     339                 :             : 
     340                 :             :     if constexpr (CheckNoneLeft) {
     341                 :           0 :         tmp = std::nullopt;
     342                 :           0 :         T::CheckNoneExist();
     343                 :             :     }
     344                 :           0 : }
     345                 :             : 
     346                 :             : /** Data structure with built-in tracking of all existing objects. */
     347                 :             : template<size_t Size>
     348                 :             : class TrackedObj
     349                 :             : {
     350                 :             :     static_assert(Size > 0);
     351                 :             : 
     352                 :             :     /* Data type for map that actually stores the object data.
     353                 :             :      *
     354                 :             :      * The key is a pointer to the TrackedObj, the value is the uint64_t it was initialized with.
     355                 :             :      * Default-constructed and moved-from objects hold an std::nullopt.
     356                 :             :      */
     357                 :             :     using track_map_type = std::map<const TrackedObj<Size>*, std::optional<uint64_t>>;
     358                 :             : 
     359                 :             : private:
     360                 :             : 
     361                 :             :     /** Actual map. */
     362                 :             :     static inline track_map_type g_tracker;
     363                 :             : 
     364                 :             :     /** Iterators into the tracker map for this object.
     365                 :             :      *
     366                 :             :      * This is an array of size Size, all holding the same value, to give the object configurable
     367                 :             :      * size. The value is g_tracker.end() if this object is not fully initialized. */
     368                 :             :     typename track_map_type::iterator m_track_entry[Size];
     369                 :             : 
     370                 :           0 :     void Check() const
     371                 :             :     {
     372                 :           0 :         auto it = g_tracker.find(this);
     373         [ #  # ]:           0 :         for (size_t i = 0; i < Size; ++i) {
     374         [ #  # ]:           0 :             assert(m_track_entry[i] == it);
     375                 :             :         }
     376                 :           0 :     }
     377                 :             : 
     378                 :             :     /** Create entry for this object in g_tracker and populate m_track_entry. */
     379                 :           0 :     void Register()
     380                 :             :     {
     381         [ #  # ]:           0 :         auto [it, inserted] = g_tracker.emplace(this, std::nullopt);
     382         [ #  # ]:           0 :         assert(inserted);
     383         [ #  # ]:           0 :         for (size_t i = 0; i < Size; ++i) {
     384                 :           0 :             m_track_entry[i] = it;
     385                 :             :         }
     386                 :           0 :     }
     387                 :             : 
     388                 :           0 :     void Deregister()
     389                 :             :     {
     390         [ #  # ]:           0 :         Check();
     391         [ #  # ]:           0 :         assert(m_track_entry[0] != g_tracker.end());
     392                 :           0 :         g_tracker.erase(m_track_entry[0]);
     393         [ #  # ]:           0 :         for (size_t i = 0; i < Size; ++i) {
     394                 :           0 :             m_track_entry[i] = g_tracker.end();
     395                 :             :         }
     396                 :           0 :     }
     397                 :             : 
     398                 :             :     /** Get value corresponding to this object in g_tracker. */
     399                 :           0 :     std::optional<uint64_t>& Deref()
     400                 :             :     {
     401         [ #  # ]:           0 :         Check();
     402         [ #  # ]:           0 :         assert(m_track_entry[0] != g_tracker.end());
     403                 :           0 :         return m_track_entry[0]->second;
     404                 :             :     }
     405                 :             : 
     406                 :             :     /** Get value corresponding to this object in g_tracker. */
     407                 :           0 :     const std::optional<uint64_t>& Deref() const
     408                 :             :     {
     409         [ #  # ]:           0 :         Check();
     410         [ #  # ]:           0 :         assert(m_track_entry[0] != g_tracker.end());
     411                 :           0 :         return m_track_entry[0]->second;
     412                 :             :     }
     413                 :             : 
     414                 :             : public:
     415   [ #  #  #  #  :           0 :     ~TrackedObj() { Deregister(); }
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                   #  # ]
     416         [ #  # ]:           0 :     TrackedObj() { Register(); }
     417                 :             : 
     418                 :           0 :     TrackedObj(uint64_t value)
     419         [ #  # ]:           0 :     {
     420                 :           0 :         Register();
     421                 :           0 :         Deref() = value;
     422                 :           0 :     }
     423                 :             : 
     424                 :           0 :     TrackedObj(const TrackedObj& other)
     425         [ #  # ]:           0 :     {
     426                 :           0 :         Register();
     427                 :           0 :         Deref() = other.Deref();
     428                 :           0 :     }
     429                 :             : 
     430                 :           0 :     TrackedObj(TrackedObj&& other)
     431         [ #  # ]:           0 :     {
     432                 :           0 :         Register();
     433                 :           0 :         Deref() = other.Deref();
     434         [ #  # ]:           0 :         other.Deref() = std::nullopt;
     435                 :           0 :     }
     436                 :             : 
     437                 :           0 :     TrackedObj& operator=(const TrackedObj& other)
     438                 :             :     {
     439         [ #  # ]:           0 :         if (this == &other) return *this;
     440                 :           0 :         Deref() = other.Deref();
     441                 :           0 :         return *this;
     442                 :             :     }
     443                 :             : 
     444                 :           0 :     TrackedObj& operator=(TrackedObj&& other)
     445                 :             :     {
     446         [ #  # ]:           0 :         if (this == &other) return *this;
     447                 :           0 :         Deref() = other.Deref();
     448         [ #  # ]:           0 :         other.Deref() = std::nullopt;
     449                 :             :         return *this;
     450                 :             :     }
     451                 :             : 
     452                 :           0 :     friend bool operator==(const TrackedObj& a, const TrackedObj& b)
     453                 :             :     {
     454         [ #  # ]:           0 :         return a.Deref() == b.Deref();
     455                 :             :     }
     456                 :             : 
     457                 :           0 :     friend std::strong_ordering operator<=>(const TrackedObj& a, const TrackedObj& b)
     458                 :             :     {
     459                 :             :         // Libc++ 15 & 16 do not support std::optional<T>::operator<=> yet. See
     460                 :             :         // https://reviews.llvm.org/D146392.
     461   [ #  #  #  # ]:           0 :         if (!a.Deref().has_value() || !b.Deref().has_value()) {
     462   [ #  #  #  # ]:           0 :             return a.Deref().has_value() <=> b.Deref().has_value();
     463                 :             :         }
     464   [ #  #  #  # ]:           0 :         return *a.Deref() <=> *b.Deref();
     465                 :             :     }
     466                 :             : 
     467         [ #  # ]:           0 :     static void CheckNoneExist()
     468                 :             :     {
     469         [ #  # ]:           0 :         assert(g_tracker.empty());
     470                 :           0 :     }
     471                 :             : };
     472                 :             : 
     473                 :             : } // namespace
     474                 :             : 
     475         [ #  # ]:           0 : FUZZ_TARGET(vecdeque)
     476                 :             : {
     477                 :             :     // Run the test with simple uints (which satisfy all the trivial properties).
     478                 :           0 :     static_assert(std::is_trivially_copyable_v<uint32_t>);
     479                 :           0 :     static_assert(std::is_trivially_destructible_v<uint64_t>);
     480                 :           0 :     TestType<uint8_t, false>(buffer, 1);
     481                 :           0 :     TestType<uint16_t, false>(buffer, 2);
     482                 :           0 :     TestType<uint32_t, false>(buffer, 3);
     483                 :           0 :     TestType<uint64_t, false>(buffer, 4);
     484                 :             : 
     485                 :             :     // Run the test with TrackedObjs (which do not).
     486                 :           0 :     static_assert(!std::is_trivially_copyable_v<TrackedObj<3>>);
     487                 :           0 :     static_assert(!std::is_trivially_destructible_v<TrackedObj<17>>);
     488                 :           0 :     TestType<TrackedObj<1>, true>(buffer, 5);
     489                 :           0 :     TestType<TrackedObj<3>, true>(buffer, 6);
     490                 :           0 :     TestType<TrackedObj<17>, true>(buffer, 7);
     491                 :           0 : }
        

Generated by: LCOV version 2.0-1