From cf41d632cfb7badf747f081f4d8b293d33e1e723 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Mon, 12 Jan 2026 22:18:39 +0800 Subject: [PATCH 1/7] erase_index should return idx or fromidx for deque, string and vector --- .../deque/0001.push_back/fast_io.cc | 2 +- .../deque/0001.push_back/fast_io_reverse.cc | 26 +++++++++++++++++++ include/fast_io_dsal/impl/deque.h | 17 ++++++++++++ include/fast_io_dsal/impl/string.h | 8 +++--- include/fast_io_dsal/impl/vector.h | 6 +++-- 5 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 benchmark/0011.containers/deque/0001.push_back/fast_io_reverse.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 index 7a60f7341..7b5193105 100644 --- a/benchmark/0011.containers/deque/0001.push_back/fast_io.cc +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc @@ -20,7 +20,7 @@ int main() 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_reverse.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_reverse.cc new file mode 100644 index 000000000..8491a8690 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_reverse.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::deque reverse"); + 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 reverse"); + for (auto const e : ::std::ranges::reverse_view(deq)) + { + sum += e; + } + } + ::fast_io::io::perrln("sum=",sum); +} diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 4fd47cbc1..f567592ef 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -1850,6 +1850,23 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } #endif +#if 0 + inline constexpr iterator erase(const_iterator from, const_iterator to) noexcept + { + + } + + inline constexpr size_type erase_index(size_type fromidx, size_type toidx) noexcept + { + size_type const n{this->size()}; + if (n < fromidx || n < todix) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + return fromidx; + } +#endif + inline constexpr ~deque() { destroy_deque_controller(this->controller); diff --git a/include/fast_io_dsal/impl/string.h b/include/fast_io_dsal/impl/string.h index a4e728aaf..a65eaa54e 100644 --- a/include/fast_io_dsal/impl/string.h +++ b/include/fast_io_dsal/impl/string.h @@ -524,7 +524,7 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE this->assign_impl(other.imp.begin_ptr, static_cast<::std::size_t>(other.imp.curr_ptr - other.imp.begin_ptr)); return *this; } - inline constexpr basic_string& operator=(string_view_type const &other) noexcept + inline constexpr basic_string &operator=(string_view_type const &other) noexcept { this->assign(other); return *this; @@ -888,7 +888,7 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE return this->erase_impl(const_cast(first), const_cast(last)); } } - inline constexpr void erase_index(size_type firstidx, size_type lastidx) noexcept + inline constexpr size_type erase_index(size_type firstidx, size_type lastidx) noexcept { auto beginptr{this->imp.begin_ptr}; auto currptr{this->imp.curr_ptr}; @@ -898,6 +898,7 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::fast_io::fast_terminate(); } this->erase_impl(beginptr + firstidx, beginptr + lastidx); + return firstidx; } inline constexpr iterator erase(const_iterator it) noexcept { @@ -911,7 +912,7 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE return this->erase_impl(const_cast(it)); } } - inline constexpr void erase_index(size_type idx) noexcept + inline constexpr size_type erase_index(size_type idx) noexcept { auto beginptr{this->imp.begin_ptr}; auto currptr{this->imp.curr_ptr}; @@ -921,6 +922,7 @@ class basic_string FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::fast_io::fast_terminate(); } this->erase_impl(beginptr + idx); + return idx; } inline constexpr void swap(basic_string &other) noexcept { diff --git a/include/fast_io_dsal/impl/vector.h b/include/fast_io_dsal/impl/vector.h index 7fbd3f463..a3f90a75b 100644 --- a/include/fast_io_dsal/impl/vector.h +++ b/include/fast_io_dsal/impl/vector.h @@ -1011,7 +1011,7 @@ class vector FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } } - inline constexpr void erase_index(size_type idx) noexcept + inline constexpr size_type erase_index(size_type idx) noexcept { auto beginptr{imp.begin_ptr}; auto currptr{imp.curr_ptr}; @@ -1021,6 +1021,7 @@ class vector FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::fast_io::fast_terminate(); } this->erase_common(beginptr + idx); + return idx; } inline constexpr iterator erase(const_iterator first, const_iterator last) noexcept @@ -1035,7 +1036,7 @@ class vector FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } } - inline constexpr void erase_index(size_type firstidx, size_type lastidx) noexcept + inline constexpr size_type erase_index(size_type firstidx, size_type lastidx) noexcept { auto beginptr{imp.begin_ptr}; auto currptr{imp.curr_ptr}; @@ -1045,6 +1046,7 @@ class vector FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::fast_io::fast_terminate(); } this->erase_iters_common(beginptr + firstidx, beginptr + lastidx); + return firstidx; } inline constexpr void resize(size_type n) noexcept(::std::is_nothrow_default_constructible_v) From 10d689d34ee45d15a6cab1ba807b70fed751af21 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Tue, 13 Jan 2026 04:39:19 +0800 Subject: [PATCH 2/7] remove duplicated function in end_common() --- include/fast_io_dsal/impl/deque.h | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index f567592ef..81ddd46cb 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -1693,19 +1693,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } private: - inline constexpr ::fast_io::containers::details::deque_control_block end_common() noexcept - { - ::fast_io::containers::details::deque_control_block backblock{this->controller.back_block}; - if (backblock.curr_ptr == this->controller.back_end_ptr) [[unlikely]] - { - if (backblock.controller_ptr) [[likely]] - { - backblock.curr_ptr = backblock.begin_ptr = (*++backblock.controller_ptr); - } - } - return {backblock}; - } - inline constexpr ::fast_io::containers::details::deque_control_block end_common() const noexcept { ::fast_io::containers::details::deque_control_block backblock{this->controller.back_block}; From af086dcbc3f9361361ff57821115a1a1643ed6d0 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Wed, 14 Jan 2026 06:47:39 +0800 Subject: [PATCH 3/7] [skip ci] backup fuzz_deque_erase code --- .../0007.containers/deque/fuzz_deque_erase.cc | 130 +++++++ include/fast_io_dsal/impl/deque.h | 139 +++++++- include/fast_io_dsal/impl/freestanding.h | 111 +++++- tests/0026.container/0003.deque/erase.cc | 316 ++++++++++++++++++ 4 files changed, 689 insertions(+), 7 deletions(-) create mode 100644 fuzzing/0007.containers/deque/fuzz_deque_erase.cc create mode 100644 tests/0026.container/0003.deque/erase.cc diff --git a/fuzzing/0007.containers/deque/fuzz_deque_erase.cc b/fuzzing/0007.containers/deque/fuzz_deque_erase.cc new file mode 100644 index 000000000..5844a868b --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_erase.cc @@ -0,0 +1,130 @@ +// deque_erase_fuzz.cc +#include +#include +#include +#include + +#include +#include +#include + +using T = std::size_t; + +// Check equality between fast_io::deque and std::deque +static void check_equal(::fast_io::deque const& dq, + ::std::deque const& ref) +{ + if (dq.size() != ref.size()) + { + __builtin_trap(); // mismatch: size + } + + for (std::size_t i{}; i != dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + __builtin_trap(); // mismatch: value + } + } +} + +// Map a byte to an operation kind +enum class OpKind : uint8_t +{ + PushBack = 0, + PushFront = 1, + EraseIndex = 2, + EraseRangeIter = 3, + // you can add more later +}; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, std::size_t size) +{ + ::fast_io::deque dq; + ::std::deque ref; + + // We interpret the input as a stream of small commands. + // Each command may consume 1~3 bytes depending on the op. + std::size_t i = 0; + while (i < size) + { + uint8_t op_raw = data[i++] % 4; // we currently have 4 ops + OpKind op = static_cast(op_raw); + + switch (op) + { + case OpKind::PushBack: + { + if (i >= size) break; + // use the next byte as low bits of the value + T v = static_cast(data[i++]); + dq.push_back(v); + ref.push_back(v); + break; + } + case OpKind::PushFront: + { + if (i >= size) break; + T v = static_cast(data[i++]); + dq.push_front(v); + ref.push_front(v); + break; + } + case OpKind::EraseIndex: + { + if (dq.empty()) + break; + + if (i >= size) break; + std::size_t pos = static_cast(data[i++]); + if (!dq.empty()) + { + pos %= dq.size(); + + dq.erase_index(pos); + ref.erase(ref.begin() + static_cast(pos)); + } + break; + } + case OpKind::EraseRangeIter: + { + if (dq.empty()) + break; + + if (i + 1 > size) // need at least 1 byte + break; + + std::size_t n = dq.size(); + if (n == 0) + break; + + // choose two indices from bytes + std::size_t a = static_cast(data[i++]) % n; + std::size_t b = static_cast(data[i++ % size]) % (n + 1); + + std::size_t first_idx = (a < b ? a : b); + std::size_t last_idx = (a < b ? b : a); + + if (last_idx > n) last_idx = n; + if (first_idx > last_idx) first_idx = last_idx; + if (first_idx == last_idx) + break; // empty range, skip + + auto dq_first = dq.begin() + static_cast(first_idx); + auto dq_last = dq.begin() + static_cast(last_idx); + auto ref_first = ref.begin() + static_cast(first_idx); + auto ref_last = ref.begin() + static_cast(last_idx); + + dq.erase(dq_first, dq_last); + ref.erase(ref_first, ref_last); + break; + } + } + + // After each operation, verify dq == ref + check_equal(dq, ref); + } + + return 0; +} + diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 81ddd46cb..e89c47979 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -1332,6 +1332,33 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::fast_io::containers::details::deque_init_space_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller)), n); } } + inline static constexpr void destroy_elements_range( + ::fast_io::containers::details::deque_control_block const &first, + ::fast_io::containers::details::deque_control_block const &last) noexcept + { + if constexpr (!::std::is_trivially_destructible_v) + { + auto front_controller_ptr{first.controller_ptr}; + auto back_controller_ptr{last.controller_ptr}; + T *lastblockbegin; + if (front_controller_ptr == back_controller_ptr) + { + lastblockbegin = first.curr_ptr; + } + else + { + ::std::destroy(first.curr_ptr, first.begin_ptr + block_size); + for (T **it{front_controller_ptr + 1}, **ed{back_controller_ptr}; it != ed; ++it) + { + T *blockptr{*it}; + ::std::destroy(blockptr, blockptr + block_size); + } + lastblockbegin = last.begin_ptr; + } + ::std::destroy(lastblockbegin, last.curr_ptr); + } + } + inline static constexpr void destroy_all_elements(controller_type &controller) noexcept { auto front_controller_ptr{controller.front_block.controller_ptr}; @@ -1837,22 +1864,124 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } #endif +private: + inline constexpr iterator erase_unchecked_impl(iterator first, iterator last, bool moveleft) noexcept + { + if constexpr (!::std::is_trivially_destructible_v) + { + destroy_elements_range(first, last); + } #if 0 - inline constexpr iterator erase(const_iterator from, const_iterator to) noexcept + if constexpr(::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) + { +//Todo + } + else +#endif + { + if (moveleft) + { + this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), first, last).itercontent; + return last; + } + else + { + auto back_block{::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent}; + if (back_block.begin_ptr == back_block.curr_ptr) + { + if (this->controller.front_block.controller_ptr != back_block.controller_ptr) + { + back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); + } + } + this->controller.back_block = back_block; + return first; + } + } + } + inline constexpr iterator erase_unchecked_single_impl(iterator pos, bool moveleft) noexcept { + if constexpr (!::std::is_trivially_destructible_v) + { + ::std::destroy(pos.itercontent.curr_ptr); + } +#if 0 + if constexpr(::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) + { +//Todo + } + else +#endif + { + auto posp1{pos}; + ++posp1; + if (moveleft) + { + this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), pos, posp1).itercontent; + return posp1; + } + else + { + ::fast_io::io::perrln(::std::source_location::current(),"\t",::fast_io::mnp::pointervw(posp1.itercontent.controller_ptr), + " \tposp1=", ::fast_io::mnp::pointervw(posp1.itercontent.controller_ptr), " \tthis->end()=", + ::fast_io::mnp::pointervw(this->end().itercontent.begin_ptr)); + auto back_block{::fast_io::freestanding::uninitialized_relocate(posp1, this->end(), pos).itercontent}; + ::fast_io::io::perrln(::std::source_location::current()); + if (back_block.begin_ptr == back_block.curr_ptr) + { + if (this->controller.front_block.controller_ptr != back_block.controller_ptr) + { + back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); + } + } + this->controller.back_block = back_block; + return pos; + } + } + } + +public: + inline constexpr iterator erase(const_iterator first, const_iterator last) noexcept + { + return this->erase_unchecked_impl(iterator{first.itercontent}, + iterator{last.itercontent}, + static_cast(first - this->cbegin()) < + static_cast(this->cend() - last)); } - inline constexpr size_type erase_index(size_type fromidx, size_type toidx) noexcept + inline constexpr size_type erase_index(size_type firstidx, size_type lastidx) noexcept { size_type const n{this->size()}; - if (n < fromidx || n < todix) [[unlikely]] + if (n < firstidx || n < lastidx) [[unlikely]] { ::fast_io::fast_terminate(); } - return fromidx; + auto bg{this->begin()}; + this->erase_unchecked_impl(bg + firstidx, bg + lastidx, firstidx < (n - lastidx)); + return firstidx; + } + + inline constexpr iterator erase(const_iterator first) noexcept + { + auto firstp1{first}; + ++firstp1; + return this->erase_unchecked_single_impl(iterator{first.itercontent}, + static_cast(first - this->cbegin()) < + static_cast(this->cend() - firstp1)); + } + + inline constexpr size_type erase_index(size_type firstidx) noexcept + { + size_type const n{this->size()}; + if (n <= firstidx) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + this->erase_unchecked_single_impl(this->begin() + firstidx, + firstidx < static_cast(n - static_cast(firstidx + 1u))); + return firstidx; } -#endif inline constexpr ~deque() { diff --git a/include/fast_io_dsal/impl/freestanding.h b/include/fast_io_dsal/impl/freestanding.h index 1f18ad34f..db5e76baf 100644 --- a/include/fast_io_dsal/impl/freestanding.h +++ b/include/fast_io_dsal/impl/freestanding.h @@ -7,6 +7,11 @@ concept has_uninitialized_relocate_define = requires(T *ptr) { { uninitialized_relocate_define(ptr, ptr, ptr) } -> ::std::same_as; }; +template +concept has_uninitialized_relocate_backward_define = requires(T *ptr) { + { uninitialized_relocate_backward_define(ptr, ptr, ptr) } -> ::std::same_as; +}; + template concept has_uninitialized_move_define = requires(T *ptr) { { uninitialized_move_define(ptr, ptr, ptr) } -> ::std::same_as; @@ -72,7 +77,7 @@ inline constexpr Iter2 uninitialized_relocate(Iter1 first, Iter1 last, Iter2 des // we do not allow move constructor to throw EH. while (first != last) { - ::std::construct_at(dest, ::std::move(*first)); + ::std::construct_at(::std::addressof(*dest), ::std::move(*first)); if constexpr (!::std::is_trivially_destructible_v) { if constexpr (::std::is_pointer_v) @@ -91,6 +96,108 @@ inline constexpr Iter2 uninitialized_relocate(Iter1 first, Iter1 last, Iter2 des } } +template <::std::bidirectional_iterator Iter1, ::std::bidirectional_iterator Iter2> +inline constexpr Iter2 uninitialized_relocate_backward(Iter1 first, Iter1 last, Iter2 dest) noexcept +{ + // Semantics: + // Relocate the range [first, last) into the uninitialized memory ending at `dest`. + // `dest` is treated as the end iterator (one past the last element) of the destination range. + // The function returns the begin iterator of the destination range: + // dest - (last - first) + + if constexpr (::std::contiguous_iterator && !::std::is_pointer_v && + ::std::contiguous_iterator && !::std::is_pointer_v) + { + return uninitialized_relocate_backward(::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 uninitialized_relocate_backward(::std::to_address(first), + ::std::to_address(last), + dest); + } + else if constexpr (::std::contiguous_iterator && !::std::is_pointer_v) + { + return uninitialized_relocate_backward(first, + last, + ::std::to_address(dest)) - + ::std::to_address(dest) + dest; + } + else + { + using iter1valuetype = ::std::iter_value_t; + using iter2valuetype = ::std::iter_value_t; + + // Fast path: trivial relocation via byte copy + if constexpr (::std::is_pointer_v && ::std::is_pointer_v && + (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v && + ::fast_io::freestanding::is_trivially_copyable_or_relocatable_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 + { + auto const firstbyteptr{reinterpret_cast<::std::byte const *>(first)}; + auto const lastbyteptr{reinterpret_cast<::std::byte const *>(last)}; + auto const n{lastbyteptr - firstbyteptr}; + + // Compute the beginning of the destination range + auto const destfirst{reinterpret_cast<::std::byte *>(dest) - n}; + + // Perform the raw byte copy + ::fast_io::freestanding::bytes_copy(firstbyteptr, + lastbyteptr, + destfirst); + + return reinterpret_cast(destfirst); + } + } + // Custom relocate_backward hook for user-defined types + else if constexpr (::std::same_as && + ::fast_io::operations::defines::has_uninitialized_relocate_define) + { + return uninitialized_relocate_define_backward(first, last, dest); + } + + // Generic slow path: + // Move-construct elements in reverse order into uninitialized memory, + // then destroy the original elements. + while (first != last) + { + --last; + --dest; +#if 0 + __builtin_fprintf(stderr, "%s %d\n", __FILE__, __LINE__); +#endif + ::std::construct_at(::std::addressof(*dest), ::std::move(*last)); + + if constexpr (!::std::is_trivially_destructible_v) + { + if constexpr (::std::is_pointer_v) + { + ::std::destroy_at(last); + } + else + { + ::std::destroy_at(__builtin_addressof(*last)); + } + } + } + + // Return the begin iterator of the relocated range + return dest; + } +} + template <::std::input_or_output_iterator Iter1, ::std::input_or_output_iterator Iter2> inline constexpr Iter2 uninitialized_move(Iter1 first, Iter1 last, Iter2 dest) noexcept { @@ -138,7 +245,7 @@ inline constexpr Iter2 uninitialized_move(Iter1 first, Iter1 last, Iter2 dest) n // we do not allow move constructor to throw EH. while (first != last) { - ::std::construct_at(dest, ::std::move(*first)); + ::std::construct_at(::std::addressof(*dest), ::std::move(*first)); ++first; ++dest; } diff --git a/tests/0026.container/0003.deque/erase.cc b/tests/0026.container/0003.deque/erase.cc new file mode 100644 index 000000000..66c4c7d4e --- /dev/null +++ b/tests/0026.container/0003.deque/erase.cc @@ -0,0 +1,316 @@ +#include +#include +#include +#include + +namespace +{ + +inline void test_erase() +{ + ::fast_io::io::perr("=== deque erase test ===\n"); + + using T = ::std::size_t; + ::fast_io::deque dq; + ::std::deque ref; + + // Fill initial data + for (::std::size_t i{}; i != 200u; ++i) + { + dq.push_back(i); + ref.push_back(i); + } + + // Helper to compare dq and ref + auto check_equal = [&](auto const &msg, + ::std::source_location src = ::std::source_location::current()) { + if (dq.size() != ref.size()) + { + ::fast_io::io::panicln(src, "\tERROR: size mismatch: ", msg); + } + for (::std::size_t i{}; i != dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + ::fast_io::io::panicln(src, + "\tERROR: value mismatch at index ", + i, + "\tdq[i]=", + dq[i], + "\tref[i]=", + ref[i], + " : ", + msg); + } + } + }; + + // 1. Erase single element at front + { + dq.erase(dq.begin()); + ref.erase(ref.begin()); + check_equal("erase single at front"); + } + + // 2. Erase single element in the middle + { + if (!dq.empty()) + { + ::std::size_t pos{dq.size() / 2}; + dq.erase(dq.begin() + pos); + ref.erase(ref.begin() + pos); + check_equal("erase single in middle"); + } + } + + // 3. Erase single element at back (last element) + { + if (!dq.empty()) + { + ::std::size_t pos{dq.size() - 1}; + dq.erase(dq.begin() + pos); + ref.erase(ref.begin() + pos); + check_equal("erase single at back"); + } + } + + // 4. Erase a range at the front + { + if (dq.size() >= 10u) + { + dq.erase(dq.begin(), dq.begin() + 10); + ref.erase(ref.begin(), ref.begin() + 10); + check_equal("erase range at front"); + } + } + + // 5. Erase a range in the middle + { + if (dq.size() >= 30u) + { + ::std::size_t start{dq.size() / 3}; + ::std::size_t len{20u}; + if (start + len > dq.size()) + { + len = dq.size() - start; + } + + dq.erase(dq.begin() + start, dq.begin() + start + len); + ref.erase(ref.begin() + start, ref.begin() + start + len); + check_equal("erase range in middle"); + } + } + + // 6. Erase a range at the back + { + if (dq.size() >= 15u) + { + ::std::size_t len{15u}; + ::std::size_t start{dq.size() - len}; + + dq.erase(dq.begin() + start, dq.end()); + ref.erase(ref.begin() + start, ref.end()); + check_equal("erase range at back"); + } + } + + // 7. Randomized erases: erase random ranges until we have done enough iterations + for (::std::size_t iter{}; iter != 200u; ++iter) + { + if (dq.empty()) + { + break; + } + + ::std::size_t current_size{dq.size()}; + ::std::size_t pos{iter % current_size}; + + // Choose a small length depending on iter, clamped to the remaining size + ::std::size_t max_len{5u}; + ::std::size_t len{(iter * 7u) % max_len + 1u}; + if (pos + len > current_size) + { + len = current_size - pos; + } + + auto dq_first = dq.begin() + pos; + auto dq_last = dq.begin() + pos + len; + + auto ref_first = ref.begin() + pos; + auto ref_last = ref.begin() + pos + len; + + dq.erase(dq_first, dq_last); + ref.erase(ref_first, ref_last); + + check_equal("randomized erase"); + } + + ::fast_io::io::print("deque erase test finished\n"); +} + +inline void test_erase_index() +{ + ::fast_io::io::perr("=== deque erase_index test ===\n"); + + using T = ::std::size_t; + ::fast_io::deque dq; + ::std::deque ref; + + // Fill initial data + for (::std::size_t i{}; i != 200u; ++i) + { + dq.push_back(i); + ref.push_back(i); + } + + // Helper to compare dq and ref + auto check_equal = [&](auto const &msg, + ::std::source_location src = ::std::source_location::current()) { + if (dq.size() != ref.size()) + { + ::fast_io::io::panicln(src, "\tERROR: size mismatch: ", msg); + } + for (::std::size_t i{}; i != dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + ::fast_io::io::panicln(src, + "\tERROR: value mismatch at index ", + i, + "\tdq[i]=", dq[i], + "\tref[i]=", ref[i], + " : ", msg); + } + } + }; + + // + // === 1. Single‑index erase === + // + + // Erase at front + { + dq.erase_index(0); + ref.erase(ref.begin()); + check_equal("erase_index(pos) front"); + } + + // Erase in middle + { + if (!dq.empty()) + { + ::std::size_t pos{dq.size() / 2}; + dq.erase_index(pos); + ref.erase(ref.begin() + pos); + check_equal("erase_index(pos) middle"); + } + } + + // Erase at back + { + if (!dq.empty()) + { + ::std::size_t pos{dq.size() - 1}; + dq.erase_index(pos); + ref.erase(ref.begin() + pos); + check_equal("erase_index(pos) back"); + } + } + + // + // === 2. Range erase === + // + + // Erase range at front + { + if (dq.size() >= 10u) + { + dq.erase_index(0, 10); + ref.erase(ref.begin(), ref.begin() + 10); + check_equal("erase_index(first,last) front"); + } + } + + // Erase range in middle + { + if (dq.size() >= 30u) + { + ::std::size_t start{dq.size() / 3}; + ::std::size_t len{20u}; + if (start + len > dq.size()) + { + len = dq.size() - start; + } + + dq.erase_index(start, start + len); + ref.erase(ref.begin() + start, ref.begin() + start + len); + check_equal("erase_index(first,last) middle"); + } + } + + // Erase range at back + { + if (dq.size() >= 15u) + { + ::std::size_t len{15u}; + ::std::size_t start{dq.size() - len}; + + dq.erase_index(start, dq.size()); + ref.erase(ref.begin() + start, ref.end()); + check_equal("erase_index(first,last) back"); + } + } + + // + // === 3. Randomized single‑index erase === + // + for (::std::size_t iter{}; iter != 200u; ++iter) + { + if (dq.empty()) + { + break; + } + + ::std::size_t pos = iter % dq.size(); + + dq.erase_index(pos); + ref.erase(ref.begin() + pos); + + check_equal("randomized erase_index(pos)"); + } + + // + // === 4. Randomized range erase === + // + for (::std::size_t iter{}; iter != 200u; ++iter) + { + if (dq.empty()) + { + break; + } + + ::std::size_t n = dq.size(); + ::std::size_t first = (iter * 7) % n; + ::std::size_t last = first + (iter % 5 + 1); + + if (last > n) + { + last = n; + } + + dq.erase_index(first, last); + ref.erase(ref.begin() + first, ref.begin() + last); + + check_equal("randomized erase_index(first,last)"); + } + + ::fast_io::io::print("deque erase_index test finished\n"); +} + +} // namespace + +int main() +{ + test_erase(); + test_erase_index(); +} From c0f0578085ec3fff75fba3a38e3bf866ad9d7838 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 15 Jan 2026 18:53:36 +0800 Subject: [PATCH 4/7] Correctly implementing integers for this->controller.back_end_ptr --- .../0007.containers/deque/fuzz_deque_erase.cc | 237 ++++++++++-------- include/fast_io_core_impl/integers/impl.h | 9 + include/fast_io_dsal/impl/deque.h | 49 ++-- include/fast_io_dsal/impl/freestanding.h | 4 +- 4 files changed, 166 insertions(+), 133 deletions(-) diff --git a/fuzzing/0007.containers/deque/fuzz_deque_erase.cc b/fuzzing/0007.containers/deque/fuzz_deque_erase.cc index 5844a868b..bb1e7b9a8 100644 --- a/fuzzing/0007.containers/deque/fuzz_deque_erase.cc +++ b/fuzzing/0007.containers/deque/fuzz_deque_erase.cc @@ -11,120 +11,143 @@ using T = std::size_t; // Check equality between fast_io::deque and std::deque -static void check_equal(::fast_io::deque const& dq, - ::std::deque const& ref) +static void check_equal(::fast_io::deque const &dq, + ::std::deque const &ref) { - if (dq.size() != ref.size()) - { - __builtin_trap(); // mismatch: size - } - - for (std::size_t i{}; i != dq.size(); ++i) - { - if (dq[i] != ref[i]) - { - __builtin_trap(); // mismatch: value - } - } + if (dq.size() != ref.size()) + { + __builtin_trap(); // mismatch: size + } + + for (std::size_t i{}; i != dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + __builtin_trap(); // mismatch: value + } + } } // Map a byte to an operation kind enum class OpKind : uint8_t { - PushBack = 0, - PushFront = 1, - EraseIndex = 2, - EraseRangeIter = 3, - // you can add more later + PushBack = 0, + PushFront = 1, + EraseIndex = 2, + EraseRangeIter = 3, + // you can add more later }; -extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, std::size_t size) +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, std::size_t size) { - ::fast_io::deque dq; - ::std::deque ref; - - // We interpret the input as a stream of small commands. - // Each command may consume 1~3 bytes depending on the op. - std::size_t i = 0; - while (i < size) - { - uint8_t op_raw = data[i++] % 4; // we currently have 4 ops - OpKind op = static_cast(op_raw); - - switch (op) - { - case OpKind::PushBack: - { - if (i >= size) break; - // use the next byte as low bits of the value - T v = static_cast(data[i++]); - dq.push_back(v); - ref.push_back(v); - break; - } - case OpKind::PushFront: - { - if (i >= size) break; - T v = static_cast(data[i++]); - dq.push_front(v); - ref.push_front(v); - break; - } - case OpKind::EraseIndex: - { - if (dq.empty()) - break; - - if (i >= size) break; - std::size_t pos = static_cast(data[i++]); - if (!dq.empty()) - { - pos %= dq.size(); - - dq.erase_index(pos); - ref.erase(ref.begin() + static_cast(pos)); - } - break; - } - case OpKind::EraseRangeIter: - { - if (dq.empty()) - break; - - if (i + 1 > size) // need at least 1 byte - break; - - std::size_t n = dq.size(); - if (n == 0) - break; - - // choose two indices from bytes - std::size_t a = static_cast(data[i++]) % n; - std::size_t b = static_cast(data[i++ % size]) % (n + 1); - - std::size_t first_idx = (a < b ? a : b); - std::size_t last_idx = (a < b ? b : a); - - if (last_idx > n) last_idx = n; - if (first_idx > last_idx) first_idx = last_idx; - if (first_idx == last_idx) - break; // empty range, skip - - auto dq_first = dq.begin() + static_cast(first_idx); - auto dq_last = dq.begin() + static_cast(last_idx); - auto ref_first = ref.begin() + static_cast(first_idx); - auto ref_last = ref.begin() + static_cast(last_idx); - - dq.erase(dq_first, dq_last); - ref.erase(ref_first, ref_last); - break; - } - } - - // After each operation, verify dq == ref - check_equal(dq, ref); - } - - return 0; + ::fast_io::deque dq; + ::std::deque ref; + + // We interpret the input as a stream of small commands. + // Each command may consume 1~3 bytes depending on the op. + std::size_t i = 0; + while (i < size) + { + uint8_t op_raw = data[i++] % 4; // we currently have 4 ops + OpKind op = static_cast(op_raw); + + switch (op) + { + case OpKind::PushBack: + { + if (i >= size) + { + break; + } + // use the next byte as low bits of the value + T v = static_cast(data[i++]); + dq.push_back(v); + ref.push_back(v); + break; + } + case OpKind::PushFront: + { + if (i >= size) + { + break; + } + T v = static_cast(data[i++]); + dq.push_front(v); + ref.push_front(v); + break; + } + case OpKind::EraseIndex: + { + if (dq.empty()) + { + break; + } + + if (i >= size) + { + break; + } + std::size_t pos = static_cast(data[i++]); + if (!dq.empty()) + { + pos %= dq.size(); + dq.erase_index(pos); + ref.erase(ref.begin() + static_cast(pos)); + } + break; + } + case OpKind::EraseRangeIter: + { + if (dq.empty()) + { + break; + } + + if (i + 1 > size) // need at least 1 byte + { + break; + } + + std::size_t n = dq.size(); + if (n == 0) + { + break; + } + + // choose two indices from bytes + std::size_t a = static_cast(data[i++]) % n; + std::size_t b = static_cast(data[i++ % size]) % (n + 1); + + std::size_t first_idx = (a < b ? a : b); + std::size_t last_idx = (a < b ? b : a); + + if (last_idx > n) + { + last_idx = n; + } + if (first_idx > last_idx) + { + first_idx = last_idx; + } + if (first_idx == last_idx) + { + break; // empty range, skip + } + + auto dq_first = dq.begin() + static_cast(first_idx); + auto dq_last = dq.begin() + static_cast(last_idx); + auto ref_first = ref.begin() + static_cast(first_idx); + auto ref_last = ref.begin() + static_cast(last_idx); + + dq.erase(dq_first, dq_last); + ref.erase(ref_first, ref_last); + break; + } + } + + // After each operation, verify dq == ref + check_equal(dq, ref); + } + + return 0; } - diff --git a/include/fast_io_core_impl/integers/impl.h b/include/fast_io_core_impl/integers/impl.h index 15a7721f7..6cc823190 100644 --- a/include/fast_io_core_impl/integers/impl.h +++ b/include/fast_io_core_impl/integers/impl.h @@ -617,6 +617,15 @@ inline constexpr auto pointervw(scalar_type t) noexcept ::fast_io::details::base_mani_flags_cache<16, uppercase, true, true, false>>(t); } +template + requires((::std::is_pointer_v || ::std::contiguous_iterator) && + (!::std::is_function_v<::std::remove_cvref_t>)) +inline constexpr auto itervw(scalar_type t) noexcept +{ + return ::fast_io::details::scalar_flags_int_cache< + ::fast_io::details::base_mani_flags_cache<16, uppercase, true, true, false>>(t); +} + template requires(::std::is_function_v) inline constexpr auto funcvw(scalar_type *t) noexcept diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index e89c47979..e4a52cfae 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -1867,6 +1867,10 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE private: inline constexpr iterator erase_unchecked_impl(iterator first, iterator last, bool moveleft) noexcept { + if (first == last) + { + return first; + } if constexpr (!::std::is_trivially_destructible_v) { destroy_elements_range(first, last); @@ -1879,24 +1883,26 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE else #endif { + ::fast_io::containers::details::deque_control_block back_block; if (moveleft) { this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), first, last).itercontent; - return last; + first = last; + back_block = this->controller.back_block; } else { - auto back_block{::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent}; - if (back_block.begin_ptr == back_block.curr_ptr) + back_block = ::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent; + } + if (back_block.begin_ptr == back_block.curr_ptr) + { + if (this->controller.front_block.controller_ptr != back_block.controller_ptr) { - if (this->controller.front_block.controller_ptr != back_block.controller_ptr) - { - back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); - } + this->controller.back_end_ptr = back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); } - this->controller.back_block = back_block; - return first; } + this->controller.back_block = back_block; + return first; } } inline constexpr iterator erase_unchecked_single_impl(iterator pos, bool moveleft) noexcept @@ -1913,31 +1919,28 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE else #endif { + ::fast_io::containers::details::deque_control_block back_block; auto posp1{pos}; ++posp1; if (moveleft) { this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), pos, posp1).itercontent; - return posp1; + pos = posp1; + back_block = this->controller.back_block; } else { - ::fast_io::io::perrln(::std::source_location::current(),"\t",::fast_io::mnp::pointervw(posp1.itercontent.controller_ptr), - " \tposp1=", ::fast_io::mnp::pointervw(posp1.itercontent.controller_ptr), " \tthis->end()=", - ::fast_io::mnp::pointervw(this->end().itercontent.begin_ptr)); - auto back_block{::fast_io::freestanding::uninitialized_relocate(posp1, this->end(), pos).itercontent}; - ::fast_io::io::perrln(::std::source_location::current()); - - if (back_block.begin_ptr == back_block.curr_ptr) + back_block = ::fast_io::freestanding::uninitialized_relocate(posp1, this->end(), pos).itercontent; + } + if (back_block.begin_ptr == back_block.curr_ptr) + { + if (this->controller.front_block.controller_ptr != back_block.controller_ptr) { - if (this->controller.front_block.controller_ptr != back_block.controller_ptr) - { - back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); - } + this->controller.back_end_ptr = back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); } - this->controller.back_block = back_block; - return pos; } + this->controller.back_block = back_block; + return pos; } } diff --git a/include/fast_io_dsal/impl/freestanding.h b/include/fast_io_dsal/impl/freestanding.h index db5e76baf..673f385b4 100644 --- a/include/fast_io_dsal/impl/freestanding.h +++ b/include/fast_io_dsal/impl/freestanding.h @@ -78,6 +78,7 @@ inline constexpr Iter2 uninitialized_relocate(Iter1 first, Iter1 last, Iter2 des while (first != last) { ::std::construct_at(::std::addressof(*dest), ::std::move(*first)); + if constexpr (!::std::is_trivially_destructible_v) { if constexpr (::std::is_pointer_v) @@ -175,9 +176,6 @@ inline constexpr Iter2 uninitialized_relocate_backward(Iter1 first, Iter1 last, { --last; --dest; -#if 0 - __builtin_fprintf(stderr, "%s %d\n", __FILE__, __LINE__); -#endif ::std::construct_at(::std::addressof(*dest), ::std::move(*last)); if constexpr (!::std::is_trivially_destructible_v) From f5cb7597abc4e989e8fdb758def136bf3298784f Mon Sep 17 00:00:00 2001 From: trcrsired Date: Thu, 15 Jan 2026 21:40:39 +0800 Subject: [PATCH 5/7] deque implements erase with trivially_relocatable but untested --- include/fast_io_dsal/impl/deque.h | 260 ++++++++++++++++++++++++------ 1 file changed, 213 insertions(+), 47 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index e4a52cfae..f36773be2 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -820,7 +820,6 @@ 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) { @@ -1028,6 +1027,167 @@ inline constexpr void deque_clone_trivial_common(dequecontroltype &controller, d ::fast_io::containers::details::deque_clone_trivial_impl(controller, fromcontroller, align, blockbytes); } +template +inline constexpr Itercontent +deque_copy_impl(Itercontent first, Itercontent last, + Itercontent dest, ::std::size_t blocksize) +{ + if (first.curr_ptr == last.curr_ptr) + { + return dest; + } + for (;;) + { + decltype(first.begin_ptr) firstend; + if (first.controller_ptr == last.controller_ptr) + { + firstend = last.curr_ptr; + } + else + { + firstend = first.begin_ptr + blocksize; + } + auto firstcurrptr{first.curr_ptr}; + ::std::size_t curr_block_to_end{ + static_cast<::std::size_t>(firstend - firstcurrptr)}; + auto destcurrptr{dest.curr_ptr}; + ::std::size_t dest_block_to_end{ + static_cast<::std::size_t>(dest.begin_ptr + blocksize - destcurrptr)}; + auto cmp{curr_block_to_end <=> dest_block_to_end}; + ::std::size_t tocopy; + if (cmp < 0) + { + tocopy = curr_block_to_end; + } + else + { + tocopy = dest_block_to_end; + } + ::fast_io::freestanding::overlapped_copy_n(firstcurrptr, tocopy, destcurrptr); + if (0 < cmp) + { + dest.curr_ptr = (dest.begin_ptr = (*++dest.controller_ptr)); + first.curr_ptr += tocopy; + } + else + { + if (cmp < 0) + { + dest.curr_ptr += tocopy; + } + else if (cmp == 0) + { + dest.curr_ptr = (dest.begin_ptr = (*++dest.controller_ptr)); + } + if (first.controller_ptr == last.controller_ptr) + { + break; + } + first.curr_ptr = (first.begin_ptr = (*++first.controller_ptr)); + } + } + return dest; +} + +template +inline constexpr Itercontent +deque_copy_backward_impl(Itercontent first, Itercontent last, + Itercontent dest, ::std::size_t blocksize) +{ + ::std::size_t const blocksizem1{blocksize - 1u}; + if (first.curr_ptr == last.curr_ptr) + { + return dest; + } + for (;;) + { + decltype(first.begin_ptr) lastbegin; + if (first.controller_ptr == last.controller_ptr) + { + lastbegin = first.curr_ptr; + } + else + { + lastbegin = last.begin_ptr; + } + auto lastcurrptr{last.curr_ptr}; + ::std::size_t curr_block_to_begin{ + static_cast<::std::size_t>(lastcurrptr - lastbegin)}; + auto destcurrptr{dest.curr_ptr}; + ::std::size_t dest_block_to_begin{ + static_cast<::std::size_t>(destcurrptr - dest.begin_ptr)}; + auto cmp{curr_block_to_begin <=> dest_block_to_begin}; + ::std::size_t tocopy; + if (cmp < 0) + { + tocopy = curr_block_to_begin; + } + else + { + tocopy = dest_block_to_begin; + } + ::fast_io::freestanding::overlapped_copy_n(lastcurrptr - tocopy, tocopy, + destcurrptr - tocopy); + if (0 < cmp) + { + dest.curr_ptr = (dest.begin_ptr = (*--dest.controller_ptr)) + blocksizem1; + last.curr_ptr -= tocopy; + } + else + { + if (cmp == 0) + { + if (last.controller_ptr == first.controller_ptr) + { + dest.curr_ptr = dest.begin_ptr; + break; + } + dest.curr_ptr = (dest.begin_ptr = (*--dest.controller_ptr)) + blocksizem1; + } + else + { + dest.curr_ptr -= tocopy; + if (last.controller_ptr == first.controller_ptr) + { + break; + } + } + last.curr_ptr = (last.begin_ptr = (*--last.controller_ptr)) + blocksizem1; + } + } + return dest; +} + +inline ::fast_io::containers::details::deque_control_block_common +deque_erase_common_trivial_impl(::fast_io::containers::details::deque_controller_common &controller, + ::fast_io::containers::details::deque_control_block_common first, + ::fast_io::containers::details::deque_control_block_common last, + bool moveleft, + ::std::size_t blockbytes) noexcept +{ + ::fast_io::containers::details::deque_control_block_common back_block; + if (moveleft) + { + controller.front_block = ::fast_io::containers::details::deque_copy_backward_impl(controller.front_block, first, last, blockbytes); + first = last; + back_block = controller.back_block; + } + else + { + back_block = ::fast_io::containers::details::deque_copy_impl(last, controller.back_block, first, blockbytes); + } + if (back_block.begin_ptr == back_block.curr_ptr) + { + if (controller.front_block.controller_ptr != back_block.controller_ptr) + { + back_block.curr_ptr = (back_block.begin_ptr = (*--back_block.controller_ptr)); + } + } + controller.back_block = back_block; + controller.back_end_ptr = back_block.begin_ptr + blockbytes; + return first; +} + } // namespace details template <::std::forward_iterator ForwardIt> @@ -1865,6 +2025,30 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE #endif private: + inline constexpr iterator erase_no_destroy_common_impl(iterator first, iterator last, bool moveleft) noexcept + { + ::fast_io::containers::details::deque_control_block back_block; + if (moveleft) + { + this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), first, last).itercontent; + first = last; + back_block = this->controller.back_block; + } + else + { + back_block = ::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent; + } + if (back_block.begin_ptr == back_block.curr_ptr) + { + if (this->controller.front_block.controller_ptr != back_block.controller_ptr) + { + this->controller.back_end_ptr = back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); + } + } + this->controller.back_block = back_block; + this->controller.back_end_ptr = back_block.begin_ptr + block_size; + return first; + } inline constexpr iterator erase_unchecked_impl(iterator first, iterator last, bool moveleft) noexcept { if (first == last) @@ -1878,32 +2062,19 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE #if 0 if constexpr(::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) { -//Todo - } - else -#endif - { - ::fast_io::containers::details::deque_control_block back_block; - if (moveleft) + if !consteval { - this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), first, last).itercontent; - first = last; - back_block = this->controller.back_block; - } - else - { - back_block = ::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent; - } - if (back_block.begin_ptr == back_block.curr_ptr) - { - if (this->controller.front_block.controller_ptr != back_block.controller_ptr) - { - this->controller.back_end_ptr = back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); - } + constexpr size_type blockbytes{block_size*sizeof(value_type)}; + return ::std::bit_cast( + ::fast_io::containers::details::deque_erase_common_trivial_impl( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( + this->controller)), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(first.itercontent), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(last.itercontent), moveleft, blockbytes)); } - this->controller.back_block = back_block; - return first; } +#endif + return this->erase_no_destroy_common_impl(first, last, moveleft); } inline constexpr iterator erase_unchecked_single_impl(iterator pos, bool moveleft) noexcept { @@ -1914,34 +2085,29 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE #if 0 if constexpr(::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) { -//Todo - } - else -#endif - { - ::fast_io::containers::details::deque_control_block back_block; - auto posp1{pos}; - ++posp1; - if (moveleft) + if !consteval { - this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), pos, posp1).itercontent; - pos = posp1; - back_block = this->controller.back_block; - } - else - { - back_block = ::fast_io::freestanding::uninitialized_relocate(posp1, this->end(), pos).itercontent; - } - if (back_block.begin_ptr == back_block.curr_ptr) - { - if (this->controller.front_block.controller_ptr != back_block.controller_ptr) + auto posp1{pos.itercontent}; + if (++posp1.curr_ptr == posp1.begin_ptr + block_size) { - this->controller.back_end_ptr = back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); + if (posp1.curr_ptr != this->controller.back_end_ptr) + { + posp1.curr_ptr = posp1.begin_ptr = *(++posp1.controller_ptr); + } } + constexpr size_type blockbytes{block_size*sizeof(value_type)}; + return ::std::bit_cast( + ::fast_io::containers::details::deque_erase_common_trivial_impl( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( + this->controller)), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(pos.itercontent), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(posp1), moveleft, blockbytes)); } - this->controller.back_block = back_block; - return pos; } +#endif + auto posp1{pos}; + ++posp1; + return this->erase_no_destroy_common_impl(pos, posp1, moveleft); } public: From f733f23988a8b7ba08b84d2047ea7ab41a9db393 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Fri, 16 Jan 2026 02:31:07 +0800 Subject: [PATCH 6/7] deque erase complete --- include/fast_io_dsal/impl/deque.h | 63 +++++++++++++++++-------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index f36773be2..4a9f182a9 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -1048,10 +1048,10 @@ deque_copy_impl(Itercontent first, Itercontent last, firstend = first.begin_ptr + blocksize; } auto firstcurrptr{first.curr_ptr}; - ::std::size_t curr_block_to_end{ + ::std::size_t const curr_block_to_end{ static_cast<::std::size_t>(firstend - firstcurrptr)}; auto destcurrptr{dest.curr_ptr}; - ::std::size_t dest_block_to_end{ + ::std::size_t const dest_block_to_end{ static_cast<::std::size_t>(dest.begin_ptr + blocksize - destcurrptr)}; auto cmp{curr_block_to_end <=> dest_block_to_end}; ::std::size_t tocopy; @@ -1094,7 +1094,6 @@ inline constexpr Itercontent deque_copy_backward_impl(Itercontent first, Itercontent last, Itercontent dest, ::std::size_t blocksize) { - ::std::size_t const blocksizem1{blocksize - 1u}; if (first.curr_ptr == last.curr_ptr) { return dest; @@ -1111,10 +1110,10 @@ deque_copy_backward_impl(Itercontent first, Itercontent last, lastbegin = last.begin_ptr; } auto lastcurrptr{last.curr_ptr}; - ::std::size_t curr_block_to_begin{ + ::std::size_t const curr_block_to_begin{ static_cast<::std::size_t>(lastcurrptr - lastbegin)}; auto destcurrptr{dest.curr_ptr}; - ::std::size_t dest_block_to_begin{ + ::std::size_t const dest_block_to_begin{ static_cast<::std::size_t>(destcurrptr - dest.begin_ptr)}; auto cmp{curr_block_to_begin <=> dest_block_to_begin}; ::std::size_t tocopy; @@ -1130,7 +1129,7 @@ deque_copy_backward_impl(Itercontent first, Itercontent last, destcurrptr - tocopy); if (0 < cmp) { - dest.curr_ptr = (dest.begin_ptr = (*--dest.controller_ptr)) + blocksizem1; + dest.curr_ptr = (dest.begin_ptr = (*--dest.controller_ptr)) + blocksize; last.curr_ptr -= tocopy; } else @@ -1142,7 +1141,7 @@ deque_copy_backward_impl(Itercontent first, Itercontent last, dest.curr_ptr = dest.begin_ptr; break; } - dest.curr_ptr = (dest.begin_ptr = (*--dest.controller_ptr)) + blocksizem1; + dest.curr_ptr = (dest.begin_ptr = (*--dest.controller_ptr)) + blocksize; } else { @@ -1152,7 +1151,7 @@ deque_copy_backward_impl(Itercontent first, Itercontent last, break; } } - last.curr_ptr = (last.begin_ptr = (*--last.controller_ptr)) + blocksizem1; + last.curr_ptr = (last.begin_ptr = (*--last.controller_ptr)) + blocksize; } } return dest; @@ -1165,22 +1164,28 @@ deque_erase_common_trivial_impl(::fast_io::containers::details::deque_controller bool moveleft, ::std::size_t blockbytes) noexcept { - ::fast_io::containers::details::deque_control_block_common back_block; + ::fast_io::containers::details::deque_control_block_common back_block{controller.back_block}; if (moveleft) { controller.front_block = ::fast_io::containers::details::deque_copy_backward_impl(controller.front_block, first, last, blockbytes); first = last; - back_block = controller.back_block; } else { - back_block = ::fast_io::containers::details::deque_copy_impl(last, controller.back_block, first, blockbytes); + if (back_block.curr_ptr == controller.back_end_ptr) [[unlikely]] + { + if (back_block.controller_ptr) [[likely]] + { + back_block.curr_ptr = back_block.begin_ptr = (*++back_block.controller_ptr); + } + } + back_block = ::fast_io::containers::details::deque_copy_impl(last, back_block, first, blockbytes); } if (back_block.begin_ptr == back_block.curr_ptr) { if (controller.front_block.controller_ptr != back_block.controller_ptr) { - back_block.curr_ptr = (back_block.begin_ptr = (*--back_block.controller_ptr)); + back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + blockbytes); } } controller.back_block = back_block; @@ -2042,7 +2047,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { if (this->controller.front_block.controller_ptr != back_block.controller_ptr) { - this->controller.back_end_ptr = back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); + back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); } } this->controller.back_block = back_block; @@ -2057,20 +2062,20 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } if constexpr (!::std::is_trivially_destructible_v) { - destroy_elements_range(first, last); + this->destroy_elements_range(first, last); } -#if 0 - if constexpr(::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) +#if 1 + if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) { if !consteval { - constexpr size_type blockbytes{block_size*sizeof(value_type)}; + constexpr size_type blockbytes{block_size * sizeof(value_type)}; return ::std::bit_cast( ::fast_io::containers::details::deque_erase_common_trivial_impl( - *reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( - this->controller)), - ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(first.itercontent), - ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(last.itercontent), moveleft, blockbytes)); + *reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( + this->controller)), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(first.itercontent), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(last.itercontent), moveleft, blockbytes)); } } #endif @@ -2082,26 +2087,26 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { ::std::destroy(pos.itercontent.curr_ptr); } -#if 0 - if constexpr(::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) +#if 1 + if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) { if !consteval { auto posp1{pos.itercontent}; - if (++posp1.curr_ptr == posp1.begin_ptr + block_size) + if (++posp1.curr_ptr == posp1.begin_ptr + block_size) { if (posp1.curr_ptr != this->controller.back_end_ptr) { posp1.curr_ptr = posp1.begin_ptr = *(++posp1.controller_ptr); } } - constexpr size_type blockbytes{block_size*sizeof(value_type)}; + constexpr size_type blockbytes{block_size * sizeof(value_type)}; return ::std::bit_cast( ::fast_io::containers::details::deque_erase_common_trivial_impl( - *reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( - this->controller)), - ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(pos.itercontent), - ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(posp1), moveleft, blockbytes)); + *reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( + this->controller)), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(pos.itercontent), + ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(posp1), moveleft, blockbytes)); } } #endif From e8e689b90e1b1e554f5a869ee1a64797254568e4 Mon Sep 17 00:00:00 2001 From: trcrsired Date: Fri, 16 Jan 2026 06:00:42 +0800 Subject: [PATCH 7/7] Disable trivially_relocatable_if_eligible it breaks compilation and i am not sure how clang actually deals with it --- include/fast_io_dsal/impl/deque.h | 73 +++++++++++++++++--- include/fast_io_dsal/impl/misc/push_macros.h | 5 ++ 2 files changed, 69 insertions(+), 9 deletions(-) diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index 4a9f182a9..b816d5cb1 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -1996,14 +1996,51 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE return this->insert_range_impl(pos, rg, n).pos; } +private: + template + struct prepend_or_append_range_guard + { + deque *thisdeq{}; + size_type oldn{}; + constexpr ~prepend_or_append_range_guard() + { + if (thisdeq) + { + if constexpr (isprepend) + { + thisdeq->erase(thisdeq->cbegin(), thisdeq->cend() - oldn); + } + else + { + thisdeq->erase(thisdeq->cbegin() + oldn, thisdeq->cend()); + } + } + } + }; + using append_range_guard = prepend_or_append_range_guard; + using prepend_range_guard = prepend_or_append_range_guard; + +public: template <::std::ranges::range R> requires ::std::constructible_from> inline constexpr void append_range(R &&rg) noexcept(::std::is_nothrow_constructible_v>) { // To do: cleanup code - for (auto &e : rg) + if constexpr (::std::is_nothrow_constructible_v>) + { + for (auto &e : rg) + { + this->push_back(e); + } + } + else { - this->push_back(e); + append_range_guard guard{this, this->size()}; + for (auto &e : rg) + { + this->push_back(e); + } + guard.thisdeq = nullptr; } } #if 0 @@ -2019,15 +2056,37 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } return this->size() - old_size; } +#endif public: template <::std::ranges::range R> requires ::std::constructible_from> - inline constexpr void prepend_range(R &&rg) noexcept(::std::is_nothrow_constructible_v>) + inline constexpr void prepend_range(R &&rg) noexcept( + ::std::is_nothrow_constructible_v> && + ::std::is_nothrow_swappable_v) { // To do: cleanup code - this->prepend_range_impl(::std::forward(rg)); + size_type oldn{this->size()}; + if constexpr ( + ::std::is_nothrow_constructible_v> && + ::std::is_nothrow_swappable_v) + { + for (auto &e : rg) + { + this->push_front(e); + } + ::std::reverse(this->begin(), this->end() - oldn); + } + else + { + prepend_range_guard guard{this, oldn}; + for (auto &e : rg) + { + this->push_front(e); + } + ::std::reverse(this->begin(), this->end() - oldn); + guard.thisdeq = nullptr; + } } -#endif private: inline constexpr iterator erase_no_destroy_common_impl(iterator first, iterator last, bool moveleft) noexcept @@ -2064,7 +2123,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { this->destroy_elements_range(first, last); } -#if 1 if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) { if !consteval @@ -2078,7 +2136,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(last.itercontent), moveleft, blockbytes)); } } -#endif return this->erase_no_destroy_common_impl(first, last, moveleft); } inline constexpr iterator erase_unchecked_single_impl(iterator pos, bool moveleft) noexcept @@ -2087,7 +2144,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { ::std::destroy(pos.itercontent.curr_ptr); } -#if 1 if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) { if !consteval @@ -2109,7 +2165,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::std::bit_cast<::fast_io::containers::details::deque_control_block_common>(posp1), moveleft, blockbytes)); } } -#endif auto posp1{pos}; ++posp1; return this->erase_no_destroy_common_impl(pos, posp1, moveleft); diff --git a/include/fast_io_dsal/impl/misc/push_macros.h b/include/fast_io_dsal/impl/misc/push_macros.h index 5a3e84007..8df97a675 100644 --- a/include/fast_io_dsal/impl/misc/push_macros.h +++ b/include/fast_io_dsal/impl/misc/push_macros.h @@ -218,6 +218,7 @@ Internal assert macros for fuzzing fast_io. #endif #pragma push_macro("FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE") +#if 0 #if defined(__cpp_trivial_relocatability) #undef FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE #if defined(__clang__) @@ -229,6 +230,10 @@ Internal assert macros for fuzzing fast_io. #else #define FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE #endif +#else +#undef FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE +#define FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE +#endif #pragma push_macro("FAST_IO_HAS_BUILTIN") #undef FAST_IO_HAS_BUILTIN