16static constexpr size_t MAX_BUFFERS{3};
18static constexpr size_t MAX_BUFFER_SIZE{48};
20static constexpr size_t MAX_OPERATIONS{1024};
26template<
typename T,
bool CheckNoneLeft>
34 std::vector<VecDeque<T>> real;
35 real.reserve(MAX_BUFFERS);
37 std::vector<std::deque<T>> sim;
38 sim.reserve(MAX_BUFFERS);
43 auto compare_fn = [](
const VecDeque<T>& r,
const std::deque<T>&
s) {
47 if (
s.size() == 0)
return;
50 for (
size_t i = 0; i <
s.size(); ++i) {
56 int command = provider.ConsumeIntegral<uint8_t>() % 64;
57 unsigned idx = real.empty() ? 0 : provider.ConsumeIntegralInRange<
unsigned>(0, real.size() - 1);
58 const size_t num_buffers = sim.size();
62 const bool non_empty{num_buffers != 0};
63 const bool non_full{num_buffers < MAX_BUFFERS};
64 const bool partially_full{non_empty && non_full};
65 const bool multiple_exist{num_buffers > 1};
66 const bool existing_buffer_non_full{non_empty && sim[idx].size() < MAX_BUFFER_SIZE};
67 const bool existing_buffer_non_empty{non_empty && !sim[idx].empty()};
68 assert(non_full || non_empty);
70 if (non_full &&
command-- == 0) {
76 if (non_empty &&
command-- == 0) {
78 compare_fn(real[idx], sim[idx]);
79 size_t new_size = provider.ConsumeIntegralInRange<
size_t>(0, MAX_BUFFER_SIZE);
80 real[idx].resize(new_size);
81 sim[idx].resize(new_size);
82 assert(real[idx].size() == new_size);
85 if (non_empty &&
command-- == 0) {
87 compare_fn(real[idx], sim[idx]);
93 if (non_empty &&
command-- == 0) {
95 compare_fn(real[idx], sim[idx]);
98 assert(real[idx].size() == 0);
101 if (non_empty &&
command-- == 0) {
103 compare_fn(real.back(), sim.back());
108 if (partially_full &&
command-- == 0) {
110 real.emplace_back(real[idx]);
111 sim.emplace_back(sim[idx]);
114 if (partially_full &&
command-- == 0) {
117 real.emplace_back(std::move(copy));
118 sim.emplace_back(sim[idx]);
121 if (multiple_exist &&
command-- == 0) {
123 swap(real[idx], real[(idx + 1) % num_buffers]);
124 swap(sim[idx], sim[(idx + 1) % num_buffers]);
127 if (multiple_exist &&
command-- == 0) {
129 compare_fn(real[idx], sim[idx]);
130 real[idx] = real[(idx + 1) % num_buffers];
131 sim[idx] = sim[(idx + 1) % num_buffers];
134 if (multiple_exist &&
command-- == 0) {
137 compare_fn(real[idx], sim[idx]);
138 real[idx] = std::move(copy);
139 sim[idx] = sim[(idx + 1) % num_buffers];
142 if (non_empty &&
command-- == 0) {
144 swap(real[idx], real[idx]);
147 if (non_empty &&
command-- == 0) {
149 real[idx] = real[idx];
152 if (non_empty &&
command-- == 0) {
158 if (non_empty &&
command-- == 0) {
160 size_t res_size = provider.ConsumeIntegralInRange<
size_t>(0, MAX_BUFFER_SIZE);
161 size_t old_cap = real[idx].
capacity();
162 size_t old_size = real[idx].size();
163 real[idx].reserve(res_size);
164 assert(real[idx].size() == old_size);
165 assert(real[idx].capacity() == std::max(old_cap, res_size));
168 if (non_empty &&
command-- == 0) {
170 size_t old_size = real[idx].size();
171 real[idx].shrink_to_fit();
172 assert(real[idx].size() == old_size);
173 assert(real[idx].capacity() == old_size);
176 if (existing_buffer_non_full &&
command-- == 0) {
178 tmp =
T(rng.rand64());
179 size_t old_size = real[idx].size();
180 size_t old_cap = real[idx].capacity();
181 real[idx].push_back(*tmp);
182 sim[idx].push_back(*tmp);
183 assert(real[idx].size() == old_size + 1);
184 if (old_cap > old_size) {
185 assert(real[idx].capacity() == old_cap);
187 assert(real[idx].capacity() > old_cap);
188 assert(real[idx].capacity() <= 2 * (old_cap + 1));
192 if (existing_buffer_non_full &&
command-- == 0) {
194 tmp =
T(rng.rand64());
195 size_t old_size = real[idx].size();
196 size_t old_cap = real[idx].capacity();
197 sim[idx].push_back(*tmp);
198 real[idx].push_back(std::move(*tmp));
199 assert(real[idx].size() == old_size + 1);
200 if (old_cap > old_size) {
201 assert(real[idx].capacity() == old_cap);
203 assert(real[idx].capacity() > old_cap);
204 assert(real[idx].capacity() <= 2 * (old_cap + 1));
208 if (existing_buffer_non_full &&
command-- == 0) {
210 uint64_t seed{rng.rand64()};
211 size_t old_size = real[idx].size();
212 size_t old_cap = real[idx].capacity();
213 sim[idx].emplace_back(seed);
214 real[idx].emplace_back(seed);
215 assert(real[idx].size() == old_size + 1);
216 if (old_cap > old_size) {
217 assert(real[idx].capacity() == old_cap);
219 assert(real[idx].capacity() > old_cap);
220 assert(real[idx].capacity() <= 2 * (old_cap + 1));
224 if (existing_buffer_non_full &&
command-- == 0) {
226 tmp =
T(rng.rand64());
227 size_t old_size = real[idx].size();
228 size_t old_cap = real[idx].capacity();
229 real[idx].push_front(*tmp);
230 sim[idx].push_front(*tmp);
231 assert(real[idx].size() == old_size + 1);
232 if (old_cap > old_size) {
233 assert(real[idx].capacity() == old_cap);
235 assert(real[idx].capacity() > old_cap);
236 assert(real[idx].capacity() <= 2 * (old_cap + 1));
240 if (existing_buffer_non_full &&
command-- == 0) {
242 tmp =
T(rng.rand64());
243 size_t old_size = real[idx].size();
244 size_t old_cap = real[idx].capacity();
245 sim[idx].push_front(*tmp);
246 real[idx].push_front(std::move(*tmp));
247 assert(real[idx].size() == old_size + 1);
248 if (old_cap > old_size) {
249 assert(real[idx].capacity() == old_cap);
251 assert(real[idx].capacity() > old_cap);
252 assert(real[idx].capacity() <= 2 * (old_cap + 1));
256 if (existing_buffer_non_full &&
command-- == 0) {
258 uint64_t seed{rng.rand64()};
259 size_t old_size = real[idx].size();
260 size_t old_cap = real[idx].capacity();
261 sim[idx].emplace_front(seed);
262 real[idx].emplace_front(seed);
263 assert(real[idx].size() == old_size + 1);
264 if (old_cap > old_size) {
265 assert(real[idx].capacity() == old_cap);
267 assert(real[idx].capacity() > old_cap);
268 assert(real[idx].capacity() <= 2 * (old_cap + 1));
272 if (existing_buffer_non_empty &&
command-- == 0) {
274 tmp =
T(rng.rand64());
275 size_t old_size = real[idx].size();
276 assert(sim[idx].front() == real[idx].front());
277 sim[idx].front() = *tmp;
278 real[idx].front() = std::move(*tmp);
279 assert(real[idx].size() == old_size);
282 if (existing_buffer_non_empty &&
command-- == 0) {
284 tmp =
T(rng.rand64());
285 size_t old_size = real[idx].size();
286 assert(sim[idx].back() == real[idx].back());
287 sim[idx].back() = *tmp;
288 real[idx].back() = *tmp;
289 assert(real[idx].size() == old_size);
292 if (existing_buffer_non_empty &&
command-- == 0) {
294 tmp =
T(rng.rand64());
295 size_t pos = provider.ConsumeIntegralInRange<
size_t>(0, sim[idx].size() - 1);
296 size_t old_size = real[idx].size();
297 assert(sim[idx][pos] == real[idx][pos]);
298 sim[idx][pos] = *tmp;
299 real[idx][pos] = std::move(*tmp);
300 assert(real[idx].size() == old_size);
303 if (existing_buffer_non_empty &&
command-- == 0) {
305 assert(sim[idx].front() == real[idx].front());
306 size_t old_size = real[idx].size();
307 sim[idx].pop_front();
308 real[idx].pop_front();
309 assert(real[idx].size() == old_size - 1);
312 if (existing_buffer_non_empty &&
command-- == 0) {
314 assert(sim[idx].back() == real[idx].back());
315 size_t old_size = real[idx].size();
317 real[idx].pop_back();
318 assert(real[idx].size() == old_size - 1);
325 for (
unsigned i = 0; i < sim.size(); ++i) {
328 const std::deque<T>& simbuf = sim[i];
329 compare_fn(realbuf, simbuf);
330 for (
unsigned j = 0; j < sim.size(); ++j) {
331 assert((realbuf == real[j]) == (simbuf == sim[j]));
332 assert(((realbuf <=> real[j]) >= 0) == (simbuf >= sim[j]));
333 assert(((realbuf <=> real[j]) <= 0) == (simbuf <= sim[j]));
340 if constexpr (CheckNoneLeft) {
350 static_assert(Size > 0);
357 using track_map_type = std::map<const TrackedObj<Size>*, std::optional<uint64_t>>;
362 static inline track_map_type g_tracker;
368 typename track_map_type::iterator m_track_entry[Size];
372 auto it = g_tracker.find(
this);
373 for (
size_t i = 0; i < Size; ++i) {
374 assert(m_track_entry[i] == it);
381 auto [it, inserted] = g_tracker.emplace(
this, std::nullopt);
383 for (
size_t i = 0; i < Size; ++i) {
384 m_track_entry[i] = it;
391 assert(m_track_entry[0] != g_tracker.end());
392 g_tracker.erase(m_track_entry[0]);
393 for (
size_t i = 0; i < Size; ++i) {
394 m_track_entry[i] = g_tracker.end();
399 std::optional<uint64_t>& Deref()
402 assert(m_track_entry[0] != g_tracker.end());
403 return m_track_entry[0]->second;
407 const std::optional<uint64_t>& Deref()
const
410 assert(m_track_entry[0] != g_tracker.end());
411 return m_track_entry[0]->second;
415 ~TrackedObj() { Deregister(); }
416 TrackedObj() { Register(); }
418 TrackedObj(uint64_t value)
424 TrackedObj(
const TrackedObj& other)
427 Deref() = other.Deref();
430 TrackedObj(TrackedObj&& other)
433 Deref() = other.Deref();
434 other.Deref() = std::nullopt;
437 TrackedObj& operator=(
const TrackedObj& other)
439 if (
this == &other)
return *
this;
440 Deref() = other.Deref();
444 TrackedObj& operator=(TrackedObj&& other)
446 if (
this == &other)
return *
this;
447 Deref() = other.Deref();
448 other.Deref() = std::nullopt;
452 friend bool operator==(
const TrackedObj& a,
const TrackedObj& b)
454 return a.Deref() == b.Deref();
457 friend std::strong_ordering operator<=>(
const TrackedObj& a,
const TrackedObj& b)
461 if (!a.Deref().has_value() || !b.Deref().has_value()) {
462 return a.Deref().has_value() <=> b.Deref().has_value();
464 return *a.Deref() <=> *b.Deref();
467 static void CheckNoneExist()
469 assert(g_tracker.empty());
478 static_assert(std::is_trivially_copyable_v<uint32_t>);
479 static_assert(std::is_trivially_destructible_v<uint64_t>);
480 TestType<uint8_t, false>(buffer, 1);
481 TestType<uint16_t, false>(buffer, 2);
482 TestType<uint32_t, false>(buffer, 3);
483 TestType<uint64_t, false>(buffer, 4);
486 static_assert(!std::is_trivially_copyable_v<TrackedObj<3>>);
487 static_assert(!std::is_trivially_destructible_v<TrackedObj<17>>);
488 TestType<TrackedObj<1>,
true>(buffer, 5);
489 TestType<TrackedObj<3>,
true>(buffer, 6);
490 TestType<TrackedObj<17>,
true>(buffer, 7);
A Span is an object that can refer to a contiguous sequence of objects.
constexpr std::size_t size() const noexcept
constexpr C * data() const noexcept
Data structure largely mimicking std::deque, but using single preallocated ring buffer.
bool empty() const noexcept
Test whether the contents of this deque is empty.
size_t size() const noexcept
Get the number of elements in this deque.
T & front() noexcept
Get a mutable reference to the first element of the deque.
size_t capacity() const noexcept
Get the capacity of this deque (maximum size it can have without reallocating).
T & back() noexcept
Get a mutable reference to the last element of the deque.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
#define T(expected, seed, data)
bool operator==(const CNetAddr &a, const CNetAddr &b)