diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..7bbb44f7b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,102 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +**fast_io** is a header-only C++20 I/O library designed to replace `` and `` with dramatically better performance. It makes direct system calls, bypassing intermediary layers, and uses C++20 concepts extensively for type abstraction. + +## Build & Development + +### Prerequisites +- C++20 compiler: GCC >= 15, Clang >= 21, or MSVC +- CMake >= 3.15 + +### Build Tests, Benchmarks, and Examples + +```bash +# Prebuild: generate test files (only needed once) +cmake -B fast_io_prebuild -S . -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=On -DTESTS_PREBUILD=On +cmake --build fast_io_prebuild +./fast_io_prebuild/tests_prebuild linux # or "windows msvc" + +# Build tests/examples/benchmarks +cmake -B fast_io_build -S . -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTS=On +cmake --build fast_io_build + +# Run tests +cmake --build fast_io_build --target test +``` + +### Run a Single Test + +Tests are located under `tests/`. After building, individual test binaries are in the `fast_io_build/tests/` subdirectories. Run them directly. + +### CI + +GitHub Actions (`.github/workflows/c-cpp.yml`) runs: +- **Linux**: GCC with `-fsanitize=address,undefined -Wall -Wextra -Wpedantic -Wshadow -Wconversion -Werror` +- **Windows**: MSVC with `/EHsc /W3 /WX /sdl` +- Uses Ninja generator on both platforms + +## Architecture + +The library uses a layered architecture: + +| Layer | Header | Description | +|-------|--------|-------------| +| Concepts | `fast_io_concept.h` | C++20 concepts for I/O device abstraction | +| Core | `fast_io_core.h` | Freestanding-capable: bit ops, integer formatting, codecvt, SIMD | +| Freestanding | `fast_io_freestanding.h` | Adds buffered I/O, decorators, serializers, transcoders | +| Hosted | `fast_io_hosted.h` | Full features: platform abstractions, filesystem, threads, process/IPC | +| Main | `fast_io.h` | Entry point: combines hosted + legacy stream interop | + +### Key Directories + +- `include/fast_io_core_impl/` — Core implementation internals (operations: `printimpl`, `readimpl`, `writeimpl`, `transmitimpl`, `transcodeimpl`) +- `include/fast_io_hosted/` — Hosted platform implementations (console, filesystem, mmap, threads, process) +- `include/fast_io_freestanding_impl/` — Buffered I/O, decorators, scanners, serialization +- `include/fast_io_legacy_impl/` — FILE*/streambuf hacks for glibc, MSVCRT, UCRT, MUSL, BSD libc, libstdc++, libc++, MSVC STL +- `include/fast_io_dsal/` — Data Structure Abstraction Layer (vector, string, deque, list, etc.) +- `include/fast_io_driver/` — Third-party integrations (Boost.Asio, OpenSSL, Qt, MFC, LLVM, Python, zlib) +- `include/fast_io_crypto/` — SHA-1, SHA-256, SHA-512, HMAC, ciphers +- `share/fast_io/` — C++20 module files + +### Header Organization + +- `fast_io.h` — Primary entry point (most common include) +- `fast_io_device.h` — Device types (files, pipes, sockets) +- `fast_io_crypto.h` — Cryptographic hash functions +- `fast_io_i18n.h` — Internationalization/locale +- `fast_io_legacy.h` — Legacy C/C++ stream compatibility + +### fast_io's 6 layers of files from bottom to top +- wine_file +- nt_file +- win32_file +- posix_file +- c_file +- filebuf_file + +From bottom to top we do a move. +From top to bottom we do a static cast to the +lower level of io_observer. + +## Testing + +- Tests are organized into numbered categories under `tests/` +- `.test_prop.toml` controls which tests run per platform/compiler +- Test files are generated by `tests/0000.tests_prebuild/gentests.cc` +- Some tests are ignored for incomplete features or platform requirements + +## Benchmarking + +Benchmarks live under `benchmark/` with numbered categories (integer I/O, floating-point, file I/O, concat, containers, codecvt, syscalls, etc.). Many have standalone `Makefile`s using `clang++ -Ofast -march=native -std=c++20` with precompiled headers. + +## Style & Conventions + +- `.clang-format` and `.editorconfig` are present — format code before committing +- This is a header-only library — no `.cpp` source files in `include/` +- The library targets both freestanding (no OS) and hosted environments +- Consistent error handling via exceptions only (no `std::error_code` or `std::system_error`) +- License: Anti-Tivo License (ATL) v1.0 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 7b5193105..781792796 100644 --- a/benchmark/0011.containers/deque/0001.push_back/fast_io.cc +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io.cc @@ -22,5 +22,5 @@ int main() sum += e; } } - ::fast_io::io::perrln("sum=",sum); + ::fast_io::io::perrln("sum=", sum); } diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io_resize_for_overwrite.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_resize_for_overwrite.cc new file mode 100644 index 000000000..0c8b6f140 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_resize_for_overwrite.cc @@ -0,0 +1,35 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::deque resize_for_overwrite"); + fast_io::deque deq; + constexpr std::size_t n{100000000}; + { + fast_io::timer tm1(u8"push_back"); + deq.resize(n, ::fast_io::for_overwrite); + ::std::size_t idx{}; + for (::std::size_t i{}, segs{deq.segments_count()}; i != segs; ++i) + { + for (auto &e : deq.nth_segment(i)) + { + e = idx; + ++idx; + } + } + } + ::std::size_t sum{}; + { + fast_io::timer tm1(u8"loop"); + for (::std::size_t i{}, segs{deq.segments_count()}; i != segs; ++i) + { + for (auto const e : deq.const_nth_segment(i)) + { + sum += e; + } + } + } + ::fast_io::io::perrln("sum=", sum); +} diff --git a/benchmark/0011.containers/deque/0001.push_back/fast_io_segments.cc b/benchmark/0011.containers/deque/0001.push_back/fast_io_segments.cc new file mode 100644 index 000000000..4af6241e3 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/fast_io_segments.cc @@ -0,0 +1,29 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"fast_io::deque segments"); + 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 (::std::size_t i{}, segs{deq.segments_count()}; i != segs; ++i) + { + for (auto const e : deq.const_nth_segment(i)) + { + sum += e; + } + } + } + ::fast_io::io::perrln("sum=", sum); +} diff --git a/benchmark/0011.containers/deque/0001.push_back/std.cc b/benchmark/0011.containers/deque/0001.push_back/std.cc index b2744c2a4..b7a34242f 100644 --- a/benchmark/0011.containers/deque/0001.push_back/std.cc +++ b/benchmark/0011.containers/deque/0001.push_back/std.cc @@ -20,7 +20,7 @@ int main() for (auto const e : deq) { sum += e; - } + } } - ::fast_io::io::perrln("sum=",sum); + ::fast_io::io::perrln("sum=", sum); } diff --git a/benchmark/0011.containers/deque/0001.push_back/std_vec.cc b/benchmark/0011.containers/deque/0001.push_back/std_vec.cc new file mode 100644 index 000000000..8b3407e89 --- /dev/null +++ b/benchmark/0011.containers/deque/0001.push_back/std_vec.cc @@ -0,0 +1,26 @@ +#include +#include +#include + +int main() +{ + fast_io::timer tm(u8"std::vector"); + std::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/examples/.test_prop.toml b/examples/.test_prop.toml index 989d59389..f13689bf8 100644 --- a/examples/.test_prop.toml +++ b/examples/.test_prop.toml @@ -43,5 +43,11 @@ ignore = true ["0040.wifianalyzer"] ignore = true +["0041.keepwinealive"] +ignore = true + ["0042.scn_ip_port"] interactive = true + +["0043.scanwinprocesspasswords"] +ignore = true diff --git a/examples/0041.keepwinealive/keepwinealive.cc b/examples/0041.keepwinealive/keepwinealive.cc new file mode 100644 index 000000000..965ee51c5 --- /dev/null +++ b/examples/0041.keepwinealive/keepwinealive.cc @@ -0,0 +1,12 @@ +#include +#define NOMINMAX 1 +#define _WIN32_LEAN_AND_MEAN +#include +#undef min +#undef max + +int main() +{ + ::fast_io::io::perrln("keep wine alive: ", utc(::fast_io::posix_clock_gettime(::fast_io::posix_clock_id::realtime))); + WaitForSingleObject(GetCurrentProcess(), INFINITE); +} diff --git a/examples/0043.scanwinprocesspasswords/scanpass.cc b/examples/0043.scanwinprocesspasswords/scanpass.cc new file mode 100644 index 000000000..1034cf979 --- /dev/null +++ b/examples/0043.scanwinprocesspasswords/scanpass.cc @@ -0,0 +1,208 @@ +/** + * =============================================================== + * Edge Credential Memory Scanner (Educational Security Research) + * =============================================================== + * + * CONTEXT: + * In early 2026, security researchers identified that Microsoft Edge (and other + * Chromium-based browsers) may store sensitive data, including password vault + * entries and session cookies, in plaintext within the process heap (MEM_PRIVATE). + * While modern Windows OS provides process isolation, any process running with + * the same user privileges (like this tool) can utilize NtReadVirtualMemory + * (via ReadProcessMemory) to scrape these credentials. + * + * REFERENCE: + * https://isc.sans.edu/diary/Cleartext+Passwords+in+MS+Edge+In+2026/32954 + * + * FUNCTIONALITY: + * 1. Takes a process name (e.g., msedge.exe) and an optional search term. + * 2. Iterates through all running processes using ToolHelp32 snapshots. + * 3. For every matching process, it scans committed private read/write memory. + * 4. Utilizes the C++17 Boyer-Moore-Horspool algorithm for high-performance searching. + * 5. Uses fast_io for optimized I/O operations and memory-safe buffer handling. + * + * USAGE: + * scanpass.exe msedge.exe "your_email@outlook.com" + * scanpass.exe msedge.exe "your_known_password" + * + * ====================================================================================== + */ + +#define NOMINMAX 1 +#define _WIN32_LEAN_AND_MEAN 1 +#include +#include +#include +#include +#include +#include +#include +#include // Required for Boyer-Moore + +/** + * Prints a sanitized snippet of memory around a discovered match. + */ +inline void PrintSnippet(auto &outstm, ::fast_io::string_view buffer, size_t offset, size_t contextSize = 100) +{ + using namespace ::fast_io::io; + size_t start = (offset > contextSize) ? offset - contextSize : 0; + size_t end = (offset + contextSize < buffer.size()) ? offset + contextSize : buffer.size(); + + print(outstm, " Snippet: ["); + for (size_t i = start; i < end; ++i) + { + if (i == offset) + { + print(outstm, " >>"); // Highlight start of match + } + + // Print printable ASCII, otherwise use a placeholder + auto chval{static_cast(buffer[i])}; + print(outstm, ::fast_io::mnp::chvw(::fast_io::char_category::is_c_graph(chval) ? chval : u8'.')); + + if (i == offset + (contextSize >> 1u)) + { + print(outstm, "<< "); + } + } + print(outstm, "]\n"); +} + +inline void ScanProcessMemory(auto &outstm, DWORD pid, ::fast_io::string_view target) +{ + using namespace ::fast_io::io; + HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (!hProcess) + { + ::fast_io::throw_win32_error(); + } + ::fast_io::win32_file hprocessfile(hProcess); + + SYSTEM_INFO si; + GetSystemInfo(__builtin_addressof(si)); + + MEMORY_BASIC_INFORMATION mbi; + auto addr{reinterpret_cast<::std::byte *>(si.lpMinimumApplicationAddress)}; + // Pre-initialize the Boyer-Moore searcher if a target is provided + ::std::boyer_moore_horspool_searcher searcher(target.cbegin(), target.cend()); + + print(outstm, "[+] Scanning PID ", pid, "...\n"); + + while (addr < reinterpret_cast<::std::byte *>(si.lpMaximumApplicationAddress)) + { + if (VirtualQueryEx(hProcess, addr, ::std::addressof(mbi), sizeof(mbi)) == sizeof(mbi)) + { + // We search MEM_PRIVATE & PAGE_READWRITE because that's where + // heap data (passwords/strings) usually resides. + bool isDataRegion = (mbi.State == MEM_COMMIT) && + (mbi.Protect & PAGE_READWRITE) && + (mbi.Type == MEM_PRIVATE); + + if (isDataRegion && !target.is_empty()) + { + ::fast_io::vector buffer(mbi.RegionSize, ::fast_io::for_overwrite); + SIZE_T bytesRead; + + if (ReadProcessMemory(hProcess, addr, buffer.data(), mbi.RegionSize, __builtin_addressof(bytesRead))) + { + auto it = ::std::search(buffer.begin(), buffer.begin() + bytesRead, searcher); + + while (it != (buffer.begin() + bytesRead)) + { + size_t offset = std::distance(buffer.begin(), it); + println(outstm, "[!] Match found at 0x", + ::fast_io::mnp::pointervw(addr + offset)); + PrintSnippet(outstm, ::fast_io::string_view(buffer.data(), buffer.size()), offset); + + // Continue searching in the same block + it = std::search(it + target.size(), buffer.begin() + bytesRead, searcher); + } + } + } + addr += mbi.RegionSize; + } + else + { + break; + } + } + + print(outstm, "[+] Scan finished.\n"); +} + +inline void FindAndScanProcesses(auto &obf, ::fast_io::u16cstring_view processName, ::fast_io::string_view target) +{ + using namespace ::fast_io::io; + + HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnap == INVALID_HANDLE_VALUE) + { + ::fast_io::throw_win32_error(); + } + ::fast_io::win32_file snap_file(hSnap); + + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(PROCESSENTRY32W); + + if (!Process32FirstW(hSnap, ::std::addressof(pe32))) + { + return; + } + + do + { + // Convert WCHAR to a comparable format. + // Prevent strict aliasing violation with [[__gnu__::__may_alias__]] attribute + using char16_const_may_alias_ptr +#if __has_cpp_attribute(__gnu__::__may_alias__) + [[__gnu__::__may_alias__]] +#endif + = char16_t const *; + ::fast_io::u16string_view current_name(::fast_io::mnp::os_c_str(reinterpret_cast(pe32.szExeFile), processName.size())); + + // Use a wide search or conversion if necessary; here we do a simple match + // Note: For Edge, you'll see many "msedge.exe" entries + if (current_name == processName) + { + try + { + ScanProcessMemory(obf, pe32.th32ProcessID, target); + } + catch (::fast_io::error e) + { + println(obf, "[-] Failed: ", e); + } + } + } while (Process32NextW(hSnap, ::std::addressof(pe32))); +} + +int main(int argc, char *argv[]) +{ + using namespace ::fast_io::io; + if (argc < 2) + { + if (argc == 0) + { + return 1; + } + perr("Usage: ", ::fast_io::mnp::os_c_str(*argv), " [search_term]\n"); + return 1; + } + + try + { + ::fast_io::out_buf_type obf(::fast_io::out()); + ::fast_io::string_view procName(::fast_io::mnp::os_c_str(argv[1])); + ::fast_io::string_view searchTerm(::fast_io::mnp::os_c_str((argc >= 3) ? argv[2] : "")); + + println(obf, "[*] Searching for all instances of: ", procName); + ::fast_io::u16string uProcName(::fast_io::u16concat_fast_io(::fast_io::mnp::code_cvt(procName))); + FindAndScanProcesses(obf, ::fast_io::u16cstring_view(::std::from_range, uProcName), searchTerm); + print(obf, "[*] All scans complete.\n"); + } + catch (::fast_io::error e) + { + perrln(e); + return 1; + } +} diff --git a/fuzzing/0007.containers/deque/fuzz_deque_assign.cc b/fuzzing/0007.containers/deque/fuzz_deque_assign.cc new file mode 100644 index 000000000..d7fe3e5de --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_assign.cc @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) +{ + fast_io::deque dq; + std::deque ref; + + auto read_u8 = [&](size_t &i) -> uint8_t { + if (i >= size) + { + return 0; + } + return data[i++]; + }; + + auto read_u64 = [&](size_t &i) -> std::size_t { + if (i + 7 >= size) + { + return 0; + } + std::size_t v{}; + for (int k = 0; k < 8; ++k) + { + v |= static_cast(data[i++]) << (k * 8); + } + return v; + }; + + size_t i = 0; + while (i < size) + { + uint8_t op = read_u8(i); + + switch (op % 8u) + { + case 0: + { // assign(count, val) + std::size_t n = read_u64(i); + std::size_t v = read_u64(i); + // Cap n to avoid OOM + if (n > 1000000) + { + n = n % 1000001; + } + dq.assign(n, v); + ref.assign(n, v); + break; + } + + case 1: + { // assign_range from vector + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = read_u64(i); + } + dq.assign_range(tmp); + ref.assign(tmp.begin(), tmp.end()); + break; + } + + case 2: + { // assign after growth: push_back then assign + std::size_t pushes = read_u8(i); + for (std::size_t k = 0; k != pushes; ++k) + { + dq.push_back(k); + ref.push_back(k); + } + std::size_t n = read_u64(i); + std::size_t v = read_u64(i); + if (n > 1000000) + { + n = n % 1000001; + } + dq.assign(n, v); + ref.assign(n, v); + break; + } + + case 3: + { // assign(0, val) — clear via assign + std::size_t v = read_u64(i); + dq.assign(0, v); + ref.assign(0, v); + break; + } + + case 4: + { // assign_range from empty range + std::vector tmp; + dq.assign_range(tmp); + ref.assign(tmp.begin(), tmp.end()); + break; + } + + case 5: + { // assign after push_front (tests front block) + std::size_t pushes = read_u8(i); + for (std::size_t k = 0; k != pushes; ++k) + { + dq.push_front(k * 7); + ref.push_front(k * 7); + } + std::size_t n = read_u64(i); + std::size_t v = read_u64(i); + if (n > 1000000) + { + n = n % 1000001; + } + dq.assign(n, v); + ref.assign(n, v); + break; + } + + case 6: + { // assign_range from std::deque + std::size_t n = read_u8(i); + std::deque src(n); + for (std::size_t k = 0; k != n; ++k) + { + src[k] = read_u64(i); + } + dq.assign_range(src); + ref.assign(src.begin(), src.end()); + break; + } + + case 7: + { // repeated small assigns (stress re-allocation) + std::size_t rounds = read_u8(i); + for (std::size_t r = 0; r != rounds; ++r) + { + std::size_t n = read_u8(i); + std::size_t v = read_u64(i); + dq.assign(n, v); + ref.assign(n, v); + } + break; + } + } + + // Validate + 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_insert_counts.cc b/fuzzing/0007.containers/deque/fuzz_deque_insert_counts.cc new file mode 100644 index 000000000..e7d595aa7 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_insert_counts.cc @@ -0,0 +1,85 @@ +#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; + + for (size_t i{}; i != size; ++i) + { + uint8_t b = data[i]; + + // 4 operations: insert counts, erase, etc. + uint8_t op = b & 0x3u; + + // Position: [0, size] + std::size_t pos = + dq.size() == 0 ? 0 : (static_cast(b) * 131u) % (dq.size() + 1); + + // Count: small to avoid explosion + std::size_t count = (b >> 2) % 4; // 0–3 + if (count == 0) + { + count = 1; // avoid no‑op + } + + std::size_t value = i * 2654435761ull; + + switch (op) + { + case 0: // insert_index(count, value) + { + dq.insert_index(pos, count, value); + ref.insert(ref.begin() + pos, count, value); + break; + } + + case 1: // insert(iterator, count, value) + { + auto it = dq.insert(dq.cbegin() + pos, count, value); + (void)it; + ref.insert(ref.begin() + pos, count, value); + break; + } + + case 2: // erase single element + { + if (!ref.empty()) + { + std::size_t p = pos % ref.size(); + dq.erase_index(p); + ref.erase(ref.begin() + p); + } + break; + } + + case 3: // mix: insert_index with larger count + { + std::size_t big_count = (b % 5) + 1; // 1–5 + dq.insert_index(pos, big_count, value ^ 0x9E3779B97F4A7C15ull); + ref.insert(ref.begin() + pos, big_count, + value ^ 0x9E3779B97F4A7C15ull); + 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_nontrivial.cc b/fuzzing/0007.containers/deque/fuzz_deque_nontrivial.cc new file mode 100644 index 000000000..8e363dc10 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_nontrivial.cc @@ -0,0 +1,368 @@ +#include +#if FAST_IO_FUZZ_DEBUG != 0 +#include +#endif +#include +#include +#include +#include +#include +#include +#include + +using TestType = ::std::shared_ptr; + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) +{ + fast_io::deque dq; + std::deque ref; + + auto read_u8 = [&](size_t &i) -> uint8_t { + if (i >= size) + { + return 0; + } + return data[i++]; + }; + + auto read_u64 = [&](size_t &i) -> std::size_t { + if (i + 7 >= size) + { + return 0; + } + std::size_t v{}; + for (int k = 0; k < 8; ++k) + { + v |= static_cast(data[i++]) << (k * 8); + } + return v; + }; + + auto validate = [&]() { + if (dq.size() != ref.size()) + { + __builtin_trap(); + } + if (dq.empty() != ref.empty()) + { + __builtin_trap(); + } + for (std::size_t k = 0, sz = dq.size(); k != sz; ++k) + { + if ((dq[k] == nullptr) != (ref[k] == nullptr)) + { + __builtin_trap(); + } + if (dq[k] && ref[k] && *dq[k] != *ref[k]) + { + __builtin_trap(); + } + } + if (!ref.empty()) + { + if ((dq.front() == nullptr) != (ref.front() == nullptr)) + { + __builtin_trap(); + } + if (dq.front() && ref.front() && *dq.front() != *ref.front()) + { + __builtin_trap(); + } + if ((dq.back() == nullptr) != (ref.back() == nullptr)) + { + __builtin_trap(); + } + if (dq.back() && ref.back() && *dq.back() != *ref.back()) + { + __builtin_trap(); + } + } + }; + + size_t i = 0; + while (i < size) + { + uint8_t op = read_u8(i); +#if FAST_IO_FUZZ_DEBUG != 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), " op%20=", op % 20, " ", ::fast_io::mnp::debug_view(dq)); +#endif + switch (op % 20u) + { + case 0: + { // push_back (Copy construction via shared_ptr) + std::size_t val = read_u64(i); + auto ptr = std::make_shared(val); + dq.push_back(ptr); // Tests copy + ref.push_back(ptr); + break; + } + + case 1: + { // push_front (Copy construction) + std::size_t val = read_u64(i); + auto ptr = std::make_shared(val); + dq.push_front(ptr); + ref.push_front(ptr); + break; + } + + case 2: + { // pop_back (Tests destruction / reference count decrement) + if (!ref.empty()) + { + dq.pop_back(); + ref.pop_back(); + } + break; + } + + case 3: + { // pop_front (Tests destruction / reference count decrement) + if (!ref.empty()) + { + dq.pop_front(); + ref.pop_front(); + } + break; + } + + case 4: + { // emplace_back (Tests forwarding / move mechanics) + std::size_t val = read_u64(i); + dq.emplace_back(std::make_shared(val)); + ref.emplace_back(std::make_shared(val)); + break; + } + + case 5: + { // emplace_front (Tests forwarding / move mechanics) + std::size_t val = read_u64(i); + dq.emplace_front(std::make_shared(val)); + ref.emplace_front(std::make_shared(val)); + break; + } + + case 6: + { // insert (iterator) + std::size_t val = read_u64(i); + auto ptr = std::make_shared(val); + if (ref.empty()) + { + dq.insert(dq.cbegin(), ptr); + ref.insert(ref.begin(), ptr); + } + else + { + std::size_t pos = (val * 37) % (ref.size() + 1); + dq.insert(dq.cbegin() + pos, ptr); + ref.insert(ref.begin() + pos, ptr); + } + break; + } + + case 7: + { // insert_index + std::size_t val = read_u64(i); + auto ptr = std::make_shared(val); + if (ref.empty()) + { + dq.insert_index(0, ptr); + ref.insert(ref.begin(), ptr); + } + else + { + std::size_t pos = (val * 37) % (ref.size() + 1); + dq.insert_index(pos, ptr); + ref.insert(ref.begin() + pos, ptr); + } + break; + } + + case 8: + { // erase single index + if (!ref.empty()) + { + std::size_t seed = read_u8(i); + std::size_t pos = seed % ref.size(); + dq.erase_index(pos); + ref.erase(ref.begin() + pos); + } + break; + } + + case 9: + { // erase range index + if (ref.size() >= 2) + { + std::size_t a = read_u8(i); + std::size_t b = read_u8(i); + std::size_t lo = a % ref.size(); + std::size_t hi = b % (ref.size() - lo) + lo + 1; + if (hi > ref.size()) + { + hi = ref.size(); + } + dq.erase_index(lo, hi); + ref.erase(ref.begin() + lo, ref.begin() + hi); + } + break; + } + + case 10: + { // resize default (creates null shared_ptrs) + std::size_t n = read_u64(i); + if (n > 100000) + { + n = n % 100001; + } + dq.resize(n); + ref.resize(n); + break; + } + + case 11: + { // resize with value (copies prototype value) + std::size_t n = read_u64(i); + std::size_t val = read_u64(i); + if (n > 100000) + { + n = n % 100001; + } + auto prototype = std::make_shared(val); + dq.resize(n, prototype); + ref.resize(n, prototype); + break; + } + + case 12: + { // assign(count, val) + std::size_t n = read_u64(i); + std::size_t val = read_u64(i); + if (n > 100000) + { + n = n % 100001; + } + auto prototype = std::make_shared(val); + dq.assign(n, prototype); + ref.assign(n, prototype); + break; + } + + case 13: + { // assign_range (Triggers true collection duplication copying) + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = std::make_shared(read_u64(i)); + } + dq.assign_range(tmp); + ref.assign(tmp.begin(), tmp.end()); + break; + } + + case 14: + { // clear + dq.clear(); + ref.clear(); + break; + } + + case 15: + { // clear_destroy + dq.clear_destroy(); + ref.clear(); + break; + } + + case 16: + { // swap with another deque + std::size_t n = read_u8(i); + fast_io::deque other; + std::deque other_ref; + for (std::size_t k = 0; k != n; ++k) + { + auto ptr = std::make_shared(read_u64(i)); + other.push_back(ptr); + other_ref.push_back(ptr); + } + dq.swap(other); + std::swap(ref, other_ref); + break; + } + + case 17: + { // append_range (Triggers copies from input range) + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = std::make_shared(read_u64(i)); + } + dq.append_range(tmp); + ref.insert(ref.end(), tmp.begin(), tmp.end()); + break; + } + + case 18: + { // prepend_range (Triggers copies from input range) + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = std::make_shared(read_u64(i)); + } + dq.prepend_range(tmp); + ref.insert(ref.begin(), tmp.begin(), tmp.end()); + break; + } + + case 19: + { // emplace_index + if (ref.empty()) + { + std::size_t val = read_u64(i); + dq.emplace_index(0, std::make_shared(val)); + ref.emplace(ref.begin(), std::make_shared(val)); + } + else + { + std::size_t seed = read_u8(i); + std::size_t val = read_u64(i); + std::size_t pos = seed % ref.size(); + dq.emplace_index(pos, std::make_shared(val)); + ref.emplace(ref.begin() + pos, std::make_shared(val)); + } + break; + } + } +#if FAST_IO_FUZZ_DEBUG != 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), " op%20=", op % 20, " ", ::fast_io::mnp::debug_view(dq)); +#endif + validate(); +#if FAST_IO_FUZZ_DEBUG != 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), " op%20=", op % 20, " ", ::fast_io::mnp::debug_view(dq)); +#endif + } + + // Final iterator validation + { + auto it1 = dq.begin(); + auto it2 = ref.begin(); + auto e1 = dq.end(); + auto e2 = ref.end(); + for (; it1 != e1 && it2 != e2; ++it1, ++it2) + { + if ((*it1 == nullptr) != (*it2 == nullptr)) + { + __builtin_trap(); + } + if (*it1 && *it2 && **it1 != **it2) + { + __builtin_trap(); + } + } + } + + return 0; +} diff --git a/fuzzing/0007.containers/deque/fuzz_deque_resize.cc b/fuzzing/0007.containers/deque/fuzz_deque_resize.cc new file mode 100644 index 000000000..aeed8cd16 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_resize.cc @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) +{ + fast_io::deque fq; + std::deque sq; + + auto read_u8 = [&](size_t &i) -> uint8_t { + if (i >= size) + { + return 0; + } + return data[i++]; + }; + + size_t i = 0; + while (i < size) + { + uint8_t op = read_u8(i); + switch (op % 6) + { + case 0: + { // push_back + int v = read_u8(i); + fq.push_back(v); + sq.push_back(v); + break; + } + case 1: + { // push_front + int v = read_u8(i); + fq.push_front(v); + sq.push_front(v); + break; + } + case 2: + { // resize + size_t n = read_u8(i); + fq.resize(n); + sq.resize(n); + break; + } + case 3: + { // resize overwrite + size_t n = read_u8(i); + int v = read_u8(i); + fq.resize(n, v); + sq.resize(n, v); + break; + } + case 4: + { // assign_range + size_t n = read_u8(i); + std::vector tmp(n); + for (size_t k = 0; k < n; ++k) + { + tmp[k] = read_u8(i); + } + + fq.assign_range(tmp); + sq.assign(tmp.begin(), tmp.end()); + break; + } + case 5: + { // pop ops + if (!sq.empty()) + { + fq.pop_back(); + sq.pop_back(); + } + if (!sq.empty()) + { + fq.pop_front(); + sq.pop_front(); + } + break; + } + } + // Validate element correctness + ::std::size_t fqsize{fq.size()}; + if (fqsize != sq.size()) + { + __builtin_trap(); + } + for (size_t k = 0; k != fqsize; ++k) + { + if (fq[k] != sq[k]) + { + __builtin_trap(); + } + } + } + + return 0; +} diff --git a/fuzzing/0007.containers/deque/fuzz_deque_shrink_to_fit.cc b/fuzzing/0007.containers/deque/fuzz_deque_shrink_to_fit.cc new file mode 100644 index 000000000..a97a116a5 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_shrink_to_fit.cc @@ -0,0 +1,87 @@ +#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; + + for (size_t i{}; i != size; ++i) + { + uint8_t b = data[i]; + + // 0–3 → insert/erase + // 4–7 → shrink_to_fit + uint8_t op = b & 0x7u; + + // Position: [0, size] + std::size_t pos = + dq.size() == 0 ? 0 : (static_cast(b) * 131u) % (dq.size() + 1); + + std::size_t value = i * 11400714819323198485ull; + + switch (op) + { + case 0: // insert_index + { + dq.insert_index(pos, value); + ref.insert(ref.begin() + pos, value); + break; + } + + case 1: // insert(iterator) + { + dq.insert(dq.cbegin() + pos, value); + ref.insert(ref.begin() + pos, value); + break; + } + + case 2: // erase single + { + if (!ref.empty()) + { + std::size_t p = pos % ref.size(); + dq.erase_index(p); + ref.erase(ref.begin() + p); + } + break; + } + + case 3: // emplace_index + { + dq.emplace_index(pos, value); + ref.emplace(ref.begin() + pos, value); + break; + } + + case 4: // shrink_to_fit + case 5: + case 6: + case 7: + { + dq.shrink_to_fit(); + // std::deque::shrink_to_fit is non-binding but safe to call + ref.shrink_to_fit(); + 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_trivial.cc b/fuzzing/0007.containers/deque/fuzz_deque_trivial.cc new file mode 100644 index 000000000..7a0a9f328 --- /dev/null +++ b/fuzzing/0007.containers/deque/fuzz_deque_trivial.cc @@ -0,0 +1,344 @@ +#include +#if FAST_IO_FUZZ_DEBUG != 0 +#include +#endif +#include +#include +#include +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) +{ + fast_io::deque dq; + std::deque ref; + + auto read_u8 = [&](size_t &i) -> uint8_t { + if (i >= size) + { + return 0; + } + return data[i++]; + }; + + auto read_u64 = [&](size_t &i) -> std::size_t { + if (i + 7 >= size) + { + return 0; + } + std::size_t v{}; + for (int k = 0; k < 8; ++k) + { + v |= static_cast(data[i++]) << (k * 8); + } + return v; + }; + + auto validate = [&]() { + if (dq.size() != ref.size()) + { + __builtin_trap(); + } + if (dq.empty() != ref.empty()) + { + __builtin_trap(); + } + for (std::size_t k = 0, sz = dq.size(); k != sz; ++k) + { + if (dq[k] != ref[k]) + { + __builtin_trap(); + } + } + if (!ref.empty()) + { + if (dq.front() != ref.front()) + { + __builtin_trap(); + } + if (dq.back() != ref.back()) + { + __builtin_trap(); + } + } + }; + + size_t i = 0; + while (i < size) + { + uint8_t op = read_u8(i); +#if FAST_IO_FUZZ_DEBUG != 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), " op%20=", op % 20, " ", ::fast_io::mnp::debug_view(dq)); +#endif + switch (op % 20u) + { + case 0: + { // push_back + std::size_t v = read_u64(i); + dq.push_back(v); + ref.push_back(v); + break; + } + + case 1: + { // push_front + std::size_t v = read_u64(i); + dq.push_front(v); + ref.push_front(v); + 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: + { // emplace_back + std::size_t v = read_u64(i); + dq.emplace_back(v); + ref.emplace_back(v); + break; + } + + case 5: + { // emplace_front + std::size_t v = read_u64(i); + dq.emplace_front(v); + ref.emplace_front(v); + break; + } + + case 6: + { // insert (iterator) + std::size_t v = read_u64(i); + if (ref.empty()) + { + auto it = dq.insert(dq.cbegin(), v); + (void)it; + ref.insert(ref.begin(), v); + } + else + { + std::size_t pos = (v * 37) % (ref.size() + 1); + dq.insert(dq.cbegin() + pos, v); + ref.insert(ref.begin() + pos, v); + } + break; + } + + case 7: + { // insert_index + std::size_t v = read_u64(i); + if (ref.empty()) + { + dq.insert_index(0, v); + ref.insert(ref.begin(), v); + } + else + { + std::size_t pos = (v * 37) % (ref.size() + 1); + dq.insert_index(pos, v); + ref.insert(ref.begin() + pos, v); + } + break; + } + + case 8: + { // erase single index + if (!ref.empty()) + { + std::size_t seed = read_u8(i); + std::size_t pos = seed % ref.size(); + dq.erase_index(pos); + ref.erase(ref.begin() + pos); + } + break; + } + + case 9: + { // erase range index + if (ref.size() >= 2) + { + std::size_t a = read_u8(i); + std::size_t b = read_u8(i); + std::size_t lo = a % ref.size(); + std::size_t hi = b % (ref.size() - lo) + lo + 1; + if (hi > ref.size()) + { + hi = ref.size(); + } + dq.erase_index(lo, hi); + ref.erase(ref.begin() + lo, ref.begin() + hi); + } + break; + } + + case 10: + { // resize default + std::size_t n = read_u64(i); + if (n > 100000) + { + n = n % 100001; + } + dq.resize(n); + ref.resize(n); + break; + } + + case 11: + { // resize with value + std::size_t n = read_u64(i); + std::size_t v = read_u64(i); + if (n > 100000) + { + n = n % 100001; + } + dq.resize(n, v); + ref.resize(n, v); + break; + } + + case 12: + { // assign(count, val) + std::size_t n = read_u64(i); + std::size_t v = read_u64(i); + if (n > 100000) + { + n = n % 100001; + } + dq.assign(n, v); + ref.assign(n, v); + break; + } + + case 13: + { // assign_range + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = read_u64(i); + } + dq.assign_range(tmp); + ref.assign(tmp.begin(), tmp.end()); + break; + } + + case 14: + { // clear + dq.clear(); + ref.clear(); + break; + } + + case 15: + { // clear_destroy + dq.clear_destroy(); + ref.clear(); + break; + } + + case 16: + { // swap with another deque + std::size_t n = read_u8(i); + fast_io::deque other(n); + std::deque other_ref(n); + for (std::size_t k = 0; k != n; ++k) + { + other[k] = read_u64(i); + other_ref[k] = other[k]; + } + dq.swap(other); + std::swap(ref, other_ref); + break; + } + + case 17: + { // append_range + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = read_u64(i); + } + dq.append_range(tmp); + ref.insert(ref.end(), tmp.begin(), tmp.end()); + break; + } + + case 18: + { // prepend_range + std::size_t n = read_u8(i); + std::vector tmp(n); + for (std::size_t k = 0; k != n; ++k) + { + tmp[k] = read_u64(i); + } + dq.prepend_range(tmp); + ref.insert(ref.begin(), tmp.begin(), tmp.end()); + break; + } + + case 19: + { // emplace_index + if (ref.empty()) + { + std::size_t v = read_u64(i); + dq.emplace_index(0, v); + ref.emplace(ref.begin(), v); + } + else + { + std::size_t seed = read_u8(i); + std::size_t v = read_u64(i); + std::size_t pos = seed % ref.size(); + dq.emplace_index(pos, v); + ref.emplace(ref.begin() + pos, v); + } + break; + } + } +#if FAST_IO_FUZZ_DEBUG != 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), " op%20=", op % 20, " ", ::fast_io::mnp::debug_view(dq)); +#endif + validate(); +#if FAST_IO_FUZZ_DEBUG != 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), " op%20=", op % 20, " ", ::fast_io::mnp::debug_view(dq)); +#endif + } + + // Final iterator validation + { + auto it1 = dq.begin(); + auto it2 = ref.begin(); + auto e1 = dq.end(); + auto e2 = ref.end(); + for (; + it1 != e1 && it2 != e2; ++it1, ++it2) + { + if (*it1 != *it2) + { + __builtin_trap(); + } + } + } + + return 0; +} diff --git a/include/fast_io_core_impl/allocation/adapters.h b/include/fast_io_core_impl/allocation/adapters.h index 9adcba2f3..38dad7e60 100644 --- a/include/fast_io_core_impl/allocation/adapters.h +++ b/include/fast_io_core_impl/allocation/adapters.h @@ -58,18 +58,41 @@ inline void *allocator_adjust_ptr_to_aligned_impl(void *p, ::std::size_t alignme return aligned_ptr; } -template -inline constexpr void *allocator_pointer_aligned_impl(::std::size_t, ::std::size_t) noexcept; +template +inline constexpr void *allocator_pointer_aligned_impl(::std::size_t, ::std::size_t, bool) noexcept; -template -inline constexpr ::fast_io::allocation_least_result allocator_pointer_aligned_at_least_impl(::std::size_t, ::std::size_t) noexcept; +template +inline constexpr ::fast_io::allocation_least_result allocator_pointer_aligned_at_least_impl(::std::size_t, ::std::size_t, bool) noexcept; + +template +inline constexpr void *status_allocator_pointer_aligned_impl(typename alloc::handle_type, ::std::size_t, ::std::size_t, bool) noexcept; + +template +inline constexpr ::fast_io::allocation_least_result status_allocator_pointer_aligned_at_least_impl(typename alloc::handle_type, ::std::size_t, ::std::size_t, bool) noexcept; -template -inline constexpr void *status_allocator_pointer_aligned_impl(typename alloc::handle_type, ::std::size_t, ::std::size_t) noexcept; -template -inline constexpr ::fast_io::allocation_least_result status_allocator_pointer_aligned_at_least_impl(typename alloc::handle_type, ::std::size_t, ::std::size_t) noexcept; +template +concept native_allocate_aligned_has_none_zero_ops = + ::fast_io::details::has_allocate_aligned_impl || + ::fast_io::details::has_allocate_aligned_at_least_impl; +template +concept native_allocate_aligned_has_zero_ops = + ::fast_io::details::has_allocate_aligned_zero_impl || + ::fast_io::details::has_allocate_aligned_zero_at_least_impl; + +template +concept native_allocate_aligned_has_ops = + ::fast_io::details::native_allocate_aligned_has_none_zero_ops || + ::fast_io::details::native_allocate_aligned_has_zero_ops; +template +concept native_allocate_has_none_zero_ops = ::fast_io::details::has_allocate_impl || + ::fast_io::details::has_allocate_at_least_impl || + ::fast_io::details::native_allocate_aligned_has_none_zero_ops; +template +concept native_allocate_has_zero_ops = + ::fast_io::details::has_allocate_zero_impl || + ::fast_io::details::has_allocate_zero_at_least_impl || ::fast_io::details::native_allocate_aligned_has_zero_ops; } // namespace details #if 0 @@ -102,52 +125,186 @@ class generic_allocator_adapter ::fast_io::details::has_allocate_impl || ::fast_io::details::has_allocate_aligned_impl || ::fast_io::details::has_allocate_zero_impl || - ::fast_io::details::has_allocate_aligned_zero_impl)}; + ::fast_io::details::has_allocate_aligned_zero_impl || + ::fast_io::details::has_allocate_conditional_zero_impl || + ::fast_io::details::has_allocate_aligned_conditional_zero_impl || + ::fast_io::details::has_allocate_conditional_zero_at_least_impl || + ::fast_io::details::has_allocate_aligned_conditional_zero_at_least_impl)}; #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline - constexpr - void * - allocate(::std::size_t n) noexcept + static inline void *allocate_conditional_zero(::std::size_t n, bool zero) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { - return ::operator new(n); + auto p{::operator new(n)}; + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + } + return p; } else -#endif { - if constexpr (::fast_io::details::has_allocate_impl) + if constexpr (::fast_io::details::has_allocate_conditional_zero_impl) { - return allocator_type::allocate(n); + return allocator_type::allocate_conditional_zero(n, zero); } - else if constexpr (::fast_io::details::has_allocate_at_least_impl) + else if constexpr (::fast_io::details::has_allocate_conditional_zero_at_least_impl) { - return allocator_type::allocate_at_least(n).ptr; + return allocator_type::allocate_conditional_zero_at_least(n, zero).ptr; } - else if constexpr (::fast_io::details::has_allocate_aligned_impl) + else if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_impl) { - return allocator_type::allocate_aligned(default_alignment, n); + return allocator_type::allocate_aligned_conditional_zero(default_alignment, n, zero); } - else if constexpr (::fast_io::details::has_allocate_zero_impl) + else if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_at_least_impl) { - return allocator_type::allocate_zero(n); + return allocator_type::allocate_aligned_conditional_zero_at_least(default_alignment, n, zero).ptr; } - else if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) + else { - return allocator_type::allocate_zero_at_least(n).ptr; + constexpr bool has_none_zero_ops{::fast_io::details::native_allocate_has_none_zero_ops}; + constexpr bool has_zero_ops{::fast_io::details::native_allocate_has_zero_ops}; + if constexpr (!has_none_zero_ops && !has_zero_ops) + { + ::fast_io::fast_terminate(); +#if __has_cpp_attribute(unreachable) + [[unreachable]]; +#endif + } + else if constexpr (!has_none_zero_ops && has_zero_ops) + { + if constexpr (::fast_io::details::has_allocate_zero_impl) + { + return allocator_type::allocate_zero(n); + } + else if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) + { + return allocator_type::allocate_zero_at_least(n).ptr; + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + return allocator_type::allocate_aligned_zero(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + { + return allocator_type::allocate_aligned_zero_at_least(default_alignment, n).ptr; + } + } + else if constexpr (has_none_zero_ops && !has_zero_ops) + { + void *ptr; + if constexpr (::fast_io::details::has_allocate_impl) + { + ptr = allocator_type::allocate(n); + } + else if constexpr (::fast_io::details::has_allocate_at_least_impl) + { + ptr = allocator_type::allocate_at_least(n).ptr; + } + else if constexpr (::fast_io::details::has_allocate_aligned_impl) + { + ptr = allocator_type::allocate_aligned(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) + { + ptr = allocator_type::allocate_aligned_at_least(default_alignment, n).ptr; + } + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(ptr), n); + } + return ptr; + } + else + { + void *ptr; + if (zero) + { + if constexpr (::fast_io::details::has_allocate_zero_impl) + { + ptr = allocator_type::allocate_zero(n); + } + else if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) + { + ptr = allocator_type::allocate_zero_at_least(n).ptr; + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + ptr = allocator_type::allocate_aligned_zero(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + { + ptr = allocator_type::allocate_aligned_zero_at_least(default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + else + { + if constexpr (::fast_io::details::has_allocate_impl) + { + ptr = allocator_type::allocate(n); + } + else if constexpr (::fast_io::details::has_allocate_at_least_impl) + { + ptr = allocator_type::allocate_at_least(n).ptr; + } + else if constexpr (::fast_io::details::has_allocate_aligned_impl) + { + ptr = allocator_type::allocate_aligned(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) + { + ptr = allocator_type::allocate_aligned_at_least(default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + return ptr; + } } - else if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + } + } +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline constexpr void * + allocate(::std::size_t n) noexcept + requires(!has_status) + { +#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#else + if (__builtin_is_constant_evaluated()) +#endif + { + return generic_allocator_adapter::allocate_conditional_zero(n, false); + } + else +#endif + { + if constexpr (::fast_io::details::has_allocate_impl) { - return allocator_type::allocate_aligned_zero(default_alignment, n); + return allocator_type::allocate(n); } else { - static_assert(::fast_io::details::has_allocate_impl); + return generic_allocator_adapter::allocate_conditional_zero(n, false); } } } @@ -158,27 +315,26 @@ class generic_allocator_adapter static inline void *allocate_zero(::std::size_t n) noexcept requires(!has_status) { - if constexpr (::fast_io::details::has_allocate_zero_impl) - { - return allocator_type::allocate_zero(n); - } - else if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) - { - return allocator_type::allocate_zero_at_least(n).ptr; - } - else if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) - { - return allocator_type::allocate_aligned_zero(default_alignment, n); - } - else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) +#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#else + if (__builtin_is_constant_evaluated()) +#endif { - return allocator_type::allocate_aligned_zero_at_least(default_alignment, n).ptr; + return generic_allocator_adapter::allocate_conditional_zero(n, true); } else +#endif { - auto p{generic_allocator_adapter::allocate(n)}; - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); - return p; + if constexpr (::fast_io::details::has_allocate_zero_impl) + { + return allocator_type::allocate_zero(n); + } + else + { + return generic_allocator_adapter::allocate_conditional_zero(n, true); + } } } @@ -189,45 +345,126 @@ class generic_allocator_adapter ::fast_io::details::has_reallocate_zero_impl || ::fast_io::details::has_reallocate_zero_at_least_impl || ::fast_io::details::has_reallocate_aligned_zero_impl || - ::fast_io::details::has_reallocate_aligned_zero_at_least_impl); + ::fast_io::details::has_reallocate_aligned_zero_at_least_impl || + ::fast_io::details::has_reallocate_conditional_zero_impl || + ::fast_io::details::has_reallocate_aligned_conditional_zero_impl || + ::fast_io::details::has_reallocate_conditional_zero_at_least_impl || + ::fast_io::details::has_reallocate_aligned_conditional_zero_at_least_impl); #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline void *reallocate(void *p, ::std::size_t n) noexcept + static inline void *reallocate_conditional_zero(void *p, ::std::size_t n, bool zero) noexcept requires(!has_status && has_reallocate) { - if constexpr (::fast_io::details::has_reallocate_impl) + if constexpr (::fast_io::details::has_reallocate_conditional_zero_impl) { - return allocator_type::reallocate(p, n); + return allocator_type::reallocate_conditional_zero(p, n, zero); } - else if constexpr (::fast_io::details::has_reallocate_at_least_impl) + else if constexpr (::fast_io::details::has_reallocate_conditional_zero_at_least_impl) { - return allocator_type::reallocate_at_least(p, n).ptr; + return allocator_type::reallocate_conditional_zero_at_least(p, n, zero).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + else if constexpr (::fast_io::details::has_reallocate_aligned_conditional_zero_impl) { - return allocator_type::reallocate_aligned(p, default_alignment, n); + return allocator_type::reallocate_aligned_conditional_zero(p, default_alignment, n, zero); } - else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) + else if constexpr (::fast_io::details::has_reallocate_aligned_conditional_zero_at_least_impl) { - return allocator_type::reallocate_aligned_at_least(p, default_alignment, n).ptr; + return allocator_type::reallocate_aligned_conditional_zero_at_least(p, default_alignment, n, zero).ptr; } - else if constexpr (::fast_io::details::has_reallocate_zero_impl) + else if constexpr (::fast_io::details::has_reallocate_impl || + ::fast_io::details::has_reallocate_at_least_impl || + ::fast_io::details::has_reallocate_aligned_impl || + ::fast_io::details::has_reallocate_aligned_at_least_impl) { - return allocator_type::reallocate_zero(p, n); + // Non-zero APIs exist - need runtime branch + if (zero) + { + if constexpr (::fast_io::details::has_reallocate_zero_impl) + { + return allocator_type::reallocate_zero(p, n); + } + else if constexpr (::fast_io::details::has_reallocate_zero_at_least_impl) + { + return allocator_type::reallocate_zero_at_least(p, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return allocator_type::reallocate_aligned_zero(p, default_alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + else + { + if constexpr (::fast_io::details::has_reallocate_impl) + { + return allocator_type::reallocate(p, n); + } + else if constexpr (::fast_io::details::has_reallocate_at_least_impl) + { + return allocator_type::reallocate_at_least(p, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + { + return allocator_type::reallocate_aligned(p, default_alignment, n); + } + else + { + return allocator_type::reallocate_aligned_at_least(p, default_alignment, n).ptr; + } + } } - else if constexpr (::fast_io::details::has_reallocate_zero_at_least_impl) + else { - return allocator_type::reallocate_zero_at_least(p, n).ptr; + // Only zero APIs exist - use zero API for both cases + if constexpr (::fast_io::details::has_reallocate_zero_impl) + { + return allocator_type::reallocate_zero(p, n); + } + else if constexpr (::fast_io::details::has_reallocate_zero_at_least_impl) + { + return allocator_type::reallocate_zero_at_least(p, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return allocator_type::reallocate_aligned_zero(p, default_alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *reallocate(void *p, ::std::size_t n) noexcept + requires(!has_status && has_reallocate) + { + if constexpr (::fast_io::details::has_reallocate_impl) { - return allocator_type::reallocate_aligned_zero(p, default_alignment, n); + return allocator_type::reallocate(p, n); } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + else if constexpr (::fast_io::details::has_reallocate_at_least_impl) + { + return allocator_type::reallocate_at_least(p, n).ptr; + } + else { - return allocator_type::reallocate_aligned_zero_at_least(p, default_alignment, n).ptr; + return generic_allocator_adapter::reallocate_conditional_zero(p, n, false); } } @@ -251,13 +488,141 @@ class generic_allocator_adapter { return allocator_type::reallocate_zero_at_least(p, n).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + else { - return allocator_type::reallocate_aligned_zero(p, default_alignment, n); + return generic_allocator_adapter::reallocate_conditional_zero(p, n, true); } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *reallocate_n_conditional_zero(void *p, ::std::size_t oldn, ::std::size_t n, bool zero) noexcept + requires(!has_status) + { + if (p != nullptr && oldn == n) + { + return p; + } +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif + { + auto newptr{::operator new(n)}; + if (p != nullptr) + { + if (n) + { + ::std::size_t copyn{oldn < n ? oldn : n}; + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), copyn, reinterpret_cast<::std::byte *>(newptr)); + } + ::operator delete(p); + } + if (zero && oldn < n) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, n - oldn); + } + return newptr; + } + else { - return allocator_type::reallocate_aligned_zero_at_least(p, default_alignment, n).ptr; + if constexpr (::fast_io::details::has_reallocate_n_conditional_zero_impl) + { + return allocator_type::reallocate_n_conditional_zero(p, oldn, n, zero); + } + else if constexpr (::fast_io::details::has_reallocate_n_conditional_zero_at_least_impl) + { + return allocator_type::reallocate_n_conditional_zero_at_least(p, oldn, n, zero).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_conditional_zero_impl) + { + return allocator_type::reallocate_aligned_n_conditional_zero(p, oldn, default_alignment, n, zero); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_conditional_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_n_conditional_zero_at_least(p, oldn, default_alignment, n, zero).ptr; + } + else if (zero) + { + if constexpr (::fast_io::details::has_reallocate_zero_n_impl) + { + return allocator_type::reallocate_zero_n(p, oldn, n); + } + else if constexpr (::fast_io::details::has_reallocate_zero_n_at_least_impl) + { + return allocator_type::reallocate_zero_n_at_least(p, oldn, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + { + return allocator_type::reallocate_aligned_zero_n(p, oldn, default_alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, default_alignment, n).ptr; + } + else + { + auto newptr{generic_allocator_adapter::reallocate_n(p, oldn, n)}; + if (oldn < n) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, n - oldn); + } + return newptr; + } + } + else + { + if constexpr (::fast_io::details::has_reallocate_n_impl) + { + return allocator_type::reallocate_n(p, oldn, n); + } + else if constexpr (::fast_io::details::has_reallocate_n_at_least_impl) + { + return allocator_type::reallocate_n_at_least(p, oldn, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_impl) + { + return allocator_type::reallocate_aligned_n(p, oldn, default_alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) + { + return allocator_type::reallocate_aligned_n_at_least(p, oldn, default_alignment, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + { + return allocator_type::reallocate_aligned_zero_n(p, oldn, default_alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, default_alignment, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_zero_n_impl) + { + return allocator_type::reallocate_zero_n(p, oldn, n); + } + else if constexpr (::fast_io::details::has_reallocate_zero_n_at_least_impl) + { + return allocator_type::reallocate_zero_n_at_least(p, oldn, n).ptr; + } + else + { + auto newptr{generic_allocator_adapter::allocate(n)}; + if (p != nullptr) + { + if (n) + { + ::std::size_t copyn{oldn < n ? oldn : n}; + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), copyn, reinterpret_cast<::std::byte *>(newptr)); + } + generic_allocator_adapter::deallocate_n(p, oldn); + } + return newptr; + } + } } } @@ -279,78 +644,9 @@ class generic_allocator_adapter { return allocator_type::reallocate_n_at_least(p, oldn, n).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_n_impl) - { - return allocator_type::reallocate_aligned_n(p, oldn, default_alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) + else { - return allocator_type::reallocate_aligned_n_at_least(p, oldn, default_alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_zero_n_impl) - { - return allocator_type::reallocate_zero_n(p, oldn, n); - } - else if constexpr (::fast_io::details::has_reallocate_zero_n_at_least_impl) - { - return allocator_type::reallocate_zero_n_at_least(p, oldn, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) - { - return allocator_type::reallocate_aligned_zero_n(p, oldn, default_alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) - { - return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, default_alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_impl) - { - return allocator_type::reallocate(p, n); - } - else if constexpr (::fast_io::details::has_reallocate_at_least_impl) - { - return allocator_type::reallocate_at_least(p, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_impl) - { - return allocator_type::reallocate_aligned(p, default_alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) - { - return allocator_type::reallocate_aligned_at_least(p, default_alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_zero_impl) - { - return allocator_type::reallocate_zero(p, n); - } - else if constexpr (::fast_io::details::has_reallocate_zero_at_least_impl) - { - return allocator_type::reallocate_zero_at_least(p, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) - { - return allocator_type::reallocate_aligned_zero(p, default_alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) - { - return allocator_type::reallocate_aligned_zero_at_least(p, default_alignment, n).ptr; - } - else - { - auto newptr{generic_allocator_adapter::allocate(n)}; - if (p != nullptr) - { - if (n) - { - if (oldn < n) - { - n = oldn; - } - ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), n, reinterpret_cast<::std::byte *>(newptr)); - } - generic_allocator_adapter::deallocate_n(p, oldn); - } - return newptr; + return generic_allocator_adapter::reallocate_n_conditional_zero(p, oldn, n, false); } } @@ -372,39 +668,9 @@ class generic_allocator_adapter { return allocator_type::reallocate_zero_n_at_least(p, oldn, n).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) - { - return allocator_type::reallocate_aligned_zero_n(p, oldn, default_alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) - { - return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, default_alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_zero_impl) - { - return allocator_type::reallocate_zero(p, n); - } - else if constexpr (::fast_io::details::has_reallocate_zero_at_least_impl) - { - return allocator_type::reallocate_zero_at_least(p, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) - { - return allocator_type::reallocate_aligned_zero(p, default_alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) - { - return allocator_type::reallocate_aligned_zero_at_least(p, default_alignment, n).ptr; - } else { - auto newptr{generic_allocator_adapter::reallocate_n(p, oldn, n)}; - if (oldn < n) - { - ::std::size_t const to_zero_bytes{static_cast<::std::size_t>(n - oldn)}; - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, to_zero_bytes); - } - return newptr; + return generic_allocator_adapter::reallocate_n_conditional_zero(p, oldn, n, true); } } @@ -469,41 +735,154 @@ class generic_allocator_adapter #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline - constexpr - void * - allocate_aligned(::std::size_t alignment, ::std::size_t n) noexcept + static inline constexpr void * + allocate_aligned_conditional_zero(::std::size_t alignment, ::std::size_t n, bool zero) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { - return ::operator new(n); + auto p{::operator new(n)}; + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + } + return p; + } + if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_impl) + { + return allocator_type::allocate_aligned_conditional_zero(alignment, n, zero); + } + else if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_at_least_impl) + { + return allocator_type::allocate_aligned_conditional_zero_at_least(alignment, n, zero).ptr; + } + else if constexpr (::fast_io::details::native_allocate_aligned_has_ops) + { + constexpr bool has_none_zero_ops{::fast_io::details::native_allocate_aligned_has_none_zero_ops}; + constexpr bool has_zero_ops{::fast_io::details::native_allocate_aligned_has_zero_ops}; + if constexpr (!has_none_zero_ops && has_zero_ops) + { + if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + return allocator_type::allocate_aligned_zero(alignment, n); + } + else + { + return allocator_type::allocate_aligned_zero_at_least(alignment, n).ptr; + } + } + else if constexpr (has_none_zero_ops && !has_zero_ops) + { + void *ptr; + if constexpr (::fast_io::details::has_allocate_aligned_impl) + { + ptr = allocator_type::allocate_aligned(alignment, n); + } + else + { + ptr = allocator_type::allocate_aligned_at_least(alignment, n).ptr; + } + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(ptr), n); + } + return ptr; + } + else if constexpr (has_none_zero_ops && has_zero_ops) + { + void *ptr; + if (zero) + { + if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + ptr = allocator_type::allocate_aligned_zero(alignment, n); + } + else + { + ptr = allocator_type::allocate_aligned_at_least(alignment, n).ptr; + } + } + else + { + if constexpr (::fast_io::details::has_allocate_aligned_impl) + { + ptr = allocator_type::allocate_aligned_zero(alignment, n); + } + else + { + ptr = allocator_type::allocate_aligned_at_least(alignment, n).ptr; + } + } + return ptr; + } + else + { + ::fast_io::fast_terminate(); + } } else -#endif { - return ::fast_io::details::allocator_pointer_aligned_impl(alignment, n); + return ::fast_io::details::allocator_pointer_aligned_impl(alignment, n, zero); } } #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline constexpr - void * - allocate_aligned_zero(::std::size_t alignment, ::std::size_t n) noexcept + static inline constexpr void * + allocate_aligned(::std::size_t alignment, ::std::size_t n) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif + { + return ::operator new(n); + } + if constexpr (::fast_io::details::has_allocate_aligned_impl) { - return ::operator new(n); // this is problematic. No way to clean it up at compile time. + return allocator_type::allocate_aligned(alignment, n); } else + { + return generic_allocator_adapter::allocate_aligned_conditional_zero(alignment, n, false); + } + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline constexpr void * + allocate_aligned_zero(::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status) + { +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) #endif { - return ::fast_io::details::allocator_pointer_aligned_impl(alignment, n); + return ::operator new(n); + } + if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + return allocator_type::allocate_aligned_zero(alignment, n); + } + else + { + return generic_allocator_adapter::allocate_aligned_conditional_zero(alignment, n, true); } } @@ -517,30 +896,22 @@ class generic_allocator_adapter allocate_at_least(::std::size_t n) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { return {::operator new(n), n}; } else -#endif { if constexpr (::fast_io::details::has_allocate_at_least_impl) { return allocator_type::allocate_at_least(n); } - else if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) - { - return allocator_type::allocate_aligned_at_least(default_alignment, n); - } - else if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) - { - return allocator_type::allocate_zero_at_least(n); - } - else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) - { - return allocator_type::allocate_aligned_zero_at_least(default_alignment, n); - } else { return {generic_allocator_adapter::allocate(n), n}; @@ -552,66 +923,198 @@ class generic_allocator_adapter allocate_zero_at_least(::std::size_t n) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { return {::operator new(n), n}; } else -#endif { if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) { return allocator_type::allocate_zero_at_least(n); } - else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + else + { + return generic_allocator_adapter::allocate_conditional_zero_at_least(n, true); + } + } + } + + static inline constexpr allocation_least_result + allocate_conditional_zero_at_least(::std::size_t n, bool zero) noexcept + requires(!has_status) + { +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif + { + auto p{::operator new(n)}; + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + } + return {p, n}; + } + else + { + if constexpr (::fast_io::details::has_allocate_conditional_zero_at_least_impl) + { + return allocator_type::allocate_conditional_zero_at_least(n, zero); + } + if constexpr (::fast_io::details::has_allocate_conditional_zero_impl) { - return allocator_type::allocate_aligned_zero_at_least(default_alignment, n); + return {allocator_type::allocate_conditional_zero(n, zero), n}; } - else if constexpr (::fast_io::details::has_allocate_at_least_impl || - ::fast_io::details::has_allocate_aligned_at_least_impl) + else if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_at_least_impl) { - auto temp{generic_allocator_adapter::allocate_at_least(n)}; - auto ptr{temp.ptr}; - auto sz{temp.count}; - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(ptr), sz); - return temp; + return allocator_type::allocate_aligned_conditional_zero_at_least(default_alignment, n, zero); + } + else if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_impl) + { + return {allocator_type::allocate_aligned_conditional_zero(default_alignment, n, zero), n}; } else { - return {generic_allocator_adapter::allocate_zero(n), n}; + constexpr bool has_none_zero_ops{::fast_io::details::native_allocate_has_none_zero_ops}; + constexpr bool has_zero_ops{::fast_io::details::native_allocate_has_zero_ops}; + if constexpr (!has_none_zero_ops && !has_zero_ops) + { + ::fast_io::fast_terminate(); +#if __has_cpp_attribute(unreachable) + [[unreachable]]; +#endif + } + else if constexpr (!has_none_zero_ops && has_zero_ops) + { + if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) + { + return allocator_type::allocate_zero_at_least(n); + } + else if constexpr (::fast_io::details::has_allocate_zero_impl) + { + return {allocator_type::allocate_zero(n), n}; + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + { + return allocator_type::allocate_aligned_zero_at_least(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + return {allocator_type::allocate_aligned_zero(default_alignment, n), n}; + } + } + else if constexpr (has_none_zero_ops && !has_zero_ops) + { + ::fast_io::allocation_least_result res; + if constexpr (::fast_io::details::has_allocate_at_least_impl) + { + res = allocator_type::allocate_at_least(n); + } + else if constexpr (::fast_io::details::has_allocate_impl) + { + res = {allocator_type::allocate(n), n}; + } + else if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) + { + res = allocator_type::allocate_aligned_at_least(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_impl) + { + res = {allocator_type::allocate_aligned(default_alignment, n), n}; + } + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(res.ptr), res.count); + } + return res; + } + else + { + ::fast_io::allocation_least_result res; + if (zero) + { + if constexpr (::fast_io::details::has_allocate_at_least_impl) + { + res = allocator_type::allocate_at_least(n); + } + else if constexpr (::fast_io::details::has_allocate_impl) + { + res = {allocator_type::allocate(n), n}; + } + else if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) + { + res = allocator_type::allocate_aligned_at_least(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_impl) + { + res = {allocator_type::allocate_aligned(default_alignment, n), n}; + } + else + { + ::fast_io::fast_terminate(); + } + } + else + { + if constexpr (::fast_io::details::has_allocate_zero_at_least_impl) + { + res = allocator_type::allocate_zero_at_least(n); + } + else if constexpr (::fast_io::details::has_allocate_zero_impl) + { + res = {allocator_type::allocate_zero(n), n}; + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + { + res = allocator_type::allocate_aligned_zero_at_least(default_alignment, n); + } + else if constexpr (::fast_io::details::has_allocate_aligned_zero_impl) + { + res = {allocator_type::allocate_aligned_zero(default_alignment, n), n}; + } + else + { + ::fast_io::fast_terminate(); + } + } + return res; + } } } } - static inline constexpr allocation_least_result allocate_aligned_at_least(::std::size_t alignment, ::std::size_t n) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { - return {::operator new(n), n}; + return generic_allocator_adapter::allocate_aligned_conditional_zero_at_least(alignment, n, false); } else -#endif { if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) { return allocator_type::allocate_aligned_at_least(alignment, n); } - else if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) - { - return allocator_type::allocate_aligned_zero_at_least(alignment, n); - } - else if constexpr (::fast_io::details::has_allocate_at_least_impl || - ::fast_io::details::has_allocate_zero_at_least_impl) - { - return ::fast_io::details::allocator_pointer_aligned_at_least_impl(alignment, n); - } else { - return {generic_allocator_adapter::allocate_aligned(alignment, n), n}; + return generic_allocator_adapter::allocate_aligned_conditional_zero_at_least(alignment, n, false); } } } @@ -620,46 +1123,191 @@ class generic_allocator_adapter allocate_aligned_zero_at_least(::std::size_t alignment, ::std::size_t n) noexcept requires(!has_status) { -#if __cpp_constexpr_dynamic_alloc >= 201907L +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { - return {::operator new(n), n}; + return generic_allocator_adapter::allocate_aligned_conditional_zero_at_least(alignment, n, true); } else -#endif { if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) { return allocator_type::allocate_aligned_zero_at_least(alignment, n); } - else if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) - { - auto temp{allocator_type::allocate_aligned_at_least(alignment, n)}; - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(temp.ptr), temp.count); - return temp; - } - else if constexpr (::fast_io::details::has_allocate_at_least_impl || - ::fast_io::details::has_allocate_zero_at_least_impl) - { - return ::fast_io::details::allocator_pointer_aligned_at_least_impl(alignment, n); - } else { - return {generic_allocator_adapter::allocate_aligned_zero(alignment, n), n}; + return generic_allocator_adapter::allocate_aligned_conditional_zero_at_least(alignment, n, true); } } } - - static inline constexpr bool has_reallocate_aligned = (::fast_io::details::has_reallocate_aligned_impl || - ::fast_io::details::has_reallocate_aligned_at_least_impl || - ::fast_io::details::has_reallocate_aligned_zero_impl || - ::fast_io::details::has_reallocate_aligned_zero_at_least_impl); - + static inline constexpr allocation_least_result + allocate_aligned_conditional_zero_at_least(::std::size_t alignment, ::std::size_t n, bool zero) noexcept + requires(!has_status) + { +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif + { + auto p{::operator new(n)}; + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + } + return {p, n}; + } + if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_at_least_impl) + { + return allocator_type::allocate_aligned_conditional_zero_at_least(alignment, n, zero); + } + else if constexpr (::fast_io::details::has_allocate_aligned_conditional_zero_impl) + { + return {allocator_type::allocate_aligned_conditional_zero(alignment, n, zero), n}; + } + else if constexpr (::fast_io::details::native_allocate_aligned_has_ops) + { + constexpr bool has_none_zero_ops{::fast_io::details::native_allocate_aligned_has_none_zero_ops}; + constexpr bool has_zero_ops{::fast_io::details::native_allocate_aligned_has_zero_ops}; + if constexpr (!has_none_zero_ops && has_zero_ops) + { + if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + { + return allocator_type::allocate_aligned_zero_at_least(alignment, n); + } + else + { + return {allocator_type::allocate_aligned_zero(alignment, n), n}; + } + } + else if constexpr (has_none_zero_ops && !has_zero_ops) + { + ::fast_io::allocation_least_result res; + if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) + { + res = allocator_type::allocate_aligned_at_least(alignment, n); + } + else + { + res = {allocator_type::allocate_aligned(alignment, n), n}; + } + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(res.ptr), res.count); + } + return res; + } + else if constexpr (has_none_zero_ops && has_zero_ops) + { + ::fast_io::allocation_least_result res; + if (zero) + { + if constexpr (::fast_io::details::has_allocate_aligned_zero_at_least_impl) + { + res = allocator_type::allocate_aligned_zero_at_least(alignment, n); + } + else + { + res = {allocator_type::allocate_aligned_zero(alignment, n), n}; + } + } + else + { + if constexpr (::fast_io::details::has_allocate_aligned_at_least_impl) + { + res = allocator_type::allocate_aligned_at_least(alignment, n); + } + else + { + res = {allocator_type::allocate_aligned(alignment, n), n}; + } + } + return res; + } + else + { + ::fast_io::fast_terminate(); + } + } + else + { + return ::fast_io::details::allocator_pointer_aligned_at_least_impl(default_alignment, n, zero); + } + } + + static inline constexpr bool has_reallocate_aligned = (::fast_io::details::has_reallocate_aligned_impl || + ::fast_io::details::has_reallocate_aligned_at_least_impl || + ::fast_io::details::has_reallocate_aligned_zero_impl || + ::fast_io::details::has_reallocate_aligned_zero_at_least_impl || + ::fast_io::details::has_reallocate_aligned_conditional_zero_impl || + ::fast_io::details::has_reallocate_aligned_conditional_zero_at_least_impl); + #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline - void * + static inline void * + reallocate_aligned_conditional_zero(void *p, ::std::size_t alignment, ::std::size_t n, bool zero) noexcept + requires(!has_status && has_reallocate_aligned) + { + if constexpr (::fast_io::details::has_reallocate_aligned_conditional_zero_impl) + { + return allocator_type::reallocate_aligned_conditional_zero(p, alignment, n, zero); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_conditional_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_conditional_zero_at_least(p, alignment, n, zero).ptr; + } + else if (zero) + { + if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return allocator_type::reallocate_aligned_zero(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + else + { + if constexpr (::fast_io::details::has_reallocate_aligned_impl) + { + return allocator_type::reallocate_aligned(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) + { + return allocator_type::reallocate_aligned_at_least(p, alignment, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return allocator_type::reallocate_aligned_zero(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void * reallocate_aligned(void *p, ::std::size_t alignment, ::std::size_t n) noexcept requires(!has_status && has_reallocate_aligned) { @@ -671,13 +1319,9 @@ class generic_allocator_adapter { return allocator_type::reallocate_aligned_at_least(p, alignment, n).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) - { - return allocator_type::reallocate_aligned_zero(p, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + else { - return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n).ptr; + return generic_allocator_adapter::reallocate_aligned_conditional_zero(p, alignment, n, false); } } @@ -686,8 +1330,7 @@ class generic_allocator_adapter #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline - void * + static inline void * reallocate_aligned_zero(void *p, ::std::size_t alignment, ::std::size_t n) noexcept requires(!has_status && has_reallocate_aligned_zero) { @@ -699,86 +1342,169 @@ class generic_allocator_adapter { return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n).ptr; } + else + { + return generic_allocator_adapter::reallocate_aligned_conditional_zero(p, alignment, n, true); + } } #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline - void * - reallocate_aligned_n(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept + static inline void * + reallocate_aligned_n_conditional_zero(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n, bool zero) noexcept requires(!has_status) { if (p != nullptr && oldn == n) { return p; } - if constexpr (::fast_io::details::has_reallocate_aligned_n_impl) - { - return allocator_type::reallocate_aligned_n(p, oldn, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) - { - return allocator_type::reallocate_aligned_n_at_least(p, oldn, alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_impl) - { - return allocator_type::reallocate_aligned(p, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) - { - return allocator_type::reallocate_aligned_at_least(p, alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) - { - return allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) - { - return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n).ptr; - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) - { - return allocator_type::reallocate_aligned_zero(p, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif { - return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n).ptr; + auto newptr{::operator new(n)}; + if (p != nullptr) + { + if (n) + { + ::std::size_t copyn{oldn < n ? oldn : n}; + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), copyn, reinterpret_cast<::std::byte *>(newptr)); + } + ::operator delete(p); + } + if (zero && oldn < n) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, n - oldn); + } + return newptr; } else { - if constexpr (::fast_io::details::has_reallocate_n_impl || - ::fast_io::details::has_reallocate_zero_n_impl || - ::fast_io::details::has_reallocate_impl || - ::fast_io::details::has_reallocate_zero_impl) + if constexpr (::fast_io::details::has_reallocate_aligned_n_conditional_zero_impl) { - if (alignment <= default_alignment) + return allocator_type::reallocate_aligned_n_conditional_zero(p, oldn, alignment, n, zero); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_conditional_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_n_conditional_zero_at_least(p, oldn, alignment, n, zero).ptr; + } + else if (zero) + { + if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + { + return allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n).ptr; + } + else { - return generic_allocator_adapter::reallocate_n(p, oldn, n); + auto newptr{generic_allocator_adapter::reallocate_aligned_n(p, oldn, alignment, n)}; + if (oldn < n) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, n - oldn); + } + return newptr; } } - auto newptr{::fast_io::details::allocator_pointer_aligned_impl(alignment, n)}; - if (p != nullptr) + else { - if (n) + if constexpr (::fast_io::details::has_reallocate_aligned_n_impl) { - if (oldn < n) + return allocator_type::reallocate_aligned_n(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) + { + return allocator_type::reallocate_aligned_n_at_least(p, oldn, alignment, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + { + return allocator_type::reallocate_aligned(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) + { + return allocator_type::reallocate_aligned_at_least(p, alignment, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + { + return allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n).ptr; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return allocator_type::reallocate_aligned_zero(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n).ptr; + } + else + { + if constexpr (::fast_io::details::has_reallocate_n_impl || + ::fast_io::details::has_reallocate_zero_n_impl || + ::fast_io::details::has_reallocate_impl || + ::fast_io::details::has_reallocate_zero_impl) { - n = oldn; + if (alignment <= default_alignment) + { + return generic_allocator_adapter::reallocate_n(p, oldn, n); + } } - ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), n, reinterpret_cast<::std::byte *>(newptr)); + auto newptr{::fast_io::details::allocator_pointer_aligned_impl(alignment, n, false)}; + if (p != nullptr) + { + if (n) + { + bool moren{oldn < n}; + ::std::size_t copyn{moren ? oldn : n}; + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), copyn, reinterpret_cast<::std::byte *>(newptr)); + } + generic_allocator_adapter::deallocate_aligned_n(p, alignment, oldn); + } + return newptr; } - generic_allocator_adapter::deallocate_aligned_n(p, alignment, oldn); } - return newptr; } } #if __has_cpp_attribute(__gnu__::__returns_nonnull__) [[__gnu__::__returns_nonnull__]] #endif - static inline - void * + static inline void * + reallocate_aligned_n(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status) + { + if (p != nullptr && oldn == n) + { + return p; + } + if constexpr (::fast_io::details::has_reallocate_aligned_n_impl) + { + return allocator_type::reallocate_aligned_n(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) + { + return allocator_type::reallocate_aligned_n_at_least(p, oldn, alignment, n).ptr; + } + else + { + return generic_allocator_adapter::reallocate_aligned_n_conditional_zero(p, oldn, alignment, n, false); + } + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void * reallocate_aligned_zero_n(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept requires(!has_status) { @@ -809,8 +1535,7 @@ class generic_allocator_adapter static inline constexpr bool has_native_reallocate_at_least = (has_reallocate && (::fast_io::details::has_reallocate_aligned_at_least_impl || ::fast_io::details::has_reallocate_aligned_zero_at_least_impl)); - static inline - ::fast_io::allocation_least_result + static inline ::fast_io::allocation_least_result reallocate_at_least(void *p, ::std::size_t n) noexcept requires(!has_status && has_reallocate) { @@ -852,8 +1577,7 @@ class generic_allocator_adapter (::fast_io::details::has_reallocate_zero_at_least_impl || ::fast_io::details::has_reallocate_aligned_zero_at_least_impl)); - static inline - ::fast_io::allocation_least_result + static inline ::fast_io::allocation_least_result reallocate_zero_at_least(void *p, ::std::size_t n) noexcept requires(!has_status && has_reallocate) { @@ -875,8 +1599,7 @@ class generic_allocator_adapter } } - static inline - ::fast_io::allocation_least_result + static inline ::fast_io::allocation_least_result reallocate_n_at_least(void *p, ::std::size_t oldn, ::std::size_t n) noexcept requires(!has_status) { @@ -945,27 +1668,26 @@ class generic_allocator_adapter return {allocator_type::reallocate_aligned_zero(p, default_alignment, n), n}; } else + { + auto newres{generic_allocator_adapter::allocate_at_least(n)}; + auto newptr{newres.ptr}; + if (p != nullptr) { - auto newres{generic_allocator_adapter::allocate_at_least(n)}; - auto newptr{newres.ptr}; - if (p != nullptr) + if (n) { - if (n) + if (oldn < n) { - if (oldn < n) - { - n = oldn; - } - ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), n, reinterpret_cast<::std::byte *>(newptr)); + n = oldn; } - generic_allocator_adapter::deallocate_n(p, oldn); + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), n, reinterpret_cast<::std::byte *>(newptr)); } - return newres; + generic_allocator_adapter::deallocate_n(p, oldn); } + return newres; + } } - static inline - ::fast_io::allocation_least_result + static inline ::fast_io::allocation_least_result reallocate_zero_n_at_least(void *p, ::std::size_t oldn, ::std::size_t n) noexcept requires(!has_status) { @@ -987,212 +1709,772 @@ class generic_allocator_adapter } else { - auto newres{generic_allocator_adapter::reallocate_n_at_least(p, oldn, n)}; - auto newptr{newres.ptr}; - n = newres.count; - if (oldn < n) + auto newres{generic_allocator_adapter::reallocate_n_at_least(p, oldn, n)}; + auto newptr{newres.ptr}; + n = newres.count; + if (oldn < n) + { + ::std::size_t const to_zero_bytes{static_cast<::std::size_t>(n - oldn)}; + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, to_zero_bytes); + } + return newres; + } + } + + static inline constexpr bool has_native_reallocate_aligned_at_least = (has_reallocate_aligned && + (::fast_io::details::has_reallocate_aligned_zero_at_least_impl || + ::fast_io::details::has_reallocate_aligned_at_least_impl)); + + static inline ::fast_io::allocation_least_result + reallocate_aligned_at_least(void *p, ::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status && has_reallocate_aligned_zero) + { + if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) + { + return allocator_type::reallocate_aligned_at_least(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + { + return {allocator_type::reallocate_aligned(p, alignment, n), n}; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return {allocator_type::reallocate_aligned_zero(p, alignment, n), n}; + } + } + + static inline constexpr bool has_native_reallocate_aligned_zero_at_least = (has_reallocate_aligned_zero && + ::fast_io::details::has_reallocate_aligned_zero_at_least_impl); + + static inline ::fast_io::allocation_least_result + reallocate_aligned_zero_at_least(void *p, ::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status && has_reallocate_aligned_zero) + { + if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return {allocator_type::reallocate_aligned_zero(p, alignment, n), n}; + } + } + + static inline constexpr bool has_native_reallocate_aligned_n_at_least = (has_reallocate_aligned && + (::fast_io::details::has_reallocate_aligned_n_at_least_impl || + ::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl)); + static inline ::fast_io::allocation_least_result + reallocate_aligned_n_at_least(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status) + { + if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) + { + return allocator_type::reallocate_aligned_n_at_least(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) + { + return allocator_type::reallocate_aligned_at_least(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + { + return {allocator_type::reallocate_aligned_n(p, oldn, alignment, n), n}; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + { + return {allocator_type::reallocate_aligned(p, alignment, n), n}; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + { + return {allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n), n}; + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + { + return {allocator_type::reallocate_aligned_zero(p, alignment, n), n}; + } + else + { + if constexpr ( + ::fast_io::details::has_reallocate_at_least_impl || + ::fast_io::details::has_reallocate_zero_at_least_impl || + ::fast_io::details::has_reallocate_n_at_least_impl || + ::fast_io::details::has_reallocate_zero_n_at_least_impl) + { + if (alignment <= default_alignment) + { + return generic_allocator_adapter::reallocate_n_at_least(p, oldn, n); + } + } + auto newres{::fast_io::details::allocator_pointer_aligned_at_least_impl(alignment, n, false)}; + auto newptr{newres.ptr}; + if (p != nullptr) + { + if (n) + { + if (oldn < n) + { + n = oldn; + } + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), n, reinterpret_cast<::std::byte *>(newptr)); + } + generic_allocator_adapter::deallocate_aligned_n(p, alignment, oldn); + } + return newres; + } + } + + static inline constexpr bool has_native_reallocate_aligned_zero_n_at_least = (has_reallocate_aligned_zero && ::fast_io::details::has_reallocate_aligned_zero_at_least_impl); + + static inline ::fast_io::allocation_least_result reallocate_aligned_zero_n_at_least(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status) + { + if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n); + } + else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + { + return {allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n), n}; + } + else + { + auto newres = generic_allocator_adapter::reallocate_aligned_n_at_least(p, oldn, alignment, n); + auto newptr{newres.ptr}; + n = newres.count; + if (oldn < n) + { + ::std::size_t const to_zero_bytes{static_cast<::std::size_t>(n - oldn)}; + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, to_zero_bytes); + } + return newres; + } + } + + static inline constexpr bool has_deallocate_aligned = (::fast_io::details::has_deallocate_aligned_impl || + ::fast_io::details::has_deallocate_impl); + static inline void deallocate_aligned(void *p, ::std::size_t alignment) noexcept + requires(!has_status && has_deallocate_aligned) + { + if constexpr (::fast_io::details::has_deallocate_aligned_impl) + { + allocator_type::deallocate_aligned(p, alignment); + } + else + { + if (p == nullptr) + { + return; + } + if (default_alignment < alignment) + { + p = reinterpret_cast(p)[-1]; + } + allocator_type::deallocate(p); + } + } + + static inline void deallocate_aligned_n(void *p, ::std::size_t alignment, ::std::size_t n) noexcept + requires(!has_status) + { + if constexpr (::fast_io::details::has_deallocate_aligned_n_impl) + { + allocator_type::deallocate_aligned_n(p, alignment, n); + } + else if constexpr (::fast_io::details::has_deallocate_aligned_impl) + { + allocator_type::deallocate_aligned(p, alignment); + } + else + { + if (p == nullptr) + { + return; + } + if (default_alignment < alignment) + { + auto start{reinterpret_cast(p)[-1]}; + n += static_cast<::std::size_t>(reinterpret_cast(p) - reinterpret_cast(start)); + p = start; + } + if constexpr (::fast_io::details::has_deallocate_impl) + { + allocator_type::deallocate(p); + } + else + { + allocator_type::deallocate_n(p, n); + } + } + } + + // Handle-based allocation functions (for allocators with non-empty handle_type) + static inline constexpr bool has_native_handle_allocate{ + has_status && (::fast_io::details::has_handle_allocate_impl || + ::fast_io::details::has_handle_allocate_aligned_impl || + ::fast_io::details::has_handle_allocate_zero_impl || + ::fast_io::details::has_handle_allocate_aligned_zero_impl || + ::fast_io::details::has_handle_allocate_at_least_impl || + ::fast_io::details::has_handle_allocate_aligned_at_least_impl || + ::fast_io::details::has_handle_allocate_zero_at_least_impl || + ::fast_io::details::has_handle_allocate_aligned_zero_at_least_impl || + ::fast_io::details::has_handle_allocate_conditional_zero_impl || + ::fast_io::details::has_handle_allocate_aligned_conditional_zero_impl || + ::fast_io::details::has_handle_allocate_conditional_zero_at_least_impl || + ::fast_io::details::has_handle_allocate_aligned_conditional_zero_at_least_impl)}; + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_allocate_conditional_zero(handle_type handle, ::std::size_t n, bool zero) noexcept + requires(has_status && has_native_handle_allocate) + { +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif + { + auto p{::operator new(n)}; + if (zero) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + } + return p; + } + else + { + if constexpr (::fast_io::details::has_handle_allocate_conditional_zero_impl) + { + return allocator_type::handle_allocate_conditional_zero(handle, n, zero); + } + else if constexpr (::fast_io::details::has_handle_allocate_conditional_zero_at_least_impl) + { + return allocator_type::handle_allocate_conditional_zero_at_least(handle, n, zero).ptr; + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_conditional_zero_impl) + { + return allocator_type::handle_allocate_aligned_conditional_zero(handle, default_alignment, n, zero); + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_conditional_zero_at_least_impl) + { + return allocator_type::handle_allocate_aligned_conditional_zero_at_least(handle, default_alignment, n, zero).ptr; + } + else if constexpr (::fast_io::details::has_handle_allocate_impl || + ::fast_io::details::has_handle_allocate_at_least_impl || + ::fast_io::details::has_handle_allocate_aligned_impl || + ::fast_io::details::has_handle_allocate_aligned_at_least_impl) + { + if (zero) + { + if constexpr (::fast_io::details::has_handle_allocate_zero_impl) + { + return allocator_type::handle_allocate_zero(handle, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_zero_at_least_impl) + { + return allocator_type::handle_allocate_zero_at_least(handle, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_zero_impl) + { + return allocator_type::handle_allocate_aligned_zero(handle, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_zero_at_least_impl) + { + return allocator_type::handle_allocate_aligned_zero_at_least(handle, default_alignment, n).ptr; + } + else + { + auto p{generic_allocator_adapter::handle_allocate(handle, n)}; + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(p), n); + return p; + } + } + else + { + if constexpr (::fast_io::details::has_handle_allocate_impl) + { + return allocator_type::handle_allocate(handle, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_at_least_impl) + { + return allocator_type::handle_allocate_at_least(handle, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_impl) + { + return allocator_type::handle_allocate_aligned(handle, default_alignment, n); + } + else + { + return allocator_type::handle_allocate_aligned_at_least(handle, default_alignment, n).ptr; + } + } + } + else + { + if constexpr (::fast_io::details::has_handle_allocate_zero_impl) + { + return allocator_type::handle_allocate_zero(handle, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_zero_at_least_impl) + { + return allocator_type::handle_allocate_zero_at_least(handle, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_zero_impl) + { + return allocator_type::handle_allocate_aligned_zero(handle, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_aligned_zero_at_least_impl) + { + return allocator_type::handle_allocate_aligned_zero_at_least(handle, default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + } + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_allocate(handle_type handle, ::std::size_t n) noexcept + requires(has_status && has_native_handle_allocate) + { +#if __cpp_if_consteval >= 202106L + if consteval +#elif __cpp_lib_is_constant_evaluated >= 201811L && __cpp_constexpr_dynamic_alloc >= 201907L + if (__builtin_is_constant_evaluated()) +#else + if (false) +#endif + { + return ::operator new(n); + } + else + { + if constexpr (::fast_io::details::has_handle_allocate_impl) + { + return allocator_type::handle_allocate(handle, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_at_least_impl) + { + return allocator_type::handle_allocate_at_least(handle, n).ptr; + } + else + { + return generic_allocator_adapter::handle_allocate_conditional_zero(handle, n, false); + } + } + } + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_allocate_zero(handle_type handle, ::std::size_t n) noexcept + requires(has_status && has_native_handle_allocate) + { + if constexpr (::fast_io::details::has_handle_allocate_zero_impl) + { + return allocator_type::handle_allocate_zero(handle, n); + } + else if constexpr (::fast_io::details::has_handle_allocate_zero_at_least_impl) + { + return allocator_type::handle_allocate_zero_at_least(handle, n).ptr; + } + else + { + return generic_allocator_adapter::handle_allocate_conditional_zero(handle, n, true); + } + } + + static inline constexpr bool has_handle_reallocate{ + has_status && (::fast_io::details::has_handle_reallocate_impl || + ::fast_io::details::has_handle_reallocate_aligned_impl || + ::fast_io::details::has_handle_reallocate_zero_impl || + ::fast_io::details::has_handle_reallocate_aligned_zero_impl || + ::fast_io::details::has_handle_reallocate_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_at_least_impl || + ::fast_io::details::has_handle_reallocate_zero_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_zero_at_least_impl || + ::fast_io::details::has_handle_reallocate_conditional_zero_impl || + ::fast_io::details::has_handle_reallocate_aligned_conditional_zero_impl || + ::fast_io::details::has_handle_reallocate_conditional_zero_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_conditional_zero_at_least_impl)}; + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_reallocate_conditional_zero(handle_type handle, void *p, ::std::size_t n, bool zero) noexcept + requires(has_status && has_handle_reallocate) + { + if constexpr (::fast_io::details::has_handle_reallocate_conditional_zero_impl) + { + return allocator_type::handle_reallocate_conditional_zero(handle, p, n, zero); + } + else if constexpr (::fast_io::details::has_handle_reallocate_conditional_zero_at_least_impl) + { + return allocator_type::handle_reallocate_conditional_zero_at_least(handle, p, n, zero).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_conditional_zero_impl) + { + return allocator_type::handle_reallocate_aligned_conditional_zero(handle, p, default_alignment, n, zero); + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_conditional_zero_at_least_impl) + { + return allocator_type::handle_reallocate_aligned_conditional_zero_at_least(handle, p, default_alignment, n, zero).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_impl || + ::fast_io::details::has_handle_reallocate_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_impl || + ::fast_io::details::has_handle_reallocate_aligned_at_least_impl) + { + if (zero) + { + if constexpr (::fast_io::details::has_handle_reallocate_zero_impl) + { + return allocator_type::handle_reallocate_zero(handle, p, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_zero_at_least_impl) + { + return allocator_type::handle_reallocate_zero_at_least(handle, p, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_impl) + { + return allocator_type::handle_reallocate_aligned_zero(handle, p, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::handle_reallocate_aligned_zero_at_least(handle, p, default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); + } + } + else + { + if constexpr (::fast_io::details::has_handle_reallocate_impl) + { + return allocator_type::handle_reallocate(handle, p, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_at_least_impl) + { + return allocator_type::handle_reallocate_at_least(handle, p, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_impl) + { + return allocator_type::handle_reallocate_aligned(handle, p, default_alignment, n); + } + else + { + return allocator_type::handle_reallocate_aligned_at_least(handle, p, default_alignment, n).ptr; + } + } + } + else + { + if constexpr (::fast_io::details::has_handle_reallocate_zero_impl) { - ::std::size_t const to_zero_bytes{static_cast<::std::size_t>(n - oldn)}; - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, to_zero_bytes); + return allocator_type::handle_reallocate_zero(handle, p, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_zero_at_least_impl) + { + return allocator_type::handle_reallocate_zero_at_least(handle, p, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_impl) + { + return allocator_type::handle_reallocate_aligned_zero(handle, p, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_at_least_impl) + { + return allocator_type::handle_reallocate_aligned_zero_at_least(handle, p, default_alignment, n).ptr; + } + else + { + ::fast_io::fast_terminate(); } - return newres; } } - static inline constexpr bool has_native_reallocate_aligned_at_least = (has_reallocate_aligned && - (::fast_io::details::has_reallocate_aligned_zero_at_least_impl || - ::fast_io::details::has_reallocate_aligned_at_least_impl)); - - static inline ::fast_io::allocation_least_result - reallocate_aligned_at_least(void *p, ::std::size_t alignment, ::std::size_t n) noexcept - requires(!has_status && has_reallocate_aligned_zero) +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_reallocate(handle_type handle, void *p, ::std::size_t n) noexcept + requires(has_status && has_handle_reallocate) { - if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) - { - return allocator_type::reallocate_aligned_at_least(p, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + if constexpr (::fast_io::details::has_handle_reallocate_impl) { - return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n); + return allocator_type::handle_reallocate(handle, p, n); } - else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + else if constexpr (::fast_io::details::has_handle_reallocate_at_least_impl) { - return {allocator_type::reallocate_aligned(p, alignment, n), n}; + return allocator_type::handle_reallocate_at_least(handle, p, n).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + else { - return {allocator_type::reallocate_aligned_zero(p, alignment, n), n}; + return generic_allocator_adapter::handle_reallocate_conditional_zero(handle, p, n, false); } } - static inline constexpr bool has_native_reallocate_aligned_zero_at_least = (has_reallocate_aligned_zero && - ::fast_io::details::has_reallocate_aligned_zero_at_least_impl); - - static inline ::fast_io::allocation_least_result - reallocate_aligned_zero_at_least(void *p, ::std::size_t alignment, ::std::size_t n) noexcept - requires(!has_status && has_reallocate_aligned_zero) +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_reallocate_zero(handle_type handle, void *p, ::std::size_t n) noexcept + requires(has_status && has_handle_reallocate) { - if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + if constexpr (::fast_io::details::has_handle_reallocate_zero_impl) { - return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n); + return allocator_type::handle_reallocate_zero(handle, p, n); } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + else if constexpr (::fast_io::details::has_handle_reallocate_zero_at_least_impl) { - return {allocator_type::reallocate_aligned_zero(p, alignment, n), n}; + return allocator_type::handle_reallocate_zero_at_least(handle, p, n).ptr; + } + else + { + return generic_allocator_adapter::handle_reallocate_conditional_zero(handle, p, n, true); } } - static inline constexpr bool has_native_reallocate_aligned_n_at_least = (has_reallocate_aligned && - (::fast_io::details::has_reallocate_aligned_n_at_least_impl || - ::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl)); - static inline ::fast_io::allocation_least_result - reallocate_aligned_n_at_least(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept - requires(!has_status) + static inline constexpr bool has_handle_reallocate_n{ + has_status && (::fast_io::details::has_handle_reallocate_n_impl || + ::fast_io::details::has_handle_reallocate_aligned_n_impl || + ::fast_io::details::has_handle_reallocate_zero_n_impl || + ::fast_io::details::has_handle_reallocate_aligned_zero_n_impl || + ::fast_io::details::has_handle_reallocate_n_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_n_at_least_impl || + ::fast_io::details::has_handle_reallocate_zero_n_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_zero_n_at_least_impl || + ::fast_io::details::has_handle_reallocate_n_conditional_zero_impl || + ::fast_io::details::has_handle_reallocate_aligned_n_conditional_zero_impl || + ::fast_io::details::has_handle_reallocate_n_conditional_zero_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_n_conditional_zero_at_least_impl)}; + +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_reallocate_n_conditional_zero(handle_type handle, void *p, ::std::size_t oldn, ::std::size_t n, bool zero) noexcept + requires(has_status && has_handle_reallocate_n) { - if constexpr (::fast_io::details::has_reallocate_aligned_n_at_least_impl) - { - return allocator_type::reallocate_aligned_n_at_least(p, oldn, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_at_least_impl) - { - return allocator_type::reallocate_aligned_at_least(p, alignment, n); - } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + if (p != nullptr && oldn == n) { - return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n); + return p; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_at_least_impl) + if constexpr (::fast_io::details::has_handle_reallocate_n_conditional_zero_impl) { - return allocator_type::reallocate_aligned_zero_at_least(p, alignment, n); + return allocator_type::handle_reallocate_n_conditional_zero(handle, p, oldn, n, zero); } - else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + else if constexpr (::fast_io::details::has_handle_reallocate_n_conditional_zero_at_least_impl) { - return {allocator_type::reallocate_aligned_n(p, oldn, alignment, n), n}; + return allocator_type::handle_reallocate_n_conditional_zero_at_least(handle, p, oldn, n, zero).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_impl) + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_n_conditional_zero_impl) { - return {allocator_type::reallocate_aligned(p, alignment, n), n}; + return allocator_type::handle_reallocate_aligned_n_conditional_zero(handle, p, oldn, default_alignment, n, zero); } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_n_conditional_zero_at_least_impl) { - return {allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n), n}; + return allocator_type::handle_reallocate_aligned_n_conditional_zero_at_least(handle, p, oldn, default_alignment, n, zero).ptr; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_impl) + else if constexpr (::fast_io::details::has_handle_reallocate_n_impl || + ::fast_io::details::has_handle_reallocate_n_at_least_impl || + ::fast_io::details::has_handle_reallocate_aligned_n_impl || + ::fast_io::details::has_handle_reallocate_aligned_n_at_least_impl) { - return {allocator_type::reallocate_aligned_zero(p, alignment, n), n}; + if (zero) + { + if constexpr (::fast_io::details::has_handle_reallocate_zero_n_impl) + { + return allocator_type::handle_reallocate_zero_n(handle, p, oldn, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_zero_n_at_least_impl) + { + return allocator_type::handle_reallocate_zero_n_at_least(handle, p, oldn, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_n_impl) + { + return allocator_type::handle_reallocate_aligned_zero_n(handle, p, oldn, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::handle_reallocate_aligned_zero_n_at_least(handle, p, oldn, default_alignment, n).ptr; + } + else + { + auto newptr{generic_allocator_adapter::handle_reallocate_n(handle, p, oldn, n)}; + if (oldn < n) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, n - oldn); + } + return newptr; + } + } + else + { + if constexpr (::fast_io::details::has_handle_reallocate_n_impl) + { + return allocator_type::handle_reallocate_n(handle, p, oldn, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_n_at_least_impl) + { + return allocator_type::handle_reallocate_n_at_least(handle, p, oldn, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_n_impl) + { + return allocator_type::handle_reallocate_aligned_n(handle, p, oldn, default_alignment, n); + } + else + { + return allocator_type::handle_reallocate_aligned_n_at_least(handle, p, oldn, default_alignment, n).ptr; + } + } } else { - if constexpr ( - ::fast_io::details::has_reallocate_at_least_impl || - ::fast_io::details::has_reallocate_zero_at_least_impl || - ::fast_io::details::has_reallocate_n_at_least_impl || - ::fast_io::details::has_reallocate_zero_n_at_least_impl) + if constexpr (::fast_io::details::has_handle_reallocate_zero_n_impl) { - if (alignment <= default_alignment) - { - return generic_allocator_adapter::reallocate_n_at_least(p, oldn, n); - } + return allocator_type::handle_reallocate_zero_n(handle, p, oldn, n); } - auto newres{::fast_io::details::allocator_pointer_aligned_at_least_impl(alignment, n)}; - auto newptr{newres.ptr}; - if (p != nullptr) + else if constexpr (::fast_io::details::has_handle_reallocate_zero_n_at_least_impl) { - if (n) + return allocator_type::handle_reallocate_zero_n_at_least(handle, p, oldn, n).ptr; + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_n_impl) + { + return allocator_type::handle_reallocate_aligned_zero_n(handle, p, oldn, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_aligned_zero_n_at_least_impl) + { + return allocator_type::handle_reallocate_aligned_zero_n_at_least(handle, p, oldn, default_alignment, n).ptr; + } + else + { + auto newptr{generic_allocator_adapter::handle_allocate(handle, n)}; + if (p != nullptr) { - if (oldn < n) + if (n) { - n = oldn; + ::std::size_t copyn{oldn < n ? oldn : n}; + ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), copyn, reinterpret_cast<::std::byte *>(newptr)); } - ::fast_io::freestanding::nonoverlapped_bytes_copy_n(reinterpret_cast<::std::byte const *>(p), n, reinterpret_cast<::std::byte *>(newptr)); + generic_allocator_adapter::handle_deallocate_n(handle, p, oldn); } - generic_allocator_adapter::deallocate_aligned_n(p, alignment, oldn); + if (zero && oldn < n) + { + ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, n - oldn); + } + return newptr; } - return newres; } } - static inline constexpr bool has_native_reallocate_aligned_zero_n_at_least = (has_reallocate_aligned_zero && ::fast_io::details::has_reallocate_aligned_zero_at_least_impl); +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_reallocate_n(handle_type handle, void *p, ::std::size_t oldn, ::std::size_t n) noexcept + requires(has_status && has_handle_reallocate_n) + { + if (p != nullptr && oldn == n) + { + return p; + } + if constexpr (::fast_io::details::has_handle_reallocate_n_impl) + { + return allocator_type::handle_reallocate_n(handle, p, oldn, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_n_at_least_impl) + { + return allocator_type::handle_reallocate_n_at_least(handle, p, oldn, n).ptr; + } + else + { + return generic_allocator_adapter::handle_reallocate_n_conditional_zero(handle, p, oldn, n, false); + } + } - static inline ::fast_io::allocation_least_result reallocate_aligned_zero_n_at_least(void *p, ::std::size_t oldn, ::std::size_t alignment, ::std::size_t n) noexcept - requires(!has_status) +#if __has_cpp_attribute(__gnu__::__returns_nonnull__) + [[__gnu__::__returns_nonnull__]] +#endif + static inline void *handle_reallocate_zero_n(handle_type handle, void *p, ::std::size_t oldn, ::std::size_t n) noexcept + requires(has_status && has_handle_reallocate_n) { - if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_at_least_impl) + if (p != nullptr && oldn == n) { - return allocator_type::reallocate_aligned_zero_n_at_least(p, oldn, alignment, n); + return p; } - else if constexpr (::fast_io::details::has_reallocate_aligned_zero_n_impl) + if constexpr (::fast_io::details::has_handle_reallocate_zero_n_impl) { - return {allocator_type::reallocate_aligned_zero_n(p, oldn, alignment, n), n}; + return allocator_type::handle_reallocate_zero_n(handle, p, oldn, n); + } + else if constexpr (::fast_io::details::has_handle_reallocate_zero_n_at_least_impl) + { + return allocator_type::handle_reallocate_zero_n_at_least(handle, p, oldn, n).ptr; } else { - auto newres = generic_allocator_adapter::reallocate_aligned_n_at_least(p, oldn, alignment, n); - auto newptr{newres.ptr}; - n = newres.count; - if (oldn < n) - { - ::std::size_t const to_zero_bytes{static_cast<::std::size_t>(n - oldn)}; - ::fast_io::freestanding::bytes_clear_n(reinterpret_cast<::std::byte *>(newptr) + oldn, to_zero_bytes); - } - return newres; + return generic_allocator_adapter::handle_reallocate_n_conditional_zero(handle, p, oldn, n, true); } } - static inline constexpr bool has_deallocate_aligned = (::fast_io::details::has_deallocate_aligned_impl || - ::fast_io::details::has_deallocate_impl); - static inline void deallocate_aligned(void *p, ::std::size_t alignment) noexcept - requires(!has_status && has_deallocate_aligned) + static inline constexpr bool has_handle_deallocate{ + has_status && (::fast_io::details::has_handle_deallocate_impl || + ::fast_io::details::has_handle_deallocate_aligned_impl || + ::fast_io::details::has_handle_deallocate_n_impl || + ::fast_io::details::has_handle_deallocate_aligned_n_impl)}; + + static inline void handle_deallocate(handle_type handle, void *p) noexcept + requires(has_status && has_handle_deallocate) { - if constexpr (::fast_io::details::has_deallocate_aligned_impl) + if constexpr (::fast_io::details::has_handle_deallocate_impl) { - allocator_type::deallocate_aligned(p, alignment); + allocator_type::handle_deallocate(handle, p); + } + else if constexpr (::fast_io::details::has_handle_deallocate_aligned_impl) + { + allocator_type::handle_deallocate_aligned(handle, p, default_alignment); + } + else if constexpr (::fast_io::details::has_handle_deallocate_n_impl) + { + allocator_type::handle_deallocate_n(handle, p, 0); } else { - if (p == nullptr) - { - return; - } - if (default_alignment < alignment) - { - p = reinterpret_cast(p)[-1]; - } - allocator_type::deallocate(p); + allocator_type::handle_deallocate_aligned_n(handle, p, default_alignment, 0); } } - static inline void deallocate_aligned_n(void *p, ::std::size_t alignment, ::std::size_t n) noexcept - requires(!has_status) + static inline void handle_deallocate_n(handle_type handle, void *p, ::std::size_t n) noexcept + requires(has_status && has_handle_deallocate) { - if constexpr (::fast_io::details::has_deallocate_aligned_n_impl) + if constexpr (::fast_io::details::has_handle_deallocate_n_impl) { - allocator_type::deallocate_aligned_n(p, alignment, n); + allocator_type::handle_deallocate_n(handle, p, n); } - else if constexpr (::fast_io::details::has_deallocate_aligned_impl) + else if constexpr (::fast_io::details::has_handle_deallocate_aligned_n_impl) { - allocator_type::deallocate_aligned(p, alignment); + allocator_type::handle_deallocate_aligned_n(handle, p, default_alignment, n); + } + else if constexpr (::fast_io::details::has_handle_deallocate_impl) + { + allocator_type::handle_deallocate(handle, p); } else { - if (p == nullptr) - { - return; - } - if (default_alignment < alignment) - { - auto start{reinterpret_cast(p)[-1]}; - n += static_cast<::std::size_t>(reinterpret_cast(p) - reinterpret_cast(start)); - p = start; - } - if constexpr (::fast_io::details::has_deallocate_impl) - { - allocator_type::deallocate(p); - } - else - { - allocator_type::deallocate_n(p, n); - } + allocator_type::handle_deallocate_aligned(handle, p, default_alignment); } } }; @@ -1835,8 +3117,8 @@ class typed_generic_allocator_adapter namespace details { -template -inline constexpr void *allocator_pointer_aligned_impl(::std::size_t alignment, ::std::size_t n) noexcept +template +inline constexpr void *allocator_pointer_aligned_impl(::std::size_t alignment, ::std::size_t n, bool zero) noexcept { static_assert(::fast_io::generic_allocator_adapter::has_native_allocate); @@ -1846,15 +3128,7 @@ inline constexpr void *allocator_pointer_aligned_impl(::std::size_t alignment, : { n = ::fast_io::details::allocator_compute_aligned_total_size_impl(alignment, n); } - void *p; - if constexpr (zero) - { - p = ::fast_io::generic_allocator_adapter::allocate_zero(n); - } - else - { - p = ::fast_io::generic_allocator_adapter::allocate(n); - } + void *p = ::fast_io::generic_allocator_adapter::allocate_conditional_zero(n, zero); if (alignedadjustment) { p = ::fast_io::details::allocator_adjust_ptr_to_aligned_impl(p, alignment); @@ -1862,8 +3136,8 @@ inline constexpr void *allocator_pointer_aligned_impl(::std::size_t alignment, : return p; } -template -inline constexpr ::fast_io::allocation_least_result allocator_pointer_aligned_at_least_impl(::std::size_t alignment, ::std::size_t n) noexcept +template +inline constexpr ::fast_io::allocation_least_result allocator_pointer_aligned_at_least_impl(::std::size_t alignment, ::std::size_t n, bool zero) noexcept { static_assert(::fast_io::generic_allocator_adapter::has_native_allocate); @@ -1873,15 +3147,7 @@ inline constexpr ::fast_io::allocation_least_result allocator_pointer_aligned_at { n = ::fast_io::details::allocator_compute_aligned_total_size_impl(alignment, n); } - ::fast_io::allocation_least_result res; - if constexpr (zero) - { - res = ::fast_io::generic_allocator_adapter::allocate_zero_at_least(n); - } - else - { - res = ::fast_io::generic_allocator_adapter::allocate_at_least(n); - } + ::fast_io::allocation_least_result res = ::fast_io::generic_allocator_adapter::allocate_conditional_zero_at_least(n, zero); if (alignedadjustment) { auto resptr{res.ptr}; diff --git a/include/fast_io_core_impl/allocation/has_methods_detect.h b/include/fast_io_core_impl/allocation/has_methods_detect.h index 9a97cd770..747df39df 100644 --- a/include/fast_io_core_impl/allocation/has_methods_detect.h +++ b/include/fast_io_core_impl/allocation/has_methods_detect.h @@ -20,6 +20,16 @@ concept has_allocate_aligned_zero_impl = requires(::std::size_t n) { { alloc::allocate_aligned_zero(n, n) } -> ::std::same_as; }; +template +concept has_allocate_conditional_zero_impl = requires(::std::size_t n, bool zero) { + { alloc::allocate_conditional_zero(n, zero) } -> ::std::same_as; +}; + +template +concept has_allocate_aligned_conditional_zero_impl = requires(::std::size_t n, bool zero) { + { alloc::allocate_aligned_conditional_zero(n, n, zero) } -> ::std::same_as; +}; + template concept has_reallocate_impl = requires(void *p, ::std::size_t n) { { alloc::reallocate(p, n) } -> ::std::same_as; @@ -40,6 +50,16 @@ concept has_reallocate_aligned_zero_impl = requires(void *p, ::std::size_t n) { { alloc::reallocate_aligned_zero(p, n, n) } -> ::std::same_as; }; +template +concept has_reallocate_conditional_zero_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_conditional_zero(p, n, zero) } -> ::std::same_as; +}; + +template +concept has_reallocate_aligned_conditional_zero_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_aligned_conditional_zero(p, n, n, zero) } -> ::std::same_as; +}; + template concept has_reallocate_n_impl = requires(void *p, ::std::size_t n) { { alloc::reallocate_n(p, n, n) } -> ::std::same_as; @@ -60,6 +80,16 @@ concept has_reallocate_aligned_zero_n_impl = requires(void *p, ::std::size_t n) { alloc::reallocate_aligned_zero_n(p, n, n, n) } -> ::std::same_as; }; +template +concept has_reallocate_n_conditional_zero_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_n_conditional_zero(p, n, n, zero) } -> ::std::same_as; +}; + +template +concept has_reallocate_aligned_n_conditional_zero_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_aligned_n_conditional_zero(p, n, n, n, zero) } -> ::std::same_as; +}; + template concept has_deallocate_impl = requires(void *p) { { alloc::deallocate(p) } -> ::std::same_as; @@ -100,6 +130,16 @@ concept has_allocate_aligned_zero_at_least_impl = requires(::std::size_t n) { { alloc::allocate_aligned_zero_at_least(n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; }; +template +concept has_allocate_conditional_zero_at_least_impl = requires(::std::size_t n, bool zero) { + { alloc::allocate_conditional_zero_at_least(n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + +template +concept has_allocate_aligned_conditional_zero_at_least_impl = requires(::std::size_t n, bool zero) { + { alloc::allocate_aligned_conditional_zero_at_least(n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + template concept has_reallocate_at_least_impl = requires(void *p, ::std::size_t n) { { alloc::reallocate_at_least(p, n) } -> ::std::same_as<::fast_io::allocation_least_result>; @@ -120,6 +160,16 @@ concept has_reallocate_aligned_zero_at_least_impl = requires(void *p, ::std::siz { alloc::reallocate_aligned_zero_at_least(p, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; }; +template +concept has_reallocate_conditional_zero_at_least_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_conditional_zero_at_least(p, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + +template +concept has_reallocate_aligned_conditional_zero_at_least_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_aligned_conditional_zero_at_least(p, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + template concept has_reallocate_n_at_least_impl = requires(void *p, ::std::size_t n) { { alloc::reallocate_n_at_least(p, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; @@ -140,6 +190,16 @@ concept has_reallocate_aligned_zero_n_at_least_impl = requires(void *p, ::std::s { alloc::reallocate_aligned_zero_n_at_least(p, n, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; }; +template +concept has_reallocate_n_conditional_zero_at_least_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_n_conditional_zero_at_least(p, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + +template +concept has_reallocate_aligned_n_conditional_zero_at_least_impl = requires(void *p, ::std::size_t n, bool zero) { + { alloc::reallocate_aligned_n_conditional_zero_at_least(p, n, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + template concept has_non_empty_handle_type = requires { typename alloc::handle_type; @@ -166,6 +226,16 @@ concept has_handle_allocate_aligned_zero_impl = requires(typename alloc::handle_ { alloc::handle_allocate_aligned_zero(handle, n, n) } -> ::std::same_as; }; +template +concept has_handle_allocate_conditional_zero_impl = requires(typename alloc::handle_type handle, ::std::size_t n, bool zero) { + { alloc::handle_allocate_conditional_zero(handle, n, zero) } -> ::std::same_as; +}; + +template +concept has_handle_allocate_aligned_conditional_zero_impl = requires(typename alloc::handle_type handle, ::std::size_t n, bool zero) { + { alloc::handle_allocate_aligned_conditional_zero(handle, n, n, zero) } -> ::std::same_as; +}; + template concept has_handle_reallocate_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n) { { alloc::handle_reallocate(handle, p, n) } -> ::std::same_as; @@ -186,6 +256,16 @@ concept has_handle_reallocate_aligned_zero_impl = requires(typename alloc::handl { alloc::handle_reallocate_aligned_zero(handle, p, n, n) } -> ::std::same_as; }; +template +concept has_handle_reallocate_conditional_zero_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_conditional_zero(handle, p, n, zero) } -> ::std::same_as; +}; + +template +concept has_handle_reallocate_aligned_conditional_zero_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_aligned_conditional_zero(handle, p, n, n, zero) } -> ::std::same_as; +}; + template concept has_handle_reallocate_n_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n) { { alloc::handle_reallocate_n(handle, p, n, n) } -> ::std::same_as; @@ -206,6 +286,16 @@ concept has_handle_reallocate_aligned_zero_n_impl = requires(typename alloc::han { alloc::handle_reallocate_aligned_zero_n(handle, p, n, n, n) } -> ::std::same_as; }; +template +concept has_handle_reallocate_n_conditional_zero_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_n_conditional_zero(handle, p, n, n, zero) } -> ::std::same_as; +}; + +template +concept has_handle_reallocate_aligned_n_conditional_zero_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_aligned_n_conditional_zero(handle, p, n, n, n, zero) } -> ::std::same_as; +}; + template concept has_handle_allocate_at_least_impl = requires(typename alloc::handle_type handle, ::std::size_t n) { @@ -227,6 +317,16 @@ concept has_handle_allocate_aligned_zero_at_least_impl = requires(typename alloc { alloc::handle_allocate_aligned_zero_at_least(handle, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; }; +template +concept has_handle_allocate_conditional_zero_at_least_impl = requires(typename alloc::handle_type handle, ::std::size_t n, bool zero) { + { alloc::handle_allocate_conditional_zero_at_least(handle, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + +template +concept has_handle_allocate_aligned_conditional_zero_at_least_impl = requires(typename alloc::handle_type handle, ::std::size_t n, bool zero) { + { alloc::handle_allocate_aligned_conditional_zero_at_least(handle, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + template concept has_handle_reallocate_at_least_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n) { { alloc::handle_reallocate_at_least(handle, p, n) } -> ::std::same_as<::fast_io::allocation_least_result>; @@ -247,6 +347,16 @@ concept has_handle_reallocate_aligned_zero_at_least_impl = requires(typename all { alloc::handle_reallocate_aligned_zero_at_least(handle, p, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; }; +template +concept has_handle_reallocate_conditional_zero_at_least_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_conditional_zero_at_least(handle, p, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + +template +concept has_handle_reallocate_aligned_conditional_zero_at_least_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_aligned_conditional_zero_at_least(handle, p, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + template concept has_handle_reallocate_n_at_least_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n) { { alloc::handle_reallocate_n_at_least(handle, p, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; @@ -267,6 +377,16 @@ concept has_handle_reallocate_aligned_zero_n_at_least_impl = requires(typename a { alloc::handle_reallocate_aligned_zero_n_at_least(handle, p, n, n, n) } -> ::std::same_as<::fast_io::allocation_least_result>; }; +template +concept has_handle_reallocate_n_conditional_zero_at_least_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_n_conditional_zero_at_least(handle, p, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + +template +concept has_handle_reallocate_aligned_n_conditional_zero_at_least_impl = requires(typename alloc::handle_type handle, void *p, ::std::size_t n, bool zero) { + { alloc::handle_reallocate_aligned_n_conditional_zero_at_least(handle, p, n, n, n, zero) } -> ::std::same_as<::fast_io::allocation_least_result>; +}; + template concept has_handle_deallocate_impl = requires(typename alloc::handle_type handle, void *p) { diff --git a/include/fast_io_core_impl/allocation/nt_rtlheapalloc.h b/include/fast_io_core_impl/allocation/nt_rtlheapalloc.h index 80ca4a833..05c242e48 100644 --- a/include/fast_io_core_impl/allocation/nt_rtlheapalloc.h +++ b/include/fast_io_core_impl/allocation/nt_rtlheapalloc.h @@ -90,16 +90,9 @@ class nt_rtlallocateheap_allocator #if __has_cpp_attribute(__gnu__::__malloc__) [[__gnu__::__malloc__]] #endif - static inline void *allocate(::std::size_t n) noexcept + static inline void *allocate_conditional_zero(::std::size_t n, bool zero) noexcept { - return ::fast_io::details::nt_rtlallocate_heap_common_impl(n, 0u); - } -#if __has_cpp_attribute(__gnu__::__malloc__) - [[__gnu__::__malloc__]] -#endif - static inline void *allocate_zero(::std::size_t n) noexcept - { - return ::fast_io::details::nt_rtlallocate_heap_common_impl(n, 0x00000008u); + return ::fast_io::details::nt_rtlallocate_heap_common_impl(n, zero ? 0x00000008u : 0u); } static inline void *reallocate(void *addr, ::std::size_t n) noexcept { diff --git a/include/fast_io_core_impl/allocation/win32_heapalloc.h b/include/fast_io_core_impl/allocation/win32_heapalloc.h index ea8fa37b0..303a818a1 100644 --- a/include/fast_io_core_impl/allocation/win32_heapalloc.h +++ b/include/fast_io_core_impl/allocation/win32_heapalloc.h @@ -139,21 +139,6 @@ inline ::fast_io::allocation_least_result win32_heaprealloc_least_common_impl(vo class win32_heapalloc_allocator { public: -#if __has_cpp_attribute(__gnu__::__malloc__) - [[__gnu__::__malloc__]] -#endif - static inline void *allocate(::std::size_t n) noexcept - { - return ::fast_io::details::win32_heapalloc_common_impl(n, 0u); - } - -#if __has_cpp_attribute(__gnu__::__malloc__) - [[__gnu__::__malloc__]] -#endif - static inline void *allocate_zero(::std::size_t n) noexcept - { - return ::fast_io::details::win32_heapalloc_common_impl(n, 0x00000008u); - } #if __has_cpp_attribute(__gnu__::__malloc__) [[__gnu__::__malloc__]] #endif diff --git a/include/fast_io_core_impl/operations/readimpl/decay.h b/include/fast_io_core_impl/operations/readimpl/decay.h index 23eca1794..014f90877 100644 --- a/include/fast_io_core_impl/operations/readimpl/decay.h +++ b/include/fast_io_core_impl/operations/readimpl/decay.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once namespace fast_io::operations::decay { @@ -82,7 +82,7 @@ inline constexpr ::std::byte *pread_some_bytes_decay(instmtype insm, ::std::byte template inline constexpr void pread_all_bytes_decay(instmtype insm, ::std::byte *first, ::std::byte *last, - ::fast_io::intfpos_t off) + ::fast_io::intfpos_t off) { ::fast_io::details::pread_all_bytes_impl(insm, first, last, off); } @@ -97,7 +97,7 @@ scatter_pread_some_decay(instmtype insm, basic_io_scatter_t inline constexpr io_scatter_status_t scatter_pread_some_bytes_decay(instmtype insm, io_scatter_t const *pscatters, - ::std::size_t n, ::fast_io::intfpos_t off) + ::std::size_t n, ::fast_io::intfpos_t off) { return ::fast_io::details::scatter_pread_some_bytes_impl(insm, pscatters, n, off); } @@ -112,9 +112,9 @@ inline constexpr void scatter_pread_all_decay(instmtype insm, template inline constexpr void scatter_pread_all_bytes_decay(instmtype insm, io_scatter_t const *pscatters, ::std::size_t n, - ::fast_io::intfpos_t off) + ::fast_io::intfpos_t off) { ::fast_io::details::scatter_pread_all_bytes_impl(insm, pscatters, n, off); } -} // namespace fast_io::operations::decay +} // namespace fast_io::operations::decay \ No newline at end of file diff --git a/include/fast_io_core_impl/operations/writeimpl/decay.h b/include/fast_io_core_impl/operations/writeimpl/decay.h index 9d14e3336..6c8ed5c13 100644 --- a/include/fast_io_core_impl/operations/writeimpl/decay.h +++ b/include/fast_io_core_impl/operations/writeimpl/decay.h @@ -1,4 +1,4 @@ -#pragma once +#pragma once namespace fast_io::operations::decay { @@ -41,7 +41,7 @@ scatter_write_some_decay(outstmtype outsm, basic_io_scatter_t inline constexpr io_scatter_status_t scatter_write_some_bytes_decay(outstmtype outsm, io_scatter_t const *pscatters, - ::std::size_t n) + ::std::size_t n) { return ::fast_io::details::scatter_write_some_bytes_impl(outsm, pscatters, n); } @@ -99,7 +99,7 @@ scatter_pwrite_some_decay(outstmtype outsm, basic_io_scatter_t inline constexpr io_scatter_status_t scatter_pwrite_some_bytes_decay(outstmtype outsm, io_scatter_t const *pscatters, - ::std::size_t n, ::fast_io::intfpos_t off) + ::std::size_t n, ::fast_io::intfpos_t off) { return ::fast_io::details::scatter_pwrite_some_bytes_impl(outsm, pscatters, n, off); } @@ -130,4 +130,4 @@ inline constexpr void char_put_decay(outstmtype outstm, typename outstmtype::out ::fast_io::details::char_put_impl(outstm, ch); } -} // namespace fast_io::operations::decay +} // namespace fast_io::operations::decay \ No newline at end of file diff --git a/include/fast_io_dsal/deque.h b/include/fast_io_dsal/deque.h index e3af9db9e..928c313dd 100644 --- a/include/fast_io_dsal/deque.h +++ b/include/fast_io_dsal/deque.h @@ -24,6 +24,7 @@ #include "impl/freestanding.h" #include "impl/common.h" +#include "span.h" #include "impl/deque.h" #if ((__STDC_HOSTED__ == 1 && (!defined(_GLIBCXX_HOSTED) || _GLIBCXX_HOSTED == 1) && \ diff --git a/include/fast_io_dsal/impl/cstring_view.h b/include/fast_io_dsal/impl/cstring_view.h index 9b1e446c3..046b96cac 100644 --- a/include/fast_io_dsal/impl/cstring_view.h +++ b/include/fast_io_dsal/impl/cstring_view.h @@ -60,9 +60,19 @@ class basic_cstring_view : private ::fast_io::containers::basic_string_view - requires(::std::constructible_from) - inline explicit constexpr basic_cstring_view(::fast_io::containers::null_terminated_t, ::fast_io::freestanding::from_range_t, rg const &&r) noexcept - : string_view_type(::std::forward(r)) + requires(::std::same_as<::std::ranges::range_value_t, char_type> && !::std::is_array_v<::std::remove_cvref_t> && + !::std::is_rvalue_reference_v) + inline explicit constexpr basic_cstring_view(::fast_io::containers::null_terminated_t, ::fast_io::freestanding::from_range_t, rg &&r) noexcept + : string_view_type(::fast_io::freestanding::from_range, ::std::forward(r)) + { + } + template <::std::ranges::contiguous_range rg> + requires(requires(rg r) { + { r.c_str() } -> ::std::convertible_to; + } && ::std::same_as<::std::ranges::range_value_t, char_type> && !::std::is_array_v<::std::remove_cvref_t> && + !::std::is_rvalue_reference_v) + inline explicit constexpr basic_cstring_view(::fast_io::freestanding::from_range_t, rg &&r) noexcept + : string_view_type(::fast_io::freestanding::from_range, ::std::forward(r)) { } diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index ec46a2ce8..c1f8607a6 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -470,21 +470,27 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; -#if (defined(__GNUC__) || defined(__clang__)) ::std::size_t new_blocks_count_least_p1; - if (__builtin_add_overflow(new_blocks_count_least, 1zu, __builtin_addressof(new_blocks_count_least_p1))) [[unlikely]] +#if (defined(__GNUC__) || defined(__clang__)) + if constexpr (true) { - ::fast_io::fast_terminate(); + if (__builtin_add_overflow(new_blocks_count_least, 1zu, __builtin_addressof(new_blocks_count_least_p1))) [[unlikely]] + { + ::fast_io::fast_terminate(); + } } -#else - constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; - ::std::size_t new_blocks_count_least_p1{new_blocks_count_least}; - if (mx == new_blocks_count_least) + else +#endif { - ::fast_io::fast_terminate(); + constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; + new_blocks_count_least_p1 = new_blocks_count_least; + if (mx == new_blocks_count_least) + { + ::fast_io::fast_terminate(); + } + ++new_blocks_count_least_p1; } - ++new_blocks_count_least_p1; -#endif + auto [new_start_ptr, new_blocks_count] = block_typed_allocator::allocate_at_least(new_blocks_count_least_p1); auto const old_reserved_blocks_count{ @@ -538,107 +544,69 @@ inline constexpr void deque_grow_to_new_blocks_count_impl(dequecontroltype &cont } template -inline constexpr void deque_rebalance_or_grow_2x_after_blocks_impl(dequecontroltype &controller) noexcept +inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltype &controller, ::std::size_t initial_allocated_block_counts, ::std::size_t align, ::std::size_t bytes) noexcept { - 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 + ::std::size_t initial_allocated_block_counts_with_sentinel; +#if (defined(__GNUC__) || defined(__clang__)) + if constexpr (true) { - 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) + if (__builtin_add_overflow(initial_allocated_block_counts, 1u, + __builtin_addressof(initial_allocated_block_counts_with_sentinel))) { ::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 +#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) + constexpr ::std::size_t maxval{::std::numeric_limits<::std::size_t>::max()}; + if (initial_allocated_block_counts == maxval) [[unlikely]] { - ::std::ptrdiff_t diff{reserved_pivot - used_blocks_pivot}; - auto rotate_pivot{diff < 0 ? start_reserved_ptr : after_reserved_ptr}; - rotate_pivot -= diff; - ::std::rotate(start_reserved_ptr, rotate_pivot, after_reserved_ptr); - controller.front_block.controller_ptr += diff; - controller.back_block.controller_ptr += diff; - } - - auto slots_pivot{controller.controller_block.controller_start_ptr + half_slots_count}; - if (slots_pivot != reserved_pivot) - { - ::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; + ::fast_io::fast_terminate(); } + initial_allocated_block_counts_with_sentinel = initial_allocated_block_counts + 1u; } -} - -template -inline constexpr void deque_allocate_on_empty_common_with_n_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes, - ::std::size_t initial_allocated_block_counts) noexcept -{ -#if (defined(__GNUC__) || defined(__clang__)) - ::std::size_t initial_allocated_block_counts_with_sentinel; - if (__builtin_add_overflow(initial_allocated_block_counts, 1u, - __builtin_addressof(initial_allocated_block_counts_with_sentinel))) - { - ::fast_io::fast_terminate(); - } -#else - constexpr ::std::size_t maxval{::std::numeric_limits<::std::size_t>::max()}; - if (initial_allocated_block_counts == maxval) [[unlikely]] - { - ::fast_io::fast_terminate(); - } - ::std::size_t initial_allocated_block_counts_with_sentinel{initial_allocated_block_counts + 1u}; -#endif using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; 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}; using begin_ptrtype = typename dequecontroltype::replacetype *; - auto begin_ptr{static_cast(allocator::allocate_aligned(align, bytes))}; + controller_block.controller_after_ptr = (controller_block.controller_start_ptr = allocated_blocks_ptr) + allocated_blocks_count; - 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); + ::std::size_t const allocated_blocks_count_half{allocated_blocks_count >> 1u}; + ::std::size_t const initial_allocated_block_counts_half{initial_allocated_block_counts >> 1u}; - *(controller_block.controller_after_reserved_ptr = allocated_mid_block + 1) = nullptr; // set nullptr as a sentinel + auto allocated_mid_block{allocated_blocks_ptr + allocated_blocks_count_half}; - controller_block.controller_after_ptr = allocated_blocks_ptr + allocated_blocks_count; - ::std::size_t halfsize{bytes >> 1u}; + auto start_block_ptr{allocated_mid_block - initial_allocated_block_counts_half}; - back_block.begin_ptr = front_block.begin_ptr = begin_ptr; - controller.back_end_ptr = controller.front_end_ptr = (begin_ptr + bytes); - auto halfposptr{begin_ptr + halfsize}; - front_block.curr_ptr = halfposptr; - back_block.curr_ptr = halfposptr; + auto end_block_ptr{start_block_ptr + initial_allocated_block_counts}; + for (auto i{start_block_ptr}; i != end_block_ptr; ++i) + { + ::std::construct_at(i, static_cast(allocator::allocate_aligned(align, bytes))); + } + *end_block_ptr = nullptr; + controller_block.controller_start_reserved_ptr = start_block_ptr; + controller_block.controller_after_reserved_ptr = end_block_ptr; +} + + +template +inline constexpr void deque_allocate_empty_single_block_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t bytes) noexcept +{ + ::fast_io::containers::details::deque_allocate_on_empty_common_with_n_impl(controller, 1u, align, bytes); + + auto controllerptr{controller.controller_block.controller_start_reserved_ptr}; + auto begin_ptr{*controllerptr}; + auto mid_ptr{begin_ptr + (bytes >> 1u)}; + + controller.front_block = + controller.back_block = {begin_ptr, mid_ptr, controllerptr}; + controller.front_end_ptr = controller.back_end_ptr = (begin_ptr + bytes); } template @@ -696,14 +664,7 @@ inline constexpr void deque_allocate_init_blocks_dezeroing_impl(dequecontroltype using begin_ptrtype = typename dequecontroltype::replacetype *; for (auto it{reserve_start}, ed{reserve_after}; it != ed; ++it) { - if (zeroing) - { - ::std::construct_at(it, static_cast(allocator::allocate_aligned_zero(align, blockbytes))); - } - else - { - ::std::construct_at(it, static_cast(allocator::allocate_aligned(align, blockbytes))); - } + ::std::construct_at(it, static_cast(allocator::allocate_aligned_conditional_zero(align, blockbytes, zeroing))); } ::std::construct_at(reserve_after, nullptr); using replacetype = typename dequecontroltype::replacetype; @@ -717,21 +678,16 @@ inline constexpr void deque_allocate_init_blocks_dezeroing_impl(dequecontroltype controller.controller_block = { start_ptr, reserve_start, reserve_after, start_ptr + blocks_count}; } -template -inline constexpr void deque_allocate_init_blocks_impl(dequecontroltype &controller, ::std::size_t align, ::std::size_t blockbytes, ::std::size_t blocks_count_least) noexcept -{ - ::fast_io::containers::details::deque_allocate_init_blocks_dezeroing_impl(controller, align, blockbytes, blocks_count_least, zeroing); -} -template -inline constexpr void deque_init_space_common(dequecontroltype &controller, ::std::size_t n) noexcept +template +inline constexpr void deque_init_space_common(dequecontroltype &controller, ::std::size_t n, bool zeroing) noexcept { constexpr ::std::size_t blockbytes{sz * block_size}; ::std::size_t const ndivsz{n / block_size}; ::std::size_t const nmodsz{n % block_size}; ::std::size_t const counts{ndivsz + static_cast<::std::size_t>(nmodsz != 0u)}; - ::fast_io::containers::details::deque_allocate_init_blocks_impl(controller, align, blockbytes, counts); + ::fast_io::containers::details::deque_allocate_init_blocks_dezeroing_impl(controller, align, blockbytes, counts, zeroing); if (!n) { return; @@ -745,6 +701,95 @@ inline constexpr void deque_init_space_common(dequecontroltype &controller, ::st back_curr_ptr += offset_for_back; } +template +inline constexpr void deque_shrink_to_fit_common(dequecontroltype &controller, ::std::size_t align, ::std::size_t block_bytes) noexcept +{ + if (controller.front_block.curr_ptr == controller.back_block.curr_ptr) + { + if (controller.front_block.curr_ptr == nullptr) + { + return; + } + ::fast_io::containers::details::deque_destroy_trivial_common_align(controller.controller_block, align, block_bytes); + controller = dequecontroltype{}; + return; + } + + auto &cb{controller.controller_block}; + auto start_reserved{cb.controller_start_reserved_ptr}; + auto const after_reserved{cb.controller_after_reserved_ptr}; + auto const front_ptr{controller.front_block.controller_ptr}; + auto const back_ptr{controller.back_block.controller_ptr}; + + auto const start_ptr{cb.controller_start_ptr}; + auto const after_ptr{cb.controller_after_ptr + 1}; + + // 1. Check if deallocation is needed for data blocks + // We use after_reserved vs back_ptr + 1 safely by checking boundaries + bool const needs_block_shrink = (start_reserved != front_ptr) || (after_reserved != (back_ptr + 1)); + + if (needs_block_shrink) + { + for (; start_reserved != front_ptr; ++start_reserved) + { + allocator::deallocate_aligned_n(*start_reserved, align, block_bytes); + } + cb.controller_start_reserved_ptr = front_ptr; + // it is safe to start from back_ptr + 1 because if we are here, + // back_ptr must be within [start_ptr, after_ptr - 1] + for (auto it{back_ptr + 1}; it != after_reserved; ++it) + { + allocator::deallocate_aligned_n(*it, align, block_bytes); + } + *(cb.controller_after_reserved_ptr = back_ptr + 1) = nullptr; + } + + // 2. Controller Shrink Check + // If blocks were already tight AND the controller array is already tight, return + ::std::size_t const used_blocks_count{static_cast<::std::size_t>(back_ptr + 1 - front_ptr)}; + ::std::size_t const current_controller_size{static_cast<::std::size_t>(after_ptr - start_ptr)}; + + if (!needs_block_shrink && current_controller_size <= (used_blocks_count + 1u)) + { + return; + } + + if constexpr (true) + { + // 3. Reallocate Controller + ::std::size_t const new_controller_size_least{used_blocks_count + 1u}; + // 3. Manually Reallocate and Move Controller Array + using block_ptr_allocator = ::fast_io::typed_generic_allocator_adapter; + + // Allocate new, smaller controller array + auto [new_start_ptr, new_actual_count] = block_ptr_allocator::allocate_at_least(new_controller_size_least); + + // Move the pointers (non-overlapped because it's a new allocation) + ::fast_io::freestanding::non_overlapped_copy_n(front_ptr, used_blocks_count, new_start_ptr); + + // 4. Update controller metadata and pointers + // We align the new reserved range to the start of the new allocation + cb.controller_start_ptr = new_start_ptr; + cb.controller_start_reserved_ptr = new_start_ptr; + cb.controller_after_reserved_ptr = new_start_ptr + used_blocks_count; + cb.controller_after_ptr = new_start_ptr + new_actual_count - 1u; + + // Patch the front and back block references + controller.front_block.controller_ptr = new_start_ptr; + controller.back_block.controller_ptr = new_start_ptr + (used_blocks_count - 1u); + + // 5. Set sentinel and clean up old controller + *(cb.controller_after_reserved_ptr) = nullptr; + block_ptr_allocator::deallocate_n(start_ptr, current_controller_size); + } +} + +template +inline constexpr void deque_shrink_to_fit_impl(dequecontroltype &controller) noexcept +{ + ::fast_io::containers::details::deque_shrink_to_fit_common(controller, align, blockbytes); +} + template struct uninitialized_copy_n_for_deque_guard { @@ -1120,6 +1165,7 @@ deque_erase_common_trivial_impl(::fast_io::containers::details::deque_controller if (moveleft) { controller.front_block = ::fast_io::containers::details::deque_copy_backward_impl(controller.front_block, first, last, blockbytes); + controller.front_end_ptr = controller.front_block.begin_ptr + blockbytes; first = last; if (controller.front_block.curr_ptr == back_block.curr_ptr && back_block.curr_ptr == controller.back_end_ptr) [[unlikely]] { @@ -1144,7 +1190,11 @@ deque_erase_common_trivial_impl(::fast_io::containers::details::deque_controller } if (back_block.begin_ptr == back_block.curr_ptr) { - if (controller.front_block.controller_ptr != back_block.controller_ptr) + if (controller.front_block.controller_ptr == back_block.controller_ptr) + { + controller.front_block.curr_ptr = back_block.curr_ptr = (back_block.begin_ptr) + (blockbytes >> 1u); + } + else { back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + blockbytes); } @@ -1154,107 +1204,207 @@ deque_erase_common_trivial_impl(::fast_io::containers::details::deque_controller return first; } -template -inline constexpr void deque_rebalance_or_grow_insertation_impl(dequecontroltype &controller, ::std::size_t extrablocks) noexcept +inline constexpr ::std::size_t deque_new_blocks_count_compute_impl(::std::size_t new_blocks_count_least, ::std::size_t blocks, ::std::size_t reserved_space_at_opposite_direction) noexcept { - // ignore overchecked first - 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 defined(__GNUC__) || defined(__clang__) - ::std::size_t new_used_blocks_count; - if (__builtin_add_overflow(used_blocks_count, extrablocks, __builtin_addressof(new_used_blocks_count))) [[unlikely]] +#if (defined(__GNUC__) || defined(__clang__)) + if constexpr (true) { - ::fast_io::fast_terminate(); + ::std::size_t blocksx2; + if (__builtin_add_overflow(blocks, blocks, __builtin_addressof(blocksx2))) + { + ::fast_io::fast_terminate(); + } + if (reserved_space_at_opposite_direction < blocksx2) + { + reserved_space_at_opposite_direction = blocksx2; + } + if (__builtin_add_overflow(new_blocks_count_least, reserved_space_at_opposite_direction, __builtin_addressof(reserved_space_at_opposite_direction))) + { + ::fast_io::fast_terminate(); + } + if (__builtin_add_overflow(reserved_space_at_opposite_direction, 1u, __builtin_addressof(reserved_space_at_opposite_direction))) + { + ::fast_io::fast_terminate(); + } + return reserved_space_at_opposite_direction; } -#else - constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; - ::std::size_t const mx_sub_extrablocks{mx - extrablocks}; - if (mx_sub_extrablocks < used_blocks_count) + else +#endif { - ::fast_io::fast_terminate(); + ::std::size_t blocksx2{blocks + blocks}; + if (blocksx2 < blocks) + { + ::fast_io::fast_terminate(); + } + if (reserved_space_at_opposite_direction < blocksx2) + { + reserved_space_at_opposite_direction = blocksx2; + } + constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; + ::std::size_t const mxm{mx - new_blocks_count_least}; + if (mxm < reserved_space_at_opposite_direction) + { + ::fast_io::fast_terminate(); + } + reserved_space_at_opposite_direction += new_blocks_count_least; + if (mx == reserved_space_at_opposite_direction) + { + ::fast_io::fast_terminate(); + } + ++reserved_space_at_opposite_direction; + return reserved_space_at_opposite_direction; } +} - auto const new_used_blocks_count{used_blocks_count + extrablocks}; +template +inline constexpr void deque_grow_to_new_blocks_count_direction_impl(dequecontroltype &controller, ::std::size_t new_blocks_count_least, bool no_space_at_back) noexcept +{ + auto old_start_ptr{controller.controller_block.controller_start_ptr}; +#if __has_cpp_attribute(assume) + [[assume(old_start_ptr != nullptr)]]; #endif + auto old_after_ptr{controller.controller_block.controller_after_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}; + auto old_front_block_controller_ptr{controller.front_block.controller_ptr}; + auto old_back_block_controller_ptr{controller.back_block.controller_ptr}; + auto old_back_block_controller_ptr_p1{old_back_block_controller_ptr + 1}; + + ::std::size_t old_start_reserved_pos{ + static_cast<::std::size_t>(old_start_reserved_ptr - old_start_ptr)}; + ::std::size_t old_after_reserved_pos{ + static_cast<::std::size_t>(old_after_reserved_ptr - old_start_ptr)}; + + ::std::size_t old_front_block_controller_pos{ + static_cast<::std::size_t>(old_front_block_controller_ptr - old_start_ptr)}; + ::std::size_t old_back_block_controller_pos{ + static_cast<::std::size_t>(old_back_block_controller_ptr - old_start_ptr)}; - if (half_slots_count < new_used_blocks_count) // grow blocks + ::std::size_t old_blocks_count{static_cast<::std::size_t>(old_back_block_controller_ptr_p1 - old_front_block_controller_ptr)}; +#if 0 + ::fast_io::io::debug_perr(::std::source_location::current(), "\n" + "old_start_ptr=",::fast_io::mnp::pointervw(old_start_ptr),"\n" + "old_after_pos=",old_after_ptr-old_start_ptr,"\n" + "old_start_reserved_pos=",old_start_reserved_pos,"\n" + "old_after_reserved_pos=",old_after_reserved_pos,"\n" + "old_front_block_controller_pos=",old_front_block_controller_pos,"\n" + "old_back_block_controller_pos=",old_back_block_controller_pos,"\n" + "old_blocks_count=",old_blocks_count,"\n" + "new_blocks_count_least=",new_blocks_count_least,"\n\n" + ); +#endif + ::std::size_t to_allocated_blocks_least_p1{::fast_io::containers::details::deque_new_blocks_count_compute_impl(new_blocks_count_least, + old_blocks_count, (no_space_at_back ? static_cast<::std::size_t>(old_back_block_controller_ptr_p1 - old_start_reserved_ptr) : static_cast<::std::size_t>(old_after_reserved_ptr - old_front_block_controller_ptr)))}; + using block_typed_allocator = ::fast_io::typed_generic_allocator_adapter; + decltype(old_start_ptr) new_start_ptr; + ::std::size_t allocated_blocks; + + ::std::size_t old_allocated_sz{static_cast<::std::size_t>(old_after_ptr - old_start_ptr)}; + ::std::size_t old_allocated_sz_p1{old_allocated_sz + 1zu}; + bool is_allocating_new_block{old_allocated_sz_p1 < to_allocated_blocks_least_p1}; +#if 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), "\tto_allocated_blocks_least_p1=", to_allocated_blocks_least_p1, + "\tnew_blocks_count_least=",new_blocks_count_least); +#endif + if (is_allocating_new_block) { -#if defined(__GNUC__) || defined(__clang__) - ::std::size_t doubleslotsextra; - if (__builtin_add_overflow(total_slots_count, extrablocks, __builtin_addressof(doubleslotsextra))) + auto allocate_result = block_typed_allocator::allocate_at_least(to_allocated_blocks_least_p1); + new_start_ptr = allocate_result.ptr; + allocated_blocks = allocate_result.count; + --allocated_blocks; + } + else + { + new_start_ptr = old_start_ptr; + allocated_blocks = old_allocated_sz; + } + ::std::size_t new_start_reserved_ptr_pos; + if (no_space_at_back) + { + new_start_reserved_ptr_pos = allocated_blocks - new_blocks_count_least - static_cast<::std::size_t>(old_front_block_controller_pos - old_start_reserved_pos); + } + else + { + new_start_reserved_ptr_pos = new_blocks_count_least - static_cast<::std::size_t>(old_back_block_controller_pos - old_start_reserved_pos); + } + auto new_start_reserved_ptr{new_start_ptr + new_start_reserved_ptr_pos}; + auto ed{::fast_io::freestanding::overlapped_copy(old_start_ptr + old_start_reserved_pos, old_start_ptr + old_after_reserved_pos, new_start_reserved_ptr)}; + ::std::construct_at(ed, nullptr); + + controller.controller_block.controller_start_ptr = new_start_ptr; + controller.controller_block.controller_after_ptr = new_start_ptr + allocated_blocks; + controller.controller_block.controller_start_reserved_ptr = new_start_reserved_ptr; + controller.controller_block.controller_after_reserved_ptr = ed; + controller.front_block.controller_ptr = new_start_reserved_ptr + static_cast<::std::size_t>(old_front_block_controller_pos - old_start_reserved_pos); + controller.back_block.controller_ptr = new_start_reserved_ptr + static_cast<::std::size_t>(old_back_block_controller_pos - old_start_reserved_pos); + if (is_allocating_new_block) + { + block_typed_allocator::deallocate_n(old_start_ptr, old_allocated_sz_p1); + } +} + +template +inline constexpr void deque_rebalance_or_grow_insertation_direction_impl(dequecontroltype &controller, ::std::size_t extrablocks, bool no_space_at_back) noexcept +{ + auto old_front_controller_ptr{controller.front_block.controller_ptr}; + auto old_back_controller_ptr{controller.back_block.controller_ptr}; + auto old_back_after_controller_ptr{old_back_controller_ptr + 1zu}; + ::std::size_t const old_elements_blocks_count{static_cast<::std::size_t>(old_back_after_controller_ptr - old_front_controller_ptr)}; + ::std::size_t new_elements_blocks_count; + +#if (defined(__GNUC__) || defined(__clang__)) + if constexpr (true) + { + if (__builtin_add_overflow(old_elements_blocks_count, extrablocks, __builtin_addressof(new_elements_blocks_count))) [[unlikely]] { ::fast_io::fast_terminate(); } - if (__builtin_add_overflow(doubleslotsextra, doubleslotsextra, __builtin_addressof(doubleslotsextra))) + } + else +#endif + { + constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; + ::std::size_t const mx_sub_extrablocks{mx - extrablocks}; + if (mx_sub_extrablocks < old_elements_blocks_count) { ::fast_io::fast_terminate(); } -#else - ::std::size_t mx_total_slots{mx - extrablocks}; - if (mx_total_slots < total_slots_count) + new_elements_blocks_count = old_elements_blocks_count + extrablocks; + } + auto start_ptr{controller.controller_block.controller_start_ptr}; + auto after_ptr{controller.controller_block.controller_after_ptr}; + ::std::size_t capacity_blocks_count_direction{ + no_space_at_back ? static_cast<::std::size_t>(after_ptr - old_front_controller_ptr) : static_cast<::std::size_t>(old_back_after_controller_ptr - start_ptr)}; + +#if (defined(__GNUC__) || defined(__clang__)) + if constexpr (true) + { + if (__builtin_add_overflow(capacity_blocks_count_direction, capacity_blocks_count_direction, __builtin_addressof(capacity_blocks_count_direction))) { ::fast_io::fast_terminate(); } - ::std::size_t doubleslotsextra{extrablocks + total_slots_count}; + } + else +#endif + { + constexpr ::std::size_t mx{::std::numeric_limits<::std::size_t>::max()}; constexpr ::std::size_t mxdv2m1{(mx >> 1u)}; - if (mxdv2m1 < doubleslotsextra) + if (mxdv2m1 < capacity_blocks_count_direction) { ::fast_io::fast_terminate(); } - doubleslotsextra <<= 1u; -#endif - - ::fast_io::containers::details::deque_grow_to_new_blocks_count_impl(controller, doubleslotsextra); + capacity_blocks_count_direction <<= 1u; } - else + if (capacity_blocks_count_direction < new_elements_blocks_count) { - // 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)}; // this place needs to deal with extra block - auto used_blocks_pivot{controller.front_block.controller_ptr + half_used_blocks_count}; - if (used_blocks_pivot != reserved_pivot) - { - ::std::ptrdiff_t diff{reserved_pivot - used_blocks_pivot}; - auto rotate_pivot{diff < 0 ? start_reserved_ptr : after_reserved_ptr}; - rotate_pivot -= diff; - ::std::rotate(start_reserved_ptr, rotate_pivot, after_reserved_ptr); - controller.front_block.controller_ptr += diff; - controller.back_block.controller_ptr += diff; - } - auto const half_slotsextra_count{static_cast<::std::size_t>((total_slots_count + extrablocks) >> 1u)}; - auto slots_pivot{controller.controller_block.controller_start_ptr + half_slotsextra_count}; - if (slots_pivot != reserved_pivot) - { - ::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; - } + capacity_blocks_count_direction = new_elements_blocks_count; } + return ::fast_io::containers::details::deque_grow_to_new_blocks_count_direction_impl(controller, capacity_blocks_count_direction, no_space_at_back); } template -inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controller, ::std::size_t nb, ::std::size_t align, ::std::size_t blockbytes) noexcept +inline constexpr void deque_reserve_back_blocks_impl_none_empty(dequecontroltype &controller, ::std::size_t nb, ::std::size_t align, ::std::size_t blockbytes) noexcept { - if (controller.controller_block.controller_start_ptr == nullptr) - { - ::fast_io::containers::details::deque_allocate_on_empty_common_with_n_impl( - controller, align, blockbytes, nb); - return false; - } using replacetype = typename dequecontroltype::replacetype; using begin_ptrtype = replacetype *; @@ -1262,6 +1412,11 @@ inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controlle static_cast<::std::size_t>( controller.controller_block.controller_after_reserved_ptr - controller.back_block.controller_ptr); +#if 0 + ::fast_io::io::debug_perr(::std::source_location::current(), + "\ndiff_to_after_ptr=",diff_to_after_ptr, + "\nnb=",nb,"\n\n"); +#endif if (diff_to_after_ptr <= nb) { ::std::size_t distance_back_to_after{ @@ -1269,7 +1424,7 @@ inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controlle controller.back_block.controller_ptr)}; if (distance_back_to_after <= nb) { - ::fast_io::containers::details::deque_rebalance_or_grow_insertation_impl(controller, nb); + ::fast_io::containers::details::deque_rebalance_or_grow_insertation_direction_impl(controller, nb, true); } ::std::size_t diff_to_after_ptr2 = static_cast<::std::size_t>( @@ -1281,10 +1436,16 @@ inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controlle static_cast<::std::size_t>(controller.front_block.controller_ptr - controller.controller_block.controller_start_reserved_ptr)}; ::std::size_t front_borrowed_blocks_count{front_reserved_blocks}; - ::std::size_t to_allocate_blocks{nb}; - if (nb < front_reserved_blocks) + ::std::size_t to_allocate_blocks{static_cast<::std::size_t>(nb - (diff_to_after_ptr2 ? diff_to_after_ptr2 - 1zu : 0zu))}; +#if 0 + ::fast_io::io::debug_perrln(::std::source_location::current(), + "\nto_allocate_blocks=",to_allocate_blocks, + "\tnb=",nb, + "\tcontroller.front_block.controller_ptr-controller.controller_block.controller_start_ptr=",controller.front_block.controller_ptr-controller.controller_block.controller_start_ptr); +#endif + if (to_allocate_blocks < front_borrowed_blocks_count) { - front_borrowed_blocks_count = nb; + front_borrowed_blocks_count = to_allocate_blocks; to_allocate_blocks = 0u; } else @@ -1293,7 +1454,6 @@ inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controlle } auto controller_start_reserved_ptr{ controller.controller_block.controller_start_reserved_ptr}; - auto pos{ controller.controller_block.controller_after_reserved_ptr}; pos = ::fast_io::freestanding::non_overlapped_copy_n(controller_start_reserved_ptr, @@ -1301,15 +1461,24 @@ inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controlle pos); controller.controller_block.controller_start_reserved_ptr = controller_start_reserved_ptr + front_borrowed_blocks_count; +#if 0 + ::fast_io::io::debug_perrln(::std::source_location::current(),"\n" + "pos-controller.controller_block.controller_start_ptr=", + pos-controller.controller_block.controller_start_ptr,"\n" + "pos + to_allocate_blocks - controller.controller_block.controller_start_ptr=", + pos + to_allocate_blocks - controller.controller_block.controller_start_ptr,"\n" + "controller.controller_block.controller_after_ptr-controller.controller_block.controller_start_ptr=", + controller.controller_block.controller_after_ptr-controller.controller_block.controller_start_ptr,"\n" + "to_allocate_blocks=",to_allocate_blocks); +#endif for (auto e{pos + to_allocate_blocks}; pos != e; ++pos) { ::std::construct_at(pos, static_cast(allocator::allocate_aligned(align, blockbytes))); } - *pos = nullptr; + ::std::construct_at(pos, nullptr); controller.controller_block.controller_after_reserved_ptr = pos; } } - if (controller.back_block.controller_ptr == controller.front_block.controller_ptr && controller.front_block.curr_ptr == controller.front_end_ptr) { auto front_block_controller_ptr{controller.front_block.controller_ptr + 1}; @@ -1318,7 +1487,25 @@ inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controlle controller.front_block.curr_ptr = controller.front_block.begin_ptr = front_begin_ptr; controller.front_end_ptr = front_begin_ptr + blockbytes; } +} + +template +inline constexpr bool deque_reserve_back_blocks_impl(dequecontroltype &controller, ::std::size_t nb, ::std::size_t align, ::std::size_t blockbytes, ::std::size_t startoffset) noexcept +{ + if (controller.controller_block.controller_start_ptr == nullptr) + { + ::fast_io::containers::details::deque_allocate_on_empty_common_with_n_impl( + controller, nb, align, blockbytes); + auto controllerptr{controller.controller_block.controller_start_reserved_ptr}; + controller.front_block.controller_ptr = controller.back_block.controller_ptr = controllerptr; + auto begin_ptr{*controllerptr}; + controller.front_block.begin_ptr = controller.back_block.begin_ptr = begin_ptr; + controller.front_block.curr_ptr = controller.back_block.curr_ptr = begin_ptr + startoffset; + controller.front_end_ptr = controller.back_end_ptr = begin_ptr + blockbytes; + return false; + } + ::fast_io::containers::details::deque_reserve_back_blocks_impl_none_empty(controller, nb, align, blockbytes); return true; } @@ -1328,10 +1515,12 @@ inline constexpr void deque_grow_back_common_impl( std::size_t align, std::size_t bytes) noexcept { - if (!::fast_io::containers::details::deque_reserve_back_blocks_impl(controller, 1zu, align, bytes)) + if (controller.controller_block.controller_start_ptr == nullptr) { + ::fast_io::containers::details::deque_allocate_empty_single_block_impl(controller, align, bytes); return; } + ::fast_io::containers::details::deque_reserve_back_blocks_impl_none_empty(controller, 1u, align, bytes); ++controller.back_block.controller_ptr; auto begin_ptr{*controller.back_block.controller_ptr}; controller.back_block.begin_ptr = begin_ptr; @@ -1346,8 +1535,8 @@ 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_reserve_back_spaces(dequecontroltype &controller, ::std::size_t n) +template +inline constexpr void deque_reserve_back_spaces_impl(dequecontroltype &controller, ::std::size_t n, ::std::size_t align, ::std::size_t sz, ::std::size_t block_size) { if (!n) { @@ -1355,41 +1544,59 @@ inline constexpr void deque_reserve_back_spaces(dequecontroltype &controller, :: } auto back_curr_ptr{controller.back_block.curr_ptr}; ::std::size_t blocksn{static_cast<::std::size_t>(controller.back_end_ptr - back_curr_ptr)}; + if constexpr (divsz) + { + blocksn /= sz; + } if (n <= blocksn) { return; } ::std::size_t nmblocksn{n - blocksn}; ::std::size_t back_more_blocks{nmblocksn / block_size}; - ::std::size_t const back_more_blocks_mod{nmblocksn % block_size}; ::std::size_t toallocate{back_more_blocks}; - if (back_more_blocks_mod) +#if defined(__GNUC__) || defined(__clang__) + if constexpr (true) + { + if (__builtin_add_overflow(toallocate, 1u, __builtin_addressof(toallocate))) + { + ::fast_io::fast_terminate(); + } + } + else +#endif { + constexpr ::std::size_t mxval{::std::numeric_limits<::std::size_t>::max()}; + if (toallocate == mxval) + { + ::fast_io::fast_terminate(); + } ++toallocate; } + if consteval { ::fast_io::containers::details::deque_reserve_back_blocks_impl(controller, - toallocate, align, block_size); + toallocate, align, block_size, 1); } else { - constexpr ::std::size_t block_bytes{block_size * sz}; + ::std::size_t const block_bytes{block_size * sz}; ::fast_io::containers::details::deque_reserve_back_blocks_impl(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>( __builtin_addressof(controller)), - toallocate, align, block_bytes); + toallocate, align, block_bytes, sz); } } +template +inline constexpr void deque_reserve_back_spaces(dequecontroltype &controller, ::std::size_t n) +{ + ::fast_io::containers::details::deque_reserve_back_spaces_impl(controller, n, align, sz, block_size); +} + template -inline constexpr bool deque_reserve_front_blocks_impl(dequecontroltype &controller, ::std::size_t nb, ::std::size_t align, ::std::size_t blockbytes) noexcept +inline constexpr void deque_reserve_front_blocks_none_empty_impl(dequecontroltype &controller, ::std::size_t nb, ::std::size_t align, ::std::size_t blockbytes) noexcept { - if (controller.controller_block.controller_start_ptr == nullptr) - { - ::fast_io::containers::details::deque_allocate_on_empty_common_with_n_impl( - controller, align, blockbytes, nb); - return false; - } using replacetype = typename dequecontroltype::replacetype; using begin_ptrtype = replacetype *; @@ -1404,7 +1611,7 @@ inline constexpr bool deque_reserve_front_blocks_impl(dequecontroltype &controll controller.controller_block.controller_start_ptr)}; if (distance_front_to_start < nb) { - ::fast_io::containers::details::deque_rebalance_or_grow_insertation_impl(controller, nb); + ::fast_io::containers::details::deque_rebalance_or_grow_insertation_direction_impl(controller, nb, false); } ::std::size_t diff_to_start_ptr2 = static_cast<::std::size_t>( @@ -1417,8 +1624,8 @@ inline constexpr bool deque_reserve_front_blocks_impl(dequecontroltype &controll controller.back_block.controller_ptr - 1)}; ::std::size_t back_borrowed_blocks_count{back_reserved_blocks}; - ::std::size_t to_allocate_blocks{nb}; - if (nb < back_reserved_blocks) + ::std::size_t to_allocate_blocks{static_cast<::std::size_t>(nb - diff_to_start_ptr2)}; + if (to_allocate_blocks < back_reserved_blocks) { back_borrowed_blocks_count = nb; to_allocate_blocks = 0u; @@ -1441,7 +1648,7 @@ inline constexpr bool deque_reserve_front_blocks_impl(dequecontroltype &controll ::fast_io::freestanding::non_overlapped_copy_n(new_controller_after_reserved_ptr, back_borrowed_blocks_count, new_controller_start_reserved_ptr); - *(controller.controller_block.controller_after_reserved_ptr = new_controller_after_reserved_ptr) = nullptr; + ::std::construct_at(controller.controller_block.controller_after_reserved_ptr = new_controller_after_reserved_ptr, nullptr); // after this line Todo auto ed{new_controller_start_reserved_ptr}; @@ -1453,12 +1660,31 @@ inline constexpr bool deque_reserve_front_blocks_impl(dequecontroltype &controll controller.controller_block.controller_start_reserved_ptr = new_controller_start_reserved_ptr; } } - return true; } -template -inline constexpr void deque_reserve_front_spaces(dequecontroltype &controller, ::std::size_t n) +template +inline constexpr void deque_reserve_front_blocks_impl(dequecontroltype &controller, ::std::size_t nb, ::std::size_t align, ::std::size_t blockbytes, ::std::size_t startoffset) noexcept +{ + if (controller.controller_block.controller_start_ptr == nullptr) + { + ::fast_io::containers::details::deque_allocate_on_empty_common_with_n_impl( + controller, nb, align, blockbytes); + auto controllerptr{controller.controller_block.controller_after_reserved_ptr - 1}; + controller.front_block.controller_ptr = controller.back_block.controller_ptr = controllerptr; + auto begin_ptr{*controllerptr}; + controller.front_block.begin_ptr = controller.back_block.begin_ptr = begin_ptr; + controller.front_block.curr_ptr = controller.back_block.curr_ptr = begin_ptr + (blockbytes - startoffset); + controller.front_end_ptr = controller.back_end_ptr = begin_ptr + blockbytes; + return; + } + ::fast_io::containers::details::deque_reserve_front_blocks_none_empty_impl(controller, nb, align, blockbytes); + return; +} + +template +inline constexpr void deque_reserve_front_spaces_impl(dequecontroltype &controller, ::std::size_t n, ::std::size_t align, ::std::size_t sz, ::std::size_t block_size) { + if (!n) { return; @@ -1471,36 +1697,56 @@ inline constexpr void deque_reserve_front_spaces(dequecontroltype &controller, : } ::std::size_t nmblocksn{n - blocksn}; ::std::size_t front_more_blocks{nmblocksn / block_size}; - ::std::size_t const front_more_blocks_mod{nmblocksn % block_size}; ::std::size_t toallocate{front_more_blocks}; - if (front_more_blocks_mod) +#if defined(__GNUC__) || defined(__clang__) + if constexpr (true) + { + if (__builtin_add_overflow(toallocate, 1u, __builtin_addressof(toallocate))) + { + ::fast_io::fast_terminate(); + } + } + else +#endif { + constexpr ::std::size_t mxval{::std::numeric_limits<::std::size_t>::max()}; + if (toallocate == mxval) + { + ::fast_io::fast_terminate(); + } ++toallocate; } if consteval { - ::fast_io::containers::details::deque_reserve_front_blocks_impl(controller, - toallocate, align, block_size); + ::fast_io::containers::details::deque_reserve_front_blocks_impl(controller, toallocate, align, block_size, 1u); } else { - constexpr ::std::size_t block_bytes{block_size * sz}; + ::std::size_t const block_bytes{block_size * sz}; ::fast_io::containers::details::deque_reserve_front_blocks_impl(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>( __builtin_addressof(controller)), - toallocate, align, block_bytes); + toallocate, align, block_bytes, sz); } } +template +inline constexpr void deque_reserve_front_spaces(dequecontroltype &controller, ::std::size_t n) +{ + ::fast_io::containers::details::deque_reserve_front_spaces_impl(controller, n, align, sz, block_size); +} + template inline constexpr void deque_grow_front_common_impl( dequecontroltype &controller, std::size_t align, std::size_t bytes) noexcept { - if (!::fast_io::containers::details::deque_reserve_front_blocks_impl(controller, 1zu, align, bytes)) + if (controller.controller_block.controller_start_ptr == nullptr) { + ::fast_io::containers::details::deque_allocate_empty_single_block_impl(controller, align, bytes); return; } + ::fast_io::containers::details::deque_reserve_front_blocks_none_empty_impl(controller, 1zu, align, bytes); auto begin_ptr{*(--controller.front_block.controller_ptr)}; controller.front_block.begin_ptr = begin_ptr; auto end_ptr{begin_ptr + bytes}; @@ -1512,10 +1758,62 @@ template (controller, align, blockbytes); } +template +inline constexpr ::std::size_t deque_get_front_capacity_bytes_impl(dequecontroltype const &controller, ::std::size_t block_bytes) noexcept +{ + return block_bytes * static_cast<::std::size_t>(controller.back_block.controller_ptr - controller.controller_block.controller_start_reserved_ptr) + + static_cast<::std::size_t>(controller.back_block.curr_ptr - controller.back_block.begin_ptr); +} + +template <::std::size_t sz, ::std::size_t block_bytes, typename dequecontroltype> +inline constexpr ::std::size_t deque_get_front_capacity(dequecontroltype const &controller) noexcept +{ + auto ret{::fast_io::containers::details::deque_get_front_capacity_bytes_impl(controller, block_bytes)}; + if constexpr (sz != 1u) + { + ret /= sz; + } + return ret; +} + +template +inline constexpr ::std::size_t deque_get_back_capacity_bytes_impl(dequecontroltype const &controller, ::std::size_t block_bytes) noexcept +{ + return block_bytes * static_cast<::std::size_t>(controller.controller_block.controller_after_reserved_ptr - controller.front_block.controller_ptr) - + static_cast<::std::size_t>(controller.front_block.curr_ptr - controller.front_block.begin_ptr); +} + +template <::std::size_t sz, ::std::size_t block_bytes, typename dequecontroltype> +inline constexpr ::std::size_t deque_get_back_capacity(dequecontroltype const &controller) noexcept +{ + auto ret{::fast_io::containers::details::deque_get_back_capacity_bytes_impl(controller, block_bytes)}; + if constexpr (sz != 1u) + { + ret /= sz; + } + return ret; +} + +template +inline constexpr ::std::size_t deque_get_capacity_bytes_impl(dequecontroltype &controller, ::std::size_t block_bytes) noexcept +{ + return block_bytes * static_cast<::std::size_t>(controller.controller_block.controller_after_reserved_ptr - controller.controller_block.controller_start_reserved_ptr); +} + +template <::std::size_t sz, ::std::size_t block_bytes, typename dequecontroltype> +inline constexpr ::std::size_t deque_get_capacity(dequecontroltype &controller) noexcept +{ + auto ret{::fast_io::containers::details::deque_get_capacity_bytes_impl(controller, block_bytes)}; + if constexpr (sz != 1u) + { + ret /= sz; + } + return ret; +} + template struct is_fast_io_deque_iterator_impl : ::std::false_type {}; @@ -1582,6 +1880,219 @@ inline constexpr Iter uninitialized_relocate_backward_define( ::fast_io::containers::details::deque_block_size)); } +template +struct +#if __has_cpp_attribute(__gnu__::__may_alias__) + [[__gnu__::__may_alias__]] +#endif + deque_nth_element_result +{ + ptrtype start_ptr, end_ptr; +}; + +template +inline constexpr ::fast_io::containers::details::deque_nth_element_result deque_nth_element_common_impl(dequecontroltype &controller, ::std::size_t pos, ::std::size_t block_size) noexcept +{ + auto front_controller_ptr{controller.front_block.controller_ptr}; + auto back_controller_ptr{controller.back_block.controller_ptr}; + + ::std::size_t lastsegidx{static_cast<::std::size_t>(static_cast<::std::size_t>(back_controller_ptr - front_controller_ptr))}; + if (lastsegidx < pos) [[unlikely]] + { + ::fast_io::fast_terminate(); +#if __has_cpp_attribute(unreachable) + [[unreachable]]; +#endif + } + using pointer = typename dequecontroltype::controlreplacetype; + pointer start_ptr, end_ptr; + if (!pos) + { + start_ptr = controller.front_block.curr_ptr; + if (lastsegidx) + { + end_ptr = controller.front_end_ptr; + } + else + { + end_ptr = controller.back_block.curr_ptr; + } + } + else if (pos == lastsegidx) + { + start_ptr = controller.back_block.begin_ptr; + end_ptr = controller.back_block.curr_ptr; + } + else + { + start_ptr = front_controller_ptr[pos]; + end_ptr = start_ptr + block_size; + } + + return {start_ptr, end_ptr}; +} + +template <::std::size_t block_size, typename dequecontroltype> +inline constexpr ::fast_io::containers::details::deque_nth_element_result deque_nth_element_common(dequecontroltype &controller, ::std::size_t pos) noexcept +{ + return ::fast_io::containers::details::deque_nth_element_common_impl(controller, pos, block_size); +} + +template +inline constexpr ::std::size_t deque_itercontent_diff_impl(T const &a, T const &b, ::std::size_t block_size) +{ + ::std::size_t controllerdiff{static_cast<::std::size_t>(a.controller_ptr - b.controller_ptr)}; + return controllerdiff * block_size + static_cast<::std::size_t>((a.curr_ptr - a.begin_ptr) + (b.begin_ptr - b.curr_ptr)); +} + +template +inline constexpr void deque_reserve_back_impl(dequecontroltype &controller, ::std::size_t newbackcap, ::std::size_t align, ::std::size_t sz, ::std::size_t block_size) noexcept +{ + ::std::size_t deqsz; + + if constexpr (divsz) + { + deqsz = ::fast_io::containers::details::deque_itercontent_diff_impl(controller.back_block, controller.front_block, block_size * sz) / sz; + } + else + { + deqsz = ::fast_io::containers::details::deque_itercontent_diff_impl(controller.back_block, controller.front_block, block_size); + } + + ::std::size_t toaddedsz; +#if defined(__GNUC__) || defined(__clang__) + if constexpr (true) + { + if (__builtin_sub_overflow(newbackcap, deqsz, __builtin_addressof(toaddedsz))) + { + return; + } + } + else +#endif + { + if (newbackcap <= deqsz) [[unlikely]] + { + return; + } + toaddedsz = static_cast<::std::size_t>(newbackcap - deqsz); + } + if constexpr (divsz) + { + ::fast_io::containers::details::deque_reserve_back_spaces_impl(controller, toaddedsz, align, sz, block_size); + } + else + { + ::fast_io::containers::details::deque_reserve_back_spaces_impl(controller, toaddedsz, align, 1zu, block_size * sz); + } +} + +template +inline constexpr void deque_reserve_back_common(dequecontroltype &controller, ::std::size_t newbackcap) noexcept +{ + ::fast_io::containers::details::deque_reserve_back_impl(controller, newbackcap, align, sz, block_size); +} + +template +inline constexpr void deque_reserve_front_impl(dequecontroltype &controller, ::std::size_t newfrontcap, ::std::size_t align, ::std::size_t sz, ::std::size_t block_size) noexcept +{ + ::std::size_t deqsz; + + if constexpr (divsz) + { + deqsz = ::fast_io::containers::details::deque_itercontent_diff_impl(controller.back_block, controller.front_block, block_size * sz) / sz; + } + else + { + deqsz = ::fast_io::containers::details::deque_itercontent_diff_impl(controller.back_block, controller.front_block, block_size); + } + + ::std::size_t toaddedsz; +#if defined(__GNUC__) || defined(__clang__) + if constexpr (true) + { + if (__builtin_sub_overflow(newfrontcap, deqsz, __builtin_addressof(toaddedsz))) + { + return; + } + } + else +#endif + { + if (newfrontcap <= deqsz) [[unlikely]] + { + return; + } + toaddedsz = static_cast<::std::size_t>(newfrontcap - deqsz); + } + if constexpr (divsz) + { + ::fast_io::containers::details::deque_reserve_front_spaces_impl(controller, toaddedsz, align, sz, block_size); + } + else + { + ::fast_io::containers::details::deque_reserve_front_spaces_impl(controller, toaddedsz, align, 1zu, block_size * sz); + } +} + +template +inline constexpr void deque_reserve_front_common(dequecontroltype &controller, ::std::size_t newfrontcap) noexcept +{ + ::fast_io::containers::details::deque_reserve_front_impl(controller, newfrontcap, align, sz, block_size); +} + +template +inline constexpr ::std::size_t deque_itercontent_difference_unsigned_common(T const &a, T const &b, ::std::size_t blocksizedf) noexcept +{ + ::std::size_t controllerdiff{static_cast<::std::size_t>(a.controller_ptr - b.controller_ptr)}; + return controllerdiff * blocksizedf + static_cast<::std::size_t>((a.curr_ptr - a.begin_ptr) + (b.begin_ptr - b.curr_ptr)); +} +#if 0 +template +inline constexpr void deque_resize_common_impl(dequecontroltype &controller, ::std::size_t newsize, + ::std::size_t align, ::std::size_t sz, ::std::size_t block_size) noexcept +{ + ::std::size_t blockbytes{block_size * sz}; + size_type oldsz{ + ::fast_io::containers::details::deque_itercontent_difference_unsigned_common(controller.back_block, controller.front_block, blockbytes); + }; + if constexpr(divsz) + { + oldsz/=sz; + } + if (count == oldsz) + { + return; + } + iterator newed; + if (count < oldsz) + { + auto ed{}; + newed = ed; + newed -= static_cast(oldsz - count); + } + else + { + this->reserve_back(count); + auto ed{this->end()}; + newed = ed + static_cast(count - oldsz); + + } + if (newed.curr_ptr == newed.begin_ptr) + { + newed.curr_ptr = (newed.begin_ptr = *--newed.controller_ptr) + block_size; + } + controller.back_block.controller_ptr = newed.controller_ptr; + controller.back_end_ptr = (this->controller.back_block.begin_ptr = newed.begin_ptr) + block_size; + controller.back_block.curr_ptr = newed.curr_ptr; +} + +template +inline constexpr void deque_resize_common(dequecontroltype &controller, ::std::size_t newsize) noexcept +{ + ::fast_io::containers::details::deque_resize_common_impl(controller, newsize, align, sz, block_size); +} +#endif } // namespace details template <::std::forward_iterator ForwardIt> @@ -1596,6 +2107,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE public: using value_type = T; using pointer = value_type *; + using const_pointer = value_type const *; using reference = value_type &; using const_reference = value_type const &; using size_type = ::std::size_t; @@ -1625,11 +2137,23 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { return *this; } - deque temp(other); - destroy_deque_controller(this->controller); - this->controller = temp.controller; - temp.controller = {}; - return *this; +#if 0 + if constexpr (::std::is_nothrow_copy_constructible_v && + ::std::is_nothrow_copy_assignable_v) + { +// Path A: High-performance reuse of existing blocks + this->assign_range(other); + } + else +#endif + { + // Path B: Strong Exception Guarantee via Creating a Temporary + deque temp(other); + destroy_deque_controller(this->controller); + this->controller = temp.controller; + temp.controller = {}; + return *this; + } } inline constexpr deque(deque &&other) noexcept : controller(other.controller) @@ -1781,7 +2305,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE ::std::is_nothrow_default_constructible_v) { constexpr bool iszeroconstr{::fast_io::freestanding::is_zero_default_constructible_v}; - this->init_blocks_common(n); + this->init_blocks_common(n, iszeroconstr); if constexpr (!iszeroconstr) { this->default_construct_impl(); @@ -1793,15 +2317,15 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { if constexpr (::std::is_trivially_default_constructible_v) { - this->init_blocks_common(n); + this->init_blocks_common(n, false); } else if constexpr (::fast_io::freestanding::is_zero_default_constructible_v) { - this->init_blocks_common(n); + this->init_blocks_common(n, true); } else { - this->init_blocks_common(n); + this->init_blocks_common(n, false); this->default_construct_impl(); } } @@ -1817,6 +2341,29 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE this->construct_deque_common_impl(ilist.begin(), ilist.end()); } + inline explicit constexpr deque(size_type n, const_reference val) noexcept(::std::is_nothrow_copy_constructible_v) + { + if constexpr (::std::is_nothrow_copy_constructible_v) + { + this->reserve_back(n); + auto bg{this->begin()}; + auto ed{bg + n}; + ::fast_io::freestanding::uninitialized_fill(bg, ed, val); + this->set_newed_common(ed.itercontent); + } + else + { + deque d; + d.reserve_back(n); + auto bg{d.begin()}; + auto ed{bg + n}; + ::fast_io::freestanding::uninitialized_fill(bg, ed, val); + d.set_newed_common(ed.itercontent); + this->controller = d.controller; + d.controller = {}; + } + } + private: template inline constexpr void construct_deque_common_impl(Iter first, Sentinel last) @@ -1831,7 +2378,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { auto const dist{::std::ranges::distance(first, last)}; - this->init_blocks_common(static_cast<::std::size_t>(dist)); + this->init_blocks_common(static_cast<::std::size_t>(dist), false); auto front_controller_ptr{controller.front_block.controller_ptr}; auto back_controller_ptr{controller.back_block.controller_ptr}; auto dq_back_backup{this->controller.back_block}; @@ -1874,16 +2421,15 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE des.thiscontroller = nullptr; } - template - inline constexpr void init_blocks_common(::std::size_t n) noexcept + inline constexpr void init_blocks_common(::std::size_t n, bool iszeroconstr) noexcept { if (__builtin_is_constant_evaluated()) { - ::fast_io::containers::details::deque_init_space_common(controller, n); + ::fast_io::containers::details::deque_init_space_common(controller, n, iszeroconstr); } else { - ::fast_io::containers::details::deque_init_space_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller)), n); + ::fast_io::containers::details::deque_init_space_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(controller)), n, iszeroconstr); } } inline static constexpr void destroy_elements_range( @@ -2252,6 +2798,133 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE return size() * sizeof(value_type); } + inline constexpr size_type front_capacity_bytes() const noexcept + { + if consteval + { + return ::fast_io::containers::details::deque_get_front_capacity<1u, block_size>(this->controller) * sizeof(value_type); + } + else + { + return ::fast_io::containers::details::deque_get_front_capacity<1u, block_size * sizeof(value_type)>( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common const *>(this->controller)); + } + } + + inline constexpr size_type front_capacity() const noexcept + { + if consteval + { + return ::fast_io::containers::details::deque_get_front_capacity<1u, block_size>(this->controller); + } + else + { + return ::fast_io::containers::details::deque_get_front_capacity( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common const *>(__builtin_addressof(this->controller))); + } + } + + inline constexpr size_type back_capacity_bytes() const noexcept + { + if consteval + { + return ::fast_io::containers::details::deque_get_back_capacity<1u, block_size>(this->controller) * sizeof(value_type); + } + else + { + return ::fast_io::containers::details::deque_get_back_capacity( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common const *>(this->controller)); + } + } + + inline constexpr size_type back_capacity() const noexcept + { + if consteval + { + return ::fast_io::containers::details::deque_get_back_capacity<1u, block_size>(this->controller); + } + else + { + return ::fast_io::containers::details::deque_get_back_capacity( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common const *>(__builtin_addressof(this->controller))); + } + } + + inline constexpr size_type capacity_bytes() const noexcept + { + if consteval + { + return ::fast_io::containers::details::deque_get_capacity<1u, block_size>(this->controller) * sizeof(value_type); + } + else + { + return ::fast_io::containers::details::deque_get_capacity<1u, block_size * sizeof(value_type)>( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common const *>(this->controller)); + } + } + + inline constexpr size_type capacity() const noexcept + { + if consteval + { + return ::fast_io::containers::details::deque_get_capacity<1u, block_size>(this->controller); + } + else + { + return ::fast_io::containers::details::deque_get_capacity( + *reinterpret_cast<::fast_io::containers::details::deque_controller_common const *>(__builtin_addressof(this->controller))); + } + } + + inline static constexpr size_type size_per_segment() noexcept + { + return block_size; + } + + inline static constexpr size_type size_bytes_per_segment() noexcept + { + constexpr size_type sz{block_size * sizeof(value_type)}; + return sz; + } + + inline constexpr size_type segments_count() noexcept + { + return static_cast(static_cast(this->controller.back_block.controller_ptr - this->controller.front_block.controller_ptr) + 1u); + } + + inline constexpr ::fast_io::containers::span nth_segment(size_type pos) noexcept + { + if consteval + { + auto [start_ptr, end_ptr] = ::fast_io::containers::details::deque_nth_element_common(this->controller, pos); + return ::fast_io::containers::span(start_ptr, end_ptr); + } + else + { + auto [start_ptr, end_ptr] = ::std::bit_cast<::fast_io::containers::details::deque_nth_element_result>(::fast_io::containers::details::deque_nth_element_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(this->controller)), pos)); + return ::fast_io::containers::span(start_ptr, end_ptr); + } + } + inline constexpr ::fast_io::containers::span const_nth_segment(size_type pos) const noexcept + { + if consteval + { + auto [start_ptr, end_ptr] = ::fast_io::containers::details::deque_nth_element_common(this->controller, pos); + return ::fast_io::containers::span(start_ptr, end_ptr); + } + else + { + auto [start_ptr, end_ptr] = ::std::bit_cast<::fast_io::containers::details::deque_nth_element_result>(::fast_io::containers::details::deque_nth_element_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>( + const_cast(__builtin_addressof(this->controller))), + pos)); + return ::fast_io::containers::span(start_ptr, end_ptr); + } + } + inline constexpr ::fast_io::containers::span nth_segment(size_type pos) const noexcept + { + return this->const_nth_segment(pos); + } + inline constexpr iterator begin() noexcept { return {this->controller.front_block}; @@ -2337,6 +3010,21 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE return controller.front_block.curr_ptr == controller.back_block.curr_ptr; } + inline constexpr size_type index_of(const_iterator it) const noexcept + { + return ::fast_io::containers::details::deque_iter_difference_unsigned_common(it.itercontent, this->controller.front_block); + } + + inline constexpr size_type segment_nth_of(const_iterator it) const noexcept + { + auto controllerptr{it.itercontent.controller_ptr}; + if (this->controller.back_block.controller_ptr < controllerptr) [[unlikely]] + { + --controllerptr; + } + return static_cast(controllerptr - this->controller.front_controller_ptr); + } + inline constexpr void clear_destroy() noexcept { destroy_deque_controller(this->controller); @@ -2356,8 +3044,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE auto thisbg{this->begin()}; auto posit{thisbg + pos}; auto thisbgrgsize{thisbg - rgsize}; - auto thisbgrgsizenew{::fast_io::freestanding::uninitialized_relocate(thisbg, - posit, thisbgrgsize)}; + auto thisbgrgsizenew{::fast_io::freestanding::uninitialized_relocate(thisbg, posit, thisbgrgsize)}; this->controller.front_block = thisbgrgsize.itercontent; this->controller.front_end_ptr = thisbgrgsize.itercontent.begin_ptr + block_size; return {pos, thisbgrgsizenew}; @@ -2709,7 +3396,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE pointer, emplace_index_decision> emplace_index_decision_common(::std::size_t idx) noexcept { - auto oldsize{this->size()}; if (oldsize < idx) [[unlikely]] { @@ -2718,7 +3404,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE [[unreachable]]; #endif } -#if 1 else if (idx == oldsize) { if (this->controller.back_block.curr_ptr != controller.back_end_ptr) [[likely]] @@ -2749,7 +3434,6 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } } } -#endif auto result{this->emplace_index_impl(idx, oldsize)}; pointer retptr{result.it.itercontent.curr_ptr}; if constexpr (isnothrow) @@ -2852,51 +3536,266 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE return this->emplace_index(idx, ::std::move(val)); } -#if 0 + +private: + struct insert_index_guard + { + deque *thisdeq; + size_type pos, count; + constexpr ~insert_index_guard() + { + if (thisdeq) [[unlikely]] + { + thisdeq->erase_unchecked_nodestroy_for_insert_counts_impl(pos, count); + } + } + }; + + inline constexpr insert_range_result insert_index_impl(size_type idx, size_type count, const_reference val, size_type oldn) noexcept(::std::is_nothrow_copy_constructible_v) + { + insert_range_result res{this->emplace_index_n_impl(idx, count, oldn)}; + if constexpr (::std::is_nothrow_copy_constructible_v) + { + ::fast_io::freestanding::uninitialized_fill_n(res.it, count, val); + } + else + { + insert_index_guard g{this, res.pos, count}; + ::fast_io::freestanding::uninitialized_fill_n(res.it, count, val); + g.thisdeq = nullptr; + } + return res; + } + +public: inline constexpr iterator insert(const_iterator iter, size_type count, const_reference val) noexcept(::std::is_nothrow_copy_constructible_v) { + return this->insert_index_impl(::fast_io::containers::details::deque_iter_difference_unsigned_common(iter.itercontent, this->controller.front_block), count, val, this->size()).it; } - inline constexpr iterator insert_index(size_type idx, size_type count, const_reference val) noexcept(::std::is_nothrow_copy_constructible_v) + inline constexpr size_type insert_index(size_type idx, size_type count, const_reference val) noexcept(::std::is_nothrow_copy_constructible_v) { - + size_type const n{this->size()}; + if (n < idx) [[unlikely]] + { + ::fast_io::fast_terminate(); + } + return this->insert_index_impl(idx, count, val, n).pos; } -#endif + inline constexpr void shrink_to_fit() noexcept(::std::is_nothrow_move_constructible_v) + { + if consteval + { + ::fast_io::containers::details::deque_shrink_to_fit_impl(this->controller); + } + else + { + ::fast_io::containers::details::deque_shrink_to_fit_impl(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof( + this->controller))); + } + } + +public: + /* + "The reserve_back and reserve_front APIs are fundamentally problematic for a deque. Because our logic redistributes and borrows blocks from both ends to maintain balance, direction-specific reservation is semantically unstable. A unified reserve(n) is likely more appropriate. Boost's approach appears to share this design flaw; for deques, resize for_overwrite provides clear utility, whereas direction-based reserve is often a misnomer." + */ + inline constexpr void reserve_back(size_type backcap) noexcept + { + if consteval + { + ::fast_io::containers::details::deque_reserve_back_common(this->controller, backcap); + } + else + { + ::fast_io::containers::details::deque_reserve_back_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(this->controller)), backcap); + } + } + inline constexpr void reserve_front(size_type frontcap) noexcept + { + if consteval + { + ::fast_io::containers::details::deque_reserve_front_common(this->controller, frontcap); + } + else + { + ::fast_io::containers::details::deque_reserve_front_common(*reinterpret_cast<::fast_io::containers::details::deque_controller_common *>(__builtin_addressof(this->controller)), frontcap); + } + } + + inline constexpr void set_newed_common(::fast_io::containers::details::deque_control_block newed) noexcept + { + if (newed.curr_ptr == newed.begin_ptr) + { + if (newed.controller_ptr == this->controller.front_block.controller_ptr) + { + this->controller.front_block.curr_ptr = newed.curr_ptr = newed.begin_ptr + (block_size >> 1u); + } + else + { + newed.curr_ptr = (newed.begin_ptr = *--newed.controller_ptr) + block_size; + } + } + this->controller.back_block.controller_ptr = newed.controller_ptr; + this->controller.back_end_ptr = (this->controller.back_block.begin_ptr = newed.begin_ptr) + block_size; + this->controller.back_block.curr_ptr = newed.curr_ptr; + } + +public: + inline constexpr void assign(size_type count, const_reference val) noexcept(::std::is_nothrow_copy_constructible_v) + { + if constexpr (::std::is_nothrow_copy_constructible_v) + { + this->clear(); + if (!count) + { + return; + } + this->reserve_back(count); + auto bg{this->begin()}; + auto newed{bg + count}; + ::fast_io::freestanding::uninitialized_fill(bg, newed, val); + this->set_newed_common(newed.itercontent); + } + else + { + deque d(count, val); + this->controller = d.controller; + d.controller = {}; + } + } + template <::std::ranges::range R> + requires ::std::constructible_from> + inline constexpr void assign_range(R &&rg) noexcept(::std::is_nothrow_constructible_v>) + { + deque temp(::fast_io::freestanding::from_range, ::std::forward(rg)); + this->swap(temp); + } + private: - inline constexpr iterator erase_no_destroy_common_impl(iterator first, iterator last, bool moveleft) noexcept + inline constexpr void resize_impl(size_type count, T const *pval) noexcept(::std::is_nothrow_move_constructible_v) { - ::fast_io::containers::details::deque_control_block back_block; - if (moveleft) + size_type oldsz{this->size()}; + if (count == oldsz) { - this->controller.front_block = ::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), first, last).itercontent; - first = last; - back_block = this->controller.back_block; + return; + } + iterator newed; + + if (count < oldsz) + { + auto ed{this->end()}; + newed = ed; + newed -= static_cast(oldsz - count); + if constexpr (!::std::is_trivially_destructible_v) + { + this->destroy_elements_range(newed.itercontent, ed.itercontent); + } } else { - back_block = ::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent; + this->reserve_back(count); + + auto ed{this->end()}; + newed = ed + static_cast(count - oldsz); + if (pval) + { + ::fast_io::freestanding::uninitialized_fill(ed, newed, *pval); + } + else + { + ::fast_io::freestanding::uninitialized_default_construct(ed, newed); + } + } + this->set_newed_common(newed.itercontent); + } + inline constexpr void resize_for_overwrite_impl(size_type count) noexcept(::std::is_nothrow_move_constructible_v) + { + size_type oldsz{this->size()}; + if (count == oldsz) + { + return; } - if (back_block.begin_ptr == back_block.curr_ptr) + iterator newed; + if (count < oldsz) { - if (this->controller.front_block.controller_ptr != back_block.controller_ptr) + auto ed{this->end()}; + newed = ed; + newed -= static_cast(oldsz - count); + if constexpr (!::std::is_trivially_destructible_v) { - back_block.curr_ptr = ((back_block.begin_ptr = (*--back_block.controller_ptr)) + block_size); + this->erase(newed, ed); + return; } } - this->controller.back_block = back_block; - this->controller.back_end_ptr = back_block.begin_ptr + block_size; - return first; + else + { + this->reserve_back(count); + auto ed{this->end()}; + newed = ed + static_cast(count - oldsz); + } + this->set_newed_common(newed.itercontent); } - inline constexpr iterator erase_unchecked_impl(iterator first, iterator last, bool moveleft) noexcept + +public: + inline constexpr void resize(size_type count) noexcept(::std::is_nothrow_default_constructible_v && ::std::is_nothrow_move_constructible_v) { - if (first == last) + this->resize_impl(count, nullptr); + } + inline constexpr void resize(size_type count, ::fast_io::for_overwrite_t) noexcept(::fast_io::freestanding::is_zero_default_constructible_v || + ::std::is_nothrow_default_constructible_v) + { + /* + Todo + */ + if constexpr (::std::is_trivially_default_constructible_v && + ::std::is_trivially_destructible_v) { - return first; + this->resize_for_overwrite_impl(count); } - if constexpr (!::std::is_trivially_destructible_v) +#if 0 + else if constexpr(::fast_io::freestanding::is_zero_default_constructible_v&& + ::std::is_trivially_destructible_v) { - this->destroy_elements_range(first, last); } - if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) +#endif + else + { + this->resize(count); + } + } + inline constexpr void resize(size_type count, const_reference value) noexcept(::std::is_nothrow_copy_constructible_v && ::std::is_nothrow_move_constructible_v) + { + this->resize_impl(count, __builtin_addressof(value)); + } + +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) + { + auto ed{this->end()}; + auto front_block{::fast_io::freestanding::uninitialized_relocate_backward(this->begin(), first, last).itercontent}; + back_block = this->controller.back_block; + if (last == ed) + { + front_block = back_block; + } + this->controller.front_block = front_block; + this->controller.front_end_ptr = front_block.begin_ptr + block_size; + first = last; + } + else + { + back_block = ::fast_io::freestanding::uninitialized_relocate(last, this->end(), first).itercontent; + } + + this->set_newed_common(back_block); + return first; + } + + inline constexpr iterator erase_unchecked_nodestroy_impl(iterator first, iterator last, bool moveleft) noexcept + { + if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v && 0) { if !consteval { @@ -2911,6 +3810,34 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE } return this->erase_no_destroy_common_impl(first, last, moveleft); } + inline constexpr void erase_unchecked_nodestroy_for_insert_counts_impl(iterator first, ::std::size_t count) noexcept + { + if (!count) + { + return; + } + + ::std::size_t const distofront{ + ::fast_io::containers::details::deque_iter_difference_unsigned_common(first, this->controller.front_block)}; + + auto last{first + count}; + ::std::size_t const distoback{ + ::fast_io::containers::details::deque_iter_difference_unsigned_common(this->controller.last_block, last)}; + + this->erase_unchecked_nodestroy_impl(first, last, distofront < distoback); + } + 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) + { + this->destroy_elements_range(first.itercontent, last.itercontent); + } + return this->erase_unchecked_nodestroy_impl(first, last, moveleft); + } inline constexpr iterator erase_unchecked_single_nodestroy_impl(iterator pos, bool moveleft) noexcept { if constexpr (::fast_io::freestanding::is_trivially_copyable_or_relocatable_v) @@ -2942,7 +3869,7 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { if constexpr (!::std::is_trivially_destructible_v) { - ::std::destroy(pos.itercontent.curr_ptr); + ::std::destroy_at(pos.itercontent.curr_ptr); } return this->erase_unchecked_single_nodestroy_impl(pos, moveleft); } @@ -2993,8 +3920,19 @@ class deque FAST_IO_TRIVIALLY_RELOCATABLE_IF_ELIGIBLE { destroy_deque_controller(this->controller); } + + inline constexpr void swap(deque &rhs) noexcept + { + ::std::swap(this->controller, rhs.controller); + } }; +template +inline constexpr void swap(::fast_io::containers::deque &lhs, ::fast_io::containers::deque &rhs) noexcept +{ + lhs.swap(rhs); +} + template requires ::std::equality_comparable inline constexpr bool operator==(::fast_io::containers::deque const &lhs, ::fast_io::containers::deque const &rhs) noexcept diff --git a/include/fast_io_dsal/impl/index_span.h b/include/fast_io_dsal/impl/index_span.h index 27e03a720..2760e62c2 100644 --- a/include/fast_io_dsal/impl/index_span.h +++ b/include/fast_io_dsal/impl/index_span.h @@ -46,13 +46,14 @@ class index_span : ptr{::std::to_address(first)} {} template <::std::ranges::contiguous_range R> - requires(::std::same_as>) - inline explicit constexpr index_span(::fast_io::containers::index_unchecked_t, R &&range) noexcept(noexcept(::std::ranges::data(range))) + requires(::std::same_as> && + !::std::is_rvalue_reference_v) + inline explicit constexpr index_span(::fast_io::containers::index_unchecked_t, ::fast_io::freestanding::from_range_t, R &&range) noexcept(noexcept(::std::ranges::data(range))) : ptr{::std::ranges::data(range)} {} template <::std::ranges::contiguous_range R> - requires(::std::same_as> && !::std::same_as<::std::remove_cvref_t, ::fast_io::containers::index_span>) - inline constexpr index_span(R &&range) noexcept(noexcept(::std::ranges::data(range)) && noexcept(::std::ranges::size(range))) + requires(::std::same_as> && !::std::is_rvalue_reference_v) + inline constexpr index_span(::fast_io::freestanding::from_range_t, R &&range) noexcept(noexcept(::std::ranges::data(range)) && noexcept(::std::ranges::size(range))) : ptr{::std::ranges::data(range)} { if (::std::ranges::size(range) < N) [[unlikely]] diff --git a/include/fast_io_dsal/impl/span.h b/include/fast_io_dsal/impl/span.h index 1f0cd152f..4035f5407 100644 --- a/include/fast_io_dsal/impl/span.h +++ b/include/fast_io_dsal/impl/span.h @@ -8,7 +8,7 @@ class span { public: using element_type = T; - using value_type = std::remove_cv_t; + using value_type = ::std::remove_cv_t; using size_type = ::std::size_t; using difference_type = ::std::ptrdiff_t; using pointer = element_type *; @@ -31,20 +31,21 @@ class span #endif pointer ptr{}; size_type n{}; - inline constexpr span() noexcept = default; + inline constexpr explicit span() noexcept = default; template <::std::contiguous_iterator Iter> requires ::std::same_as> - inline constexpr span(Iter first, size_type count) noexcept(noexcept(::std::to_address(first))) + inline constexpr explicit span(Iter first, size_type count) noexcept(noexcept(::std::to_address(first))) : ptr{::std::to_address(first)}, n{count} {} template <::std::contiguous_iterator Iter, ::std::sentinel_for S> requires ::std::same_as> - inline constexpr span(Iter first, S snt) noexcept(noexcept(::std::to_address(first))) + inline constexpr explicit span(Iter first, S snt) noexcept(noexcept(::std::to_address(first))) : ptr{::std::to_address(first)}, n{static_cast(snt - first)} {} template <::std::ranges::contiguous_range R> - requires(::std::same_as> && !::std::same_as<::std::remove_cvref_t, ::fast_io::containers::span>) - inline constexpr span(R &&range) noexcept(noexcept(::std::ranges::data(range)) && noexcept(::std::ranges::size(range))) + requires(::std::same_as> && + !::std::is_rvalue_reference_v) + inline constexpr explicit span(::fast_io::freestanding::from_range_t, R &&range) noexcept(noexcept(::std::ranges::data(range)) && noexcept(::std::ranges::size(range))) : ptr{::std::ranges::data(range)}, n{::std::ranges::size(range)} {} @@ -335,7 +336,7 @@ inline constexpr ::fast_io::containers::span<::std::byte> as_writable_bytes(::fa } template <::std::ranges::contiguous_range R> -span(R &&r) -> span<::std::remove_reference_t<::std::ranges::range_reference_t>>; +span(::fast_io::freestanding::from_range_t, R &&r) -> span<::std::remove_reference_t<::std::ranges::range_reference_t>>; template requires ::std::equality_comparable diff --git a/include/fast_io_dsal/impl/string_view.h b/include/fast_io_dsal/impl/string_view.h index ac377d3c0..0b6f55b1e 100644 --- a/include/fast_io_dsal/impl/string_view.h +++ b/include/fast_io_dsal/impl/string_view.h @@ -44,7 +44,7 @@ class basic_string_view template <::std::ranges::contiguous_range rg> requires(::std::same_as<::std::ranges::range_value_t, char_type> && !::std::is_array_v<::std::remove_cvref_t> && - !::std::is_rvalue_reference_v) + !::std::is_rvalue_reference_v) inline explicit constexpr basic_string_view(::fast_io::freestanding::from_range_t, rg &&r) noexcept : ptr{::std::ranges::cdata(r)}, n{::std::ranges::size(r)} { diff --git a/include/fast_io_legacy_impl/io.h b/include/fast_io_legacy_impl/io.h index 4c3cac0c8..c14bcaaf7 100644 --- a/include/fast_io_legacy_impl/io.h +++ b/include/fast_io_legacy_impl/io.h @@ -217,7 +217,7 @@ template } // Allow debug print -#ifndef FAST_IO_DISABLE_DEBUG_PRINT +#if FAST_IO_DISABLE_DEBUG_PRINT == 0 // With debugging. We output to POSIX fd or Win32 Handle directly instead of C's stdout. template inline constexpr void debug_print(T &&t, Args &&...args) diff --git a/tests/0000.tests_prebuild/gentests.cc b/tests/0000.tests_prebuild/gentests.cc index 76c435b8b..bb4928760 100644 --- a/tests/0000.tests_prebuild/gentests.cc +++ b/tests/0000.tests_prebuild/gentests.cc @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include #include @@ -77,17 +77,17 @@ inline constexpr file_entry_t operator|(file_entry_t const &lhs, file_entry_t co .will_fail = lhs.will_fail | rhs.will_fail, .platform = lhs.platform | rhs.platform}; } -using file_property_t = std::unordered_map; +using file_property_t = std::map; inline platform_t global_platform; inline bool msvc{}; inline void parse_prop_files(fast_io::native_file_loader &&file, file_property_t &file_properties) { - std::unordered_map> file_contents; + std::map> file_contents; fast_io::u8ibuffer_view u8fv{reinterpret_cast(file.begin()), reinterpret_cast(file.end())}; - std::unordered_map *curr_entry{}; + std::map *curr_entry{}; for (std::u8string line; scan(u8fv, fast_io::mnp::line_get(line));) { std::u8string_view linevw{line}; diff --git a/tests/0026.container/0003.deque/assign.cc b/tests/0026.container/0003.deque/assign.cc new file mode 100644 index 000000000..b23f1373c --- /dev/null +++ b/tests/0026.container/0003.deque/assign.cc @@ -0,0 +1,10 @@ +#include +#include + +int main() +{ + ::fast_io::deque<::std::size_t> b(1000); + ::fast_io::io::println("before assign size()=", b.size()); + b.assign(100000, 10); + ::fast_io::io::println("after assign size()", b.size()); +} diff --git a/tests/0026.container/0003.deque/insert_middle.cc b/tests/0026.container/0003.deque/insert_middle.cc new file mode 100644 index 000000000..f369d2c73 --- /dev/null +++ b/tests/0026.container/0003.deque/insert_middle.cc @@ -0,0 +1,214 @@ +#include +#include +#include + +namespace +{ + +inline void test_insert_middle() +{ + ::fast_io::io::perr("=== deque insert middle test ===\n"); + + ::fast_io::deque<::std::size_t> dq; + ::std::deque<::std::size_t> ref; + + // Add some elements + for (::std::size_t i = 0; i < 100; ++i) + { + dq.push_back(i); + ref.push_back(i); + } + + 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 = 0; i < dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + ::fast_io::io::panicln(src, "\tERROR: value mismatch at ", i, " dq=", dq[i], " ref=", ref[i], " : ", msg); + } + } + }; + + // Insert at position 50 (middle) + dq.insert_index(50, 999); + ref.insert(ref.begin() + 50, 999); + check_equal("insert at middle"); + + // Insert at position 0 (front) + dq.insert_index(0, 888); + ref.insert(ref.begin(), 888); + check_equal("insert at front"); + + // Insert at end + dq.insert_index(dq.size(), 777); + ref.insert(ref.end(), 777); + check_equal("insert at end"); + + // Randomized insertions + for (::std::size_t iter = 0; iter < 200; ++iter) + { + ::std::size_t pos = iter % (dq.size() + 1); + dq.insert_index(pos, iter + 1000); + ref.insert(ref.begin() + pos, iter + 1000); + check_equal("randomized insert"); + } + + ::fast_io::io::print("deque insert middle test finished\n"); +} + +inline void test_insert_count_value() +{ + ::fast_io::io::perr("=== deque insert(count, value) 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); + } + + 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. Insert at front + { + dq.insert(dq.begin(), 5, 9999); + ref.insert(ref.begin(), 5, 9999); + check_equal("insert(count,val) at front"); + } + + // 2. Insert at middle + { + std::size_t pos = dq.size() / 2; + dq.insert(dq.begin() + pos, 3, 7777); + ref.insert(ref.begin() + pos, 3, 7777); + check_equal("insert(count,val) at middle"); + } + + // 3. Insert at back + { + dq.insert(dq.end(), 4, 5555); + ref.insert(ref.end(), 4, 5555); + check_equal("insert(count,val) at back"); + } + + // 4. Randomized insertions + for (std::size_t iter{}; iter != 200u; ++iter) + { + std::size_t pos = iter % (dq.size() + 1); + std::size_t count = (iter % 4) + 1; + std::size_t val = iter + 3000; + + dq.insert(dq.begin() + pos, count, val); + ref.insert(ref.begin() + pos, count, val); + + check_equal("randomized insert(count,val)"); + } + + ::fast_io::io::print("deque insert(count,val) test finished\n"); +} + +inline void test_insert_index_count_value() +{ + ::fast_io::io::perr("=== deque insert_index(count, value) 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); + } + + 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. Insert at front + { + dq.insert_index(0, 5, 9999); + ref.insert(ref.begin(), 5, 9999); + check_equal("insert_index(count,val) at front"); + } + + // 2. Insert at middle + { + std::size_t pos = dq.size() / 2; + dq.insert_index(pos, 3, 7777); + ref.insert(ref.begin() + pos, 3, 7777); + check_equal("insert_index(count,val) at middle"); + } + + // 3. Insert at back + { + std::size_t pos = dq.size(); + dq.insert_index(pos, 4, 5555); + ref.insert(ref.end(), 4, 5555); + check_equal("insert_index(count,val) at back"); + } + + // 4. Randomized insertions + for (std::size_t iter{}; iter != 200u; ++iter) + { + std::size_t pos = iter % (dq.size() + 1); + std::size_t count = (iter % 4) + 1; + std::size_t val = iter + 4000; + + dq.insert_index(pos, count, val); + ref.insert(ref.begin() + pos, count, val); + + check_equal("randomized insert_index(count,val)"); + } + + ::fast_io::io::print("deque insert_index(count,val) test finished\n"); +} + +} // namespace + +int main() +{ + test_insert_middle(); + test_insert_count_value(); + test_insert_index_count_value(); +} \ No newline at end of file diff --git a/tests/0026.container/0003.deque/reserve.cc b/tests/0026.container/0003.deque/reserve.cc new file mode 100644 index 000000000..e0a844e23 --- /dev/null +++ b/tests/0026.container/0003.deque/reserve.cc @@ -0,0 +1,52 @@ +#include +#include +#include + +template +inline void logging(T const &deq, ::std::source_location src = ::std::source_location::current()) +{ + ::fast_io::io::perr(src, + "\n\tdeq.front_capacity()=", deq.front_capacity(), + "\n\tdeq.back_capacity()=", deq.back_capacity(), + "\n\tdeq.capacity()=", deq.capacity(), + "\n\tdeq.size()=", deq.size(), "\n\n"); + ::fast_io::io::debug_perr(src, + "\n", ::fast_io::manipulators::debug_view(deq), "\n\n"); +} + +int main() +{ + ::fast_io::deque<::std::size_t> deq; + deq.reserve_back(10000000); + logging(deq); + deq.clear_destroy(); + deq.push_back(30); + logging(deq); + deq.reserve_back(265); + logging(deq); + deq.reserve_back(6000); + logging(deq); + deq.reserve_back(6401); + logging(deq); + deq.clear_destroy(); + logging(deq); + deq.push_front(30); + logging(deq); + deq.reserve_front(265); + logging(deq); + deq.reserve_front(6000); + logging(deq); + deq.reserve_front(6401); + logging(deq); + deq.clear_destroy(); + logging(deq); + deq.reserve_front(512); + logging(deq); + deq.push_front(30); + deq.pop_back(); + logging(deq); + for (auto e : deq) + { + ::fast_io::io::println(e); + } +} diff --git a/tests/0026.container/0003.deque/resize.cc b/tests/0026.container/0003.deque/resize.cc new file mode 100644 index 000000000..d316925ae --- /dev/null +++ b/tests/0026.container/0003.deque/resize.cc @@ -0,0 +1,25 @@ +#include +#include + +template +inline void logging(T const &deq, ::std::source_location src = ::std::source_location::current()) +{ + ::fast_io::io::perr(src, + "\n\tdeq.front_capacity()=", deq.front_capacity(), + "\n\tdeq.back_capacity()=", deq.back_capacity(), + "\n\tdeq.capacity()=", deq.capacity(), + "\n\tdeq.size()=", deq.size(), "\n\n"); +} + +int main() +{ + ::fast_io::deque<::std::size_t> deq; + deq.push_back(30); + logging(deq); + deq.resize(265, 30); + logging(deq); + deq.resize(6000); + logging(deq); + deq.resize(6401); + logging(deq); +} diff --git a/tests/0026.container/0010.span/index_span.cc b/tests/0026.container/0010.span/index_span.cc index ff9519e4c..2e6649946 100644 --- a/tests/0026.container/0010.span/index_span.cc +++ b/tests/0026.container/0010.span/index_span.cc @@ -5,7 +5,7 @@ int main() { ::fast_io::array<::std::size_t, 40> arr{4, 6, 7}; - ::fast_io::index_span<::std::size_t, 5> sp(arr); + ::fast_io::index_span<::std::size_t, 5> sp(::fast_io::freestanding::from_range, arr); for (auto const &e : sp) { ::fast_io::io::println(e); diff --git a/tests/0026.container/0010.span/span.cc b/tests/0026.container/0010.span/span.cc index fcac1e46d..c781e8070 100644 --- a/tests/0026.container/0010.span/span.cc +++ b/tests/0026.container/0010.span/span.cc @@ -1,12 +1,12 @@ -#include -#include -#include +#include +#include +#include int main() { - ::fast_io::array<::std::size_t, 40> arr{4,6,7}; - ::fast_io::span sp(arr); - for(auto const & e : sp) + ::fast_io::array<::std::size_t, 40> arr{4, 6, 7}; + ::fast_io::span sp(::fast_io::freestanding::from_range, arr); + for (auto const &e : sp) { ::fast_io::io::println(e); }