From 312c376b1343f3c6b509831ce5b21e73df81f7dc Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 29 Dec 2025 02:38:33 +0800 Subject: [PATCH 01/26] overhual implementation of deque --- include/fast_io_dsal/impl/deque.h | 279 +++++++----------------------- 1 file changed, 65 insertions(+), 214 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index b2c0753a9..08b628ff6 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -285,7 +285,7 @@ inline constexpr void deque_destroy_controller(controllerblocktype *controllerpt ::std::size_t n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr) * sizeof(void *)}; allocator::deallocate_n(controller.controller_start_ptr, n); } - + controller.controller_start_ptr = nullptr; } @@ -379,224 +379,82 @@ inline constexpr void deque_init_grow_common(dequecontroltype &controller) noexc } } -template -inline constexpr void deque_reallocate_controller_block_common_impl(dequecontroltype &controller) noexcept +template +inline constexpr void deque_grow_front_common_impl(dequecontroltype &controller) noexcept { - constexpr ::std::size_t max_size{SIZE_MAX / sizeof(void *) / 2u - 1u}; - - ::std::size_t old_size{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_block.controller_start_ptr)}; - - if (old_size > max_size) - { - ::fast_io::fast_terminate(); - } - - ::std::size_t new_size{2 * old_size}; - ::std::size_t start_reserved_index{static_cast<::std::size_t>(controller.controller_block.controller_start_reserved_ptr - controller.controller_block.controller_start_ptr)}; - ::std::size_t after_reserved_index{static_cast<::std::size_t>(controller.controller_block.controller_after_reserved_ptr - controller.controller_block.controller_start_ptr)}; - ::std::size_t front_block_index{static_cast<::std::size_t>(controller.front_block.controller_ptr - controller.controller_block.controller_start_ptr)}; - ::std::size_t back_block_index{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.controller_block.controller_start_ptr)}; - - auto block_temp{allocator::reallocate_n(controller.controller_block.controller_start_ptr, old_size * sizeof(void *), (new_size + 1u) * sizeof(void *))}; - - controller.controller_block.controller_start_ptr = static_cast(block_temp); - - controller.controller_block.controller_start_reserved_ptr = controller.controller_block.controller_start_ptr + start_reserved_index; - controller.controller_block.controller_after_reserved_ptr = controller.controller_block.controller_start_ptr + after_reserved_index; - controller.controller_block.controller_after_ptr = controller.controller_block.controller_start_ptr + new_size; - controller.front_block.controller_ptr = controller.controller_block.controller_start_ptr + front_block_index; - controller.back_block.controller_ptr = controller.controller_block.controller_start_ptr + back_block_index; } - -template -inline constexpr T **make_blocks_balance(T **begin, T **end, T **b, T **e) noexcept -{ - ::std::size_t external_diff{static_cast<::std::size_t>(end - begin)}; - ::std::size_t internal_diff{static_cast<::std::size_t>(e - b)}; - - if (external_diff - internal_diff == 1u) - { - if (begin == b) - { - T *t = end[-1]; - ::std::copy(b, e, begin + 1); - *begin = t; - return begin + 1u; - } - else - { - T *t = *begin; - ::std::copy(b, e, begin); - end[-1] = t; - return begin; - } - } - else - { - T **blocktemp{begin + (external_diff - internal_diff) / 2u}; - - if (blocktemp < b) - { - for (::std::size_t i{}; i != internal_diff; ++i) - { - ::std::swap(b[i], blocktemp[i]); - } - } - else - { - for (::std::size_t i{internal_diff}; i--;) - { - ::std::swap(b[i], blocktemp[i]); - } - } - - return blocktemp; - } -} - -template -inline constexpr void deque_make_reserved_blocks_balance_common_impl(dequecontroltype &controller) noexcept +/* +struct +#if __has_cpp_attribute(__gnu__::__may_alias__) + [[__gnu__::__may_alias__]] +#endif + deque_controller_block_common { - ::std::size_t diff{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr)}; - - controller.front_block.controller_ptr = - ::fast_io::containers::details::make_blocks_balance( - controller.controller_block.controller_start_reserved_ptr, - controller.controller_block.controller_after_reserved_ptr, - controller.front_block.controller_ptr, - controller.back_block.controller_ptr + 1); - - controller.back_block.controller_ptr = controller.front_block.controller_ptr + diff; -} + using replacetype = char unsigned; + void **controller_start_ptr; + void **controller_start_reserved_ptr; + void **controller_after_reserved_ptr; + void **controller_after_ptr; +}; +*/ -template -inline constexpr void deque_make_unreserved_blocks_balance_common_impl(dequecontroltype &controller) noexcept -{ - ::std::size_t reserved_size{static_cast<::std::size_t>(controller.controller_block.controller_after_reserved_ptr - controller.controller_block.controller_start_reserved_ptr)}; - ::std::size_t front_block_index{static_cast<::std::size_t>(controller.front_block.controller_ptr - controller.controller_block.controller_start_reserved_ptr)}; - ::std::size_t back_block_index{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.controller_block.controller_start_reserved_ptr)}; - - controller.controller_block.controller_start_reserved_ptr = - ::fast_io::containers::details::make_blocks_balance( - controller.controller_block.controller_start_ptr, - controller.controller_block.controller_after_ptr, - controller.controller_block.controller_start_reserved_ptr, - controller.controller_block.controller_after_reserved_ptr); - - controller.controller_block.controller_after_reserved_ptr = controller.controller_block.controller_start_reserved_ptr + reserved_size; - controller.front_block.controller_ptr = controller.controller_block.controller_start_reserved_ptr + front_block_index; - controller.back_block.controller_ptr = controller.controller_block.controller_start_reserved_ptr + back_block_index; +template +inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least) noexcept +{ + auto old_start_ptr{controller.controller_block.controller_start_ptr}; + + auto old_start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; + auto old_after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; + ::std::size_t const old_start_reserved_ptr_pos{static_cast<::std::size_t>(old_start_reserved_ptr - old_start_ptr)}; + ::std::size_t const old_after_reserved_ptr_pos{static_cast<::std::size_t>(old_after_reserved_ptr - old_start_ptr)}; + ::std::size_t const old_after_ptr_pos{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - old_start_ptr)}; + ::std::size_t const old_front_controller_ptr_pos{controller.front_controller.controller_ptr - old_start_ptr}; + ::std::size_t const old_back_controller_ptr_pos{controller.back_controller.controller_ptr - old_start_ptr}; + + using block_typed_allocator = ::fast_io::typed_allocator; + auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least); + /* + Unfinished + */ + ::std::size_t const new_blocks_offset{(new_blocks_count >> 2u)}; + auto new_start_reserved_ptr{new_start_ptr + (old_start_reserved_ptr_pos + new_blocks_offset)}; + auto new_after_reserved_ptr{::std::uninitialized_copy(old_start_reserved_ptr, old_after_reserved_ptr, new_start_reserved_ptr)}; + block_typed_allocator::deallocate_n(old_start_ptr, old_after_ptr_pos); + controller.controller_block.controller_start_ptr = new_start_ptr; + controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; + controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr; + controller.controller_block.controller_after_ptr = new_start_ptr + new_blocks_count; + controller.front_controller.controller_ptr = new_start_ptr + (old_front_controller_ptr_pos + new_blocks_offset); + controller.back_controller.controller_ptr = new_start_ptr + (old_back_controller_ptr_pos + new_blocks_offset); } -template -inline constexpr void deque_grow_front_common_impl(dequecontroltype &controller) noexcept +template +inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontroltype &controller) noexcept { - if (controller.controller_block.controller_start_ptr == nullptr) [[unlikely]] + ::std::size_t const used_blocks_count{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; + ::std::size_t const total_blocks{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; + ::std::size_t const total_blocks_half{static_cast<::std::size_t>(total_blocks >> 1u)}; + if (used_blocks_count < total_blocks_half) { - constexpr ::std::size_t mid = block_size / sz / 2; - ::fast_io::containers::details::deque_init_grow_common(controller); - - if (controller.front_block.curr_ptr != controller.front_block.begin_ptr) - { - return; - } + ::std::size_t const reserved_blocks_count{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.controller_block.controller_ptr) + 1zu}; } - - // has reserved block == fasle - if (controller.front_block.controller_ptr == controller.controller_block.controller_start_reserved_ptr) + else { - // could balance reserved blocks == false - if (controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr < 2u) - { - // has unreserved block == fasle - if (controller.controller_block.controller_start_ptr == controller.controller_block.controller_start_reserved_ptr) - { - // could balance unreserved blocks == false - if (controller.controller_block.controller_after_ptr == controller.controller_block.controller_after_reserved_ptr) - { - ::fast_io::containers::details::deque_reallocate_controller_block_common_impl(controller); - } - - if (controller.controller_block.controller_start_ptr == controller.controller_block.controller_start_reserved_ptr) - { - ::fast_io::containers::details::deque_make_unreserved_blocks_balance_common_impl(controller); - } - } - - void *block_temp{}; - - if constexpr (align <= allocator::default_alignment) - { - block_temp = allocator::allocate(block_size); - } - else - { - block_temp = allocator::allocate_zero_aligned(align, block_size); - } - - *--controller.controller_block.controller_start_reserved_ptr = reinterpret_cast(block_temp); - } - else - { - ::fast_io::containers::details::deque_make_reserved_blocks_balance_common_impl(controller); - } } - - controller.front_block.curr_ptr = controller.front_block.end_ptr = static_cast(controller.front_block.begin_ptr = *--controller.front_block.controller_ptr) + block_size; } -template -inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller) noexcept +template +inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { - if (controller.controller_block.controller_start_ptr == nullptr) [[unlikely]] - { - constexpr ::std::size_t mid = block_size / sz / 2; - ::fast_io::containers::details::deque_init_grow_common(controller); - return; - } - - // has reserved block == fasle - if (controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr < 2u) + ::std::size_t const diff_to_after_ptr{controller.controller_block.controller_after_ptr - controller.back_block.controller_ptr}; + if (diff_to_after_ptr < 2zu) { - // could balance reserved blocks == false - if (controller.controller_block.controller_start_reserved_ptr == controller.front_block.controller_ptr) + if (controller.controller_after_reserved_ptr == controller.controller_after_ptr) { - // has unreserved block == fasle - if (controller.controller_block.controller_after_ptr == controller.controller_block.controller_after_reserved_ptr) - { - // could balance unreserved blocks == false - if (controller.controller_block.controller_start_ptr == controller.controller_block.controller_start_reserved_ptr) - { - ::fast_io::containers::details::deque_reallocate_controller_block_common_impl(controller); - } - // has unreserved block == fasle (is same as pre step) - if (controller.controller_block.controller_after_ptr == controller.controller_block.controller_after_reserved_ptr) - { - ::fast_io::containers::details::deque_make_unreserved_blocks_balance_common_impl(controller); - } - } - - void *block_temp{}; - - if constexpr (align <= allocator::default_alignment) - { - block_temp = allocator::allocate(block_size); - } - else - { - block_temp = allocator::allocate_zero_aligned(align, block_size); - } - - *controller.controller_block.controller_after_reserved_ptr = reinterpret_cast(block_temp); - ++controller.controller_block.controller_after_reserved_ptr; - } - else - { - ::fast_io::containers::details::deque_make_reserved_blocks_balance_common_impl(controller); + ::fast_io::containers::details::deque_rebalance_or_grow_2x_blocks_impl(controller); } } - - controller.back_block.end_ptr = static_cast(controller.back_block.curr_ptr = controller.back_block.begin_ptr = *++controller.back_block.controller_ptr) + block_size; + controller.back_block.end_ptr = ((controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr))) + sz); } template @@ -608,7 +466,8 @@ inline constexpr void deque_grow_front_common(dequecontroltype &controller) noex template inline constexpr void deque_grow_back_common(dequecontroltype &controller) noexcept { - ::fast_io::containers::details::deque_grow_back_common_impl(controller); + constexpr ::std::size_t blockbytes{sz * block_size}; + ::fast_io::containers::details::deque_grow_back_common_impl(controller, align, blockbytes); } } // namespace details @@ -641,17 +500,9 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE inline constexpr deque &operator=(deque &&) noexcept = default; private: - static inline constexpr void destroy_block_element(pointer first, pointer last) noexcept - { - for (; first != last; ++first) - { - first->~value_type(); - } - } - inline constexpr void destroy_all_elements() noexcept { - destroy_block_element(controller.front_block.curr_ptr, controller.front_block.end_ptr); + ::std::ranges::destroy(controller.front_block.curr_ptr, controller.front_block.end_ptr); auto front_controller_ptr{controller.front_block.controller_ptr}; auto back_controller_ptr{controller.back_block.controller_ptr}; if (front_controller_ptr != back_controller_ptr) @@ -659,10 +510,10 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE for (T **it{front_controller_ptr + 1}, **ed{back_controller_ptr}; it != ed; ++it) { T *blockptr{*it}; - destroy_block_element(blockptr, blockptr + block_size); + ::std::ranges::destroy(blockptr, blockptr + block_size); } } - destroy_block_element(controller.back_block.begin_ptr, controller.back_block.curr_ptr); + ::std::ranges::destroy(controller.back_block.begin_ptr, controller.back_block.curr_ptr); } inline constexpr void destroy() noexcept @@ -713,7 +564,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } else { - ::fast_io::containers::details::deque_grow_back_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller))); + ::fast_io::containers::details::deque_grow_back_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller))); } } From d8ff7724500019f6be53dc482e9e0eff2af12800 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 1 Jan 2026 12:26:51 +0800 Subject: [PATCH 02/26] first commit to move the implementation to another machine --- include/fast_io_dsal/impl/deque.h | 4 +++- tests/0026.container/0003.deque/.test_prop.toml | 6 ++++++ tests/0026.container/0003.deque/test_deque.cc | 2 ++ .../0026.container/0003.deque/test_push_back.cc | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/0026.container/0003.deque/test_push_back.cc diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 08b628ff6..c4d376e71 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -411,7 +411,9 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont ::std::size_t const old_front_controller_ptr_pos{controller.front_controller.controller_ptr - old_start_ptr}; ::std::size_t const old_back_controller_ptr_pos{controller.back_controller.controller_ptr - old_start_ptr}; - using block_typed_allocator = ::fast_io::typed_allocator; + ::std::size_t const offset_to_start_reserved{old_start_ptr - controller.controller_block.controller_after_ptr}; + + using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least); /* Unfinished diff --git a/tests/0026.container/0003.deque/.test_prop.toml b/tests/0026.container/0003.deque/.test_prop.toml index fdcbe2ec8..3c1697cd1 100644 --- a/tests/0026.container/0003.deque/.test_prop.toml +++ b/tests/0026.container/0003.deque/.test_prop.toml @@ -1,2 +1,8 @@ ["dequeasm.cc"] # no auto-gen test. only to see the output assembly ignore = true +["deque_push.cc"] +ignore = true +["test_deque.cc"] +ignore = true +["test_push_back.cc"] +ignore = true diff --git a/tests/0026.container/0003.deque/test_deque.cc b/tests/0026.container/0003.deque/test_deque.cc index abb3c2a4d..c80e4c868 100644 --- a/tests/0026.container/0003.deque/test_deque.cc +++ b/tests/0026.container/0003.deque/test_deque.cc @@ -6,10 +6,12 @@ int main() ::fast_io::deque dq; dq.push_back(4); dq.push_back(6); +#if 0 static_assert(::std::random_access_iterator); ::fast_io::io::println("diff:", dq.end() - dq.begin()); for (auto const &e : dq) { ::fast_io::io::println(e); } +#endif } diff --git a/tests/0026.container/0003.deque/test_push_back.cc b/tests/0026.container/0003.deque/test_push_back.cc new file mode 100644 index 000000000..c80e4c868 --- /dev/null +++ b/tests/0026.container/0003.deque/test_push_back.cc @@ -0,0 +1,17 @@ +#include +#include + +int main() +{ + ::fast_io::deque dq; + dq.push_back(4); + dq.push_back(6); +#if 0 + static_assert(::std::random_access_iterator); + ::fast_io::io::println("diff:", dq.end() - dq.begin()); + for (auto const &e : dq) + { + ::fast_io::io::println(e); + } +#endif +} From 7fbe6e0c9f59865741cb7d24a8bca9076a1f3c98 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 1 Jan 2026 18:21:48 +0800 Subject: [PATCH 03/26] [skip ci] add partial implementation of deque.h --- include/fast_io_dsal/impl/deque.h | 46 +++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index c4d376e71..515373612 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -40,8 +40,8 @@ struct using replacetype = char unsigned; void **controller_start_ptr; void **controller_start_reserved_ptr; - void **controller_after_ptr; void **controller_after_reserved_ptr; + void **controller_after_ptr; }; template @@ -50,8 +50,8 @@ struct deque_controller_block using replacetype = T; T **controller_start_ptr; T **controller_start_reserved_ptr; - T **controller_after_ptr; T **controller_after_reserved_ptr; + T **controller_after_ptr; }; template @@ -438,25 +438,59 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt ::std::size_t const total_blocks_half{static_cast<::std::size_t>(total_blocks >> 1u)}; if (used_blocks_count < total_blocks_half) { +#if 0 ::std::size_t const reserved_blocks_count{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.controller_block.controller_ptr) + 1zu}; +#endif } else { } } +template +inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept +{ + using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; + auto [allocated_blocks_ptr, allocated_blocks_count] = block_typed_allocator::allocate_at_least(1); + auto &controller_block{controller.controller_block}; + auto &front_block{controller.front_block}; + auto &back_block{controller.back_block}; + + constexpr bool isvoidplaceholder{::std::same_as}; + using begin_ptrtype = ::std::conditional_t; + + auto begin_ptr{static_cast(allocator::allocate_aligned(align, bytes))}; + + back_block.controller_ptr = front_block.controller_ptr = controller_block.controller_start_reserved_ptr = controller_block.controller_start_ptr = ::std::construct_at(allocated_blocks_ptr, begin_ptr); + + controller_block.controller_after_reserved_ptr = allocated_blocks_ptr + 1; + controller_block.controller_after_ptr = allocated_blocks_ptr + allocated_blocks_count; + ::std::size_t halfsize{bytes >> 1u}; + + back_block.begin_ptr = front_block.begin_ptr = begin_ptr; + back_block.end_ptr = front_block.end_ptr = (begin_ptr + bytes); + back_block.curr_ptr = front_block.curr_ptr = (begin_ptr + halfsize); +} + template inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { - ::std::size_t const diff_to_after_ptr{controller.controller_block.controller_after_ptr - controller.back_block.controller_ptr}; + ::std::size_t const diff_to_after_ptr{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.back_block.controller_ptr)}; if (diff_to_after_ptr < 2zu) { - if (controller.controller_after_reserved_ptr == controller.controller_after_ptr) + if (!diff_to_after_ptr) { - ::fast_io::containers::details::deque_rebalance_or_grow_2x_blocks_impl(controller); + ::fast_io::containers::details::deque_allocate_on_empty_common_impl(controller, align, bytes); + return; + } + if (controller.controller_block.controller_after_reserved_ptr == controller.controller_block.controller_after_ptr) + { + ::fast_io::containers::details::deque_rebalance_or_grow_2x_after_blocks_impl(controller); } } - controller.back_block.end_ptr = ((controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr))) + sz); + /* + controller.back_block.end_ptr = ((controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr))) + bytes); + */ } template From 56ee798ab98929c1b544317ebe1b1dd97d6bb330 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 1 Jan 2026 18:47:40 +0800 Subject: [PATCH 04/26] [skip ci] implement deque emplace_back when we have zero blocks --- include/fast_io_dsal/impl/deque.h | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 515373612..63bc22545 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -469,7 +469,9 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont back_block.begin_ptr = front_block.begin_ptr = begin_ptr; back_block.end_ptr = front_block.end_ptr = (begin_ptr + bytes); - back_block.curr_ptr = front_block.curr_ptr = (begin_ptr + halfsize); + auto halfposptr{begin_ptr + halfsize}; + front_block.curr_ptr = halfposptr; + back_block.curr_ptr = halfposptr; } template @@ -638,11 +640,8 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE grow_back(); } auto currptr{controller.back_block.curr_ptr}; - if constexpr (!::std::is_trivially_constructible_v) - { - ::std::construct_at(currptr, ::std::forward(args)...); - } - ++controller.back_block.curr_ptr; + ::std::construct_at(currptr, ::std::forward(args)...); + controller.back_block.curr_ptr = currptr + 1; return *currptr; } @@ -716,10 +715,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { grow_front(); } - if constexpr (!::std::is_trivially_constructible_v) - { - ::std::construct_at(--controller.front_block.curr_ptr, ::std::forward(args)...); - } + ::std::construct_at(--controller.front_block.curr_ptr, ::std::forward(args)...); return *controller.front_block.curr_ptr; } From 0ee51541d5b3fdbe9bfefb3b138a0a451f913c3f Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 1 Jan 2026 20:03:27 +0800 Subject: [PATCH 05/26] [skip CI] simplify logic of destruction of deque --- include/fast_io_dsal/impl/deque.h | 74 +++++++++---------------------- 1 file changed, 20 insertions(+), 54 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 63bc22545..f8370e38c 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -272,70 +272,30 @@ inline constexpr auto operator<=>(::fast_io::containers::details::deque_iterator return block3way; } -template -inline constexpr void deque_destroy_controller(controllerblocktype *controllerptr) noexcept -{ - auto &controller{*controllerptr}; - if constexpr (allocator::has_deallocate) - { - allocator::deallocate(controller.controller_start_ptr); - } - else - { - ::std::size_t n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr) * sizeof(void *)}; - allocator::deallocate_n(controller.controller_start_ptr, n); - } - - controller.controller_start_ptr = nullptr; -} template -inline constexpr void deque_destroy_trivial_common_align(controllerblocktype *controllerptr, ::std::size_t aligns, ::std::size_t totalsz) noexcept +inline constexpr void deque_destroy_trivial_common_align(controllerblocktype &controller, ::std::size_t aligns, ::std::size_t totalsz) noexcept { - auto &controller_block{*controllerptr}; - for (auto i{controller_block.controller_start_reserved_ptr}, e{controller_block.controller_after_reserved_ptr}; i != e; ++i) + for (auto i{controller.controller_start_reserved_ptr}, e{controller.controller_after_reserved_ptr}; i != e; ++i) { allocator::deallocate_aligned_n(*i, aligns, totalsz); } - deque_destroy_controller(controllerptr); -} - -template -inline constexpr void deque_destroy_trivial_common_no_align(controllerblocktype *controllerptr, ::std::size_t totalsz) noexcept -{ - auto &controller_block{*controllerptr}; - for (auto i{controller_block.controller_start_reserved_ptr}, e{controller_block.controller_after_reserved_ptr}; i != e; ++i) - { - allocator::deallocate_n(*i, totalsz); - } - deque_destroy_controller(controllerptr); -} - -template -inline constexpr void deque_destroy_trivial_common_impl(controllerblocktype *controllerptr) noexcept -{ - constexpr ::std::size_t totalsz{sz * ::fast_io::containers::details::deque_block_size}; - if constexpr (align <= allocator::default_alignment) - { - ::fast_io::containers::details::deque_destroy_trivial_common_no_align(controllerptr, totalsz); - } - else - { - ::fast_io::containers::details::deque_destroy_trivial_common_align(controllerptr, align, totalsz); - } + ::std::size_t const n{static_cast<::std::size_t>(controller.controller_after_ptr - controller.controller_start_ptr + 1) * sizeof(void *)}; + allocator::deallocate_n(controller.controller_start_ptr, n); } template inline constexpr void deque_destroy_trivial_common(controllerblocktype &controller) noexcept { - if (__builtin_is_constant_evaluated()) + constexpr ::std::size_t totalsz{sz * ::fast_io::containers::details::deque_block_size}; + if consteval { - ::fast_io::containers::details::deque_destroy_trivial_common_impl(__builtin_addressof(controller)); + ::fast_io::containers::details::deque_destroy_trivial_common_align(controller, align, totalsz); } else { - ::fast_io::containers::details::deque_destroy_trivial_common_impl( - reinterpret_cast<::fast_io::containers::details::deque_controller_block_common *>(__builtin_addressof(controller))); + ::fast_io::containers::details::deque_destroy_trivial_common_align( + *reinterpret_cast<::fast_io::containers::details::deque_controller_block_common *>(__builtin_addressof(controller)), align, totalsz); } } @@ -451,7 +411,9 @@ template inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; - auto [allocated_blocks_ptr, allocated_blocks_count] = block_typed_allocator::allocate_at_least(1); + auto [allocated_blocks_ptr, allocated_blocks_count] = block_typed_allocator::allocate_at_least(2); + // we need a null terminator as sentinel like c style string does + --allocated_blocks_count; auto &controller_block{controller.controller_block}; auto &front_block{controller.front_block}; auto &back_block{controller.back_block}; @@ -463,7 +425,8 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont back_block.controller_ptr = front_block.controller_ptr = controller_block.controller_start_reserved_ptr = controller_block.controller_start_ptr = ::std::construct_at(allocated_blocks_ptr, begin_ptr); - controller_block.controller_after_reserved_ptr = allocated_blocks_ptr + 1; + *(controller_block.controller_after_reserved_ptr = allocated_blocks_ptr + 1) = nullptr; + controller_block.controller_after_ptr = allocated_blocks_ptr + allocated_blocks_count; ::std::size_t halfsize{bytes >> 1u}; @@ -490,9 +453,12 @@ inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller, ::fast_io::containers::details::deque_rebalance_or_grow_2x_after_blocks_impl(controller); } } - /* - controller.back_block.end_ptr = ((controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr))) + bytes); - */ + + constexpr bool isvoidplaceholder{::std::same_as}; + using begin_ptrtype = ::std::conditional_t; + auto begin_ptr{static_cast(controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr)))}; + + controller.back_block.end_ptr = (begin_ptr + bytes); } template From 82af04033aa2502b976200ddc724d9656d016640 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 1 Jan 2026 21:11:08 +0800 Subject: [PATCH 06/26] [skip CI]add logic for double the blocks --- include/fast_io_dsal/impl/deque.h | 59 ++++++++++++++++++------------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index f8370e38c..405c27e14 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -368,39 +368,44 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont ::std::size_t const old_start_reserved_ptr_pos{static_cast<::std::size_t>(old_start_reserved_ptr - old_start_ptr)}; ::std::size_t const old_after_reserved_ptr_pos{static_cast<::std::size_t>(old_after_reserved_ptr - old_start_ptr)}; ::std::size_t const old_after_ptr_pos{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - old_start_ptr)}; - ::std::size_t const old_front_controller_ptr_pos{controller.front_controller.controller_ptr - old_start_ptr}; - ::std::size_t const old_back_controller_ptr_pos{controller.back_controller.controller_ptr - old_start_ptr}; - - ::std::size_t const offset_to_start_reserved{old_start_ptr - controller.controller_block.controller_after_ptr}; + ::std::size_t const old_front_block_ptr_pos{static_cast<::std::size_t>(controller.front_block.controller_ptr - old_start_ptr)}; + ::std::size_t const old_back_block_ptr_pos{static_cast<::std::size_t>(controller.back_block.controller_ptr - old_start_ptr)}; + ::std::size_t const offset_to_start_reserved{static_cast<::std::size_t>(old_start_ptr - controller.controller_block.controller_after_ptr)}; using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; - auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least); + auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least + 1zu); /* Unfinished */ ::std::size_t const new_blocks_offset{(new_blocks_count >> 2u)}; auto new_start_reserved_ptr{new_start_ptr + (old_start_reserved_ptr_pos + new_blocks_offset)}; auto new_after_reserved_ptr{::std::uninitialized_copy(old_start_reserved_ptr, old_after_reserved_ptr, new_start_reserved_ptr)}; - block_typed_allocator::deallocate_n(old_start_ptr, old_after_ptr_pos); + block_typed_allocator::deallocate_n(old_start_ptr, old_after_ptr_pos + 1zu); controller.controller_block.controller_start_ptr = new_start_ptr; controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; - controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr; + *(controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr) = nullptr; controller.controller_block.controller_after_ptr = new_start_ptr + new_blocks_count; - controller.front_controller.controller_ptr = new_start_ptr + (old_front_controller_ptr_pos + new_blocks_offset); - controller.back_controller.controller_ptr = new_start_ptr + (old_back_controller_ptr_pos + new_blocks_offset); + controller.front_block.controller_ptr = new_start_ptr + (old_front_block_ptr_pos + new_blocks_offset); + controller.back_block.controller_ptr = new_start_ptr + (old_back_block_ptr_pos + new_blocks_offset); } template inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontroltype &controller) noexcept { ::std::size_t const used_blocks_count{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; - ::std::size_t const total_blocks{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; - ::std::size_t const total_blocks_half{static_cast<::std::size_t>(total_blocks >> 1u)}; - if (used_blocks_count < total_blocks_half) + ::std::size_t const total_blocks{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_block.controller_start_ptr)}; + ::std::size_t const total_blocks_half{total_blocks >> 1u}; + if (total_blocks_half < used_blocks_count) { -#if 0 - ::std::size_t const reserved_blocks_count{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.controller_block.controller_ptr) + 1zu}; -#endif + constexpr ::std::size_t maxcount{static_cast<::std::size_t>(::std::numeric_limits<::std::size_t>::max() / sizeof(void *))}; + constexpr ::std::size_t mxtoalign{static_cast<::std::size_t>(maxcount - 1u)}; + constexpr ::std::size_t mxtoaligndiv2{mxtoalign >> 1u}; + if (mxtoaligndiv2 < total_blocks) + { + ::fast_io::fast_terminate(); + } + ::std::size_t const toallocate{total_blocks << 1}; + deque_grow_to_new_blocks_count_impl(controller, toallocate); } else { @@ -425,7 +430,7 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont back_block.controller_ptr = front_block.controller_ptr = controller_block.controller_start_reserved_ptr = controller_block.controller_start_ptr = ::std::construct_at(allocated_blocks_ptr, begin_ptr); - *(controller_block.controller_after_reserved_ptr = allocated_blocks_ptr + 1) = nullptr; + *(controller_block.controller_after_reserved_ptr = allocated_blocks_ptr + 1) = nullptr; // set nullptr as a sentinel controller_block.controller_after_ptr = allocated_blocks_ptr + allocated_blocks_count; ::std::size_t halfsize{bytes >> 1u}; @@ -440,22 +445,28 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont template inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { - ::std::size_t const diff_to_after_ptr{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.back_block.controller_ptr)}; + if (controller.controller_block.controller_start_ptr == nullptr) + { + ::fast_io::containers::details::deque_allocate_on_empty_common_impl(controller, align, bytes); + return; + } + ::std::size_t const diff_to_after_ptr{static_cast<::std::size_t>(controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr)}; + constexpr bool isvoidplaceholder{::std::same_as}; + using begin_ptrtype = ::std::conditional_t; + if (diff_to_after_ptr < 2zu) { if (!diff_to_after_ptr) - { - ::fast_io::containers::details::deque_allocate_on_empty_common_impl(controller, align, bytes); - return; - } - if (controller.controller_block.controller_after_reserved_ptr == controller.controller_block.controller_after_ptr) { ::fast_io::containers::details::deque_rebalance_or_grow_2x_after_blocks_impl(controller); } + constexpr bool isvoidplaceholder{::std::same_as}; + using begin_ptrtype = ::std::conditional_t; + + auto begin_ptr{static_cast(allocator::allocate_aligned(align, bytes))}; + *(controller.controller_block.controller_after_reserved_ptr = (::std::construct_at(controller.controller_block.controller_after_reserved_ptr, begin_ptr)) + 1) = nullptr; } - constexpr bool isvoidplaceholder{::std::same_as}; - using begin_ptrtype = ::std::conditional_t; auto begin_ptr{static_cast(controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr)))}; controller.back_block.end_ptr = (begin_ptr + bytes); From 7e8f45595ba7e21ffade68c27cd78923d20ddaee Mon Sep 17 00:00:00 2001 From: trcrsired Date: Sat, 3 Jan 2026 04:23:54 +0800 Subject: [PATCH 07/26] [skip CI] add some implementation for deque --- include/fast_io_dsal/impl/deque.h | 139 ++++++++++++++++++++++++------ 1 file changed, 113 insertions(+), 26 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 405c27e14..221f4a65a 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -365,53 +365,108 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont auto old_start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; auto old_after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; + ::std::size_t const old_start_reserved_ptr_pos{static_cast<::std::size_t>(old_start_reserved_ptr - old_start_ptr)}; ::std::size_t const old_after_reserved_ptr_pos{static_cast<::std::size_t>(old_after_reserved_ptr - old_start_ptr)}; ::std::size_t const old_after_ptr_pos{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - old_start_ptr)}; ::std::size_t const old_front_block_ptr_pos{static_cast<::std::size_t>(controller.front_block.controller_ptr - old_start_ptr)}; ::std::size_t const old_back_block_ptr_pos{static_cast<::std::size_t>(controller.back_block.controller_ptr - old_start_ptr)}; - ::std::size_t const offset_to_start_reserved{static_cast<::std::size_t>(old_start_ptr - controller.controller_block.controller_after_ptr)}; using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least + 1zu); - /* - Unfinished - */ - ::std::size_t const new_blocks_offset{(new_blocks_count >> 2u)}; - auto new_start_reserved_ptr{new_start_ptr + (old_start_reserved_ptr_pos + new_blocks_offset)}; + + // number of used slots (excluding sentinel) + ::std::size_t const used_blocks_count{old_after_reserved_ptr_pos - old_start_reserved_ptr_pos}; + + // put used blocks roughly in the middle, with some headroom on both sides + ::std::size_t const new_blocks_offset{(new_blocks_count - used_blocks_count) >> 1u}; + + auto new_start_reserved_ptr{new_start_ptr + new_blocks_offset}; auto new_after_reserved_ptr{::std::uninitialized_copy(old_start_reserved_ptr, old_after_reserved_ptr, new_start_reserved_ptr)}; + + // sentinel + *new_after_reserved_ptr = nullptr; + ++new_after_reserved_ptr; + + // free old controller array block_typed_allocator::deallocate_n(old_start_ptr, old_after_ptr_pos + 1zu); + + // rebuild controller bookkeeping controller.controller_block.controller_start_ptr = new_start_ptr; controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; - *(controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr) = nullptr; - controller.controller_block.controller_after_ptr = new_start_ptr + new_blocks_count; - controller.front_block.controller_ptr = new_start_ptr + (old_front_block_ptr_pos + new_blocks_offset); - controller.back_block.controller_ptr = new_start_ptr + (old_back_block_ptr_pos + new_blocks_offset); + controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr - 1; + controller.controller_block.controller_after_ptr = new_start_ptr + (new_blocks_count - 1zu); + + // adjust front/back controller_ptr + controller.front_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_front_block_ptr_pos - old_start_reserved_ptr_pos)); + controller.back_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_back_block_ptr_pos - old_start_reserved_ptr_pos)); } template inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontroltype &controller) noexcept { - ::std::size_t const used_blocks_count{static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; - ::std::size_t const total_blocks{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_block.controller_start_ptr)}; + ::std::size_t const used_blocks_count{ + static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; + + ::std::size_t const total_blocks{ + static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_block.controller_start_ptr)}; + ::std::size_t const total_blocks_half{total_blocks >> 1u}; + if (total_blocks_half < used_blocks_count) { - constexpr ::std::size_t maxcount{static_cast<::std::size_t>(::std::numeric_limits<::std::size_t>::max() / sizeof(void *))}; + constexpr ::std::size_t maxcount{ + static_cast<::std::size_t>(::std::numeric_limits<::std::size_t>::max() / sizeof(void *))}; constexpr ::std::size_t mxtoalign{static_cast<::std::size_t>(maxcount - 1u)}; constexpr ::std::size_t mxtoaligndiv2{mxtoalign >> 1u}; + if (mxtoaligndiv2 < total_blocks) { ::fast_io::fast_terminate(); } - ::std::size_t const toallocate{total_blocks << 1}; + + ::std::size_t const toallocate{total_blocks << 1u}; deque_grow_to_new_blocks_count_impl(controller, toallocate); } else { + // Rebalance in-place: move reserved range toward the middle + auto start_ptr{controller.controller_block.controller_start_ptr}; + auto start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; + auto after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; + + ::std::size_t const old_start_reserved_pos{static_cast<::std::size_t>(start_reserved_ptr - start_ptr)}; + ::std::size_t const old_after_reserved_pos{static_cast<::std::size_t>(after_reserved_ptr - start_ptr)}; + ::std::size_t const used{old_after_reserved_pos - old_start_reserved_pos}; + + ::std::size_t const new_start_reserved_pos{(total_blocks - used) >> 1u}; + auto new_start_reserved_ptr{start_ptr + new_start_reserved_pos}; + + if (new_start_reserved_ptr != start_reserved_ptr) + { + if (start_reserved_ptr < new_start_reserved_ptr) + { + ::std::copy_backward(start_reserved_ptr, after_reserved_ptr, new_start_reserved_ptr + used); + } + else + { + ::std::copy(start_reserved_ptr, after_reserved_ptr, new_start_reserved_ptr); + } + } + + controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; + controller.controller_block.controller_after_reserved_ptr = new_start_reserved_ptr + used; + *(controller.controller_block.controller_after_reserved_ptr) = nullptr; + + // adjust front/back controller_ptr according to the shift + ::std::size_t const delta{new_start_reserved_pos - old_start_reserved_pos}; + + controller.front_block.controller_ptr += delta; + controller.back_block.controller_ptr += delta; } } + template inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { @@ -443,35 +498,67 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont } template -inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept +inline constexpr void deque_grow_back_common_impl( + dequecontroltype &controller, + ::std::size_t align, + ::std::size_t bytes) noexcept { + // Case 1: empty deque → allocate initial controller + one block if (controller.controller_block.controller_start_ptr == nullptr) { - ::fast_io::containers::details::deque_allocate_on_empty_common_impl(controller, align, bytes); + ::fast_io::containers::details:: + deque_allocate_on_empty_common_impl(controller, align, bytes); return; } - ::std::size_t const diff_to_after_ptr{static_cast<::std::size_t>(controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr)}; - constexpr bool isvoidplaceholder{::std::same_as}; - using begin_ptrtype = ::std::conditional_t; + using replacetype = typename dequecontroltype::replacetype; + constexpr bool isvoidplaceholder = ::std::same_as; + using begin_ptrtype = + ::std::conditional_t; + + // How many controller slots remain before hitting controller_after_reserved_ptr? + ::std::size_t const diff_to_after_ptr = + static_cast<::std::size_t>( + controller.controller_block.controller_after_reserved_ptr - + controller.back_block.controller_ptr); + + // Need at least 2 slots: one for new block, one for sentinel if (diff_to_after_ptr < 2zu) { + // No space at all → must rebalance or grow controller array if (!diff_to_after_ptr) { - ::fast_io::containers::details::deque_rebalance_or_grow_2x_after_blocks_impl(controller); + ::fast_io::containers::details:: + deque_rebalance_or_grow_2x_after_blocks_impl(controller); } - constexpr bool isvoidplaceholder{::std::same_as}; - using begin_ptrtype = ::std::conditional_t; - auto begin_ptr{static_cast(allocator::allocate_aligned(align, bytes))}; - *(controller.controller_block.controller_after_reserved_ptr = (::std::construct_at(controller.controller_block.controller_after_reserved_ptr, begin_ptr)) + 1) = nullptr; + // Now we have at least 1 slot; allocate a new block and append it + auto new_block = + static_cast(allocator::allocate_aligned(align, bytes)); + + // Write block pointer at controller_after_reserved_ptr + auto pos = controller.controller_block.controller_after_reserved_ptr; + ::std::construct_at(pos, new_block); + + // Advance after_reserved_ptr and write sentinel + *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; } - auto begin_ptr{static_cast(controller.back_block.curr_ptr = (controller.back_block.begin_ptr = (*++controller.back_block.controller_ptr)))}; + // Now we definitely have space for a new block pointer + // Advance controller pointer to the next block slot + ++controller.back_block.controller_ptr; - controller.back_block.end_ptr = (begin_ptr + bytes); + // Load the block pointer + auto begin_ptr = + static_cast(*controller.back_block.controller_ptr); + + // Update block range + controller.back_block.begin_ptr = begin_ptr; + controller.back_block.curr_ptr = begin_ptr; + controller.back_block.end_ptr = begin_ptr + bytes; } + template inline constexpr void deque_grow_front_common(dequecontroltype &controller) noexcept { From 1dd1e5e9b6857ec84d28e9770029438233ad0271 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Sat, 3 Jan 2026 04:45:56 +0800 Subject: [PATCH 08/26] [skip CI] implement deque's emplace_back --- include/fast_io_dsal/impl/deque.h | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 221f4a65a..4b7dac003 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -374,28 +374,27 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least + 1zu); + --new_blocks_count; // number of used slots (excluding sentinel) ::std::size_t const used_blocks_count{old_after_reserved_ptr_pos - old_start_reserved_ptr_pos}; // put used blocks roughly in the middle, with some headroom on both sides - ::std::size_t const new_blocks_offset{(new_blocks_count - used_blocks_count) >> 1u}; + ::std::size_t const new_blocks_offset{static_cast<::std::size_t>(new_blocks_count - used_blocks_count) >> 1u}; auto new_start_reserved_ptr{new_start_ptr + new_blocks_offset}; auto new_after_reserved_ptr{::std::uninitialized_copy(old_start_reserved_ptr, old_after_reserved_ptr, new_start_reserved_ptr)}; // sentinel *new_after_reserved_ptr = nullptr; - ++new_after_reserved_ptr; - // free old controller array block_typed_allocator::deallocate_n(old_start_ptr, old_after_ptr_pos + 1zu); // rebuild controller bookkeeping controller.controller_block.controller_start_ptr = new_start_ptr; controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; - controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr - 1; - controller.controller_block.controller_after_ptr = new_start_ptr + (new_blocks_count - 1zu); + controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr; + controller.controller_block.controller_after_ptr = new_start_ptr + new_blocks_count; // adjust front/back controller_ptr controller.front_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_front_block_ptr_pos - old_start_reserved_ptr_pos)); @@ -430,6 +429,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt } else { + // Rebalance in-place: move reserved range toward the middle auto start_ptr{controller.controller_block.controller_start_ptr}; auto start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; @@ -526,11 +526,8 @@ inline constexpr void deque_grow_back_common_impl( if (diff_to_after_ptr < 2zu) { // No space at all → must rebalance or grow controller array - if (!diff_to_after_ptr) - { - ::fast_io::containers::details:: - deque_rebalance_or_grow_2x_after_blocks_impl(controller); - } + ::fast_io::containers::details:: + deque_rebalance_or_grow_2x_after_blocks_impl(controller); // Now we have at least 1 slot; allocate a new block and append it auto new_block = @@ -541,7 +538,7 @@ inline constexpr void deque_grow_back_common_impl( ::std::construct_at(pos, new_block); // Advance after_reserved_ptr and write sentinel - *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; + *(controller.controller_block.controller_after_reserved_ptr = pos + 1u) = nullptr; } // Now we definitely have space for a new block pointer From e7b05020bcdc522541f33d44c12bd20f28b53c3d Mon Sep 17 00:00:00 2001 From: trcrsired Date: Sat, 3 Jan 2026 05:23:10 +0800 Subject: [PATCH 09/26] [skip CI] implement a draft of grow_front which is very wrong --- include/fast_io_dsal/impl/deque.h | 115 +++++++++--------- .../0026.container/0003.deque/.test_prop.toml | 2 + .../0003.deque/test_push_front.cc | 19 +++ 3 files changed, 76 insertions(+), 60 deletions(-) create mode 100644 tests/0026.container/0003.deque/test_push_front.cc diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 4b7dac003..fc7d48c8f 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -299,50 +299,6 @@ inline constexpr void deque_destroy_trivial_common(controllerblocktype &controll } } -template -inline constexpr void deque_init_grow_common_controllerallocate_impl(dequecontroltype &controller, ::std::size_t total_block_size, ::std::size_t mid, replacetype *blockptr) noexcept -{ - using controlreplacetype = typename dequecontroltype::controlreplacetype; - constexpr ::std::size_t allocatesize{sizeof(controlreplacetype) * 4}; - auto controllerstartptr{static_cast(allocator::allocate(allocatesize))}; - controller.controller_block.controller_start_ptr = controllerstartptr; - controller.controller_block.controller_after_reserved_ptr = (controller.controller_block.controller_start_reserved_ptr = controller.back_block.controller_ptr = controller.front_block.controller_ptr = controllerstartptr + 1) + 1; - controller.controller_block.controller_after_ptr = controllerstartptr + 3u; - *controller.back_block.controller_ptr = blockptr; - controller.front_block.begin_ptr = controller.back_block.begin_ptr = blockptr; - controller.back_block.curr_ptr = controller.front_block.curr_ptr = blockptr + mid; - controller.front_block.end_ptr = controller.back_block.end_ptr = blockptr + total_block_size; -} - -template -inline constexpr void deque_init_grow_common_noalign_impl(dequecontroltype &controller, ::std::size_t total_block_size, ::std::size_t mid) noexcept -{ - ::fast_io::containers::details::deque_init_grow_common_controllerallocate_impl(controller, total_block_size, mid, static_cast(allocator::allocate_zero(total_block_size))); -} - -template -inline constexpr void deque_init_grow_common_align_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t total_block_size, ::std::size_t mid) noexcept -{ - ::fast_io::containers::details::deque_init_grow_common_controllerallocate_impl(controller, total_block_size, mid, static_cast(allocator::allocate_zero_aligned(align, total_block_size))); -} - -template -inline constexpr void deque_init_grow_common(dequecontroltype &controller) noexcept -{ - if constexpr (align <= allocator::default_alignment) - { - ::fast_io::containers::details::deque_init_grow_common_noalign_impl(controller, block_size, mid); - } - else - { - ::fast_io::containers::details::deque_init_grow_common_align_impl(controller, align, block_size, mid); - } -} - -template -inline constexpr void deque_grow_front_common_impl(dequecontroltype &controller) noexcept -{ -} /* struct #if __has_cpp_attribute(__gnu__::__may_alias__) @@ -466,7 +422,6 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt } } - template inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { @@ -555,11 +510,64 @@ inline constexpr void deque_grow_back_common_impl( controller.back_block.end_ptr = begin_ptr + bytes; } +template +inline constexpr void deque_grow_front_common_impl( + dequecontroltype &controller, + std::size_t align, + std::size_t bytes) noexcept +{ + using replacetype = typename dequecontroltype::replacetype; + constexpr bool isvoidplaceholder = std::same_as; + using begin_ptrtype = + std::conditional_t; + + auto &cb = controller.controller_block; + + // Case 1: empty deque → same as back + if (cb.controller_start_ptr == nullptr) + { + ::fast_io::containers::details:: + deque_allocate_on_empty_common_impl(controller, align, bytes); + return; + } + + // How many free controller slots exist before front_block.controller_ptr? + std::size_t diff_to_start_reserved = + static_cast(controller.front_block.controller_ptr - + cb.controller_start_reserved_ptr); + + // Need at least 1 free slot to prepend a block + if (!diff_to_start_reserved) + { + ::fast_io::containers::details:: + deque_rebalance_or_grow_2x_after_blocks_impl(controller); + // Now we definitely have space + auto begin_ptr = + static_cast(allocator::allocate_aligned(align, bytes)); + + // Insert BEFORE current front block + auto new_slot = controller.front_block.controller_ptr - 1; + std::construct_at(new_slot, begin_ptr); + } + + auto new_slot{controller.front_block.controller_ptr - 1}; + auto begin_ptr{static_cast(*new_slot)}; + // Move front_block.controller_ptr to new block + controller.front_block.controller_ptr = new_slot; + + // Update block pointers + controller.front_block.begin_ptr = begin_ptr; + controller.front_block.end_ptr = begin_ptr + bytes; + + // curr_ptr starts at end (push_front will --curr_ptr) + controller.front_block.curr_ptr = controller.front_block.end_ptr; +} template inline constexpr void deque_grow_front_common(dequecontroltype &controller) noexcept { - ::fast_io::containers::details::deque_grow_front_common_impl(controller); + constexpr ::std::size_t blockbytes{sz * block_size}; + ::fast_io::containers::details::deque_grow_front_common_impl(controller, align, blockbytes); } template @@ -624,19 +632,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::fast_io::containers::details::deque_destroy_trivial_common(controller.controller_block); } - [[deprecated]] inline constexpr void init_grow() noexcept - { - constexpr size_type mid{block_size >> 1u}; - if (__builtin_is_constant_evaluated()) - { - ::fast_io::containers::details::deque_init_grow_common(controller); - } - else - { - ::fast_io::containers::details::deque_init_grow_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller))); - } - } - #if __has_cpp_attribute(__gnu__::__cold__) [[__gnu__::__cold__]] #endif @@ -648,7 +643,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } else { - ::fast_io::containers::details::deque_grow_front_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller))); + ::fast_io::containers::details::deque_grow_front_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller))); } } diff --git a/tests/0026.container/0003.deque/.test_prop.toml b/tests/0026.container/0003.deque/.test_prop.toml index 3c1697cd1..f60200299 100644 --- a/tests/0026.container/0003.deque/.test_prop.toml +++ b/tests/0026.container/0003.deque/.test_prop.toml @@ -6,3 +6,5 @@ ignore = true ignore = true ["test_push_back.cc"] ignore = true +["test_push_front.cc"] +ignore = true diff --git a/tests/0026.container/0003.deque/test_push_front.cc b/tests/0026.container/0003.deque/test_push_front.cc new file mode 100644 index 000000000..35337bb8f --- /dev/null +++ b/tests/0026.container/0003.deque/test_push_front.cc @@ -0,0 +1,19 @@ +#include +#include + +int main() +{ + ::fast_io::deque<::std::uint_least32_t> dq; + for(::std::uint_least32_t i{};i!=1048576u;++i) + { + dq.push_front(i); + } + + static_assert(::std::random_access_iterator); +#if 1 + for (auto const &e : dq) + { + ::fast_io::io::println(e); + } +#endif +} From e1d034c2d9851bd333e7e07912495d77a4ed2868 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Sat, 3 Jan 2026 06:04:49 +0800 Subject: [PATCH 10/26] [skip CI] add borrow logic and write better comments --- include/fast_io_dsal/impl/deque.h | 103 ++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 25 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index fc7d48c8f..4cbdc0d03 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -455,10 +455,14 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont template inline constexpr void deque_grow_back_common_impl( dequecontroltype &controller, - ::std::size_t align, - ::std::size_t bytes) noexcept + std::size_t align, + std::size_t bytes) noexcept { - // Case 1: empty deque → allocate initial controller + one block + /** + * If the deque is empty, allocate the initial controller array + * and a single data block. This sets up the initial front/back + * block pointers and the sentinel. + */ if (controller.controller_block.controller_start_ptr == nullptr) { ::fast_io::containers::details:: @@ -467,44 +471,93 @@ inline constexpr void deque_grow_back_common_impl( } using replacetype = typename dequecontroltype::replacetype; - constexpr bool isvoidplaceholder = ::std::same_as; + constexpr bool isvoidplaceholder = std::same_as; using begin_ptrtype = - ::std::conditional_t; + std::conditional_t; - // How many controller slots remain before hitting controller_after_reserved_ptr? - ::std::size_t const diff_to_after_ptr = - static_cast<::std::size_t>( + /** + * Compute how many controller slots remain between the current + * back block and controller_after_reserved_ptr. + * + * We require at least: + * - 1 slot for the new block pointer + * - 1 slot for the sentinel nullptr + */ + std::size_t diff_to_after_ptr = + static_cast( controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr); - // Need at least 2 slots: one for new block, one for sentinel - if (diff_to_after_ptr < 2zu) + if (diff_to_after_ptr < 2) { - // No space at all → must rebalance or grow controller array - ::fast_io::containers::details:: - deque_rebalance_or_grow_2x_after_blocks_impl(controller); + /** + * If controller_after_reserved_ptr == controller_after_ptr, + * the controller array is physically full. We must rebalance + * or grow the controller array before inserting anything. + */ + if (controller.controller_block.controller_after_reserved_ptr == + controller.controller_block.controller_after_ptr) + { + ::fast_io::containers::details:: + deque_rebalance_or_grow_2x_after_blocks_impl(controller); + } - // Now we have at least 1 slot; allocate a new block and append it - auto new_block = - static_cast(allocator::allocate_aligned(align, bytes)); + begin_ptrtype new_block; + + /** + * Borrow a capacity block from the front if available. + * + * A capacity block exists at the front if + * controller_start_reserved_ptr != front_block.controller_ptr. + * + * Such a block contains no constructed elements and its memory + * can be reused directly as the new back block. + */ + if (controller.controller_block.controller_start_reserved_ptr != + controller.front_block.controller_ptr) + { + auto start_reserved_ptr = + controller.controller_block.controller_start_reserved_ptr; - // Write block pointer at controller_after_reserved_ptr - auto pos = controller.controller_block.controller_after_reserved_ptr; - ::std::construct_at(pos, new_block); + /* Destroy the pointer object (no-op for trivially destructible types). */ + std::destroy_at(start_reserved_ptr); + + /* Reuse the block memory. */ + new_block = static_cast(*start_reserved_ptr); - // Advance after_reserved_ptr and write sentinel - *(controller.controller_block.controller_after_reserved_ptr = pos + 1u) = nullptr; + /* Consume one reserved block from the front. */ + ++controller.controller_block.controller_start_reserved_ptr; + } + else + { + /** + * No front capacity block is available. Allocate a new block. + */ + new_block = + static_cast(allocator::allocate_aligned(align, bytes)); + } + + /** + * Insert the new block pointer at controller_after_reserved_ptr, + * then advance controller_after_reserved_ptr and write the sentinel. + */ + auto pos = controller.controller_block.controller_after_reserved_ptr; + std::construct_at(pos, new_block); + *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; } - // Now we definitely have space for a new block pointer - // Advance controller pointer to the next block slot + /** + * At this point, we have guaranteed controller capacity. + * Advance back_block.controller_ptr to the new block slot. + */ ++controller.back_block.controller_ptr; - // Load the block pointer + /** + * Load the block pointer and initialize begin/curr/end pointers. + */ auto begin_ptr = static_cast(*controller.back_block.controller_ptr); - // Update block range controller.back_block.begin_ptr = begin_ptr; controller.back_block.curr_ptr = begin_ptr; controller.back_block.end_ptr = begin_ptr + bytes; From 493b63755618c9f3942d643ac743329de985ac2c Mon Sep 17 00:00:00 2001 From: trcrsired Date: Sun, 4 Jan 2026 21:49:50 +0800 Subject: [PATCH 11/26] [skip CI] add check after rebalancing for push_back --- include/fast_io_dsal/impl/deque.h | 76 +++++++++++++++++-------------- 1 file changed, 41 insertions(+), 35 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 4cbdc0d03..bd12c0521 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -501,49 +501,55 @@ inline constexpr void deque_grow_back_common_impl( ::fast_io::containers::details:: deque_rebalance_or_grow_2x_after_blocks_impl(controller); } + std::size_t diff_to_after_ptr2 = + static_cast( + controller.controller_block.controller_after_reserved_ptr - + controller.back_block.controller_ptr); + if (diff_to_after_ptr2 < 2) + { + begin_ptrtype new_block; - begin_ptrtype new_block; + /** + * Borrow a capacity block from the front if available. + * + * A capacity block exists at the front if + * controller_start_reserved_ptr != front_block.controller_ptr. + * + * Such a block contains no constructed elements and its memory + * can be reused directly as the new back block. + */ + if (controller.controller_block.controller_start_reserved_ptr != + controller.front_block.controller_ptr) + { + auto start_reserved_ptr = + controller.controller_block.controller_start_reserved_ptr; - /** - * Borrow a capacity block from the front if available. - * - * A capacity block exists at the front if - * controller_start_reserved_ptr != front_block.controller_ptr. - * - * Such a block contains no constructed elements and its memory - * can be reused directly as the new back block. - */ - if (controller.controller_block.controller_start_reserved_ptr != - controller.front_block.controller_ptr) - { - auto start_reserved_ptr = - controller.controller_block.controller_start_reserved_ptr; + /* Destroy the pointer object (no-op for trivially destructible types). */ + std::destroy_at(start_reserved_ptr); - /* Destroy the pointer object (no-op for trivially destructible types). */ - std::destroy_at(start_reserved_ptr); + /* Reuse the block memory. */ + new_block = static_cast(*start_reserved_ptr); - /* Reuse the block memory. */ - new_block = static_cast(*start_reserved_ptr); + /* Consume one reserved block from the front. */ + ++controller.controller_block.controller_start_reserved_ptr; + } + else + { + /** + * No front capacity block is available. Allocate a new block. + */ + new_block = + static_cast(allocator::allocate_aligned(align, bytes)); + } - /* Consume one reserved block from the front. */ - ++controller.controller_block.controller_start_reserved_ptr; - } - else - { /** - * No front capacity block is available. Allocate a new block. + * Insert the new block pointer at controller_after_reserved_ptr, + * then advance controller_after_reserved_ptr and write the sentinel. */ - new_block = - static_cast(allocator::allocate_aligned(align, bytes)); + auto pos = controller.controller_block.controller_after_reserved_ptr; + std::construct_at(pos, new_block); + *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; } - - /** - * Insert the new block pointer at controller_after_reserved_ptr, - * then advance controller_after_reserved_ptr and write the sentinel. - */ - auto pos = controller.controller_block.controller_after_reserved_ptr; - std::construct_at(pos, new_block); - *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; } /** From ee18421967e1247a77b4478fde3ff4ee6bda889a Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 5 Jan 2026 20:13:39 +0800 Subject: [PATCH 12/26] [skip CI]rewrite the logic (untested code for deque) --- include/fast_io_dsal/impl/deque.h | 108 +++++++++++++----------------- 1 file changed, 46 insertions(+), 62 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index bd12c0521..88f266f78 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -331,28 +331,21 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least + 1zu); --new_blocks_count; - - // number of used slots (excluding sentinel) ::std::size_t const used_blocks_count{old_after_reserved_ptr_pos - old_start_reserved_ptr_pos}; - // put used blocks roughly in the middle, with some headroom on both sides ::std::size_t const new_blocks_offset{static_cast<::std::size_t>(new_blocks_count - used_blocks_count) >> 1u}; auto new_start_reserved_ptr{new_start_ptr + new_blocks_offset}; auto new_after_reserved_ptr{::std::uninitialized_copy(old_start_reserved_ptr, old_after_reserved_ptr, new_start_reserved_ptr)}; - // sentinel *new_after_reserved_ptr = nullptr; - // free old controller array - block_typed_allocator::deallocate_n(old_start_ptr, old_after_ptr_pos + 1zu); + block_typed_allocator::deallocate_n(old_start_ptr, static_cast<::std::size_t>(old_after_ptr_pos + 1u)); - // rebuild controller bookkeeping controller.controller_block.controller_start_ptr = new_start_ptr; controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr; controller.controller_block.controller_after_ptr = new_start_ptr + new_blocks_count; - // adjust front/back controller_ptr controller.front_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_front_block_ptr_pos - old_start_reserved_ptr_pos)); controller.back_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_back_block_ptr_pos - old_start_reserved_ptr_pos)); } @@ -360,65 +353,55 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont template inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontroltype &controller) noexcept { - ::std::size_t const used_blocks_count{ + auto const used_blocks_count{ static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; - - ::std::size_t const total_blocks{ - static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_block.controller_start_ptr)}; - - ::std::size_t const total_blocks_half{total_blocks >> 1u}; - - if (total_blocks_half < used_blocks_count) - { - constexpr ::std::size_t maxcount{ - static_cast<::std::size_t>(::std::numeric_limits<::std::size_t>::max() / sizeof(void *))}; - constexpr ::std::size_t mxtoalign{static_cast<::std::size_t>(maxcount - 1u)}; - constexpr ::std::size_t mxtoaligndiv2{mxtoalign >> 1u}; - - if (mxtoaligndiv2 < total_blocks) + auto const total_slots_count{ + static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_start_ptr)}; + auto const half_slots_count{static_cast<::std::size_t>(total_slots_count >> 1u)}; + if (half_slots_count < used_blocks_count) // grow blocks + { + constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; + constexpr ::std::size_t mxdv2m1{(mx >> 1u) - 1u}; + if (mxdv2m1 < total_slots_count) { ::fast_io::fast_terminate(); } - - ::std::size_t const toallocate{total_blocks << 1u}; - deque_grow_to_new_blocks_count_impl(controller, toallocate); + ::fast_io::containers::details::deque_grow_to_new_blocks_count_impl(controller, + static_cast<::std::size_t>((total_slots_count << 1u) + 1u)); } else { - - // Rebalance in-place: move reserved range toward the middle - auto start_ptr{controller.controller_block.controller_start_ptr}; + // balance blocks auto start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; auto after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; - - ::std::size_t const old_start_reserved_pos{static_cast<::std::size_t>(start_reserved_ptr - start_ptr)}; - ::std::size_t const old_after_reserved_pos{static_cast<::std::size_t>(after_reserved_ptr - start_ptr)}; - ::std::size_t const used{old_after_reserved_pos - old_start_reserved_pos}; - - ::std::size_t const new_start_reserved_pos{(total_blocks - used) >> 1u}; - auto new_start_reserved_ptr{start_ptr + new_start_reserved_pos}; - - if (new_start_reserved_ptr != start_reserved_ptr) + auto const reserved_blocks_count{ + static_cast<::std::size_t>(after_reserved_ptr - start_reserved_ptr)}; + auto const half_reserved_blocks_count{ + static_cast<::std::size_t>(reserved_blocks_count >> 1u)}; + auto reserved_pivot{start_reserved_ptr + half_reserved_blocks_count}; + auto const half_used_blocks_count{ + static_cast<::std::size_t>(used_blocks_count >> 1u)}; + auto used_blocks_pivot{controller.front_block.controller_ptr + half_used_blocks_count}; + if (used_blocks_pivot != reserved_pivot) { - if (start_reserved_ptr < new_start_reserved_ptr) - { - ::std::copy_backward(start_reserved_ptr, after_reserved_ptr, new_start_reserved_ptr + used); - } - else - { - ::std::copy(start_reserved_ptr, after_reserved_ptr, new_start_reserved_ptr); - } + ::std::ptrdiff_t diff{reserved_pivot - used_blocks_pivot}; + auto rotate_pivot{diff < 0 ? after_reserved_ptr + diff : start_reserved_ptr + diff}; + ::std::rotate(start_reserved_ptr, rotate_pivot, after_reserved_ptr); + controller.front_block.controller_ptr += diff; + controller.back_block.controller_ptr += diff; } - controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; - controller.controller_block.controller_after_reserved_ptr = new_start_reserved_ptr + used; - *(controller.controller_block.controller_after_reserved_ptr) = nullptr; - - // adjust front/back controller_ptr according to the shift - ::std::size_t const delta{new_start_reserved_pos - old_start_reserved_pos}; - - controller.front_block.controller_ptr += delta; - controller.back_block.controller_ptr += delta; + auto slots_pivot{controller.controller_start_ptr + half_slots_count}; + if (slots_pivot != used_blocks_pivot) + { + ::std::ptrdiff_t diff{slots_pivot - used_blocks_pivot}; + ::fast_io::freestanding::overlapped_copy(start_reserved_ptr, + after_reserved_ptr, start_reserved_ptr - diff); + controller.front_block.controller_ptr += diff; + controller.back_block.controller_ptr += diff; + controller.controller_block.controller_start_reserved_ptr += diff; + *(controller.controller_block.controller_after_reserved_ptr += diff) = nullptr; + } } } @@ -426,7 +409,9 @@ template inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept { using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; - auto [allocated_blocks_ptr, allocated_blocks_count] = block_typed_allocator::allocate_at_least(2); + constexpr ::std::size_t initial_allocated_block_counts{3}; + constexpr ::std::size_t initial_allocated_block_counts_with_sentinel{initial_allocated_block_counts + 1u}; + auto [allocated_blocks_ptr, allocated_blocks_count] = block_typed_allocator::allocate_at_least(initial_allocated_block_counts_with_sentinel); // we need a null terminator as sentinel like c style string does --allocated_blocks_count; auto &controller_block{controller.controller_block}; @@ -438,11 +423,13 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont auto begin_ptr{static_cast(allocator::allocate_aligned(align, bytes))}; - back_block.controller_ptr = front_block.controller_ptr = controller_block.controller_start_reserved_ptr = controller_block.controller_start_ptr = ::std::construct_at(allocated_blocks_ptr, begin_ptr); + controller_block.controller_start_ptr = allocated_blocks_ptr; + auto allocated_mid_block{allocated_blocks_ptr + (allocated_blocks_count >> 1u)}; + back_block.controller_ptr = front_block.controller_ptr = controller_block.controller_start_reserved_ptr = ::std::construct_at(allocated_mid_block, begin_ptr); - *(controller_block.controller_after_reserved_ptr = allocated_blocks_ptr + 1) = nullptr; // set nullptr as a sentinel + *(controller_block.controller_after_reserved_ptr = allocated_mid_block + 1) = nullptr; // set nullptr as a sentinel - controller_block.controller_after_ptr = allocated_blocks_ptr + allocated_blocks_count; + controller_block.controller_after_ptr = allocated_mid_block + allocated_blocks_count; ::std::size_t halfsize{bytes >> 1u}; back_block.begin_ptr = front_block.begin_ptr = begin_ptr; @@ -524,9 +511,6 @@ inline constexpr void deque_grow_back_common_impl( auto start_reserved_ptr = controller.controller_block.controller_start_reserved_ptr; - /* Destroy the pointer object (no-op for trivially destructible types). */ - std::destroy_at(start_reserved_ptr); - /* Reuse the block memory. */ new_block = static_cast(*start_reserved_ptr); From 254927fbaba86a1e39a8f3873d629240a14d25a6 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 5 Jan 2026 21:37:48 +0800 Subject: [PATCH 13/26] [skip CI]implement more deque logic --- .../freestanding/algorithm.h | 112 +++++++++++++++++- include/fast_io_dsal/impl/deque.h | 29 +++-- include/fast_io_dsal/impl/freestanding.h | 71 ----------- share/fast_io/fast_io_inc/freestanding.inc | 2 + 4 files changed, 134 insertions(+), 80 deletions(-) diff --git a/include/fast_io_core_impl/freestanding/algorithm.h b/include/fast_io_core_impl/freestanding/algorithm.h index 560a3c60c..03418d08d 100644 --- a/include/fast_io_core_impl/freestanding/algorithm.h +++ b/include/fast_io_core_impl/freestanding/algorithm.h @@ -242,6 +242,28 @@ inline constexpr ForwardIt remove(ForwardIt first, ForwardIt last, T value) return first; } } // namespace fast_io::freestanding +namespace fast_io::details +{ +template <::std::random_access_iterator input_iter, ::std::random_access_iterator output_iter> + requires((::std::same_as<::std::iter_value_t, ::std::iter_value_t> || + (::std::contiguous_iterator && ::std::contiguous_iterator)) && + (::std::is_trivially_copyable_v<::std::iter_value_t> && + ::std::is_trivially_copyable_v<::std::iter_value_t>)) +inline constexpr output_iter overlapped_copy_trivial(input_iter first, ::std::size_t n, output_iter result) +{ + ::fast_io::details::overlapped_copy_buffer_ptr<::std::iter_value_t> tempbuffer(n); + auto tempbufferptr{tempbuffer.ptr}; + for (::std::size_t i{}; i != n; ++i) + { + tempbufferptr[i] = first[i]; + } + for (::std::size_t i{}; i != n; ++i) + { + result[i] = ::std::move(tempbufferptr[i]); + } + return result + n; +} +} // namespace fast_io::details namespace fast_io::freestanding { @@ -369,6 +391,94 @@ inline constexpr output_iter non_overlapped_copy(input_iter first, input_iter la } } +template <::std::contiguous_iterator input_iter, ::std::contiguous_iterator output_iter> + requires(::std::same_as<::std::iter_value_t, ::std::iter_value_t>) +inline constexpr output_iter overlapped_copy(input_iter first, input_iter last, output_iter result) +{ + if (__builtin_is_constant_evaluated()) + { + return ::fast_io::details::overlapped_copy_trivial(first, static_cast<::std::size_t>(last - first), result); + } + else + { + using input_value_type = ::std::iter_value_t; + using output_value_type = ::std::iter_value_t; +#if 0 + if constexpr (::std::contiguous_iterator && ::std::contiguous_iterator && + ::std::is_trivially_copyable_v && + ::std::is_trivially_copyable_v && + (::std::same_as || + (::std::integral && ::std::integral && + sizeof(input_value_type) == sizeof(output_value_type)))) +#endif + { + auto resulttoaddr{::std::to_address(result)}; + auto firsttoaddr{::std::to_address(first)}; + auto lasttoaddr{::std::to_address(last)}; + ::std::size_t count{static_cast<::std::size_t>(lasttoaddr - firsttoaddr)}; + if (count) // to avoid nullptr UB + { + my_memmove(resulttoaddr, firsttoaddr, + sizeof(::std::iter_value_t) * count); + } + return result += count; + } +#if 0 + else + { + ::std::size_t count{static_cast<::std::size_t>(last-first)}; + auto result_last{result+count}; + if (first <= result_last && result_last < last) + { + //copy_backward + ::fast_io::freestanding::copy_backward(first, last, result_last); + return result_last; + } + return ::fast_io::freestanding::copy(first, last, result); + } +#endif + } +} + +template <::std::contiguous_iterator input_iter, ::std::contiguous_iterator output_iter> + requires(::std::same_as<::std::iter_value_t, ::std::iter_value_t>) +inline output_iter overlapped_copy_n(input_iter first, ::std::size_t count, output_iter result) +{ + if (__builtin_is_constant_evaluated()) + { + return ::fast_io::details::overlapped_copy_trivial(first, count, result); + } + else + { + using input_value_type = ::std::iter_value_t; + using output_value_type = ::std::iter_value_t; +#if 0 + if constexpr (::std::contiguous_iterator && ::std::contiguous_iterator && + ::std::is_trivially_copyable_v && + ::std::is_trivially_copyable_v && + (::std::same_as || + (::std::integral && ::std::integral && + sizeof(input_value_type) == sizeof(output_value_type)))) +#endif + { + auto resulttoaddr{::std::to_address(result)}; + auto firsttoaddr{::std::to_address(first)}; + if (count) // to avoid nullptr UB + { + my_memmove(resulttoaddr, firsttoaddr, + sizeof(::std::iter_value_t) * count); + } + return result += count; + } +#if 0 + else + { + return ::fast_io::freestanding::overlapped_copy(first, first+count, result); + } +#endif + } +} + template <::std::input_iterator input_iter, ::std::input_or_output_iterator output_iter> inline constexpr output_iter my_copy_n(input_iter first, ::std::size_t count, output_iter result) { @@ -412,7 +522,7 @@ inline constexpr output_iter my_copy(input_iter first, input_iter second, output (::std::same_as || (::std::integral && ::std::integral && ::std::is_trivially_copyable_v && - ::std::is_trivially_copyable_v))) + ::std::is_trivially_copyable_v))) { my_copy_n(first, static_cast<::std::size_t>(second - first), result); return result + (second - first); diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 88f266f78..74c823fbb 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -331,14 +331,27 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least + 1zu); --new_blocks_count; - ::std::size_t const used_blocks_count{old_after_reserved_ptr_pos - old_start_reserved_ptr_pos}; - ::std::size_t const new_blocks_offset{static_cast<::std::size_t>(new_blocks_count - used_blocks_count) >> 1u}; + auto const old_reserved_blocks_count{ + static_cast<::std::size_t>(old_after_reserved_ptr - old_start_reserved_ptr)}; + auto const old_half_reserved_blocks_count{ + static_cast<::std::size_t>(old_reserved_blocks_count >> 1u)}; + auto old_reserved_pivot{old_start_reserved_ptr + old_half_reserved_blocks_count}; + auto const old_used_blocks_count{ + static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; + auto const old_half_used_blocks_count{ + static_cast<::std::size_t>(old_used_blocks_count >> 1u)}; + auto old_used_blocks_pivot{controller.front_block.controller_ptr + old_half_used_blocks_count}; + + ::std::ptrdiff_t pivot_diff{old_reserved_pivot - old_used_blocks_pivot}; + + ::std::size_t const new_blocks_offset{static_cast<::std::size_t>(new_blocks_count - old_reserved_blocks_count) >> 1u}; auto new_start_reserved_ptr{new_start_ptr + new_blocks_offset}; - auto new_after_reserved_ptr{::std::uninitialized_copy(old_start_reserved_ptr, old_after_reserved_ptr, new_start_reserved_ptr)}; - *new_after_reserved_ptr = nullptr; + auto old_pivot{old_start_reserved_ptr + pivot_diff}; + auto new_after_reserved_ptr{::fast_io::freestanding::non_overlapped_copy(old_pivot, old_after_reserved_ptr, new_start_reserved_ptr)}; + *(new_after_reserved_ptr = ::fast_io::freestanding::non_overlapped_copy(old_start_reserved_ptr, old_pivot, new_after_reserved_ptr)) = nullptr; block_typed_allocator::deallocate_n(old_start_ptr, static_cast<::std::size_t>(old_after_ptr_pos + 1u)); controller.controller_block.controller_start_ptr = new_start_ptr; @@ -346,8 +359,8 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont controller.controller_block.controller_after_reserved_ptr = new_after_reserved_ptr; controller.controller_block.controller_after_ptr = new_start_ptr + new_blocks_count; - controller.front_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_front_block_ptr_pos - old_start_reserved_ptr_pos)); - controller.back_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_back_block_ptr_pos - old_start_reserved_ptr_pos)); + controller.front_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_front_block_ptr_pos - old_start_reserved_ptr_pos)) + pivot_diff; + controller.back_block.controller_ptr = new_start_ptr + (new_blocks_offset + (old_back_block_ptr_pos - old_start_reserved_ptr_pos)) + pivot_diff; } template @@ -356,7 +369,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt auto const used_blocks_count{ static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.front_block.controller_ptr) + 1zu}; auto const total_slots_count{ - static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_start_ptr)}; + static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - controller.controller_block.controller_start_ptr)}; auto const half_slots_count{static_cast<::std::size_t>(total_slots_count >> 1u)}; if (half_slots_count < used_blocks_count) // grow blocks { @@ -391,7 +404,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt controller.back_block.controller_ptr += diff; } - auto slots_pivot{controller.controller_start_ptr + half_slots_count}; + auto slots_pivot{controller.controller_block.controller_start_ptr + half_slots_count}; if (slots_pivot != used_blocks_pivot) { ::std::ptrdiff_t diff{slots_pivot - used_blocks_pivot}; diff --git a/include/fast_io_dsal/impl/freestanding.h b/include/fast_io_dsal/impl/freestanding.h index 68c7dbedb..1f18ad34f 100644 --- a/include/fast_io_dsal/impl/freestanding.h +++ b/include/fast_io_dsal/impl/freestanding.h @@ -22,77 +22,6 @@ concept has_uninitialized_move_backward_define = requires(T *ptr) { namespace fast_io::freestanding { -template <::std::random_access_iterator Iter1, ::std::random_access_iterator Iter2> - requires ::std::is_trivially_constructible_v -inline constexpr Iter2 overlapped_copy(Iter1 first, Iter1 last, Iter2 dest) noexcept -{ - if constexpr (::std::contiguous_iterator && !::std::is_pointer_v && ::std::contiguous_iterator && !::std::is_pointer_v) - { - return overlapped_copy(::std::to_address(first), ::std::to_address(last), - ::std::to_address(dest)) - - ::std::to_address(dest) + dest; - } - else if constexpr (::std::contiguous_iterator && !::std::is_pointer_v) - { - return overlapped_copy(::std::to_address(first), ::std::to_address(last), - dest); - } - else if constexpr (::std::contiguous_iterator && !::std::is_pointer_v) - { - return overlapped_copy(first, last, ::std::to_address(dest)) - - ::std::to_address(dest) + dest; - } - else - { - using iter1valuetype = ::std::iter_value_t; - using iter2valuetype = ::std::iter_value_t; - if constexpr (::std::is_pointer_v && ::std::is_pointer_v && - (::std::is_trivially_copyable_v && - ::std::is_trivially_copyable_v && - (::std::same_as || - ((::std::integral || ::std::same_as) && - (::std::integral || ::std::same_as) && - sizeof(iter1valuetype) == sizeof(iter2valuetype))))) - { -#if __cpp_if_consteval >= 202106L - if !consteval -#else - if (!__builtin_is_constant_evaluated()) -#endif - { - return reinterpret_cast(::fast_io::freestanding::bytes_copy(reinterpret_cast<::std::byte const *>(first), reinterpret_cast<::std::byte const *>(last), reinterpret_cast<::std::byte *>(dest))); - } - } - - if (__builtin_is_constant_evaluated()) - { - ::fast_io::details::overlapped_copy_buffer_ptr tempbuffer(static_cast<::std::size_t>(::std::distance(first, last))); - auto buffered{::std::copy(first, last, tempbuffer.ptr)}; - return ::std::move(tempbuffer.ptr, buffered, dest); - } - else - { - // we do not allow move constructor to throw EH. - if (first <= dest && dest < last) - { - auto res{dest + (last - first)}; - ::std::copy_backward(first, last, res); - return res; - } - else - { - return ::std::copy(first, last, dest); - } - } - } -} - -template <::std::random_access_iterator Iter1, ::std::random_access_iterator Iter2> -inline constexpr Iter2 overlapped_copy_n(Iter1 first, ::std::size_t n, Iter2 dest) noexcept -{ - return overlapped_copy(first, first + n, dest); -} - /* uninitialized_relocate requires two range are not overlapped. */ diff --git a/share/fast_io/fast_io_inc/freestanding.inc b/share/fast_io/fast_io_inc/freestanding.inc index 6b5aeef37..76f6e41e8 100644 --- a/share/fast_io/fast_io_inc/freestanding.inc +++ b/share/fast_io/fast_io_inc/freestanding.inc @@ -48,6 +48,8 @@ using ::fast_io::freestanding::find_last_of; using ::fast_io::freestanding::find_last_not_of; using ::fast_io::freestanding::nonoverlapped_bytes_copy_n; using ::fast_io::freestanding::bytes_clear_n; +using ::fast_io::freestanding::overlapped_copy; +using ::fast_io::freestanding::overlapped_copy_n; using ::fast_io::freestanding::errc; From fcc9dab4a94d8ed2d86906ea30554ac3ddac2198 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 5 Jan 2026 23:04:18 +0800 Subject: [PATCH 14/26] [skip CI]the allocating empty function has bugs for boundary, fixed it --- include/fast_io_dsal/impl/deque.h | 39 +++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 74c823fbb..a8589d1b5 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -91,13 +91,32 @@ struct deque_iterator using difference_type = ::std::ptrdiff_t; deque_control_block itercontent; - +#if __has_cpp_attribute(clang::no_sanitize) + [[clang::no_sanitize("undefined")]] +#endif inline constexpr deque_iterator &operator++() noexcept { if (++itercontent.curr_ptr == itercontent.end_ptr) [[unlikely]] { constexpr size_type blocksize{::fast_io::containers::details::deque_block_size}; - itercontent.end_ptr = ((itercontent.curr_ptr = itercontent.begin_ptr = (*++itercontent.controller_ptr)) + blocksize); + auto tmp{(itercontent.curr_ptr = itercontent.begin_ptr = (*++itercontent.controller_ptr))}; + constexpr bool ubsandisabled{ +#if __has_cpp_attribute(clang::no_sanitize) + true +#endif + }; + if constexpr (ubsandisabled) + { + tmp += blocksize; + } + else + { + if (tmp) [[likely]] // this makes no sense for sentinel + { + tmp += blocksize; + } + } + itercontent.end_ptr = tmp; } return *this; } @@ -442,7 +461,7 @@ inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &cont *(controller_block.controller_after_reserved_ptr = allocated_mid_block + 1) = nullptr; // set nullptr as a sentinel - controller_block.controller_after_ptr = allocated_mid_block + allocated_blocks_count; + controller_block.controller_after_ptr = allocated_blocks_ptr + allocated_blocks_count; ::std::size_t halfsize{bytes >> 1u}; back_block.begin_ptr = front_block.begin_ptr = begin_ptr; @@ -487,7 +506,6 @@ inline constexpr void deque_grow_back_common_impl( static_cast( controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr); - if (diff_to_after_ptr < 2) { /** @@ -538,13 +556,24 @@ inline constexpr void deque_grow_back_common_impl( new_block = static_cast(allocator::allocate_aligned(align, bytes)); } + using namespace ::fast_io::iomnp; /** * Insert the new block pointer at controller_after_reserved_ptr, * then advance controller_after_reserved_ptr and write the sentinel. */ - auto pos = controller.controller_block.controller_after_reserved_ptr; + auto pos{controller.controller_block.controller_after_reserved_ptr}; std::construct_at(pos, new_block); +#if 0 + debug_println(::std::source_location::current(), + "\tcontroller_start_ptr=", + pointervw(controller.controller_block.controller_start_ptr), + "\tcontroller_after_reserved_ptr=", + pointervw(controller.controller_block.controller_after_reserved_ptr), + "\tcontroller_after_ptr=", + pointervw(controller.controller_block.controller_after_ptr), + "\tpos=",pointervw(pos)); +#endif *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; } } From 253c38a4594eb1e31086163effadd704b229ce37 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 5 Jan 2026 23:24:32 +0800 Subject: [PATCH 15/26] [skip CI]implementation of the push_front() for deque has something to do. then we need to test balance --- include/fast_io_dsal/impl/deque.h | 93 +++++++++++++++++-------------- 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index a8589d1b5..db1bd66a3 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -564,16 +564,6 @@ inline constexpr void deque_grow_back_common_impl( */ auto pos{controller.controller_block.controller_after_reserved_ptr}; std::construct_at(pos, new_block); -#if 0 - debug_println(::std::source_location::current(), - "\tcontroller_start_ptr=", - pointervw(controller.controller_block.controller_start_ptr), - "\tcontroller_after_reserved_ptr=", - pointervw(controller.controller_block.controller_after_reserved_ptr), - "\tcontroller_after_ptr=", - pointervw(controller.controller_block.controller_after_ptr), - "\tpos=",pointervw(pos)); -#endif *(controller.controller_block.controller_after_reserved_ptr = pos + 1) = nullptr; } } @@ -601,51 +591,68 @@ inline constexpr void deque_grow_front_common_impl( std::size_t align, std::size_t bytes) noexcept { - using replacetype = typename dequecontroltype::replacetype; - constexpr bool isvoidplaceholder = std::same_as; - using begin_ptrtype = - std::conditional_t; - - auto &cb = controller.controller_block; - - // Case 1: empty deque → same as back - if (cb.controller_start_ptr == nullptr) + /** + * If the deque is empty, allocate the initial controller array + * and a single data block. This sets up the initial front/back + * block pointers and the sentinel. + */ + if (controller.controller_block.controller_start_ptr == nullptr) { ::fast_io::containers::details:: deque_allocate_on_empty_common_impl(controller, align, bytes); return; } - // How many free controller slots exist before front_block.controller_ptr? - std::size_t diff_to_start_reserved = - static_cast(controller.front_block.controller_ptr - - cb.controller_start_reserved_ptr); - - // Need at least 1 free slot to prepend a block - if (!diff_to_start_reserved) + using replacetype = typename dequecontroltype::replacetype; + constexpr bool isvoidplaceholder = std::same_as; + using begin_ptrtype = + std::conditional_t; + if (controller.front_block.controller_ptr == + controller.controller_block.controller_start_reserved_ptr) { - ::fast_io::containers::details:: - deque_rebalance_or_grow_2x_after_blocks_impl(controller); - // Now we definitely have space - auto begin_ptr = - static_cast(allocator::allocate_aligned(align, bytes)); + if (controller.controller_block.controller_start_reserved_ptr == + controller.controller_block.controller_start_ptr) + { + ::fast_io::containers::details:: + deque_rebalance_or_grow_2x_after_blocks_impl(controller); + } + if (controller.front_block.controller_ptr == + controller.controller_block.controller_start_reserved_ptr) + { + begin_ptrtype new_block; + auto after_reserved_ptr = + controller.controller_block.controller_after_reserved_ptr; + std::size_t diff_to_after_ptr = + static_cast( + after_reserved_ptr - + controller.back_block.controller_ptr); + if (1 < diff_to_after_ptr) + { + /* Reuse the block memory. */ + new_block = static_cast(*after_reserved_ptr); + + /* Consume one reserved block from the back. */ + *(controller.controller_block.controller_after_reserved_ptr = after_reserved_ptr - 1) = nullptr; + } + else + { + new_block = + static_cast(allocator::allocate_aligned(align, bytes)); + } + using namespace ::fast_io::iomnp; - // Insert BEFORE current front block - auto new_slot = controller.front_block.controller_ptr - 1; - std::construct_at(new_slot, begin_ptr); + auto pos{--controller.controller_block.controller_start_reserved_ptr}; + std::construct_at(pos, new_block); + } } - auto new_slot{controller.front_block.controller_ptr - 1}; - auto begin_ptr{static_cast(*new_slot)}; - // Move front_block.controller_ptr to new block - controller.front_block.controller_ptr = new_slot; + --controller.front_block.controller_ptr; - // Update block pointers - controller.front_block.begin_ptr = begin_ptr; - controller.front_block.end_ptr = begin_ptr + bytes; + auto begin_ptr = + static_cast(*controller.front_block.controller_ptr); - // curr_ptr starts at end (push_front will --curr_ptr) - controller.front_block.curr_ptr = controller.front_block.end_ptr; + controller.front_block.begin_ptr = begin_ptr; + controller.front_block.end_ptr = (controller.front_block.curr_ptr = (begin_ptr + bytes)); } template From b3773f608fb7145af0d76169247a4b88c4a031ad Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 5 Jan 2026 23:43:55 +0800 Subject: [PATCH 16/26] [skip CI] deque has using namespace ::fast_io::iomnp which should be removed --- include/fast_io_dsal/impl/deque.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index db1bd66a3..a2146f1c0 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -556,7 +556,6 @@ inline constexpr void deque_grow_back_common_impl( new_block = static_cast(allocator::allocate_aligned(align, bytes)); } - using namespace ::fast_io::iomnp; /** * Insert the new block pointer at controller_after_reserved_ptr, @@ -639,7 +638,6 @@ inline constexpr void deque_grow_front_common_impl( new_block = static_cast(allocator::allocate_aligned(align, bytes)); } - using namespace ::fast_io::iomnp; auto pos{--controller.controller_block.controller_start_reserved_ptr}; std::construct_at(pos, new_block); From f99d60e50c591cf6f8a84634ac4d952e8fd0bd2f Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 01:07:55 +0800 Subject: [PATCH 17/26] [skip CI]deque balancing with realllocation has bugs we fix it --- include/fast_io_dsal/impl/deque.h | 64 +++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 8 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index a2146f1c0..456b43a2f 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -336,6 +336,9 @@ struct template inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least) noexcept { +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif auto old_start_ptr{controller.controller_block.controller_start_ptr}; auto old_start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; @@ -349,7 +352,6 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least + 1zu); - --new_blocks_count; auto const old_reserved_blocks_count{ static_cast<::std::size_t>(old_after_reserved_ptr - old_start_reserved_ptr)}; @@ -365,12 +367,37 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont ::std::ptrdiff_t pivot_diff{old_reserved_pivot - old_used_blocks_pivot}; ::std::size_t const new_blocks_offset{static_cast<::std::size_t>(new_blocks_count - old_reserved_blocks_count) >> 1u}; - + --new_blocks_count; +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current(),"\n" + "\tnew_blocks_count=",new_blocks_count,"\n" + "\told_after_ptr_pos=",old_after_ptr_pos,"\n" + "\tnew_blocks_offset=",new_blocks_offset,"\n" + "\tpivot_diff=",pivot_diff); +#endif auto new_start_reserved_ptr{new_start_ptr + new_blocks_offset}; + auto new_after_reserved_ptr{new_start_reserved_ptr + old_reserved_blocks_count}; + + decltype(old_start_reserved_ptr) old_pivot, new_pivot; + if (pivot_diff < 0) + { + old_pivot = old_start_reserved_ptr; + new_pivot = new_after_reserved_ptr; + } + else + { + old_pivot = old_after_reserved_ptr; + new_pivot = new_start_reserved_ptr; + } + old_pivot -= pivot_diff; + new_pivot += pivot_diff; + + ::fast_io::freestanding::non_overlapped_copy(old_pivot, + old_after_reserved_ptr, new_start_reserved_ptr); + ::fast_io::freestanding::non_overlapped_copy(old_start_reserved_ptr, + old_pivot, new_pivot); - auto old_pivot{old_start_reserved_ptr + pivot_diff}; - auto new_after_reserved_ptr{::fast_io::freestanding::non_overlapped_copy(old_pivot, old_after_reserved_ptr, new_start_reserved_ptr)}; - *(new_after_reserved_ptr = ::fast_io::freestanding::non_overlapped_copy(old_start_reserved_ptr, old_pivot, new_after_reserved_ptr)) = nullptr; + *new_after_reserved_ptr = 0; block_typed_allocator::deallocate_n(old_start_ptr, static_cast<::std::size_t>(old_after_ptr_pos + 1u)); controller.controller_block.controller_start_ptr = new_start_ptr; @@ -392,6 +419,9 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt auto const half_slots_count{static_cast<::std::size_t>(total_slots_count >> 1u)}; if (half_slots_count < used_blocks_count) // grow blocks { +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; constexpr ::std::size_t mxdv2m1{(mx >> 1u) - 1u}; if (mxdv2m1 < total_slots_count) @@ -403,6 +433,9 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt } else { +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif // balance blocks auto start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; auto after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; @@ -609,15 +642,24 @@ inline constexpr void deque_grow_front_common_impl( if (controller.front_block.controller_ptr == controller.controller_block.controller_start_reserved_ptr) { +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif if (controller.controller_block.controller_start_reserved_ptr == controller.controller_block.controller_start_ptr) { +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif ::fast_io::containers::details:: deque_rebalance_or_grow_2x_after_blocks_impl(controller); } if (controller.front_block.controller_ptr == controller.controller_block.controller_start_reserved_ptr) { +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif begin_ptrtype new_block; auto after_reserved_ptr = controller.controller_block.controller_after_reserved_ptr; @@ -628,10 +670,10 @@ inline constexpr void deque_grow_front_common_impl( if (1 < diff_to_after_ptr) { /* Reuse the block memory. */ - new_block = static_cast(*after_reserved_ptr); + new_block = static_cast(*(--after_reserved_ptr)); /* Consume one reserved block from the back. */ - *(controller.controller_block.controller_after_reserved_ptr = after_reserved_ptr - 1) = nullptr; + *(controller.controller_block.controller_after_reserved_ptr = after_reserved_ptr) = nullptr; } else { @@ -648,7 +690,13 @@ inline constexpr void deque_grow_front_common_impl( auto begin_ptr = static_cast(*controller.front_block.controller_ptr); - +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current(), + "\n\tcontroller_ptr:", ::fast_io::iomnp::pointervw(controller.front_block.controller_ptr), + "\n\tbegin_ptr:", ::fast_io::iomnp::pointervw(begin_ptr), + "\n\tstart_reserved_ptr:", ::fast_io::iomnp::pointervw(controller.controller_block.controller_start_reserved_ptr), + "\n\tstart_ptr:",::fast_io::iomnp::pointervw(controller.controller_block.controller_start_ptr)); +#endif controller.front_block.begin_ptr = begin_ptr; controller.front_block.end_ptr = (controller.front_block.curr_ptr = (begin_ptr + bytes)); } From bb7d9806a9744960f99303a0ee8c54ef510d8674 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 01:42:29 +0800 Subject: [PATCH 18/26] [skip CI]backup code for failed fuzzing --- include/fast_io_dsal/impl/deque.h | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 456b43a2f..1e3fdf5c4 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -336,7 +336,7 @@ struct template inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least) noexcept { -#if 0 +#if 1 ::fast_io::iomnp::debug_println(::std::source_location::current()); #endif auto old_start_ptr{controller.controller_block.controller_start_ptr}; @@ -419,7 +419,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt auto const half_slots_count{static_cast<::std::size_t>(total_slots_count >> 1u)}; if (half_slots_count < used_blocks_count) // grow blocks { -#if 0 +#if 1 ::fast_io::iomnp::debug_println(::std::source_location::current()); #endif constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; @@ -433,7 +433,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt } else { -#if 0 +#if 1 ::fast_io::iomnp::debug_println(::std::source_location::current()); #endif // balance blocks @@ -615,6 +615,7 @@ inline constexpr void deque_grow_back_common_impl( controller.back_block.begin_ptr = begin_ptr; controller.back_block.curr_ptr = begin_ptr; controller.back_block.end_ptr = begin_ptr + bytes; + ::fast_io::iomnp::debug_println(::std::source_location::current()); } template @@ -690,12 +691,12 @@ inline constexpr void deque_grow_front_common_impl( auto begin_ptr = static_cast(*controller.front_block.controller_ptr); -#if 0 +#if 1 ::fast_io::iomnp::debug_println(::std::source_location::current(), - "\n\tcontroller_ptr:", ::fast_io::iomnp::pointervw(controller.front_block.controller_ptr), - "\n\tbegin_ptr:", ::fast_io::iomnp::pointervw(begin_ptr), - "\n\tstart_reserved_ptr:", ::fast_io::iomnp::pointervw(controller.controller_block.controller_start_reserved_ptr), - "\n\tstart_ptr:",::fast_io::iomnp::pointervw(controller.controller_block.controller_start_ptr)); + "\n\tcontroller_ptr:", ::fast_io::iomnp::pointervw(controller.front_block.controller_ptr), + "\n\tbegin_ptr:", ::fast_io::iomnp::pointervw(begin_ptr), + "\n\tstart_reserved_ptr:", ::fast_io::iomnp::pointervw(controller.controller_block.controller_start_reserved_ptr), + "\n\tstart_ptr:", ::fast_io::iomnp::pointervw(controller.controller_block.controller_start_ptr)); #endif controller.front_block.begin_ptr = begin_ptr; controller.front_block.end_ptr = (controller.front_block.curr_ptr = (begin_ptr + bytes)); @@ -802,7 +803,12 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE inline constexpr void front_backspace() noexcept { - controller.front_block.end_ptr = (controller.front_block.curr_ptr = controller.front_block.begin_ptr = *++controller.front_block.controller_ptr) + block_size; + auto front_controller_ptr{controller.front_block.controller_ptr}; + if (front_controller_ptr == controller.back_block.controller_ptr) [[unlikely]] + { + return; + } + controller.front_block.end_ptr = (controller.front_block.curr_ptr = controller.front_block.begin_ptr = *(controller.front_block.controller_ptr = front_controller_ptr + 1)) + block_size; } inline constexpr void back_backspace() noexcept @@ -1074,7 +1080,12 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { if (backblock.controller_ptr) [[likely]] { - backblock.end_ptr = ((backblock.curr_ptr = backblock.begin_ptr = (*++backblock.controller_ptr)) + block_size); + auto tmp{backblock.curr_ptr = backblock.begin_ptr = (*++backblock.controller_ptr)}; + if (tmp) [[likely]] + { + tmp += block_size; + } + backblock.end_ptr = tmp; } } return {backblock}; From 042e6f9e53fd494cae8782ec019ef80bac2622e9 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 03:22:43 +0800 Subject: [PATCH 19/26] Add some tests to deque --- fuzzing/0007.containers/deque/fuzz_deque.cc | 64 +++++++++++++++++++ include/fast_io_dsal/impl/deque.h | 24 +++++-- .../0026.container/0003.deque/.test_prop.toml | 4 -- .../0003.deque/test_push_back.cc | 17 +++-- .../0003.deque/test_push_front.cc | 7 +- 5 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 fuzzing/0007.containers/deque/fuzz_deque.cc diff --git a/fuzzing/0007.containers/deque/fuzz_deque.cc b/fuzzing/0007.containers/deque/fuzz_deque.cc new file mode 100644 index 000000000..809e39b85 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque.cc @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) +{ + ::fast_io::deque dq; + std::deque ref; + + // Interpret input as a sequence of operations + for (size_t i{}; i != size; ++i) + { + uint8_t op = data[i] & 0x3u; // 4 operations + std::size_t value = i; // deterministic, stable + + switch (op) + { + case 0: // push_back + + dq.push_back(value); + ref.push_back(value); + break; + + case 1: // push_front + + dq.push_front(value); + ref.push_front(value); + break; + + case 2: // pop_back + if (!ref.empty()) + { + + dq.pop_back(); + ref.pop_back(); + } + break; + + case 3: // pop_front + if (!ref.empty()) + { + + dq.pop_front(); + ref.pop_front(); + } + break; + } + } + + // Validate correctness + if (dq.size() != ref.size()) + { + __builtin_trap(); + } + if (!std::ranges::equal(dq, ref)) + { + __builtin_trap(); + } + return 0; +} diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 1e3fdf5c4..6af94aa4e 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -336,7 +336,7 @@ struct template inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least) noexcept { -#if 1 +#if 0 ::fast_io::iomnp::debug_println(::std::source_location::current()); #endif auto old_start_ptr{controller.controller_block.controller_start_ptr}; @@ -419,7 +419,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt auto const half_slots_count{static_cast<::std::size_t>(total_slots_count >> 1u)}; if (half_slots_count < used_blocks_count) // grow blocks { -#if 1 +#if 0 ::fast_io::iomnp::debug_println(::std::source_location::current()); #endif constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; @@ -433,7 +433,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt } else { -#if 1 +#if 0 ::fast_io::iomnp::debug_println(::std::source_location::current()); #endif // balance blocks @@ -461,7 +461,7 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt { ::std::ptrdiff_t diff{slots_pivot - used_blocks_pivot}; ::fast_io::freestanding::overlapped_copy(start_reserved_ptr, - after_reserved_ptr, start_reserved_ptr - diff); + after_reserved_ptr, start_reserved_ptr + diff); controller.front_block.controller_ptr += diff; controller.back_block.controller_ptr += diff; controller.controller_block.controller_start_reserved_ptr += diff; @@ -600,6 +600,15 @@ inline constexpr void deque_grow_back_common_impl( } } + if (controller.back_block.controller_ptr == controller.front_block.controller_ptr && controller.front_block.curr_ptr == controller.front_block.end_ptr) + { + auto front_block_controller_ptr{controller.front_block.controller_ptr + 1}; + controller.front_block.controller_ptr = front_block_controller_ptr; + auto front_begin_ptr = static_cast(*front_block_controller_ptr); + controller.front_block.curr_ptr = controller.front_block.begin_ptr = front_begin_ptr; + controller.front_block.end_ptr = front_begin_ptr + bytes; + } + /** * At this point, we have guaranteed controller capacity. * Advance back_block.controller_ptr to the new block slot. @@ -615,7 +624,10 @@ inline constexpr void deque_grow_back_common_impl( controller.back_block.begin_ptr = begin_ptr; controller.back_block.curr_ptr = begin_ptr; controller.back_block.end_ptr = begin_ptr + bytes; + +#if 0 ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif } template @@ -691,7 +703,7 @@ inline constexpr void deque_grow_front_common_impl( auto begin_ptr = static_cast(*controller.front_block.controller_ptr); -#if 1 +#if 0 ::fast_io::iomnp::debug_println(::std::source_location::current(), "\n\tcontroller_ptr:", ::fast_io::iomnp::pointervw(controller.front_block.controller_ptr), "\n\tbegin_ptr:", ::fast_io::iomnp::pointervw(begin_ptr), @@ -813,7 +825,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE inline constexpr void back_backspace() noexcept { - controller.back_block.curr_ptr = controller.back_block.end_ptr = (controller.back_block.begin_ptr = *--controller.back_block.controller_ptr) + block_size; + controller.back_block.curr_ptr = (controller.back_block.end_ptr = ((controller.back_block.begin_ptr = *--controller.back_block.controller_ptr) + block_size)); } public: diff --git a/tests/0026.container/0003.deque/.test_prop.toml b/tests/0026.container/0003.deque/.test_prop.toml index f60200299..3083767cb 100644 --- a/tests/0026.container/0003.deque/.test_prop.toml +++ b/tests/0026.container/0003.deque/.test_prop.toml @@ -4,7 +4,3 @@ ignore = true ignore = true ["test_deque.cc"] ignore = true -["test_push_back.cc"] -ignore = true -["test_push_front.cc"] -ignore = true diff --git a/tests/0026.container/0003.deque/test_push_back.cc b/tests/0026.container/0003.deque/test_push_back.cc index c80e4c868..9c42fb683 100644 --- a/tests/0026.container/0003.deque/test_push_back.cc +++ b/tests/0026.container/0003.deque/test_push_back.cc @@ -1,17 +1,16 @@ -#include -#include +#include +#include int main() { - ::fast_io::deque dq; - dq.push_back(4); - dq.push_back(6); -#if 0 - static_assert(::std::random_access_iterator); - ::fast_io::io::println("diff:", dq.end() - dq.begin()); + ::fast_io::deque<::std::size_t> dq; + for (::std::size_t i{}; i != 4096u; ++i) + { + dq.push_back(i); + } + for (auto const &e : dq) { ::fast_io::io::println(e); } -#endif } diff --git a/tests/0026.container/0003.deque/test_push_front.cc b/tests/0026.container/0003.deque/test_push_front.cc index 35337bb8f..a01f2e46c 100644 --- a/tests/0026.container/0003.deque/test_push_front.cc +++ b/tests/0026.container/0003.deque/test_push_front.cc @@ -3,17 +3,14 @@ int main() { - ::fast_io::deque<::std::uint_least32_t> dq; - for(::std::uint_least32_t i{};i!=1048576u;++i) + ::fast_io::deque<::std::size_t> dq; + for (::std::size_t i{}; i != 4096u; ++i) { dq.push_front(i); } - static_assert(::std::random_access_iterator); -#if 1 for (auto const &e : dq) { ::fast_io::io::println(e); } -#endif } From eaea7b8e2e7467bcda98e23c460f7eacf6dc2f4d Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 03:50:54 +0800 Subject: [PATCH 20/26] [skip CI]why c malloc has no calls to aligned_alloc? --- .../fast_io_core_impl/allocation/c_malloc.h | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/include/fast_io_core_impl/allocation/c_malloc.h b/include/fast_io_core_impl/allocation/c_malloc.h index 1337a65d2..e6d0766b8 100644 --- a/include/fast_io_core_impl/allocation/c_malloc.h +++ b/include/fast_io_core_impl/allocation/c_malloc.h @@ -5,6 +5,7 @@ #elif __has_include() #include #endif +#include namespace fast_io { @@ -197,6 +198,30 @@ class c_malloc_allocator ::fast_io::noexcept_call(_aligned_free, p); } } +#elif !defined(__MSDOS__) && 0 + static inline void *allocate_aligned(::std::size_t alignment, ::std::size_t n) noexcept + { + if (n == 0) + { + n = 1; + } + void *p = +#if FAST_IO_HAS_BUILTIN(__builtin_aligned_alloc) + __builtin_aligned_alloc(alignment, n) +#else + ::std::aligned_alloc(alignment, n) +#endif + ; + if (p == nullptr) + { + ::fast_io::fast_terminate(); + } + return p; + } + static inline void deallocate_aligned(void *p, ::std::size_t) noexcept + { + deallocate(p); + } #endif static inline void deallocate(void *p) noexcept { @@ -204,9 +229,9 @@ class c_malloc_allocator { return; } - + #if FAST_IO_HAS_BUILTIN(__builtin_free) - __builtin_free + __builtin_free #else ::std::free #endif From aa1c97537ef3ba749c3bd4ac7f8b73f7a7f8042f Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 04:28:27 +0800 Subject: [PATCH 21/26] pass all the fuzzing for deque. now run all tests --- include/fast_io_dsal/impl/deque.h | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 6af94aa4e..ff0066a29 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -318,21 +318,6 @@ inline constexpr void deque_destroy_trivial_common(controllerblocktype &controll } } -/* -struct -#if __has_cpp_attribute(__gnu__::__may_alias__) - [[__gnu__::__may_alias__]] -#endif - deque_controller_block_common -{ - using replacetype = char unsigned; - void **controller_start_ptr; - void **controller_start_reserved_ptr; - void **controller_after_reserved_ptr; - void **controller_after_ptr; -}; -*/ - template inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least) noexcept { @@ -450,16 +435,24 @@ inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontrolt if (used_blocks_pivot != reserved_pivot) { ::std::ptrdiff_t diff{reserved_pivot - used_blocks_pivot}; - auto rotate_pivot{diff < 0 ? after_reserved_ptr + diff : start_reserved_ptr + diff}; +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current(), + "\tdiff=",diff); +#endif + auto rotate_pivot{diff < 0 ? start_reserved_ptr : after_reserved_ptr}; + rotate_pivot -= diff; ::std::rotate(start_reserved_ptr, rotate_pivot, after_reserved_ptr); controller.front_block.controller_ptr += diff; controller.back_block.controller_ptr += diff; } auto slots_pivot{controller.controller_block.controller_start_ptr + half_slots_count}; - if (slots_pivot != used_blocks_pivot) + if (slots_pivot != reserved_pivot) { - ::std::ptrdiff_t diff{slots_pivot - used_blocks_pivot}; +#if 0 + ::fast_io::iomnp::debug_println(::std::source_location::current()); +#endif + ::std::ptrdiff_t diff{slots_pivot - reserved_pivot}; ::fast_io::freestanding::overlapped_copy(start_reserved_ptr, after_reserved_ptr, start_reserved_ptr + diff); controller.front_block.controller_ptr += diff; From 023ecdbe25c2acaef8f31c62af53784df2e46ba3 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 04:43:03 +0800 Subject: [PATCH 22/26] use ::std::destroy instead ::std::ranges::destroy for deque Add some benchmarks for deque --- .../deque/0001.push_back/fast_io.cc | 15 +++++++++++++++ .../deque/0001.push_back/fast_io_vec.cc | 15 +++++++++++++++ .../deque/0001.push_back/fast_io_vec_reserve.cc | 16 ++++++++++++++++ .../0011.containers/deque/0001.push_back/std.cc | 14 ++++++++++++++ include/fast_io_dsal/impl/deque.h | 6 +++--- 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 benchmark/0011.containers/deque/0001.push_back/fast_io.cc create mode 100644 benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc create mode 100644 benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc create mode 100644 benchmark/0011.containers/deque/0001.push_back/std.cc diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc new file mode 100644 index 000000000..7594e6b44 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::deque::push_back total"); + fast_io::deque deq; + constexpr std::size_t n{100000000}; + fast_io::timer tm1(u8"fast_io::deque::push_back"); + for (std::size_t i{}; i != n; ++i) + { + deq.emplace_back(i); + } +} diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc new file mode 100644 index 000000000..c85bd4d14 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::vector::push_back total"); + fast_io::vector vec; + constexpr std::size_t n{100000000}; + fast_io::timer tm1(u8"fast_io::vector::push_back"); + for (std::size_t i{}; i != n; ++i) + { + vec.emplace_back(i); + } +} diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc new file mode 100644 index 000000000..598027fc9 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc @@ -0,0 +1,16 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::vector::push_back total"); + fast_io::vector vec; + constexpr std::size_t n{100000000}; + fast_io::timer tm1(u8"fast_io::vector::push_back"); + vec.reserve(n); + for (std::size_t i{}; i != n; ++i) + { + vec.emplace_back(i); + } +} diff --git a/benchmark/0011.containers/deque/0001.push_back/std.cc b/benchmark/0011.containers/deque/0001.push_back/std.cc new file mode 100644 index 000000000..5ea910c3b --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/std.cc @@ -0,0 +1,14 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"std::deque::push_back"); + std::deque vec; + constexpr std::size_t n{100000000}; + for (std::size_t i{}; i != n; ++i) + { + vec.push_back(i); + } +} diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index ff0066a29..cd7394b16 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -753,7 +753,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE private: inline constexpr void destroy_all_elements() noexcept { - ::std::ranges::destroy(controller.front_block.curr_ptr, controller.front_block.end_ptr); + ::std::destroy(controller.front_block.curr_ptr, controller.front_block.end_ptr); auto front_controller_ptr{controller.front_block.controller_ptr}; auto back_controller_ptr{controller.back_block.controller_ptr}; if (front_controller_ptr != back_controller_ptr) @@ -761,10 +761,10 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE for (T **it{front_controller_ptr + 1}, **ed{back_controller_ptr}; it != ed; ++it) { T *blockptr{*it}; - ::std::ranges::destroy(blockptr, blockptr + block_size); + ::std::destroy(blockptr, blockptr + block_size); } } - ::std::ranges::destroy(controller.back_block.begin_ptr, controller.back_block.curr_ptr); + ::std::destroy(controller.back_block.begin_ptr, controller.back_block.curr_ptr); } inline constexpr void destroy() noexcept From 876a1769ed757a9b1375c8c397d3df31f3bc38e0 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 04:58:23 +0800 Subject: [PATCH 23/26] remove unused variable, this should make CI happy --- include/fast_io_dsal/impl/deque.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index cd7394b16..b4df45b9f 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -330,7 +330,6 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont auto old_after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; ::std::size_t const old_start_reserved_ptr_pos{static_cast<::std::size_t>(old_start_reserved_ptr - old_start_ptr)}; - ::std::size_t const old_after_reserved_ptr_pos{static_cast<::std::size_t>(old_after_reserved_ptr - old_start_ptr)}; ::std::size_t const old_after_ptr_pos{static_cast<::std::size_t>(controller.controller_block.controller_after_ptr - old_start_ptr)}; ::std::size_t const old_front_block_ptr_pos{static_cast<::std::size_t>(controller.front_block.controller_ptr - old_start_ptr)}; ::std::size_t const old_back_block_ptr_pos{static_cast<::std::size_t>(controller.back_block.controller_ptr - old_start_ptr)}; From 4f609ad42ae404bbfc2dfefc642a4d8e58830fd0 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 05:11:47 +0800 Subject: [PATCH 24/26] [skip CI] enrich the benchmark of deque for std --- benchmark/0011.containers/deque/0001.push_back/std.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/benchmark/0011.containers/deque/0001.push_back/std.cc b/benchmark/0011.containers/deque/0001.push_back/std.cc index 5ea910c3b..808a5b6f6 100644 --- a/benchmark/0011.containers/deque/0001.push_back/std.cc +++ b/benchmark/0011.containers/deque/0001.push_back/std.cc @@ -4,11 +4,12 @@ int main() { - fast_io::timer tm(u8"std::deque::push_back"); - std::deque vec; + fast_io::timer tm(u8"std::deque::push_back total"); + std::deque deq; constexpr std::size_t n{100000000}; + fast_io::timer tm1(u8"std::deque::push_back"); for (std::size_t i{}; i != n; ++i) { - vec.push_back(i); + deq.push_back(i); } } From 16d90300db51d8a5e43afa1d3e15b280ed2531bd Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 05:22:44 +0800 Subject: [PATCH 25/26] [skip ci] make benchmarks also bench loop for deque --- .../deque/0001.push_back/fast_io.cc | 19 +++++++++++++++---- .../deque/0001.push_back/fast_io_vec.cc | 19 +++++++++++++++---- .../0001.push_back/fast_io_vec_reserve.cc | 19 +++++++++++++++---- .../deque/0001.push_back/std.cc | 19 +++++++++++++++---- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc index 7594e6b44..7a60f7341 100644 --- a/benchmark/0011.containers/deque/0001.push_back/fast_io.cc +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc @@ -4,12 +4,23 @@ int main() { - fast_io::timer tm(u8"fast_io::deque::push_back total"); + fast_io::timer tm(u8"fast_io::deque"); fast_io::deque deq; constexpr std::size_t n{100000000}; - fast_io::timer tm1(u8"fast_io::deque::push_back"); - for (std::size_t i{}; i != n; ++i) { - deq.emplace_back(i); + fast_io::timer tm1(u8"push_back"); + for (std::size_t i{}; i != n; ++i) + { + deq.push_back(i); + } } + ::std::size_t sum{}; + { + fast_io::timer tm1(u8"loop"); + for (auto const e : deq) + { + sum += e; + } + } + ::fast_io::io::perrln("sum=",sum); } diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc index c85bd4d14..2bd8bb506 100644 --- a/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc @@ -4,12 +4,23 @@ int main() { - fast_io::timer tm(u8"fast_io::vector::push_back total"); + fast_io::timer tm(u8"fast_io::vector"); fast_io::vector vec; constexpr std::size_t n{100000000}; - fast_io::timer tm1(u8"fast_io::vector::push_back"); - for (std::size_t i{}; i != n; ++i) { - vec.emplace_back(i); + fast_io::timer tm1(u8"push_back"); + for (std::size_t i{}; i != n; ++i) + { + vec.push_back(i); + } } + ::std::size_t sum{}; + { + fast_io::timer tm1(u8"loop"); + for (auto const e : vec) + { + sum += e; + } + } + ::fast_io::io::perrln("sum=",sum); } diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc index 598027fc9..463faeaab 100644 --- a/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc @@ -4,13 +4,24 @@ int main() { - fast_io::timer tm(u8"fast_io::vector::push_back total"); + fast_io::timer tm(u8"fast_io::vector reserve"); fast_io::vector vec; constexpr std::size_t n{100000000}; - fast_io::timer tm1(u8"fast_io::vector::push_back"); vec.reserve(n); - for (std::size_t i{}; i != n; ++i) { - vec.emplace_back(i); + fast_io::timer tm1(u8"push_back"); + for (std::size_t i{}; i != n; ++i) + { + vec.push_back(i); + } } + ::std::size_t sum{}; + { + fast_io::timer tm1(u8"loop"); + for (auto const e : vec) + { + sum += e; + } + } + ::fast_io::io::perrln("sum=",sum); } diff --git a/benchmark/0011.containers/deque/0001.push_back/std.cc b/benchmark/0011.containers/deque/0001.push_back/std.cc index 808a5b6f6..b2744c2a4 100644 --- a/benchmark/0011.containers/deque/0001.push_back/std.cc +++ b/benchmark/0011.containers/deque/0001.push_back/std.cc @@ -4,12 +4,23 @@ int main() { - fast_io::timer tm(u8"std::deque::push_back total"); + fast_io::timer tm(u8"std::deque"); std::deque deq; constexpr std::size_t n{100000000}; - fast_io::timer tm1(u8"std::deque::push_back"); - for (std::size_t i{}; i != n; ++i) { - deq.push_back(i); + fast_io::timer tm1(u8"push_back"); + for (std::size_t i{}; i != n; ++i) + { + deq.push_back(i); + } } + ::std::size_t sum{}; + { + fast_io::timer tm1(u8"loop"); + for (auto const e : deq) + { + sum += e; + } + } + ::fast_io::io::perrln("sum=",sum); } From 35e03e517ae24ac17dd7e0ac97d52ab20ae5a85c Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 6 Jan 2026 06:06:10 +0800 Subject: [PATCH 26/26] implement deque's clear and also add tests --- .../0007.containers/deque/fuzz_deque_clear.cc | 80 +++++++++++++++++++ include/fast_io_dsal/impl/deque.h | 58 +++++++++++--- tests/0026.container/0003.deque/clear.cc | 49 ++++++++++++ .../0003.deque/clear_destroy.cc | 63 +++++++++++++++ 4 files changed, 239 insertions(+), 11 deletions(-) create mode 100644 fuzzing/0007.containers/deque/fuzz_deque_clear.cc create mode 100644 tests/0026.container/0003.deque/clear.cc create mode 100644 tests/0026.container/0003.deque/clear_destroy.cc diff --git a/fuzzing/0007.containers/deque/fuzz_deque_clear.cc b/fuzzing/0007.containers/deque/fuzz_deque_clear.cc new file mode 100644 index 000000000..e87bb78b7 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_clear.cc @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const* data, size_t size) +{ + // Require large inputs to trigger growth/balancing + if (size < 4096) + return 0; + + ::fast_io::deque dq; + std::deque ref; + + for (size_t i{}; i != size; ++i) + { + uint8_t raw = data[i]; + uint8_t op = raw % 6u; // 6 operations + std::size_t value = i; + + bool allow_clear = + dq.size() >= 4096 || (raw == 0xFF); + // 1/256 chance of early clear + + switch (op) + { + case 0: // push_back + dq.push_back(value); + ref.push_back(value); + break; + + case 1: // push_front + dq.push_front(value); + ref.push_front(value); + break; + + case 2: // pop_back + if (!ref.empty()) + { + dq.pop_back(); + ref.pop_back(); + } + break; + + case 3: // pop_front + if (!ref.empty()) + { + dq.pop_front(); + ref.pop_front(); + } + break; + + case 4: // clear() + if (allow_clear) + { + dq.clear(); + ref.clear(); + } + break; + + case 5: // clear_destroy() + if (allow_clear) + { + dq.clear_destroy(); + ref.clear(); + } + break; + } + } + + // Validate correctness + if (dq.size() != ref.size()) + __builtin_trap(); + + if (!std::ranges::equal(dq, ref)) + __builtin_trap(); + + return 0; +} diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index b4df45b9f..52f37ae05 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -720,6 +720,41 @@ inline constexpr void deque_grow_back_common(dequecontroltype &controller) noexc ::fast_io::containers::details::deque_grow_back_common_impl(controller, align, blockbytes); } + +template +inline constexpr void deque_clear_common_impl(dequecontroltype &controller, ::std::size_t blockbytes) +{ + auto start_reserved_ptr{controller.controller_block.controller_start_reserved_ptr}; + auto after_reserved_ptr{controller.controller_block.controller_after_reserved_ptr}; + if (start_reserved_ptr == after_reserved_ptr) + { + return; + } + auto const reserved_blocks_count{ + static_cast<::std::size_t>(after_reserved_ptr - start_reserved_ptr)}; + auto const half_reserved_blocks_count{ + static_cast<::std::size_t>(reserved_blocks_count >> 1u)}; + auto reserved_pivot{start_reserved_ptr + half_reserved_blocks_count}; + using replacetype = typename dequecontroltype::replacetype; + constexpr bool isvoidplaceholder = std::same_as; + using begin_ptrtype = + std::conditional_t; + auto begin_ptr{static_cast(*reserved_pivot)}; + auto end_ptr{begin_ptr + blockbytes}; + auto mid_ptr{begin_ptr + static_cast<::std::size_t>(blockbytes >> 1u)}; + controller.back_block.controller_ptr = controller.front_block.controller_ptr = reserved_pivot; + controller.back_block.begin_ptr = controller.front_block.begin_ptr = begin_ptr; + controller.back_block.curr_ptr = controller.front_block.curr_ptr = mid_ptr; + controller.back_block.end_ptr = controller.front_block.end_ptr = end_ptr; +} + +template +inline constexpr void deque_clear_common(dequecontroltype &controller) noexcept +{ + constexpr ::std::size_t blockbytes{sz * block_size}; + ::fast_io::containers::details::deque_clear_common_impl(controller, blockbytes); +} + } // namespace details template @@ -782,7 +817,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { if (__builtin_is_constant_evaluated()) { - ::fast_io::containers::details::deque_grow_front_common(controller); + ::fast_io::containers::details::deque_grow_front_common(controller); } else { @@ -797,7 +832,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { if (__builtin_is_constant_evaluated()) { - ::fast_io::containers::details::deque_grow_back_common(controller); + ::fast_io::containers::details::deque_grow_back_common(controller); } else { @@ -821,20 +856,21 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } public: -#if 0 inline constexpr void clear() noexcept { - if (controller.controller_block.start_ptr == controller.controller_block.start_ptr) + if constexpr (!::std::is_trivially_destructible_v) { - return; + this->destroy_all_elements(); + } + if (__builtin_is_constant_evaluated()) + { + ::fast_io::containers::details::deque_clear_common(controller); + } + else + { + ::fast_io::containers::details::deque_clear_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller))); } - this->destroy(); - size_type n{(controller.back_block.controller_ptr - controller.front_block.controller_ptr)}; - n >>= 1u; - auto mid{controller.front_block.controller_ptr + n}; - *mid; } -#endif template requires ::std::constructible_from inline constexpr reference emplace_back(Args &&...args) diff --git a/tests/0026.container/0003.deque/clear.cc b/tests/0026.container/0003.deque/clear.cc new file mode 100644 index 000000000..408fbea86 --- /dev/null +++ b/tests/0026.container/0003.deque/clear.cc @@ -0,0 +1,49 @@ +#include +#include + +int main() +{ + ::fast_io::io::perr("=== clear() test ===\n"); + + ::fast_io::deque<::std::size_t> dq; + + // Fill with enough elements to force growth + for (::std::size_t i{}; i != 4096u; ++i) + { + dq.push_back(i); + } + + // Clear but keep memory + dq.clear(); + + // Validate empty state + if (!dq.is_empty()) + { + ::fast_io::io::panic("ERROR: dq.is_empty() == false after clear()\n"); + } + + if (dq.size() != 0u) + { + ::fast_io::io::panic("ERROR: dq.size() != 0 after clear()\n"); + } + + // Ensure iteration produces nothing + for (auto const &e : dq) + { + ::fast_io::io::panicln("ERROR: iterated element after clear(): ", e); + } + + // Reuse the deque + for (::std::size_t i{}; i != 4096u; ++i) + { + dq.push_front(i); + } + + // Iterate to ensure no crash + for (auto const &e : dq) + { + ::fast_io::io::println(e); + } + + ::fast_io::io::print("clear() test finished\n"); +} diff --git a/tests/0026.container/0003.deque/clear_destroy.cc b/tests/0026.container/0003.deque/clear_destroy.cc new file mode 100644 index 000000000..75986fd55 --- /dev/null +++ b/tests/0026.container/0003.deque/clear_destroy.cc @@ -0,0 +1,63 @@ +#include +#include + +int main() +{ + ::fast_io::io::perr("=== clear_destroy() test ===\n"); + + ::fast_io::deque<::std::size_t> dq; + + // Fill with enough elements to force growth + for (::std::size_t i{}; i != 4096u; ++i) + { + dq.push_back(i); + } + + // First clear() — keep memory but reset state + dq.clear(); + + // Validate empty state after clear() + if (!dq.is_empty()) + { + ::fast_io::io::panic("ERROR: dq.is_empty() == false after clear()\n"); + } + if (dq.size() != 0u) + { + ::fast_io::io::panic("ERROR: dq.size() != 0 after clear()\n"); + } + for (auto const &e : dq) + { + ::fast_io::io::panicln("ERROR: iterated element after clear(): ", e); + } + + // Now clear_destroy() — free all memory + dq.clear_destroy(); + + // Validate empty state after clear_destroy() + if (!dq.is_empty()) + { + ::fast_io::io::panic("ERROR: dq.is_empty() == false after clear_destroy()\n"); + } + if (dq.size() != 0u) + { + ::fast_io::io::panic("ERROR: dq.size() != 0 after clear_destroy()\n"); + } + for (auto const &e : dq) + { + ::fast_io::io::panicln("ERROR: iterated element after clear_destroy(): ", e); + } + + // Reuse the deque — must reinitialize cleanly + for (::std::size_t i{}; i != 4096u; ++i) + { + dq.push_front(i); + } + + // Iterate to ensure no crash and correct behavior + for (auto const &e : dq) + { + ::fast_io::io::println(e); + } + + ::fast_io::io::print("clear_destroy() test finished\n"); +}