Skip to content

Commit 35e03e5

Browse files
committed
implement deque's clear and also add tests
1 parent 16d9030 commit 35e03e5

4 files changed

Lines changed: 239 additions & 11 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include <fast_io_dsal/deque.h>
2+
#include <deque>
3+
#include <cstddef>
4+
#include <cstdint>
5+
#include <ranges>
6+
7+
extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size)
8+
{
9+
// Require large inputs to trigger growth/balancing
10+
if (size < 4096)
11+
return 0;
12+
13+
::fast_io::deque<std::size_t> dq;
14+
std::deque<std::size_t> ref;
15+
16+
for (size_t i{}; i != size; ++i)
17+
{
18+
uint8_t raw = data[i];
19+
uint8_t op = raw % 6u; // 6 operations
20+
std::size_t value = i;
21+
22+
bool allow_clear =
23+
dq.size() >= 4096 || (raw == 0xFF);
24+
// 1/256 chance of early clear
25+
26+
switch (op)
27+
{
28+
case 0: // push_back
29+
dq.push_back(value);
30+
ref.push_back(value);
31+
break;
32+
33+
case 1: // push_front
34+
dq.push_front(value);
35+
ref.push_front(value);
36+
break;
37+
38+
case 2: // pop_back
39+
if (!ref.empty())
40+
{
41+
dq.pop_back();
42+
ref.pop_back();
43+
}
44+
break;
45+
46+
case 3: // pop_front
47+
if (!ref.empty())
48+
{
49+
dq.pop_front();
50+
ref.pop_front();
51+
}
52+
break;
53+
54+
case 4: // clear()
55+
if (allow_clear)
56+
{
57+
dq.clear();
58+
ref.clear();
59+
}
60+
break;
61+
62+
case 5: // clear_destroy()
63+
if (allow_clear)
64+
{
65+
dq.clear_destroy();
66+
ref.clear();
67+
}
68+
break;
69+
}
70+
}
71+
72+
// Validate correctness
73+
if (dq.size() != ref.size())
74+
__builtin_trap();
75+
76+
if (!std::ranges::equal(dq, ref))
77+
__builtin_trap();
78+
79+
return 0;
80+
}

include/fast_io_dsal/impl/deque.h

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,41 @@ inline constexpr void deque_grow_back_common(dequecontroltype &controller) noexc
720720
::fast_io::containers::details::deque_grow_back_common_impl<allocator>(controller, align, blockbytes);
721721
}
722722

723+
724+
template <typename allocator, typename dequecontroltype>
725+
inline constexpr void deque_clear_common_impl(dequecontroltype &controller, ::std::size_t blockbytes)
726+
{
727+
auto start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr};
728+
auto after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr};
729+
if (start_reserved_ptr == after_reserved_ptr)
730+
{
731+
return;
732+
}
733+
auto const reserved_blocks_count{
734+
static_cast<::std::size_t>(after_reserved_ptr - start_reserved_ptr)};
735+
auto const half_reserved_blocks_count{
736+
static_cast<::std::size_t>(reserved_blocks_count >> 1u)};
737+
auto reserved_pivot{start_reserved_ptr + half_reserved_blocks_count};
738+
using replacetype = typename dequecontroltype::replacetype;
739+
constexpr bool isvoidplaceholder = std::same_as<replacetype, void>;
740+
using begin_ptrtype =
741+
std::conditional_t<isvoidplaceholder, std::byte *, replacetype *>;
742+
auto begin_ptr{static_cast<begin_ptrtype>(*reserved_pivot)};
743+
auto end_ptr{begin_ptr + blockbytes};
744+
auto mid_ptr{begin_ptr + static_cast<::std::size_t>(blockbytes >> 1u)};
745+
controller.back_block.controller_ptr = controller.front_block.controller_ptr = reserved_pivot;
746+
controller.back_block.begin_ptr = controller.front_block.begin_ptr = begin_ptr;
747+
controller.back_block.curr_ptr = controller.front_block.curr_ptr = mid_ptr;
748+
controller.back_block.end_ptr = controller.front_block.end_ptr = end_ptr;
749+
}
750+
751+
template <typename allocator, ::std::size_t sz, ::std::size_t block_size, typename dequecontroltype>
752+
inline constexpr void deque_clear_common(dequecontroltype &controller) noexcept
753+
{
754+
constexpr ::std::size_t blockbytes{sz * block_size};
755+
::fast_io::containers::details::deque_clear_common_impl<allocator>(controller, blockbytes);
756+
}
757+
723758
} // namespace details
724759

725760
template <typename T, typename allocator>
@@ -782,7 +817,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
782817
{
783818
if (__builtin_is_constant_evaluated())
784819
{
785-
::fast_io::containers::details::deque_grow_front_common<allocator, alignof(value_type), sizeof(value_type), block_size>(controller);
820+
::fast_io::containers::details::deque_grow_front_common<allocator, alignof(value_type), 1u, block_size>(controller);
786821
}
787822
else
788823
{
@@ -797,7 +832,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
797832
{
798833
if (__builtin_is_constant_evaluated())
799834
{
800-
::fast_io::containers::details::deque_grow_back_common<allocator, alignof(value_type), sizeof(value_type), block_size>(controller);
835+
::fast_io::containers::details::deque_grow_back_common<allocator, alignof(value_type), 1u, block_size>(controller);
801836
}
802837
else
803838
{
@@ -821,20 +856,21 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE
821856
}
822857

823858
public:
824-
#if 0
825859
inline constexpr void clear() noexcept
826860
{
827-
if (controller.controller_block.start_ptr == controller.controller_block.start_ptr)
861+
if constexpr (!::std::is_trivially_destructible_v<value_type>)
828862
{
829-
return;
863+
this->destroy_all_elements();
864+
}
865+
if (__builtin_is_constant_evaluated())
866+
{
867+
::fast_io::containers::details::deque_clear_common<allocator, 1u, block_size>(controller);
868+
}
869+
else
870+
{
871+
::fast_io::containers::details::deque_clear_common<allocator, sizeof(value_type), block_size>(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller)));
830872
}
831-
this->destroy();
832-
size_type n{(controller.back_block.controller_ptr - controller.front_block.controller_ptr)};
833-
n >>= 1u;
834-
auto mid{controller.front_block.controller_ptr + n};
835-
*mid;
836873
}
837-
#endif
838874
template <typename... Args>
839875
requires ::std::constructible_from<value_type, Args...>
840876
inline constexpr reference emplace_back(Args &&...args)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include <fast_io.h>
2+
#include <fast_io_dsal/deque.h>
3+
4+
int main()
5+
{
6+
::fast_io::io::perr("=== clear() test ===\n");
7+
8+
::fast_io::deque<::std::size_t> dq;
9+
10+
// Fill with enough elements to force growth
11+
for (::std::size_t i{}; i != 4096u; ++i)
12+
{
13+
dq.push_back(i);
14+
}
15+
16+
// Clear but keep memory
17+
dq.clear();
18+
19+
// Validate empty state
20+
if (!dq.is_empty())
21+
{
22+
::fast_io::io::panic("ERROR: dq.is_empty() == false after clear()\n");
23+
}
24+
25+
if (dq.size() != 0u)
26+
{
27+
::fast_io::io::panic("ERROR: dq.size() != 0 after clear()\n");
28+
}
29+
30+
// Ensure iteration produces nothing
31+
for (auto const &e : dq)
32+
{
33+
::fast_io::io::panicln("ERROR: iterated element after clear(): ", e);
34+
}
35+
36+
// Reuse the deque
37+
for (::std::size_t i{}; i != 4096u; ++i)
38+
{
39+
dq.push_front(i);
40+
}
41+
42+
// Iterate to ensure no crash
43+
for (auto const &e : dq)
44+
{
45+
::fast_io::io::println(e);
46+
}
47+
48+
::fast_io::io::print("clear() test finished\n");
49+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#include <fast_io.h>
2+
#include <fast_io_dsal/deque.h>
3+
4+
int main()
5+
{
6+
::fast_io::io::perr("=== clear_destroy() test ===\n");
7+
8+
::fast_io::deque<::std::size_t> dq;
9+
10+
// Fill with enough elements to force growth
11+
for (::std::size_t i{}; i != 4096u; ++i)
12+
{
13+
dq.push_back(i);
14+
}
15+
16+
// First clear() — keep memory but reset state
17+
dq.clear();
18+
19+
// Validate empty state after clear()
20+
if (!dq.is_empty())
21+
{
22+
::fast_io::io::panic("ERROR: dq.is_empty() == false after clear()\n");
23+
}
24+
if (dq.size() != 0u)
25+
{
26+
::fast_io::io::panic("ERROR: dq.size() != 0 after clear()\n");
27+
}
28+
for (auto const &e : dq)
29+
{
30+
::fast_io::io::panicln("ERROR: iterated element after clear(): ", e);
31+
}
32+
33+
// Now clear_destroy() — free all memory
34+
dq.clear_destroy();
35+
36+
// Validate empty state after clear_destroy()
37+
if (!dq.is_empty())
38+
{
39+
::fast_io::io::panic("ERROR: dq.is_empty() == false after clear_destroy()\n");
40+
}
41+
if (dq.size() != 0u)
42+
{
43+
::fast_io::io::panic("ERROR: dq.size() != 0 after clear_destroy()\n");
44+
}
45+
for (auto const &e : dq)
46+
{
47+
::fast_io::io::panicln("ERROR: iterated element after clear_destroy(): ", e);
48+
}
49+
50+
// Reuse the deque — must reinitialize cleanly
51+
for (::std::size_t i{}; i != 4096u; ++i)
52+
{
53+
dq.push_front(i);
54+
}
55+
56+
// Iterate to ensure no crash and correct behavior
57+
for (auto const &e : dq)
58+
{
59+
::fast_io::io::println(e);
60+
}
61+
62+
::fast_io::io::print("clear_destroy() test finished\n");
63+
}

0 commit comments

Comments
 (0)