diff --git a/include/fast_io_core.h b/include/fast_io_core.h index e2ed5141..5baeecc5 100644 --- a/include/fast_io_core.h +++ b/include/fast_io_core.h @@ -85,6 +85,7 @@ #include "fast_io_core_impl/integers/chrono.h" #include "fast_io_core_impl/iso/isos.h" +#include "fast_io_core_impl/linux_statx_definitions/impl.h" #include "fast_io_core_impl/enums/impl.h" #ifndef FAST_IO_DISABLE_CODECVT diff --git a/include/fast_io_core_impl/linux_statx_definitions/impl.h b/include/fast_io_core_impl/linux_statx_definitions/impl.h new file mode 100644 index 00000000..7ba843da --- /dev/null +++ b/include/fast_io_core_impl/linux_statx_definitions/impl.h @@ -0,0 +1,8 @@ +#pragma once +/* +https://www.man7.org/linux//man-pages/man2/statx.2.html +*/ +#include "linux_statx_timestamp.h" +#include "linux_statx.h" +#include "linux_statx_flags.h" +#include "linux_statx_mask.h" diff --git a/include/fast_io_core_impl/linux_statx_definitions/linux_statx.h b/include/fast_io_core_impl/linux_statx_definitions/linux_statx.h new file mode 100644 index 00000000..2dbc7bb8 --- /dev/null +++ b/include/fast_io_core_impl/linux_statx_definitions/linux_statx.h @@ -0,0 +1,59 @@ +#pragma once + +namespace fast_io +{ + +struct linux_statxbuf +{ + ::std::uint_least32_t stx_mask; /* Mask of bits indicating + filled fields */ + ::std::uint_least32_t stx_blksize; /* Block size for filesystem I/O */ + ::std::uint_least64_t stx_attributes; /* Extra file attribute indicators */ + ::std::uint_least32_t stx_nlink; /* Number of hard links */ + ::std::uint_least32_t stx_uid; /* User ID of owner */ + ::std::uint_least32_t stx_gid; /* Group ID of owner */ + ::std::uint_least16_t stx_mode; /* File type and mode */ + ::std::uint_least64_t stx_ino; /* Inode number */ + ::std::uint_least64_t stx_size; /* Total size in bytes */ + ::std::uint_least64_t stx_blocks; /* Number of 512B blocks allocated */ + ::std::uint_least64_t stx_attributes_mask; + /* Mask to show what's supported + in stx_attributes */ + + /* The following fields are file timestamps */ + ::fast_io::linux_statx_timestamp stx_atime; /* Last access */ + ::fast_io::linux_statx_timestamp stx_btime; /* Creation */ + ::fast_io::linux_statx_timestamp stx_ctime; /* Last status change */ + ::fast_io::linux_statx_timestamp stx_mtime; /* Last modification */ + + /* If this file represents a device, then the next two + fields contain the ID of the device */ + ::std::uint_least32_t stx_rdev_major; /* Major ID */ + ::std::uint_least32_t stx_rdev_minor; /* Minor ID */ + + /* The next two fields contain the ID of the device + containing the filesystem where the file resides */ + ::std::uint_least32_t stx_dev_major; /* Major ID */ + ::std::uint_least32_t stx_dev_minor; /* Minor ID */ + + ::std::uint_least64_t stx_mnt_id; /* Mount ID */ + + /* Direct I/O alignment restrictions */ + ::std::uint_least32_t stx_dio_mem_align; + ::std::uint_least32_t stx_dio_offset_align; + + ::std::uint_least64_t stx_subvol; /* Subvolume identifier */ + + /* Direct I/O atomic write limits */ + ::std::uint_least32_t stx_atomic_write_unit_min; + ::std::uint_least32_t stx_atomic_write_unit_max; + ::std::uint_least32_t stx_atomic_write_segments_max; + + /* File offset alignment for direct I/O reads */ + ::std::uint_least32_t stx_dio_read_offset_align; + + /* Direct I/O atomic write max opt limit */ + ::std::uint_least32_t stx_atomic_write_unit_max_opt; +}; + +} // namespace fast_io diff --git a/include/fast_io_core_impl/linux_statx_definitions/linux_statx_flags.h b/include/fast_io_core_impl/linux_statx_definitions/linux_statx_flags.h new file mode 100644 index 00000000..99847ff0 --- /dev/null +++ b/include/fast_io_core_impl/linux_statx_definitions/linux_statx_flags.h @@ -0,0 +1,57 @@ +#pragma once + +namespace fast_io +{ + +enum class linux_statx_flags : ::std::uint_least32_t +{ + at_empty_path = 0x1000, // AT_EMPTY_PATH + at_no_automount = 0x800, // AT_NO_AUTOMOUNT + at_symlink_nofollow = 0x100, // AT_SYMLINK_NOFOLLOW + + // statx-specific sync flags + at_statx_sync_as_stat = 0x0000, // AT_STATX_SYNC_AS_STAT + at_statx_force_sync = 0x2000, // AT_STATX_FORCE_SYNC + at_statx_dont_sync = 0x4000 // AT_STATX_DONT_SYNC +}; + +inline constexpr ::fast_io::linux_statx_flags operator&(::fast_io::linux_statx_flags x, ::fast_io::linux_statx_flags y) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_flags>::type; + return static_cast<::fast_io::linux_statx_flags>(static_cast(x) & static_cast(y)); +} + +inline constexpr ::fast_io::linux_statx_flags operator|(::fast_io::linux_statx_flags x, ::fast_io::linux_statx_flags y) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_flags>::type; + return static_cast<::fast_io::linux_statx_flags>(static_cast(x) | static_cast(y)); +} + +inline constexpr ::fast_io::linux_statx_flags operator^(::fast_io::linux_statx_flags x, ::fast_io::linux_statx_flags y) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_flags>::type; + return static_cast<::fast_io::linux_statx_flags>(static_cast(x) ^ static_cast(y)); +} + +inline constexpr ::fast_io::linux_statx_flags operator~(::fast_io::linux_statx_flags x) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_flags>::type; + return static_cast<::fast_io::linux_statx_flags>(~static_cast(x)); +} + +inline constexpr ::fast_io::linux_statx_flags &operator&=(::fast_io::linux_statx_flags &x, ::fast_io::linux_statx_flags y) noexcept +{ + return x = x & y; +} + +inline constexpr ::fast_io::linux_statx_flags &operator|=(::fast_io::linux_statx_flags &x, ::fast_io::linux_statx_flags y) noexcept +{ + return x = x | y; +} + +inline constexpr ::fast_io::linux_statx_flags &operator^=(::fast_io::linux_statx_flags &x, ::fast_io::linux_statx_flags y) noexcept +{ + return x = x ^ y; +} + +} // namespace fast_io diff --git a/include/fast_io_core_impl/linux_statx_definitions/linux_statx_mask.h b/include/fast_io_core_impl/linux_statx_definitions/linux_statx_mask.h new file mode 100644 index 00000000..94ac5f66 --- /dev/null +++ b/include/fast_io_core_impl/linux_statx_definitions/linux_statx_mask.h @@ -0,0 +1,68 @@ +#pragma once + +namespace fast_io +{ + +enum class linux_statx_mask : ::std::uint_least32_t +{ + statx_type = 0x00000001, // Request file type information (stx_mode & S_IFMT) + statx_mode = 0x00000002, // Request file mode/permission bits (stx_mode & ~S_IFMT) + statx_nlink = 0x00000004, // Request number of hard links + statx_uid = 0x00000008, // Request owning user ID + statx_gid = 0x00000010, // Request owning group ID + statx_atime = 0x00000020, // Request last access timestamp + statx_mtime = 0x00000040, // Request last modification timestamp + statx_ctime = 0x00000080, // Request inode status change timestamp + statx_ino = 0x00000100, // Request inode number + statx_size = 0x00000200, // Request file size in bytes + statx_blocks = 0x00000400, // Request number of 512-byte blocks allocated + statx_basic_stats = 0x000007ff, // Request all basic attributes (type through blocks) + statx_btime = 0x00000800, // Request file creation (birth) timestamp + statx_mnt_id = 0x00001000, // Request mount ID of the filesystem + statx_dioalign = 0x00002000, // Request direct I/O alignment constraints + statx_mnt_id_unique = 0x00004000, // Request unique mount ID (stronger than stx_mnt_id) + statx_subvol = 0x00008000, // Request subvolume identifier (e.g., btrfs) + statx_write_atomic = 0x00010000, // Request atomic write capability information + statx_dio_read_align = 0x00020000 // Request direct I/O read offset alignment +}; + +inline constexpr ::fast_io::linux_statx_mask operator&(::fast_io::linux_statx_mask x, ::fast_io::linux_statx_mask y) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_mask>::type; + return static_cast<::fast_io::linux_statx_mask>(static_cast(x) & static_cast(y)); +} + +inline constexpr ::fast_io::linux_statx_mask operator|(::fast_io::linux_statx_mask x, ::fast_io::linux_statx_mask y) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_mask>::type; + return static_cast<::fast_io::linux_statx_mask>(static_cast(x) | static_cast(y)); +} + +inline constexpr ::fast_io::linux_statx_mask operator^(::fast_io::linux_statx_mask x, ::fast_io::linux_statx_mask y) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_mask>::type; + return static_cast<::fast_io::linux_statx_mask>(static_cast(x) ^ static_cast(y)); +} + +inline constexpr ::fast_io::linux_statx_mask operator~(::fast_io::linux_statx_mask x) noexcept +{ + using utype = typename ::std::underlying_type<::fast_io::linux_statx_mask>::type; + return static_cast<::fast_io::linux_statx_mask>(~static_cast(x)); +} + +inline constexpr ::fast_io::linux_statx_mask &operator&=(::fast_io::linux_statx_mask &x, ::fast_io::linux_statx_mask y) noexcept +{ + return x = x & y; +} + +inline constexpr ::fast_io::linux_statx_mask &operator|=(::fast_io::linux_statx_mask &x, ::fast_io::linux_statx_mask y) noexcept +{ + return x = x | y; +} + +inline constexpr ::fast_io::linux_statx_mask &operator^=(::fast_io::linux_statx_mask &x, ::fast_io::linux_statx_mask y) noexcept +{ + return x = x ^ y; +} + +} // namespace fast_io diff --git a/include/fast_io_core_impl/linux_statx_definitions/linux_statx_timestamp.h b/include/fast_io_core_impl/linux_statx_definitions/linux_statx_timestamp.h new file mode 100644 index 00000000..c399c08d --- /dev/null +++ b/include/fast_io_core_impl/linux_statx_definitions/linux_statx_timestamp.h @@ -0,0 +1,57 @@ +#pragma once + +namespace fast_io +{ + +struct linux_statx_timestamp +{ + ::std::int_least64_t tv_sec; // Seconds since the Epoch (UNIX time) + ::std::uint_least32_t tv_nsec; // Nanoseconds since tv_sec +}; + +#if 0 +template +requires requires(T t) +{ + T::rep; + T::period; + t.count(); +} +inline constexpr linux_statx_timestamp linux_statx_timestamp_from_duration_std(T dur) noexcept +{ + using rep = typename T::rep; + using period = typename T::period; + + // total count + rep c = dur.count(); + + // Convert to seconds and nanoseconds WITHOUT overflow + // tv_sec = c * period::num / period::den + // tv_nsec = remainder * 1'000'000'000 / period::den + + // Step 1: compute whole seconds + // We do integer division first to avoid overflow. + constexpr std::int_least64_t num = period::num; + constexpr std::int_least64_t den = period::den; + + // seconds = floor(c * num / den) + // but compute as (c / den) * num + (c % den) * num / den + std::int_least64_t sec = (static_cast(c) / den) * num; + + // remainder part + std::int_least64_t rem = static_cast(c) % den; + + sec += (rem * num) / den; + + // Step 2: compute nanoseconds + // nsec = ((rem * num) % den) * 1e9 / den + std::int_least64_t rem2 = (rem * num) % den; + + std::uint_least32_t nsec = + static_cast((rem2 * 1000000000LL) / den); + + return linux_statx_timestamp{sec, nsec}; +} +#endif + +} // namespace fast_io diff --git a/include/fast_io_dsal/impl/deque.h b/include/fast_io_dsal/impl/deque.h index b816d5cb..f376c3e4 100644 --- a/include/fast_io_dsal/impl/deque.h +++ b/include/fast_io_dsal/impl/deque.h @@ -364,7 +364,7 @@ inline constexpr ::std::ptrdiff_t deque_iter_difference_common(::fast_io::contai template inline constexpr ::std::size_t deque_iter_difference_unsigned_common(::fast_io::containers::details::deque_control_block const &a, ::fast_io::containers::details::deque_control_block const &b) noexcept { - ::std::size_t controllerdiff{a.controller_ptr - b.controller_ptr}; + ::std::size_t controllerdiff{static_cast<::std::size_t>(a.controller_ptr - b.controller_ptr)}; constexpr ::std::size_t blocksizedf{::fast_io::containers::details::deque_block_size}; return controllerdiff * blocksizedf + static_cast<::std::size_t>((a.curr_ptr - a.begin_ptr) + (b.begin_ptr - b.curr_ptr)); } @@ -2236,6 +2236,28 @@ inline constexpr auto operator<=>(deque const &lhs, deque +constexpr typename ::fast_io::containers::deque::size_type +erase(::fast_io::containers::deque &c, U const &value) +{ + auto ed{c.end()}; + auto it = ::std::remove(c.begin(), ed, value); + auto r = ::fast_io::containers::details::deque_iter_difference_unsigned_common(ed.itercontent, it.itercontent); + c.erase(it, ed); + return r; +} + +template +constexpr typename ::fast_io::containers::deque::size_type +erase_if(::fast_io::containers::deque &c, Pred pred) +{ + auto ed{c.end()}; + auto it = ::std::remove_if(c.begin(), ed, pred); + auto r = ::fast_io::containers::details::deque_iter_difference_unsigned_common(ed.itercontent, it.itercontent); + c.erase(it, ed); + return r; +} + } // namespace containers namespace freestanding diff --git a/tests/0026.container/0003.deque/erase.cc b/tests/0026.container/0003.deque/erase.cc index 66c4c7d4..7fd8be7b 100644 --- a/tests/0026.container/0003.deque/erase.cc +++ b/tests/0026.container/0003.deque/erase.cc @@ -307,10 +307,205 @@ inline void test_erase_index() ::fast_io::io::print("deque erase_index test finished\n"); } +inline void test_erase_value() +{ + ::fast_io::io::perr("=== deque erase(value) test ===\n"); + + using T = ::std::size_t; + ::fast_io::deque dq; + ::std::deque ref; + + // Fill initial data with repeated patterns + for (::std::size_t i{}; i != 300u; ++i) + { + dq.push_back(i % 10); + ref.push_back(i % 10); + } + + auto check_equal = [&](auto const &msg, + ::std::source_location src = ::std::source_location::current()) { + if (dq.size() != ref.size()) + { + ::fast_io::io::panicln(src, "size mismatch: ", msg); + } + + for (::std::size_t i{}; i != dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + ::fast_io::io::panicln(src, "value mismatch at ", i, " : ", msg); + } + } + }; + + // 1. Erase a value that appears many times + { + auto r1 = erase(dq, static_cast(3)); + auto r2 = std::erase(ref, static_cast(3)); + if (r1 != r2) + { + ::fast_io::io::panicln("erase(value) count mismatch"); + } + check_equal("erase(value) many occurrences"); + } + + // 2. Erase a value that appears once + { + dq.push_back(12345); + ref.push_back(12345); + + auto r1 = erase(dq, static_cast(12345)); + auto r2 = std::erase(ref, static_cast(12345)); + if (r1 != r2) + { + ::fast_io::io::panicln("erase(value) count mismatch (single)"); + } + check_equal("erase(value) single occurrence"); + } + + // 3. Erase a value that does not exist + { + auto r1 = erase(dq, static_cast(99999)); + auto r2 = std::erase(ref, static_cast(99999)); + if (r1 != r2) + { + ::fast_io::io::panicln("erase(value) count mismatch (none)"); + } + check_equal("erase(value) none"); + } + + // 4. Randomized erase(value) + for (std::size_t iter{}; iter != 200u; ++iter) + { + if (dq.empty()) + { + break; + } + + std::size_t val = iter % 15; + + auto r1 = erase(dq, static_cast(val)); + auto r2 = std::erase(ref, static_cast(val)); + + if (r1 != r2) + { + ::fast_io::io::panicln("erase(value) randomized count mismatch"); + } + + check_equal("erase(value) randomized"); + } + + ::fast_io::io::print("deque erase(value) test finished\n"); +} + +inline void test_erase_if() +{ + ::fast_io::io::perr("=== deque erase_if(pred) test ===\n"); + + using T = ::std::size_t; + ::fast_io::deque dq; + ::std::deque ref; + + // Fill initial data + for (::std::size_t i{}; i != 300u; ++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, "size mismatch: ", msg); + } + + for (::std::size_t i{}; i != dq.size(); ++i) + { + if (dq[i] != ref[i]) + { + ::fast_io::io::panicln(src, "value mismatch at ", i, " : ", msg); + } + } + }; + + // 1. Remove even numbers + { + auto pred = [](T x) { return (x % 2) == 0; }; + + auto r1 = erase_if(dq, pred); + auto r2 = std::erase_if(ref, pred); + + if (r1 != r2) + { + ::fast_io::io::panicln("erase_if(pred) count mismatch (even)"); + } + + check_equal("erase_if even"); + } + + // 2. Remove numbers divisible by 3 + { + auto pred = [](T x) { return (x % 3) == 0; }; + + auto r1 = erase_if(dq, pred); + auto r2 = std::erase_if(ref, pred); + + if (r1 != r2) + { + ::fast_io::io::panicln("erase_if(pred) count mismatch (div3)"); + } + + check_equal("erase_if divisible by 3"); + } + + // 3. Remove nothing + { + auto pred = [](T x) { return x == static_cast(999999); }; + + auto r1 = erase_if(dq, pred); + auto r2 = std::erase_if(ref, pred); + + if (r1 != r2) + { + ::fast_io::io::panicln("erase_if(pred) count mismatch (none)"); + } + + check_equal("erase_if none"); + } + + // 4. Randomized erase_if + for (std::size_t iter{}; iter != 200u; ++iter) + { + if (dq.empty()) + { + break; + } + + std::size_t mod = (iter % 7) + 2; + + auto pred = [&](T x) { return (x % mod) == 1; }; + + auto r1 = erase_if(dq, pred); + auto r2 = std::erase_if(ref, pred); + + if (r1 != r2) + { + ::fast_io::io::panicln("erase_if(pred) randomized count mismatch"); + } + + check_equal("erase_if randomized"); + } + + ::fast_io::io::print("deque erase_if(pred) test finished\n"); +} + } // namespace int main() { test_erase(); test_erase_index(); + test_erase_value(); + test_erase_if(); }