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..7a60f7341 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::deque"); + fast_io::deque deq; + constexpr std::size_t n{100000000}; + { + 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 new file mode 100644 index 000000000..2bd8bb506 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::vector"); + fast_io::vector vec; + constexpr std::size_t n{100000000}; + { + 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 new file mode 100644 index 000000000..463faeaab --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_vec_reserve.cc @@ -0,0 +1,27 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::vector reserve"); + fast_io::vector vec; + constexpr std::size_t n{100000000}; + vec.reserve(n); + { + 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 new file mode 100644 index 000000000..b2744c2a4 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/std.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"std::deque"); + std::deque deq; + constexpr std::size_t n{100000000}; + { + 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/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/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_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 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 b2c0753a9..52f37ae05 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 @@ -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; } @@ -272,343 +291,468 @@ 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); } } -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 +inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least) 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))); -} +#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}; + 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_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)}; + + 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); + + 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}; + --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}; -template -inline constexpr void deque_init_grow_common(dequecontroltype &controller) noexcept -{ - if constexpr (align <= allocator::default_alignment) + decltype(old_start_reserved_ptr) old_pivot, new_pivot; + if (pivot_diff < 0) { - ::fast_io::containers::details::deque_init_grow_common_noalign_impl(controller, block_size, mid); + old_pivot = old_start_reserved_ptr; + new_pivot = new_after_reserved_ptr; } else { - ::fast_io::containers::details::deque_init_grow_common_align_impl(controller, align, block_size, mid); - } -} - -template -inline constexpr void deque_reallocate_controller_block_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(); + old_pivot = old_after_reserved_ptr; + new_pivot = new_start_reserved_ptr; } + old_pivot -= pivot_diff; + new_pivot += pivot_diff; - ::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)}; + ::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 block_temp{allocator::reallocate_n(controller.controller_block.controller_start_ptr, old_size * sizeof(void *), (new_size + 1u) * sizeof(void *))}; + *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 = static_cast(block_temp); + 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.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; + 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 -inline constexpr T **make_blocks_balance(T **begin, T **end, T **b, T **e) noexcept +template +inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontroltype &controller) 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) + 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_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 { - if (begin == b) - { - T *t = end[-1]; - ::std::copy(b, e, begin + 1); - *begin = t; - return begin + 1u; - } - else +#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) { - T *t = *begin; - ::std::copy(b, e, begin); - end[-1] = t; - return begin; + ::fast_io::fast_terminate(); } + ::fast_io::containers::details::deque_grow_to_new_blocks_count_impl(controller, + static_cast<::std::size_t>((total_slots_count << 1u) + 1u)); } else { - T **blocktemp{begin + (external_diff - internal_diff) / 2u}; - - if (blocktemp < b) +#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}; + 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) { - for (::std::size_t i{}; i != internal_diff; ++i) - { - ::std::swap(b[i], blocktemp[i]); - } + ::std::ptrdiff_t diff{reserved_pivot - used_blocks_pivot}; +#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; } - else + + auto slots_pivot{controller.controller_block.controller_start_ptr + half_slots_count}; + if (slots_pivot != reserved_pivot) { - for (::std::size_t i{internal_diff}; i--;) - { - ::std::swap(b[i], blocktemp[i]); - } +#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; + controller.back_block.controller_ptr += diff; + controller.controller_block.controller_start_reserved_ptr += diff; + *(controller.controller_block.controller_after_reserved_ptr += diff) = nullptr; } - - return blocktemp; } } -template -inline constexpr void deque_make_reserved_blocks_balance_common_impl(dequecontroltype &controller) noexcept -{ - ::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; -} - -template -inline constexpr void deque_make_unreserved_blocks_balance_common_impl(dequecontroltype &controller) noexcept +template +inline constexpr void deque_allocate_on_empty_common_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) 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; + using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; + 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}; + 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))}; + + 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_mid_block + 1) = nullptr; // set nullptr as a sentinel + + 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); + auto halfposptr{begin_ptr + halfsize}; + front_block.curr_ptr = halfposptr; + back_block.curr_ptr = halfposptr; } -template -inline constexpr void deque_grow_front_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); + /** + * 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; + } - if (controller.front_block.curr_ptr != controller.front_block.begin_ptr) + using replacetype = typename dequecontroltype::replacetype; + constexpr bool isvoidplaceholder = std::same_as; + using begin_ptrtype = + std::conditional_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); + if (diff_to_after_ptr < 2) + { + /** + * 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) { - return; + ::fast_io::containers::details:: + deque_rebalance_or_grow_2x_after_blocks_impl(controller); } - } - - // has reserved block == fasle - if (controller.front_block.controller_ptr == controller.controller_block.controller_start_reserved_ptr) - { - // could balance reserved blocks == false - if (controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr < 2u) + 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) { - // has unreserved block == fasle - if (controller.controller_block.controller_start_ptr == controller.controller_block.controller_start_reserved_ptr) + 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) { - // 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); - } + auto start_reserved_ptr = + controller.controller_block.controller_start_reserved_ptr; - 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); - } - } + /* Reuse the block memory. */ + new_block = static_cast(*start_reserved_ptr); - void *block_temp{}; - - if constexpr (align <= allocator::default_alignment) - { - block_temp = allocator::allocate(block_size); + /* Consume one reserved block from the front. */ + ++controller.controller_block.controller_start_reserved_ptr; } else { - block_temp = allocator::allocate_zero_aligned(align, block_size); + /** + * No front capacity block is available. Allocate a new block. + */ + new_block = + static_cast(allocator::allocate_aligned(align, bytes)); } - *--controller.controller_block.controller_start_reserved_ptr = reinterpret_cast(block_temp); - } - else - { - ::fast_io::containers::details::deque_make_reserved_blocks_balance_common_impl(controller); + /** + * 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; } } - controller.front_block.curr_ptr = controller.front_block.end_ptr = static_cast(controller.front_block.begin_ptr = *--controller.front_block.controller_ptr) + block_size; + 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. + */ + ++controller.back_block.controller_ptr; + + /** + * Load the block pointer and initialize begin/curr/end pointers. + */ + auto begin_ptr = + static_cast(*controller.back_block.controller_ptr); + + 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 -inline constexpr void deque_grow_back_common_impl(dequecontroltype &controller) noexcept +template +inline constexpr void deque_grow_front_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); + /** + * 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; } - // has reserved block == fasle - if (controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr < 2u) + 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) { - // could balance reserved blocks == false - if (controller.controller_block.controller_start_reserved_ptr == controller.front_block.controller_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) { - // has unreserved block == fasle - if (controller.controller_block.controller_after_ptr == controller.controller_block.controller_after_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; + std::size_t diff_to_after_ptr = + static_cast( + after_reserved_ptr - + controller.back_block.controller_ptr); + if (1 < diff_to_after_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{}; + /* Reuse the block memory. */ + new_block = static_cast(*(--after_reserved_ptr)); - if constexpr (align <= allocator::default_alignment) - { - block_temp = allocator::allocate(block_size); + /* Consume one reserved block from the back. */ + *(controller.controller_block.controller_after_reserved_ptr = after_reserved_ptr) = nullptr; } else { - block_temp = allocator::allocate_zero_aligned(align, block_size); + new_block = + static_cast(allocator::allocate_aligned(align, bytes)); } - *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); + auto pos{--controller.controller_block.controller_start_reserved_ptr}; + std::construct_at(pos, new_block); } } - 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.front_block.controller_ptr; + + 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)); } 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 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); +} + + +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 @@ -641,17 +785,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::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 +795,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::destroy(blockptr, blockptr + block_size); } } - destroy_block_element(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 @@ -674,19 +810,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 @@ -694,11 +817,11 @@ 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 { - ::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))); } } @@ -709,39 +832,45 @@ 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 { - ::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))); } } 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 { - 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: -#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) @@ -751,11 +880,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; } @@ -829,10 +955,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; } @@ -997,7 +1120,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}; 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; diff --git a/tests/0026.container/0003.deque/.test_prop.toml b/tests/0026.container/0003.deque/.test_prop.toml index fdcbe2ec8..3083767cb 100644 --- a/tests/0026.container/0003.deque/.test_prop.toml +++ b/tests/0026.container/0003.deque/.test_prop.toml @@ -1,2 +1,6 @@ ["dequeasm.cc"] # no auto-gen test. only to see the output assembly ignore = true +["deque_push.cc"] +ignore = true +["test_deque.cc"] +ignore = true 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"); +} 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..9c42fb683 --- /dev/null +++ b/tests/0026.container/0003.deque/test_push_back.cc @@ -0,0 +1,16 @@ +#include +#include + +int main() +{ + ::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); + } +} 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..a01f2e46c --- /dev/null +++ b/tests/0026.container/0003.deque/test_push_front.cc @@ -0,0 +1,16 @@ +#include +#include + +int main() +{ + ::fast_io::deque<::std::size_t> dq; + for (::std::size_t i{}; i != 4096u; ++i) + { + dq.push_front(i); + } + + for (auto const &e : dq) + { + ::fast_io::io::println(e); + } +}