#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace stdx { inline namespace v1 { template ())> class atomic_bitset { constexpr static std::size_t N = to_underlying(Size); using elem_t = atomic::atomic_type_t; constexpr static auto alignment = atomic::alignment_of; static_assert(std::is_unsigned_v, "Storage element for atomic_bitset must be an unsigned type"); constexpr static auto bit = elem_t{1U}; static_assert(N <= std::numeric_limits::digits, "atomic_bitset is limited to a single storage element"); alignas(alignment) elem_t storage{}; constexpr static auto mask = bit_mask(); auto salient_value(std::memory_order order) const -> elem_t { return atomic::load(storage, order) & mask; } [[nodiscard]] static constexpr auto value_from_string(std::string_view str, std::size_t pos, std::size_t n, char one) -> elem_t { elem_t ret{}; auto const len = std::min(n, str.size() - pos); auto const s = str.substr(pos, std::min(len, N)); auto i = bit; // NOLINTNEXTLINE(modernize-loop-convert) for (auto it = std::rbegin(s); it != std::rend(s); ++it) { if (*it == one) { ret |= i; } i = static_cast(i << 1u); } return ret; } using bitset_t = bitset; public: constexpr atomic_bitset() = default; constexpr explicit atomic_bitset(std::uint64_t value) : storage{static_cast(value & mask)} {} template constexpr explicit atomic_bitset(place_bits_t, Bs... bs) : storage{static_cast( (elem_t{} | ... | static_cast(bit << to_underlying(bs))))} {} constexpr explicit atomic_bitset(all_bits_t) : storage{mask} {} constexpr explicit atomic_bitset(std::string_view str, std::size_t pos = 0, std::size_t n = std::string_view::npos, char one = '1') : storage{value_from_string(str, pos, n, one)} {} #if __cplusplus >= 202002L constexpr explicit atomic_bitset(ct_string s) : atomic_bitset{static_cast(s)} {} #endif template [[nodiscard]] auto to(std::memory_order order = std::memory_order_seq_cst) const -> T { using U = underlying_type_t; static_assert( unsigned_integral, "Conversion must be to an unsigned integral type or enum!"); static_assert(N <= std::numeric_limits::digits, "atomic_bitset must fit within T"); return static_cast(salient_value(order)); } [[nodiscard]] auto to_natural(std::memory_order order = std::memory_order_seq_cst) const -> StorageElem { return static_cast(salient_value(order)); } // NOLINTNEXTLINE(google-explicit-constructor) operator bitset_t() const { return bitset_t{salient_value(std::memory_order_seq_cst)}; } auto load(std::memory_order order = std::memory_order_seq_cst) const -> bitset_t { return bitset_t{salient_value(order)}; } auto store(bitset_t b, std::memory_order order = std::memory_order_seq_cst) { atomic::store(storage, b.template to(), order); } constexpr static std::integral_constant size{}; template [[nodiscard]] auto operator[](T idx) const -> bool { return load()[idx]; } template auto set(T idx, bool value = true, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const pos = static_cast(to_underlying(idx)); if (value) { return bitset_t{atomic::fetch_or( storage, static_cast(bit << pos), order)}; } return bitset_t{atomic::fetch_and( storage, static_cast(~(bit << pos)), order)}; } auto set(lsb_t lsb, msb_t msb, bool value = true, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const l = to_underlying(lsb); auto const m = to_underlying(msb); auto const shifted_value = bit_mask(m, l); if (value) { return bitset_t{atomic::fetch_or(storage, shifted_value, order)}; } return bitset_t{atomic::fetch_and(storage, ~shifted_value, order)}; } auto set(lsb_t lsb, length_t len, bool value = true, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const l = to_underlying(lsb); auto const length = to_underlying(len); return set(lsb, static_cast(l + length - 1), value, order); } auto set(std::memory_order order = std::memory_order_seq_cst) LIFETIMEBOUND -> atomic_bitset & { atomic::store(storage, mask, order); return *this; } template auto reset(T idx, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const pos = static_cast(to_underlying(idx)); return bitset_t{atomic::fetch_and( storage, static_cast(~(bit << pos)), order)}; } auto reset(lsb_t lsb, msb_t msb, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const l = to_underlying(lsb); auto const m = to_underlying(msb); auto const shifted_value = bit_mask(m, l); return bitset_t{atomic::fetch_and(storage, ~shifted_value, order)}; } auto reset(lsb_t lsb, length_t len, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const l = to_underlying(lsb); auto const length = to_underlying(len); return reset(lsb, static_cast(l + length - 1), order); } auto reset(std::memory_order order = std::memory_order_seq_cst) LIFETIMEBOUND -> atomic_bitset & { atomic::store(storage, elem_t{}, order); return *this; } template auto flip(T idx, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const pos = static_cast(to_underlying(idx)); return bitset_t{ atomic::fetch_xor(storage, static_cast(bit << pos), order)}; } auto flip(lsb_t lsb, msb_t msb, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const l = to_underlying(lsb); auto const m = to_underlying(msb); auto const shifted_value = bit_mask(m, l); return bitset_t{atomic::fetch_xor(storage, shifted_value, order)}; } auto flip(lsb_t lsb, length_t len, std::memory_order order = std::memory_order_seq_cst) -> bitset_t { auto const l = to_underlying(lsb); auto const length = to_underlying(len); return flip(lsb, static_cast(l + length - 1), order); } auto flip(std::memory_order order = std::memory_order_seq_cst) -> bitset_t { return bitset_t{atomic::fetch_xor(storage, mask, order)}; } [[nodiscard]] auto all(std::memory_order order = std::memory_order_seq_cst) const -> bool { return salient_value(order) == mask; } [[nodiscard]] auto any(std::memory_order order = std::memory_order_seq_cst) const -> bool { return salient_value(order) != 0; } [[nodiscard]] auto none(std::memory_order order = std::memory_order_seq_cst) const -> bool { return salient_value(order) == 0; } [[nodiscard]] auto count(std::memory_order order = std::memory_order_seq_cst) const -> std::size_t { return static_cast(popcount(salient_value(order))); } }; #if __cplusplus >= 202002L template atomic_bitset(ct_string) -> atomic_bitset; #endif } // namespace v1 } // namespace stdx