From b2b9f9c5eb5a950c9d53bc792aecf2ced1e944ed Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 20 May 2024 14:23:31 -0700 Subject: [PATCH 1/8] Simplify BitView and BitPosIterator --- src/longeron/containers/bit_iterator.hpp | 104 ++++++++++------------- src/longeron/containers/bit_view.hpp | 57 ++++--------- 2 files changed, 65 insertions(+), 96 deletions(-) diff --git a/src/longeron/containers/bit_iterator.hpp b/src/longeron/containers/bit_iterator.hpp index 851b533..3fe7339 100644 --- a/src/longeron/containers/bit_iterator.hpp +++ b/src/longeron/containers/bit_iterator.hpp @@ -11,49 +11,22 @@ namespace lgrn { -#if 0 +struct BitPosSentinel { }; /** - * @brief TODO + * @brief Iterate positions of ones bits or zeros bits within an integer range + * + * This works by storing an internal iterator to the specified int range, and saving the current + * value. Per iteration (operator++), this iterator will scan through the bits of the current value + * from LSB to MSB, then repeatedly increment its internal iterator to search for the next int + * containing ones or zeros bits. + * + * @warning Do not modify the integer range while this iterator is alive. */ -template -class BitIterator +template +class BitPosIterator { - using int_t = typename std::iterator_traits::value_type; - - static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); -public: - using iterator_category = std::random_access_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = bool; - using pointer = void; - using reference = void; - - constexpr BitIterator() noexcept = default; - constexpr BitIterator(IT_T it, int_t bit) noexcept - : m_it(it) - , m_posMask(int_t(0x1) << bit) - { }; - constexpr BitIterator(BitIterator const& copy) noexcept = default; - constexpr BitIterator(BitIterator&& move) noexcept = default; - - constexpr BitIterator operator=(BitIterator&& move) noexcept = default; - -private: - IT_T m_it; - int_t m_posMask; // single bit denotes position. ie 00000100 for pos 2 -}; - -#endif - -/** - * @brief Iterate ones bits or zeros bits as a set of integer position - */ -template -class BitPosIterator : private ADVANCE_SKIP_T // allow EBO -{ - using int_t = typename std::iterator_traits::value_type; + using int_t = typename std::iterator_traits::value_type; static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); public: @@ -65,16 +38,15 @@ class BitPosIterator : private ADVANCE_SKIP_T // allow EBO using reference = void; constexpr BitPosIterator() noexcept = default; - constexpr BitPosIterator(ADVANCE_SKIP_T&& skip, IT_T begin, ITB_T end, IT_T it, int_t bit, std::size_t dist) noexcept - : ADVANCE_SKIP_T {skip} - , m_begin {begin} - , m_end {end} +// constexpr BitPosIterator(BitPosSentinel const&) noexcept : BitPosIterator() { } + constexpr BitPosIterator(SNTL_T end, ITER_T it, int_t bit, std::size_t dist) noexcept + : m_end {end} , m_it {it} , m_distance {dist} { if ( (it != end) ) { - m_block = (int_t(int_t(int_t(~int_t(0x0)) << bit) & read())); + m_block = (int_t(int_t(int_t(~int_t(0x0)) << bit) & int_iter_value())); // increment to first valid bit if (m_block == 0) @@ -97,14 +69,28 @@ class BitPosIterator : private ADVANCE_SKIP_T // allow EBO // move to next int if no more bits left if (m_block == 0) { - std::size_t const dist = ADVANCE_SKIP_T::operator()(m_begin, m_end, m_it); - m_distance += dist; - m_block = (m_it != m_end) ? read() : 0; + // Skip empty blocks (no ones or no zero bits) + constexpr int_t c_emptyBlock = ONES ? int_t(0) : ~int_t(0); + do + { + ++m_it; + m_distance += sizeof(int_t) * 8; + } + while (m_it != m_end && *m_it == c_emptyBlock); + + m_block = (m_it != m_end) ? int_iter_value() : 0; } return *this; } + constexpr value_type operator*() const noexcept + { + return m_distance + ctz(m_block); + } + +private: + constexpr friend bool operator==(BitPosIterator const& lhs, BitPosIterator const& rhs) noexcept { @@ -117,14 +103,19 @@ class BitPosIterator : private ADVANCE_SKIP_T // allow EBO return !(lhs == rhs); } - constexpr value_type operator*() const noexcept + constexpr friend bool operator==(BitPosIterator const& lhs, + BitPosSentinel const& rhs) noexcept { - return m_distance + ctz(m_block); + return lhs.m_it == lhs.m_end; } -private: + constexpr friend bool operator!=(BitPosIterator const& lhs, + BitPosSentinel const& rhs) noexcept + { + return lhs.m_it != lhs.m_end; + } - constexpr int_t read() + constexpr int_t int_iter_value() { if constexpr (ONES) { @@ -137,12 +128,11 @@ class BitPosIterator : private ADVANCE_SKIP_T // allow EBO } } - IT_T m_begin; - ITB_T m_end; - - IT_T m_it; - std::size_t m_distance{0}; - int_t m_block{0}; + // End need to be stored becuase of the "skip forward until we find a bit" behaviour + SNTL_T const m_end; + ITER_T m_it; + std::size_t m_distance{~std::size_t(0)}; + int_t m_block{0}; }; diff --git a/src/longeron/containers/bit_view.hpp b/src/longeron/containers/bit_view.hpp index ca7ba72..6a70592 100644 --- a/src/longeron/containers/bit_view.hpp +++ b/src/longeron/containers/bit_view.hpp @@ -22,59 +22,38 @@ namespace lgrn template class BitView : private RANGE_T { - using it_t = decltype(std::cbegin(std::declval())); - using itb_t = decltype(std::cend(std::declval())); + using iter_t = decltype(std::cbegin(std::declval())); + using sntl_t = decltype(std::cend (std::declval())); - using int_t = std::remove_cv_t::value_type>; + using int_t = std::remove_cv_t::value_type>; static constexpr int smc_bitSize = sizeof(int_t) * 8; static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); - template - struct advance_skip - { - std::size_t operator()([[maybe_unused]] it_t begin, itb_t end, it_t& it) const noexcept - { - std::size_t dist = 0; - do - { - ++it; - dist += smc_bitSize; - } - while (it != end && *it == VALUE); - return dist; - } - }; - template class BitViewValues { - using ValueIt_t = std::conditional_t >, - BitPosIterator< it_t, itb_t, ONES, advance_skip > >; - + using ValueIt_t = BitPosIterator< iter_t, sntl_t, ONES >; public: - constexpr BitViewValues(BitView const* pView) - : m_pView{pView} + constexpr BitViewValues(BitView const& view) + : first{ std::cbegin(view.ints()) } + , last { std::cend (view.ints()) } { } constexpr ValueIt_t begin() const noexcept { - auto first = std::begin(m_pView->ints()); - auto last = std::end(m_pView->ints()); - return ValueIt_t({}, first, last, first, 0, 0); + return ValueIt_t(last, first, 0, 0); } - constexpr ValueIt_t end() const noexcept + + constexpr BitPosSentinel end() const noexcept { - auto first = std::begin(m_pView->ints()); - auto last = std::end(m_pView->ints()); - std::size_t const dist = std::distance(first, last) * smc_bitSize; - return ValueIt_t({}, first, last, last, 0, dist); + return BitPosSentinel{}; } private: - BitView const *m_pView; + iter_t first; + sntl_t last; }; public: @@ -94,8 +73,8 @@ class BitView : private RANGE_T constexpr std::size_t size() const noexcept; constexpr std::size_t count() const noexcept; - constexpr BitViewValues ones() const noexcept { return this; } - constexpr BitViewValues zeros() const noexcept { return this; } + constexpr BitViewValues ones() const noexcept { return {*this}; } + constexpr BitViewValues zeros() const noexcept { return {*this}; } constexpr RANGE_T& ints() noexcept { return static_cast(*this); } constexpr RANGE_T const& ints() const noexcept { return static_cast(*this); } @@ -156,7 +135,7 @@ template constexpr std::size_t BitView::count() const noexcept { std::size_t total = 0; - it_t it = std::begin(ints()); + iter_t it = std::begin(ints()); while (it != std::end(ints())) { total += std::bitset(*it).count(); @@ -165,8 +144,8 @@ constexpr std::size_t BitView::count() const noexcept return total; } -template -constexpr auto bit_view(IT_T first, ITB_T last) +template +constexpr auto bit_view(ITER_T first, SNTL_T last) { return BitView(IteratorPair(first, last)); } From df778fd6381bc482cb09f02c4e61f72030250e2f Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 20 May 2024 22:17:01 -0700 Subject: [PATCH 2/8] Remove registry.hpp --- src/longeron/id_management/registry.hpp | 115 ------------------------ 1 file changed, 115 deletions(-) delete mode 100644 src/longeron/id_management/registry.hpp diff --git a/src/longeron/id_management/registry.hpp b/src/longeron/id_management/registry.hpp deleted file mode 100644 index d2561da..0000000 --- a/src/longeron/id_management/registry.hpp +++ /dev/null @@ -1,115 +0,0 @@ -/** - * SPDX-License-Identifier: MIT - * SPDX-FileCopyrightText: 2021 Neal Nicdao - */ -#pragma once - -#include "null.hpp" -#include "../containers/hierarchical_bitset.hpp" -#include "../utility/enum_traits.hpp" -#include "../utility/asserts.hpp" - -namespace lgrn -{ - -/** - * @brief Generates reusable sequential IDs - * - * @deprecated use IdRegistryStl - */ -template -class IdRegistry -{ - using id_int_t = underlying_int_type_t; - -public: - - IdRegistry() = default; - IdRegistry(std::size_t capacity) { reserve(capacity); }; - - /** - * @brief Create a single ID - * - * @return A newly created ID - */ - [[nodiscard]] ID_T create() - { - ID_T output{ id_null() }; - create(&output, 1); - return output; - } - - /** - * @brief Create multiple IDs - * - * @param out [out] Iterator out - * @param count [in] Number of IDs to generate - */ - template - void create(IT_T out, std::size_t count); - - /** - * @return Size required to fit all existing IDs, or allocated size if - * reserved ahead of time - */ - constexpr std::size_t capacity() const noexcept { return m_deleted.size(); } - - /** - * @return Number of registered IDs - */ - constexpr std::size_t size() const { return capacity() - m_deleted.count(); } - - /** - * @brief Allocate space for at least n IDs - * - * @param n [in] Number of IDs to allocate for - */ - void reserve(std::size_t n) - { - m_deleted.resize(n, true); - } - - /** - * @brief Remove an ID. This will mark it for reuse - * - * @param id [in] ID to remove - */ - void remove(ID_T id) - { - LGRN_ASSERTMV(exists(id), "ID does not exist", std::size_t(id)); - - m_deleted.set(id_int_t(id)); - } - - /** - * @brief Check if an ID exists - * - * @param id [in] ID to check - * - * @return true if ID exists - */ - bool exists(ID_T id) const noexcept - { - return ! m_deleted.test(id_int_t(id)); - }; - -private: - HierarchicalBitset m_deleted; - -}; // class IdRegistry - -template -template -void IdRegistry::create(IT_T out, std::size_t count) -{ - if (m_deleted.count() < count) - { - // auto-resize - LGRN_ASSERTMV(!NO_AUTO_RESIZE, "Reached max capacity with automatic resizing disabled", count, capacity()); - reserve(std::max(capacity() + count, capacity() * 2)); - } - - m_deleted.take(out, count); -} - -} // namespace lgrn From 0283324fd319a0524fbe5e7419f3310a284f607d Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 20 May 2024 22:18:43 -0700 Subject: [PATCH 3/8] Improve more ID and bit stuff --- examples/circuits/circuit_builder.cpp | 2 +- src/longeron/containers/bit_iterator.hpp | 24 +-- src/longeron/containers/bit_view.hpp | 50 ++++-- .../id_management/bitview_registry.hpp | 85 +++++----- src/longeron/id_management/registry_stl.hpp | 150 +++++++++++++----- test/id_management/registry.cpp | 59 +++++-- test/intarray_multimap.cpp | 7 +- 7 files changed, 245 insertions(+), 132 deletions(-) diff --git a/examples/circuits/circuit_builder.cpp b/examples/circuits/circuit_builder.cpp index f5f9608..90ae524 100644 --- a/examples/circuits/circuit_builder.cpp +++ b/examples/circuits/circuit_builder.cpp @@ -40,7 +40,7 @@ ElementId gate_combinatinal(CombinationalGates::GateDesc desc, std::initializer_ void populate_pub_sub(Elements const& elements, Nodes &rNodes) { std::vector nodeSubCount(rNodes.m_nodeIds.capacity(), 0); // can we reach 1 million subscribers? - for (ElementId elem : elements.m_ids.bitview().zeros()) + for (ElementId elem : elements.m_ids) { auto nodes = rNodes.m_elemConnect[elem]; // skip first, port 0 is the publisher diff --git a/src/longeron/containers/bit_iterator.hpp b/src/longeron/containers/bit_iterator.hpp index 3fe7339..6af7413 100644 --- a/src/longeron/containers/bit_iterator.hpp +++ b/src/longeron/containers/bit_iterator.hpp @@ -11,35 +11,36 @@ namespace lgrn { -struct BitPosSentinel { }; - /** * @brief Iterate positions of ones bits or zeros bits within an integer range * * This works by storing an internal iterator to the specified int range, and saving the current * value. Per iteration (operator++), this iterator will scan through the bits of the current value - * from LSB to MSB, then repeatedly increment its internal iterator to search for the next int - * containing ones or zeros bits. + * from LSB to MSB. If there's no bits left, then the internal iterator will be repeatedly + * incremented to search for the next int containing ones or zeros bits. * * @warning Do not modify the integer range while this iterator is alive. */ -template +template class BitPosIterator { using int_t = typename std::iterator_traits::value_type; static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); public: + // TODO: consider bi-directional? using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = std::size_t; + using value_type = VALUE_T; using pointer = void; using reference = void; + struct Sentinel { }; + constexpr BitPosIterator() noexcept = default; // constexpr BitPosIterator(BitPosSentinel const&) noexcept : BitPosIterator() { } - constexpr BitPosIterator(SNTL_T end, ITER_T it, int_t bit, std::size_t dist) noexcept + constexpr BitPosIterator(ITER_T it, SNTL_T end, std::size_t dist, int bit) noexcept : m_end {end} , m_it {it} , m_distance {dist} @@ -86,7 +87,8 @@ class BitPosIterator constexpr value_type operator*() const noexcept { - return m_distance + ctz(m_block); + std::size_t const pos = m_distance + ctz(m_block); + return VALUE_T{pos}; } private: @@ -104,13 +106,13 @@ class BitPosIterator } constexpr friend bool operator==(BitPosIterator const& lhs, - BitPosSentinel const& rhs) noexcept + Sentinel const& rhs) noexcept { return lhs.m_it == lhs.m_end; } constexpr friend bool operator!=(BitPosIterator const& lhs, - BitPosSentinel const& rhs) noexcept + Sentinel const& rhs) noexcept { return lhs.m_it != lhs.m_end; } @@ -129,7 +131,7 @@ class BitPosIterator } // End need to be stored becuase of the "skip forward until we find a bit" behaviour - SNTL_T const m_end; + SNTL_T m_end; ITER_T m_it; std::size_t m_distance{~std::size_t(0)}; int_t m_block{0}; diff --git a/src/longeron/containers/bit_view.hpp b/src/longeron/containers/bit_view.hpp index 6a70592..ec46067 100644 --- a/src/longeron/containers/bit_view.hpp +++ b/src/longeron/containers/bit_view.hpp @@ -22,47 +22,60 @@ namespace lgrn template class BitView : private RANGE_T { - using iter_t = decltype(std::cbegin(std::declval())); - using sntl_t = decltype(std::cend (std::declval())); +public: - using int_t = std::remove_cv_t::value_type>; + using iter_t = decltype(std::cbegin(std::declval())); + using sntl_t = decltype(std::cend (std::declval())); + using int_t = std::remove_cv_t::value_type>; static constexpr int smc_bitSize = sizeof(int_t) * 8; static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); template - class BitViewValues + class BitPositionsRangeView { using ValueIt_t = BitPosIterator< iter_t, sntl_t, ONES >; + using Sentinel_t = typename ValueIt_t::Sentinel; public: - constexpr BitViewValues(BitView const& view) + constexpr BitPositionsRangeView(BitView const& view) : first{ std::cbegin(view.ints()) } , last { std::cend (view.ints()) } { } constexpr ValueIt_t begin() const noexcept { - return ValueIt_t(last, first, 0, 0); + return ValueIt_t(first, last, 0, 0); + } + + constexpr ValueIt_t begin_at(std::size_t const bitPos) const noexcept + { + auto const intPos = bitPos / 64; + auto const intBitPos = bitPos % 64; + + return ValueIt_t(std::next(first, intPos), last, 64*intPos, intBitPos); } - constexpr BitPosSentinel end() const noexcept + constexpr Sentinel_t end() const noexcept { - return BitPosSentinel{}; + return Sentinel_t{}; } private: iter_t first; sntl_t last; }; -public: - static constexpr std::size_t int_bitsize() noexcept { return smc_bitSize; } - constexpr BitView() = default; + constexpr BitView() = default; + constexpr BitView(BitView const& copy) = default; + constexpr BitView(BitView&& move) = default; constexpr BitView(RANGE_T range) : RANGE_T(range) { } + constexpr BitView& operator=(BitView const& copy) = default; + constexpr BitView& operator=(BitView&& move) = default; + constexpr bool test(std::size_t bit) const noexcept; constexpr void set(std::size_t bit) noexcept; @@ -73,10 +86,17 @@ class BitView : private RANGE_T constexpr std::size_t size() const noexcept; constexpr std::size_t count() const noexcept; - constexpr BitViewValues ones() const noexcept { return {*this}; } - constexpr BitViewValues zeros() const noexcept { return {*this}; } + /** + * @brief Return a range type (with begin/end functions) used to iterate positions of ones bits + */ + constexpr BitPositionsRangeView ones() const noexcept { return {*this}; } - constexpr RANGE_T& ints() noexcept { return static_cast(*this); } + /** + * @brief Return a range type (with begin/end functions) used to iterate positions of zeros bits + */ + constexpr BitPositionsRangeView zeros() const noexcept { return {*this}; } + + constexpr RANGE_T& ints() noexcept { return static_cast(*this); } constexpr RANGE_T const& ints() const noexcept { return static_cast(*this); } }; @@ -85,7 +105,7 @@ constexpr bool BitView::test(std::size_t bit) const noexcept { LGRN_ASSERTMV(bit < size(), "Bit position out of range", bit, size()); - std::size_t const block = bit / smc_bitSize; + std::size_t const block = bit / smc_bitSize; std::size_t const blockBit = bit % smc_bitSize; return bit_test(*std::next(std::begin(ints()), block), blockBit); diff --git a/src/longeron/id_management/bitview_registry.hpp b/src/longeron/id_management/bitview_registry.hpp index 7924452..479318c 100644 --- a/src/longeron/id_management/bitview_registry.hpp +++ b/src/longeron/id_management/bitview_registry.hpp @@ -15,26 +15,28 @@ namespace lgrn { /** - * @brief Adapts a bitview to an interface for creating/destroying unique - * integer IDs + * @brief Adapts a bitview to an interface for creating/destroying unique integer IDs * - * Ones are used as free IDs, and zeros are used as taken. This is because - * the bitwise operations used are slightly faster at searching for ones. + * Ones are used as free IDs, and zeros are used as taken. This is because the bitwise operations + * used are slightly faster at searching for ones. */ -template -class BitViewIdRegistry : private BITVIEW_T +template +class BitViewIdRegistry : private BitView { - using id_int_t = underlying_int_type_t; - public: + + using id_int_t = underlying_int_type_t; + using base_t = BitView; + + using IdIterator_t = BitPosIterator; + + BitViewIdRegistry() = default; - BitViewIdRegistry(BITVIEW_T bitview) : BITVIEW_T(bitview) {}; - BitViewIdRegistry(BITVIEW_T bitview, [[maybe_unused]] ID_T) : BITVIEW_T(bitview) {}; + BitViewIdRegistry(base_t bitview) : base_t(bitview) {}; + BitViewIdRegistry(base_t bitview, [[maybe_unused]] ID_T) : base_t(bitview) {}; /** * @brief Create a single ID - * - * @return A newly created ID */ [[nodiscard]] ID_T create() { @@ -44,67 +46,68 @@ class BitViewIdRegistry : private BITVIEW_T } /** - * @brief Create multiple IDs + * @brief Create multiple IDs, store new IDs in a specified range * - * @param first [out] - * @param last [in] + * The number of IDs created will be limited by available space for IDs and size of the range. */ - template - IT_T create(IT_T first, ITB_T last); + template + ITER_T create(ITER_T first, SNTL_T last); /** - * @return Size required to fit all existing IDs, or allocated size if - * reserved ahead of time + * @return Max number of IDs that can be stored */ - constexpr std::size_t capacity() const noexcept { return BITVIEW_T::size(); } + constexpr std::size_t capacity() const noexcept { return base_t::size(); } /** - * @return Number of registered IDs + * @return Current number of registered IDs, determined by counting bits */ - constexpr std::size_t size() const { return capacity() - BITVIEW_T::count(); } + constexpr std::size_t size() const { return capacity() - base_t::count(); } /** * @brief Remove an ID. This will mark it for reuse - * - * @param id [in] ID to remove */ - void remove(ID_T id) + void remove(ID_T id) noexcept { LGRN_ASSERTMV(exists(id), "ID does not exist", std::size_t(id)); - - BITVIEW_T::set(id_int_t(id)); + base_t::set(id_int_t(id)); } /** * @brief Check if an ID exists - * - * @param id [in] ID to check - * - * @return true if ID exists */ bool exists(ID_T id) const noexcept { - return (id_int_t(id) < capacity()) && ( ! BITVIEW_T::test(id_int_t(id))); + return (id_int_t(id) < capacity()) && ( ! base_t::test(id_int_t(id)) ); }; - constexpr BITVIEW_T& bitview() noexcept { return static_cast(*this); } - constexpr BITVIEW_T const& bitview() const noexcept { return static_cast(*this); } + /** + * @brief Iterate all existing IDs. Read-only + */ + constexpr IdIterator_t begin() const noexcept + { + return IdIterator_t(std::begin(bitview().ints()), std::end(bitview().ints()), 0, 0); + } + + constexpr typename IdIterator_t::Sentinel end() const noexcept { return {}; } + + constexpr base_t& bitview() noexcept { return static_cast(*this); } + constexpr base_t const& bitview() const noexcept { return static_cast(*this); } }; -template -template -IT_T BitViewIdRegistry::create(IT_T first, ITB_T last) +template +template +ITER_T BitViewIdRegistry::create(ITER_T first, SNTL_T last) { - auto ones = BITVIEW_T::ones(); - auto onesFirst = std::begin(ones); - auto onesLast = std::end(ones); + auto const &ones = base_t::ones(); + auto onesFirst = ones.begin(); + auto const &onesLast = ones.end(); while ( (first != last) && (onesFirst != onesLast) ) { std::size_t const pos = *onesFirst; *first = ID_T(pos); - BITVIEW_T::reset(pos); + base_t::reset(pos); std::advance(onesFirst, 1); std::advance(first, 1); diff --git a/src/longeron/id_management/registry_stl.hpp b/src/longeron/id_management/registry_stl.hpp index a803b51..58007fb 100644 --- a/src/longeron/id_management/registry_stl.hpp +++ b/src/longeron/id_management/registry_stl.hpp @@ -4,45 +4,65 @@ */ #pragma once - #include "bitview_registry.hpp" #include "../containers/bit_view.hpp" #include "../utility/bitmath.hpp" - - #include namespace lgrn { - /** - * @brief STL-style owning IdRegistry with underlying std::vector + * @brief Manages sequential integer IDs usable as array indices with automatic reallocation. Uses + * std::vector internally. */ template > > -class IdRegistryStl : private BitViewIdRegistry + typename RANGE_T = std::vector > +class IdRegistryStl : private BitViewIdRegistry { - using underlying_t = BitViewIdRegistry; +public: - friend underlying_t; + using base_t = BitViewIdRegistry; + using bitview_t = typename base_t::base_t; -public: + class Generator + { + using Iterator_t = BitPosIterator< typename bitview_t::iter_t, typename bitview_t::sntl_t, true >; + public: + Generator(IdRegistryStl &rRegistry) + : iter{rRegistry.bitview().ones().begin()} + , rRegistry{rRegistry} + { } + + [[nodiscard]] ID_T create(); + + ID_T operator()() { return create(); } + + private: + Iterator_t iter; + IdRegistryStl &rRegistry; + }; + + friend Generator; IdRegistryStl() = default; - IdRegistryStl(underlying_t reg) : underlying_t(reg) { } + IdRegistryStl(base_t reg) : base_t(reg) { } - using underlying_t::bitview; - using underlying_t::capacity; - using underlying_t::size; - using underlying_t::remove; - using underlying_t::exists; + using base_t::begin; + using base_t::bitview; + using base_t::capacity; + using base_t::end; + using base_t::exists; + using base_t::remove; + using base_t::size; /** * @brief Create a single ID * - * @return A newly created ID + * This will automatically reallocate to fit more IDs unless NO_AUTO_RESIZE is enabled. + * + * @return New ID created or potentially null only if NO_AUTO_RESIZE is enabled */ [[nodiscard]] ID_T create() { @@ -52,60 +72,102 @@ class IdRegistryStl : private BitViewIdRegistry } /** - * @brief Create multiple IDs + * @brief Create multiple IDs, store new IDs in a specified range + * + * The number of IDs created will be limited by the size of the range. + * + * This will automatically reallocate to fit more IDs unless NO_AUTO_RESIZE is enabled. * - * @param first [out] - * @param last [in] + * @return Iterator that is one past the last value written to */ - template - IT_T create(IT_T first, ITB_T last); + template + ITER_T create(ITER_T first, SNTL_T last); - void reserve(std::size_t n) + /** + * @brief Create a type used to efficiently create multiple IDs with individual function calls + */ + Generator generator() { - // may need some considerations when hierarchical bitsets are re-added + return Generator{*this}; + } + void reserve(std::size_t n) + { // Resize with all new bits set, as 1 is for free Id - vec().resize(lgrn::div_ceil(n, underlying_t::bitview().int_bitsize()), - ~uint64_t(0)); + vec().resize(lgrn::div_ceil(n, base_t::bitview().int_bitsize()), ~uint64_t(0)); } - [[nodiscard]] constexpr auto& vec() noexcept { return underlying_t::bitview().ints(); } - [[nodiscard]] constexpr auto const& vec() const noexcept { return underlying_t::bitview().ints(); } + [[nodiscard]] constexpr auto& vec() noexcept { return base_t::bitview().ints(); } + [[nodiscard]] constexpr auto const& vec() const noexcept { return base_t::bitview().ints(); } + +private: + + /** + * @brief Expand capacity using std::vectors automatic reallocation logic in push_back + */ + void reserve_auto() + { + // initialize to all bits set, as ones are for free IDs + // semi-jank way to use vector's automatic reallocation logic + vec().push_back(~uint64_t(0)); + vec().resize(vec().capacity(), ~uint64_t(0)); + } }; -template -template -IT_T IdRegistryStl::create(IT_T first, ITB_T last) +template +template +ITER_T IdRegistryStl::create(ITER_T first, SNTL_T last) { if constexpr (NO_AUTO_RESIZE) { - return underlying_t::create(first, last); + return base_t::create(first, last); } else { while (true) { - first = underlying_t::create(first, last); + first = base_t::create(first, last); - if (first != last) - { - // out of space, not all IDs were created - - // initialize to all bits set, as ones are for free IDs - // semi-jank way to use vector's automatic reallocation logic - vec().push_back(~uint64_t(0)); - vec().resize(vec().capacity(), ~uint64_t(0)); - - } - else + if (first == last) { break; } + + // underlying_t::create didn't could fill the entire range. Out of space! + // Reallocate then retry. + reserve_auto(); } return first; } } +template +ID_T IdRegistryStl::Generator::create() +{ + using Sentinel_t = typename Generator::Iterator_t::Sentinel; + + if (iter == Sentinel_t{}) // If out of space + { + if constexpr (NO_AUTO_RESIZE) + { + return id_null(); + } + else + { + auto const prevCapacity = rRegistry.capacity(); + rRegistry.reserve_auto(); + iter = rRegistry.bitview().ones().begin_at(prevCapacity); + LGRN_ASSERT(iter != Sentinel_t{}); + } + } + + std::size_t const outInt = *iter; + ++iter; + + rRegistry.bitview().reset(outInt); + + return ID_T{outInt}; +} } // namespace lgrn diff --git a/test/id_management/registry.cpp b/test/id_management/registry.cpp index 9463bc5..e96f14b 100644 --- a/test/id_management/registry.cpp +++ b/test/id_management/registry.cpp @@ -24,33 +24,58 @@ TEST(IdRegistry, ManageIds) Id idB = registry.create(); Id idC = registry.create(); - EXPECT_TRUE( registry.exists(idA) ); - EXPECT_TRUE( registry.exists(idB) ); - EXPECT_TRUE( registry.exists(idC) ); - - EXPECT_EQ( registry.size(), 3 ); - - ASSERT_NE( idA, idB ); - ASSERT_NE( idB, idC ); - ASSERT_NE( idC, idA ); + ASSERT_EQ(std::size_t(idA), 0); + ASSERT_EQ(std::size_t(idB), 1); + ASSERT_EQ(std::size_t(idC), 2); + ASSERT_TRUE( registry.exists(idA) ); + ASSERT_TRUE( registry.exists(idB) ); + ASSERT_TRUE( registry.exists(idC) ); + ASSERT_EQ( registry.size(), 3 ); registry.remove( idB ); - EXPECT_TRUE( registry.exists(idA) ); - EXPECT_FALSE( registry.exists(idB) ); - EXPECT_TRUE( registry.exists(idC) ); + ASSERT_TRUE( registry.exists(idA) ); + ASSERT_FALSE( registry.exists(idB) ); + ASSERT_TRUE( registry.exists(idC) ); + ASSERT_EQ( registry.size(), 2 ); - EXPECT_EQ( registry.size(), 2 ); + idB = registry.create(); - std::array ids; - registry.create(std::begin(ids), std::end(ids)); + ASSERT_EQ(std::size_t(idB), 1); + ASSERT_TRUE( registry.exists(idA) ); + ASSERT_TRUE( registry.exists(idB) ); + ASSERT_TRUE( registry.exists(idC) ); + ASSERT_EQ( registry.size(), 3 ); - for (Id id : ids) + std::array idsArray; + registry.create(std::begin(idsArray), std::end(idsArray)); + + for (Id id : idsArray) { EXPECT_TRUE( registry.exists(id) ); } - EXPECT_EQ( registry.size(), 34 ); + ASSERT_TRUE(std::equal(idsArray.begin(), idsArray.end(), std::next(registry.begin(), 3))); + + ASSERT_EQ( registry.size(), 3+128 ); +} + +// IdRegistryStl generator +TEST(IdRegistry, Generator) +{ + lgrn::IdRegistryStl registry; + + auto generator = registry.generator(); + + for (std::size_t expectedId = 0; expectedId < 10000; ++expectedId) + { + Id const id = generator.create(); + + ASSERT_EQ(std::size_t(id), expectedId); + ASSERT_TRUE( registry.exists(id) ); + } + + ASSERT_EQ( registry.size(), 10000 ); } // A more chaotic test of repeated adding a random amount of new IDs then diff --git a/test/intarray_multimap.cpp b/test/intarray_multimap.cpp index 6aec029..64a19e6 100644 --- a/test/intarray_multimap.cpp +++ b/test/intarray_multimap.cpp @@ -3,7 +3,7 @@ * SPDX-FileCopyrightText: 2021 Neal Nicdao */ #include -#include +#include #include @@ -12,7 +12,7 @@ #include #include -using lgrn::IdRegistry; +using lgrn::IdRegistryStl; using lgrn::IntArrayMultiMap; using id_t = unsigned int; @@ -184,7 +184,8 @@ TEST(IntArrayMultiMap, RandomCreationAndDeletion) std::unordered_map< id_t, std::vector > control; IntArrayMultiMap multimap(sc_idMax * sc_createMax, sc_idMax); - IdRegistry ids(sc_idMax); + IdRegistryStl ids; + ids.reserve(sc_idMax); for (int i = 0; i < sc_repetitions; i ++) { From 48f3bbfa966053876cf7d7e16066217bbc199475 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 20 May 2024 23:57:27 -0700 Subject: [PATCH 4/8] Add KeyedVec from osp-magnum --- src/longeron/id_management/keyed_vec_stl.hpp | 65 ++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/longeron/id_management/keyed_vec_stl.hpp diff --git a/src/longeron/id_management/keyed_vec_stl.hpp b/src/longeron/id_management/keyed_vec_stl.hpp new file mode 100644 index 0000000..c1513f9 --- /dev/null +++ b/src/longeron/id_management/keyed_vec_stl.hpp @@ -0,0 +1,65 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2024 Neal Nicdao + */ +#pragma once + +#include "../utility/enum_traits.hpp" + +#include + +namespace lgrn +{ + +/** + * @brief Wraps an std::vector intended to be accessed using a (strong typedef) enum class ID + */ +template > +class KeyedVec : public std::vector +{ + using vector_t = std::vector; + using int_t = lgrn::underlying_int_type_t; + +public: + + using value_type = typename vector_t::value_type; + using allocator_type = typename vector_t::allocator_type; + using reference = typename vector_t::reference; + using const_reference = typename vector_t::const_reference; + using pointer = typename vector_t::pointer; + using const_pointer = typename vector_t::const_pointer; + using iterator = typename vector_t::iterator; + using const_iterator = typename vector_t::const_iterator; + using reverse_iterator = typename vector_t::reverse_iterator; + using const_reverse_iterator = typename vector_t::const_reverse_iterator; + using difference_type = typename vector_t::difference_type; + using size_type = typename vector_t::size_type; + + constexpr vector_t& base() noexcept { return *this; } + constexpr vector_t const& base() const noexcept { return *this; } + + reference at(ID_T const id) + { + return vector_t::at(std::size_t(id)); + } + + const_reference at(ID_T const id) const + { + return vector_t::at(std::size_t(id)); + } + + reference operator[] (ID_T const id) + { + return vector_t::operator[](std::size_t(id)); + } + + const_reference operator[] (ID_T const id) const + { + return vector_t::operator[](std::size_t(id)); + } + +}; // class KeyedVec + + + +} // namespace lgrn From 528496b55466f6523d9badcf6f6f607038290fd3 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Mon, 20 May 2024 23:59:12 -0700 Subject: [PATCH 5/8] Add BitViewIdSet and IdSetStl --- src/longeron/id_management/bitview_id_set.hpp | 198 ++++++++++++++++++ src/longeron/id_management/id_set_stl.hpp | 37 ++++ test/CMakeLists.txt | 1 + test/id_management/id_set.cpp | 40 ++++ 4 files changed, 276 insertions(+) create mode 100644 src/longeron/id_management/bitview_id_set.hpp create mode 100644 src/longeron/id_management/id_set_stl.hpp create mode 100644 test/id_management/id_set.cpp diff --git a/src/longeron/id_management/bitview_id_set.hpp b/src/longeron/id_management/bitview_id_set.hpp new file mode 100644 index 0000000..beeb54b --- /dev/null +++ b/src/longeron/id_management/bitview_id_set.hpp @@ -0,0 +1,198 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2024 Neal Nicdao + */ +#pragma once + +#include "../containers/bit_view.hpp" + +#include "../utility/enum_traits.hpp" + + +namespace lgrn +{ + +/** + * @brief Wraps a BitView to be used as a set of IDs. The interface similar to std::set. + */ +template +class BitViewIdSet : private BitView +{ +public: + + using id_int_t = underlying_int_type_t; + using base_t = BitView; + + using IdIterator_t = BitPosIterator; + + using base_t::base_t; + using base_t::operator=; + + constexpr base_t& bitview() noexcept { return static_cast(*this); } + constexpr base_t const& bitview() const noexcept { return static_cast(*this); } + + // Capacity + + /** + * @return Max number of IDs that can be stored + */ + constexpr std::size_t capacity() const noexcept { return base_t::size(); } + + /** + * @return Current number of contained IDs, determined by counting bits + */ + constexpr std::size_t size() const + { + if constexpr (ONES) + { + return base_t::count(); + } + else + { + return capacity() - base_t::count(); + } + } + + bool empty() const noexcept + { + constexpr id_int_t c_emptyInt = ONES ? id_int_t(0) : ~id_int_t(0); + + return std::all_of(std::begin(bitview().ints()), std::end(bitview().ints()), + [] ( id_int_t const value ) { return value == c_emptyInt; }); + } + + // Iterators + + /** + * @brief Iterate all existing IDs. Read-only + */ + constexpr IdIterator_t begin() const noexcept + { + return IdIterator_t(std::begin(bitview().ints()), std::end(bitview().ints()), 0, 0); + } + + constexpr typename IdIterator_t::Sentinel end() const noexcept { return {}; } + + // Element Lookup + + std::size_t count(ID_T id) const noexcept + { + return std::size_t(impl_contains(std::size_t(id))); + } + + bool contains(ID_T id) const noexcept + { + return impl_contains(std::size_t(id)); + } + + // Modifiers + + struct InsertResult + { + std::size_t first; ///< Position + bool second; ///< Insertion succeeded + }; + + template + InsertResult emplace(ARGS_T &&... args) + { + // this feels pretty stupid + return insert(ID_T{ std::forward(args)... }); + } + + InsertResult insert(ID_T id) + { + auto const idInt = std::size_t(id); + + bool const exists = impl_contains(idInt); + impl_insert(idInt); + + return {idInt, ! exists}; + } + + template + void insert(ITER_T first, SNTL_T const last) + { + while (first != last) + { + impl_insert( std::size_t( ID_T{*first} ) ); + ++first; + } + } + + void insert(std::initializer_list list) + { + return insert(list.begin(), list.end()); + } + + std::size_t erase(ID_T id) + { + auto const idInt = std::size_t(id); + bool const exists = impl_contains(idInt); + + impl_erase(idInt); + return exists; + } + + template + void erase(ITER_T first, SNTL_T const last) + { + while (first != last) + { + impl_erase( std::size_t{ ID_T{*first} } ); + ++first; + } + } + + void clear() + { + if constexpr (ONES) + { + base_t::reset(); + } + else + { + base_t::set(); + } + } + +private: + + bool impl_contains(std::size_t const pos) const + { + if constexpr (ONES) + { + return base_t::test(pos); + } + else + { + return ! base_t::test(pos); + } + } + + void impl_insert(std::size_t const pos) + { + if constexpr (ONES) + { + base_t::set(pos); + } + else + { + base_t::reset(pos); + } + } + + void impl_erase(std::size_t const pos) + { + if constexpr (ONES) + { + base_t::reset(pos); + } + else + { + base_t::clear(pos); + } + } +}; + +} // namespace lgrn diff --git a/src/longeron/id_management/id_set_stl.hpp b/src/longeron/id_management/id_set_stl.hpp new file mode 100644 index 0000000..a7dc3da --- /dev/null +++ b/src/longeron/id_management/id_set_stl.hpp @@ -0,0 +1,37 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2024 Neal Nicdao + */ +#pragma once + +#include "bitview_id_set.hpp" + +#include + +namespace lgrn +{ + +/** + * @brief An STL-style container to store a set of ID types. Uses std::vector + * internally. + * + * No automatic reallocations. Use \c reserve(); + */ +template +class IdSetStl : public BitViewIdSet, ID_T> +{ +public: + using base_t = BitViewIdSet, ID_T>; + using bitview_t = typename base_t::base_t; + + [[nodiscard]] constexpr auto& vec() noexcept { return base_t::bitview().ints(); } + [[nodiscard]] constexpr auto const& vec() const noexcept { return base_t::bitview().ints(); } + + void resize(std::size_t n) + { + vec().resize(lgrn::div_ceil(n, base_t::bitview().int_bitsize()), 0); + } +}; + + +} // namespace lgrn diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bebb708..2232dfb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,3 +12,4 @@ lgrn_add_test(hierarchical_bitset hierarchical_bitset.cpp longeron) lgrn_add_test(intarray_multimap intarray_multimap.cpp longeron) lgrn_add_test(bit_view bit_view.cpp longeron) lgrn_add_test(id_registry id_management/registry.cpp longeron) +lgrn_add_test(id_set id_management/id_set.cpp longeron) diff --git a/test/id_management/id_set.cpp b/test/id_management/id_set.cpp new file mode 100644 index 0000000..341c12c --- /dev/null +++ b/test/id_management/id_set.cpp @@ -0,0 +1,40 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2024 Neal Nicdao + */ +#include + +#include + + +enum class Id : uint64_t { }; + +// Touch every member function. You know how a set works, right? +TEST(IdSet, BasicUse) +{ + lgrn::IdSetStl set; + + set.resize(40); + + ASSERT_GE(set.capacity(), 40); + ASSERT_TRUE(set.empty()); + + { + auto const [pos, insertSuccess] = set.insert(Id{0}); + ASSERT_TRUE(set.contains(Id{0})); + EXPECT_TRUE(insertSuccess); + } + { + auto const [pos, insertSuccess] = set.insert(Id{0}); + ASSERT_TRUE(set.contains(Id{0})); + EXPECT_FALSE(insertSuccess); + } + + set.emplace(Id{2}); + set.insert({Id{6}, Id{9}, Id{8}}); + + auto const currentIds = {Id{0}, Id{2}, Id{6}, Id{8}, Id{9}}; + + ASSERT_EQ(set.size(), 5); + ASSERT_TRUE(std::equal(currentIds.begin(), currentIds.end(), set.begin())); +} From bb895281df809bfb2040379889c734d41e10ed98 Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Tue, 21 May 2024 00:01:45 -0700 Subject: [PATCH 6/8] Update circuit simulator example --- examples/circuits/circuits.hpp | 89 ++++++++++++++++++---------------- examples/circuits/main.cpp | 42 ++++++++-------- 2 files changed, 68 insertions(+), 63 deletions(-) diff --git a/examples/circuits/circuits.hpp b/examples/circuits/circuits.hpp index c6186fc..54ecdd0 100644 --- a/examples/circuits/circuits.hpp +++ b/examples/circuits/circuits.hpp @@ -5,6 +5,8 @@ #pragma once #include +#include +#include #include #include @@ -13,18 +15,24 @@ namespace circuits { -using ElementId = uint32_t; -using ElemLocalId = uint32_t; -using ElemTypeId = uint8_t; -using NodeId = uint32_t; + +// Enum classes used as integer "strong typedef" and can't be implicitly casted to each other, +// preventing logic errors and cognative load from accidentally mixing them up. +enum class ElemLocalId : std::uint32_t { }; +enum class ElemTypeId : std::uint8_t { }; + +// lgrn::IntArrayMultiMap does not (yet?) support strongly typedefed ID types +using ElementId = std::uint32_t; +using NodeId = std::uint32_t; + /** * @brief Keeps track of which circuit elements of a certain type exists */ struct PerElemType { - lgrn::IdRegistryStl m_localIds; - std::vector m_localToElem; + lgrn::IdRegistryStl m_localIds; + lgrn::KeyedVec m_localToElem; }; /** @@ -32,12 +40,12 @@ struct PerElemType */ struct Elements { - lgrn::IdRegistryStl m_ids; + lgrn::IdRegistryStl m_ids; - std::vector m_elemTypes; - std::vector m_elemToLocal; + lgrn::KeyedVec m_elemTypes; + lgrn::KeyedVec m_elemToLocal; - std::vector m_perType; + lgrn::KeyedVec m_perType; }; /** @@ -49,6 +57,7 @@ struct ElementPair ElemTypeId m_type; }; + /** * @brief Connects cirucit elements together as Nodes */ @@ -64,7 +73,7 @@ struct Nodes // Node-to-Element connections // Each node can have multiple subscribers, but only one publisher Subscribers_t m_nodeSubscribers; - std::vector m_nodePublisher; + lgrn::KeyedVec m_nodePublisher; // Corresponding Element-to-Node connections // [ElementId][PortId] -> NodeId @@ -80,7 +89,7 @@ struct Nodes template struct NodeValues { - std::vector m_nodeValues; + lgrn::KeyedVec m_nodeValues; }; //----------------------------------------------------------------------------- @@ -102,33 +111,31 @@ struct CombinationalGates bool m_invert; }; - std::vector m_localGates; + lgrn::KeyedVec m_localGates; }; //----------------------------------------------------------------------------- // Updating -using BitVector_t = lgrn::BitView< std::vector >; - constexpr std::size_t const gc_bitVecIntSize = 64; struct UpdateElem { - BitVector_t m_localDirty; + lgrn::IdSetStl m_localDirty; }; -using UpdateElemTypes_t = std::vector; +using UpdateElemTypes_t = lgrn::KeyedVec; template struct UpdateNodes { - BitVector_t m_nodeDirty; - std::vector m_nodeNewValues; + lgrn::IdSetStl m_nodeDirty; + lgrn::KeyedVec m_nodeNewValues; void assign(NodeId node, VALUE_T&& value) { - m_nodeDirty.set(node); + m_nodeDirty.insert(node); m_nodeNewValues[node] = std::forward(value); } }; @@ -147,26 +154,26 @@ struct UpdateNodes */ template bool update_combinational( - RANGE_T&& toUpdate, - ElementId const* pLocalToElem, - Nodes::Connections_t const& elemConnect, - ELogic const* pNodeValues, - CombinationalGates const& gates, - UpdateNodes& rUpdNodes) noexcept + RANGE_T&& toUpdate, + lgrn::KeyedVec const &localToElem, + Nodes::Connections_t const &elemConnect, + lgrn::KeyedVec &nodeValues, + CombinationalGates const &gates, + UpdateNodes &rUpdNodes) noexcept { using Op = CombinationalGates::Op; - auto const is_logic_high = [pNodeValues] (NodeId in) noexcept -> bool + auto const is_logic_high = [&nodeValues] (NodeId in) noexcept -> bool { - return pNodeValues[in] == ELogic::High; + return nodeValues[in] == ELogic::High; }; bool nodeUpdated = false; for (ElemLocalId local : toUpdate) { - ElementId const elem = pLocalToElem[local]; - CombinationalGates::GateDesc const& desc = gates.m_localGates[local]; + ElementId const elem = localToElem[local]; + CombinationalGates::GateDesc const &desc = gates.m_localGates[local]; auto connectedNodes = elemConnect[elem]; auto inFirst = connectedNodes.begin() + 1; @@ -197,10 +204,10 @@ bool update_combinational( NodeId out = *connectedNodes.begin(); // Request to write changes to node if value is changed - if (pNodeValues[out] != outLogic) + if (nodeValues[out] != outLogic) { nodeUpdated = true; - rUpdNodes.m_nodeDirty.set(out); + rUpdNodes.m_nodeDirty.insert(out); rUpdNodes.m_nodeNewValues[out] = outLogic; } } @@ -222,25 +229,25 @@ bool update_combinational( */ template bool update_nodes( - RANGE_T&& toUpdate, - Nodes::Subscribers_t const& nodeSubs, - Elements const& elements, - VALUE_T const* pNewValues, - VALUE_T* pValues, - UpdateElemTypes_t& rUpdElem) + RANGE_T &&toUpdate, + Nodes::Subscribers_t const &nodeSubs, + Elements const &elements, + lgrn::KeyedVec const &newValues, + lgrn::KeyedVec &values, + UpdateElemTypes_t &rUpdElem) { bool elemNotified = false; - for (uint32_t node : toUpdate) + for (NodeId node : toUpdate) { // Apply node value changes - pValues[node] = pNewValues[node]; + values[node] = newValues[node]; // Notify subscribed elements for (ElementPair subElem : nodeSubs[node]) { elemNotified = true; - rUpdElem[subElem.m_type].m_localDirty.set(subElem.m_id); + rUpdElem[subElem.m_type].m_localDirty.insert(subElem.m_id); } } diff --git a/examples/circuits/main.cpp b/examples/circuits/main.cpp index e165a1e..007b145 100644 --- a/examples/circuits/main.cpp +++ b/examples/circuits/main.cpp @@ -13,8 +13,6 @@ #include #include -#include - using namespace circuits; using lgrn::id_null; @@ -72,15 +70,15 @@ struct UserCircuit { UpdateElemTypes_t out; out.resize(m_maxTypes); - out[gc_elemGate].m_localDirty.ints().resize(m_elements.m_perType[gc_elemGate].m_localIds.vec().capacity()); + out[gc_elemGate].m_localDirty.resize(m_elements.m_perType[gc_elemGate].m_localIds.capacity()); // Initially set all to dirty get to valid state // middle parts of a circuit may be unresponsive otherwise - for (uint32_t elem : m_elements.m_ids.bitview().zeros()) + for (ElementId elem : m_elements.m_ids) { - ElemTypeId const type = m_elements.m_elemTypes[elem]; + ElemTypeId const type = m_elements.m_elemTypes[elem]; ElemLocalId const local = m_elements.m_elemToLocal[elem]; - out[type].m_localDirty.set(local); + out[type].m_localDirty.insert(local); } return out; @@ -89,7 +87,7 @@ struct UserCircuit UpdateNodes setup_logic_updater() { UpdateNodes out; - out.m_nodeDirty.ints().resize(m_maxNodes / gc_bitVecIntSize + 1); + out.m_nodeDirty.resize(m_maxNodes); out.m_nodeNewValues.resize(m_maxNodes); return out; @@ -133,22 +131,22 @@ static int step_until_stable( while (elemNotified && steps < maxSteps) { update_nodes( - rUpdLogic.m_nodeDirty.ones(), + rUpdLogic.m_nodeDirty, rCircuit.m_logicNodes.m_nodeSubscribers, rCircuit.m_elements, - rUpdLogic.m_nodeNewValues.data(), - rCircuit.m_logicValues.m_nodeValues.data(), + rUpdLogic.m_nodeNewValues, + rCircuit.m_logicValues.m_nodeValues, rUpdElems); - rUpdLogic.m_nodeDirty.reset(); + rUpdLogic.m_nodeDirty.clear(); elemNotified = update_combinational( - rUpdElems[gc_elemGate].m_localDirty.ones(), - rCircuit.m_elements.m_perType[gc_elemGate].m_localToElem.data(), + rUpdElems[gc_elemGate].m_localDirty, + rCircuit.m_elements.m_perType[gc_elemGate].m_localToElem, rCircuit.m_logicNodes.m_elemConnect, - rCircuit.m_logicValues.m_nodeValues.data(), + rCircuit.m_logicValues.m_nodeValues, rCircuit.m_gates, rUpdLogic); - rUpdElems[gc_elemGate].m_localDirty.reset(); + rUpdElems[gc_elemGate].m_localDirty.clear(); steps ++; } @@ -244,7 +242,7 @@ static void test_manual_build() // Connect 'ports' of XOR gate. Adds a Element->Node mapping // For basic gates, first (0) is the output, the rest are inputs - circuit.m_logicNodes.m_elemConnect.emplace(gc_elemGate, {out, A, B}); + circuit.m_logicNodes.m_elemConnect.emplace(xorElem, {out, A, B}); // Connect publishers and subscribers. Adds Node->Element mapping circuit.m_logicNodes.m_nodePublisher[out] = xorElem; @@ -253,7 +251,7 @@ static void test_manual_build() // Now everything is set, circuit can run! - UpdateElemTypes_t updElems = circuit.setup_element_updater(); + UpdateElemTypes_t updElems = circuit.setup_element_updater(); UpdateNodes updLogic = circuit.setup_logic_updater(); auto const& outVal = circuit.m_logicValues.m_nodeValues[out]; @@ -308,7 +306,7 @@ static void test_xor_nand() circuit.build_end(); - UpdateElemTypes_t updElems = circuit.setup_element_updater(); + UpdateElemTypes_t updElems = circuit.setup_element_updater(); UpdateNodes updLogic = circuit.setup_logic_updater(); auto const& outVal = circuit.m_logicValues.m_nodeValues[out]; @@ -361,9 +359,9 @@ static void test_sr_latch() // initialize to get to valid state for (uint32_t elem : circuit.m_elements.m_ids.bitview().zeros()) { - ElemTypeId const type = circuit.m_elements.m_elemTypes[elem]; + ElemTypeId const type = circuit.m_elements.m_elemTypes[elem]; ElemLocalId const local = circuit.m_elements.m_elemToLocal[elem]; - updElems[type].m_localDirty.set(local); + updElems[type].m_localDirty.insert(local); } std::cout << "NAND SR latch:\n"; @@ -417,9 +415,9 @@ static void test_edge_detect() // initialize to get to valid state for (uint32_t elem : circuit.m_elements.m_ids.bitview().zeros()) { - ElemTypeId const type = circuit.m_elements.m_elemTypes[elem]; + ElemTypeId const type = circuit.m_elements.m_elemTypes[elem]; ElemLocalId const local = circuit.m_elements.m_elemToLocal[elem]; - updElems[type].m_localDirty.set(local); + updElems[type].m_localDirty.insert(local); } std::cout << "Edge Detector:\n"; From d60fa7cef19b726332e107e1f1931a89bd887b5a Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Tue, 21 May 2024 01:35:59 -0700 Subject: [PATCH 7/8] Fix build errors from PR #18 --- src/longeron/containers/bit_iterator.hpp | 9 +++++---- src/longeron/containers/bit_view.hpp | 16 ++++++++-------- src/longeron/id_management/bitview_id_set.hpp | 8 ++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/longeron/containers/bit_iterator.hpp b/src/longeron/containers/bit_iterator.hpp index 6af7413..d4a8d9f 100644 --- a/src/longeron/containers/bit_iterator.hpp +++ b/src/longeron/containers/bit_iterator.hpp @@ -27,6 +27,8 @@ class BitPosIterator using int_t = typename std::iterator_traits::value_type; static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); + + static constexpr int_t smc_emptyBlock = ONES ? int_t(0) : ~int_t(0); public: // TODO: consider bi-directional? @@ -71,13 +73,12 @@ class BitPosIterator if (m_block == 0) { // Skip empty blocks (no ones or no zero bits) - constexpr int_t c_emptyBlock = ONES ? int_t(0) : ~int_t(0); do { ++m_it; m_distance += sizeof(int_t) * 8; } - while (m_it != m_end && *m_it == c_emptyBlock); + while (m_it != m_end && *m_it == smc_emptyBlock); m_block = (m_it != m_end) ? int_iter_value() : 0; } @@ -88,7 +89,7 @@ class BitPosIterator constexpr value_type operator*() const noexcept { std::size_t const pos = m_distance + ctz(m_block); - return VALUE_T{pos}; + return VALUE_T(pos); } private: @@ -130,7 +131,7 @@ class BitPosIterator } } - // End need to be stored becuase of the "skip forward until we find a bit" behaviour + // End needs to be stored becuase of the "skip forward until we find a bit" behaviour SNTL_T m_end; ITER_T m_it; std::size_t m_distance{~std::size_t(0)}; diff --git a/src/longeron/containers/bit_view.hpp b/src/longeron/containers/bit_view.hpp index ec46067..6cc4c41 100644 --- a/src/longeron/containers/bit_view.hpp +++ b/src/longeron/containers/bit_view.hpp @@ -51,10 +51,10 @@ class BitView : private RANGE_T constexpr ValueIt_t begin_at(std::size_t const bitPos) const noexcept { - auto const intPos = bitPos / 64; - auto const intBitPos = bitPos % 64; + auto const intPos = bitPos / smc_bitSize; + auto const intBitPos = bitPos % smc_bitSize; - return ValueIt_t(std::next(first, intPos), last, 64*intPos, intBitPos); + return ValueIt_t(std::next(first, intPos), last, smc_bitSize*intPos, intBitPos); } constexpr Sentinel_t end() const noexcept @@ -68,13 +68,13 @@ class BitView : private RANGE_T static constexpr std::size_t int_bitsize() noexcept { return smc_bitSize; } - constexpr BitView() = default; - constexpr BitView(BitView const& copy) = default; - constexpr BitView(BitView&& move) = default; + constexpr BitView() = default; + constexpr BitView(BitView const& copy) = default; + constexpr BitView(BitView&& move) noexcept = default; constexpr BitView(RANGE_T range) : RANGE_T(range) { } - constexpr BitView& operator=(BitView const& copy) = default; - constexpr BitView& operator=(BitView&& move) = default; + constexpr BitView& operator=(BitView const& copy) = default; + constexpr BitView& operator=(BitView&& move) noexcept = default; constexpr bool test(std::size_t bit) const noexcept; diff --git a/src/longeron/id_management/bitview_id_set.hpp b/src/longeron/id_management/bitview_id_set.hpp index beeb54b..f2fc5cd 100644 --- a/src/longeron/id_management/bitview_id_set.hpp +++ b/src/longeron/id_management/bitview_id_set.hpp @@ -55,10 +55,8 @@ class BitViewIdSet : private BitView bool empty() const noexcept { - constexpr id_int_t c_emptyInt = ONES ? id_int_t(0) : ~id_int_t(0); - return std::all_of(std::begin(bitview().ints()), std::end(bitview().ints()), - [] ( id_int_t const value ) { return value == c_emptyInt; }); + [] ( id_int_t const value ) { return value == smc_emptyInt; }); } // Iterators @@ -131,7 +129,7 @@ class BitViewIdSet : private BitView bool const exists = impl_contains(idInt); impl_erase(idInt); - return exists; + return std::size_t(exists); } template @@ -158,6 +156,8 @@ class BitViewIdSet : private BitView private: + static constexpr id_int_t smc_emptyInt = ONES ? id_int_t(0) : ~id_int_t(0); + bool impl_contains(std::size_t const pos) const { if constexpr (ONES) From fd0d6b94a052e510d39573bfeae9317c1701b44e Mon Sep 17 00:00:00 2001 From: Neal_Nicdao Date: Tue, 21 May 2024 22:11:42 -0700 Subject: [PATCH 8/8] Refactor more stuff --- examples/circuits/circuits.hpp | 1 - examples/circuits/main.cpp | 1 - src/longeron/containers/bit_iterator.hpp | 41 ++++++- src/longeron/containers/bit_view.hpp | 66 ++++------- src/longeron/id_management/bitview_id_set.hpp | 103 +++++++++--------- .../id_management/bitview_registry.hpp | 68 ++++++------ src/longeron/id_management/cast_iterator.hpp | 28 +++++ src/longeron/id_management/id_set_stl.hpp | 19 ++-- src/longeron/id_management/registry_stl.hpp | 44 ++++---- src/longeron/id_management/span_map.hpp | 0 test/id_management/registry.cpp | 2 +- 11 files changed, 205 insertions(+), 168 deletions(-) create mode 100644 src/longeron/id_management/cast_iterator.hpp delete mode 100644 src/longeron/id_management/span_map.hpp diff --git a/examples/circuits/circuits.hpp b/examples/circuits/circuits.hpp index 54ecdd0..f6ddb3a 100644 --- a/examples/circuits/circuits.hpp +++ b/examples/circuits/circuits.hpp @@ -231,7 +231,6 @@ template bool update_nodes( RANGE_T &&toUpdate, Nodes::Subscribers_t const &nodeSubs, - Elements const &elements, lgrn::KeyedVec const &newValues, lgrn::KeyedVec &values, UpdateElemTypes_t &rUpdElem) diff --git a/examples/circuits/main.cpp b/examples/circuits/main.cpp index 007b145..9b351da 100644 --- a/examples/circuits/main.cpp +++ b/examples/circuits/main.cpp @@ -133,7 +133,6 @@ static int step_until_stable( update_nodes( rUpdLogic.m_nodeDirty, rCircuit.m_logicNodes.m_nodeSubscribers, - rCircuit.m_elements, rUpdLogic.m_nodeNewValues, rCircuit.m_logicValues.m_nodeValues, rUpdElems); diff --git a/src/longeron/containers/bit_iterator.hpp b/src/longeron/containers/bit_iterator.hpp index d4a8d9f..0d0b8ca 100644 --- a/src/longeron/containers/bit_iterator.hpp +++ b/src/longeron/containers/bit_iterator.hpp @@ -21,7 +21,7 @@ namespace lgrn * * @warning Do not modify the integer range while this iterator is alive. */ -template +template class BitPosIterator { using int_t = typename std::iterator_traits::value_type; @@ -34,14 +34,13 @@ class BitPosIterator // TODO: consider bi-directional? using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; - using value_type = VALUE_T; + using value_type = std::size_t; using pointer = void; using reference = void; struct Sentinel { }; constexpr BitPosIterator() noexcept = default; -// constexpr BitPosIterator(BitPosSentinel const&) noexcept : BitPosIterator() { } constexpr BitPosIterator(ITER_T it, SNTL_T end, std::size_t dist, int bit) noexcept : m_end {end} , m_it {it} @@ -64,6 +63,8 @@ class BitPosIterator constexpr BitPosIterator& operator=(BitPosIterator const& copy) noexcept = default; constexpr BitPosIterator& operator=(BitPosIterator&& move) noexcept = default; + ~BitPosIterator() = default; + constexpr BitPosIterator& operator++() noexcept { // Remove LSB @@ -89,7 +90,7 @@ class BitPosIterator constexpr value_type operator*() const noexcept { std::size_t const pos = m_distance + ctz(m_block); - return VALUE_T(pos); + return pos; } private: @@ -107,13 +108,13 @@ class BitPosIterator } constexpr friend bool operator==(BitPosIterator const& lhs, - Sentinel const& rhs) noexcept + Sentinel const&) noexcept { return lhs.m_it == lhs.m_end; } constexpr friend bool operator!=(BitPosIterator const& lhs, - Sentinel const& rhs) noexcept + Sentinel const&) noexcept { return lhs.m_it != lhs.m_end; } @@ -139,4 +140,32 @@ class BitPosIterator }; +template +class BitPosRangeView +{ + static constexpr int smc_bitSize = sizeof(INT_T) * 8; +public: + + constexpr BitPosRangeView(RANGE_ITER_T first, RANGE_SNTL_T last) + : first{ std::move(first) } + , last { std::move(last) } + { } + + constexpr POS_ITER_T begin() const noexcept { return POS_ITER_T(first, last, 0, 0); } + + constexpr POS_ITER_T begin_at(std::size_t const bitPos) const noexcept + { + auto const intPos = bitPos / smc_bitSize; + auto const intBitPos = bitPos % smc_bitSize; + + return POS_ITER_T(std::next(first, intPos), last, smc_bitSize*intPos, intBitPos); + } + + constexpr POS_SNTL_T end() const noexcept { return POS_SNTL_T{}; } +private: + RANGE_ITER_T first; + RANGE_SNTL_T last; +}; + } // namespace lgrn diff --git a/src/longeron/containers/bit_view.hpp b/src/longeron/containers/bit_view.hpp index 6cc4c41..ff3c2c6 100644 --- a/src/longeron/containers/bit_view.hpp +++ b/src/longeron/containers/bit_view.hpp @@ -16,55 +16,35 @@ namespace lgrn { +// Inheritance here is mostly used for Empty Base Optimization, not an "is-a" relationship + /** * @brief Mixin that adapts a bit interface around an integer range */ template class BitView : private RANGE_T { + using RangeIter_t = decltype(std::cbegin(std::declval())); + using RangeSntl_t = decltype(std::cend (std::declval())); + public: + using IntRange_t = RANGE_T; - using iter_t = decltype(std::cbegin(std::declval())); - using sntl_t = decltype(std::cend (std::declval())); - using int_t = std::remove_cv_t::value_type>; + using ZerosIter_t = BitPosIterator< RangeIter_t, RangeSntl_t, false >; + using ZerosSntl_t = typename ZerosIter_t::Sentinel; - static constexpr int smc_bitSize = sizeof(int_t) * 8; + using OnesIter_t = BitPosIterator< RangeIter_t, RangeSntl_t, true >; + using OnesSntl_t = typename OnesIter_t::Sentinel; +private: + using int_t = std::remove_cv_t::value_type>; static_assert(std::is_unsigned_v, "Use only unsigned types for bit manipulation"); + static constexpr int smc_bitSize = sizeof(int_t) * 8; - template - class BitPositionsRangeView - { - using ValueIt_t = BitPosIterator< iter_t, sntl_t, ONES >; - using Sentinel_t = typename ValueIt_t::Sentinel; - public: - - constexpr BitPositionsRangeView(BitView const& view) - : first{ std::cbegin(view.ints()) } - , last { std::cend (view.ints()) } - { } - - constexpr ValueIt_t begin() const noexcept - { - return ValueIt_t(first, last, 0, 0); - } - - constexpr ValueIt_t begin_at(std::size_t const bitPos) const noexcept - { - auto const intPos = bitPos / smc_bitSize; - auto const intBitPos = bitPos % smc_bitSize; - - return ValueIt_t(std::next(first, intPos), last, smc_bitSize*intPos, intBitPos); - } - - constexpr Sentinel_t end() const noexcept - { - return Sentinel_t{}; - } - private: - iter_t first; - sntl_t last; - }; +public: + + using OnesRangeView_t = BitPosRangeView; + using ZerosRangeView_t = BitPosRangeView; static constexpr std::size_t int_bitsize() noexcept { return smc_bitSize; } @@ -89,15 +69,17 @@ class BitView : private RANGE_T /** * @brief Return a range type (with begin/end functions) used to iterate positions of ones bits */ - constexpr BitPositionsRangeView ones() const noexcept { return {*this}; } + constexpr OnesRangeView_t ones() const noexcept + { return { std::cbegin(ints()), std::cend(ints()) }; } /** * @brief Return a range type (with begin/end functions) used to iterate positions of zeros bits */ - constexpr BitPositionsRangeView zeros() const noexcept { return {*this}; } + constexpr ZerosRangeView_t zeros() const noexcept + { return { std::cbegin(ints()), std::cend(ints()) }; } - constexpr RANGE_T& ints() noexcept { return static_cast(*this); } - constexpr RANGE_T const& ints() const noexcept { return static_cast(*this); } + constexpr IntRange_t& ints() noexcept { return static_cast(*this); } + constexpr IntRange_t const& ints() const noexcept { return static_cast(*this); } }; template @@ -155,7 +137,7 @@ template constexpr std::size_t BitView::count() const noexcept { std::size_t total = 0; - iter_t it = std::begin(ints()); + auto it = std::begin(ints()); while (it != std::end(ints())) { total += std::bitset(*it).count(); diff --git a/src/longeron/id_management/bitview_id_set.hpp b/src/longeron/id_management/bitview_id_set.hpp index f2fc5cd..be129d6 100644 --- a/src/longeron/id_management/bitview_id_set.hpp +++ b/src/longeron/id_management/bitview_id_set.hpp @@ -4,39 +4,45 @@ */ #pragma once -#include "../containers/bit_view.hpp" - +#include "cast_iterator.hpp" #include "../utility/enum_traits.hpp" +#include namespace lgrn { /** * @brief Wraps a BitView to be used as a set of IDs. The interface similar to std::set. + * + * @warning Untested when ONES = false */ -template -class BitViewIdSet : private BitView +template +class BitViewIdSet : private BITVIEW_T { -public: - using id_int_t = underlying_int_type_t; - using base_t = BitView; + using IterBase_t = std::conditional_t; + using SntlBase_t = std::conditional_t; +public: - using IdIterator_t = BitPosIterator; + using Base_t = BITVIEW_T; + using Iterator_t = IdCastIterator; + using Sentinel_t = SntlBase_t; - using base_t::base_t; - using base_t::operator=; + using Base_t::Base_t; + using Base_t::operator=; - constexpr base_t& bitview() noexcept { return static_cast(*this); } - constexpr base_t const& bitview() const noexcept { return static_cast(*this); } + constexpr Base_t& bitview() noexcept { return static_cast(*this); } + constexpr Base_t const& bitview() const noexcept { return static_cast(*this); } // Capacity /** * @return Max number of IDs that can be stored */ - constexpr std::size_t capacity() const noexcept { return base_t::size(); } + constexpr std::size_t capacity() const noexcept { return Base_t::size(); } /** * @return Current number of contained IDs, determined by counting bits @@ -45,11 +51,11 @@ class BitViewIdSet : private BitView { if constexpr (ONES) { - return base_t::count(); + return Base_t::count(); } else { - return capacity() - base_t::count(); + return capacity() - Base_t::count(); } } @@ -64,12 +70,29 @@ class BitViewIdSet : private BitView /** * @brief Iterate all existing IDs. Read-only */ - constexpr IdIterator_t begin() const noexcept + constexpr Iterator_t begin() const noexcept { - return IdIterator_t(std::begin(bitview().ints()), std::end(bitview().ints()), 0, 0); + if constexpr (ONES) + { + return Iterator_t{ bitview().ones().begin() }; + } + else + { + return Iterator_t{ bitview().zeros().begin() }; + } } - constexpr typename IdIterator_t::Sentinel end() const noexcept { return {}; } + constexpr Sentinel_t end() const noexcept + { + if constexpr (ONES) + { + return bitview().ones().end(); + } + else + { + return bitview().zeros().end(); + } + } // Element Lookup @@ -142,56 +165,28 @@ class BitViewIdSet : private BitView } } - void clear() + void clear() noexcept { - if constexpr (ONES) - { - base_t::reset(); - } - else - { - base_t::set(); - } + if constexpr (ONES) { Base_t::reset(); } else { Base_t::set(); } } private: static constexpr id_int_t smc_emptyInt = ONES ? id_int_t(0) : ~id_int_t(0); - bool impl_contains(std::size_t const pos) const + bool impl_contains(std::size_t const pos) const noexcept { - if constexpr (ONES) - { - return base_t::test(pos); - } - else - { - return ! base_t::test(pos); - } + if constexpr (ONES) { return Base_t::test(pos); } else { return ! Base_t::test(pos); } } - void impl_insert(std::size_t const pos) + void impl_insert(std::size_t const pos) noexcept { - if constexpr (ONES) - { - base_t::set(pos); - } - else - { - base_t::reset(pos); - } + if constexpr (ONES) { Base_t::set(pos); } else { Base_t::reset(pos); } } - void impl_erase(std::size_t const pos) + void impl_erase(std::size_t const pos) noexcept { - if constexpr (ONES) - { - base_t::reset(pos); - } - else - { - base_t::clear(pos); - } + if constexpr (ONES) { Base_t::reset(pos); } else { Base_t::clear(pos); } } }; diff --git a/src/longeron/id_management/bitview_registry.hpp b/src/longeron/id_management/bitview_registry.hpp index 479318c..1f38ffb 100644 --- a/src/longeron/id_management/bitview_registry.hpp +++ b/src/longeron/id_management/bitview_registry.hpp @@ -4,10 +4,9 @@ */ #pragma once +#include "cast_iterator.hpp" #include "null.hpp" -#include "../containers/bit_view.hpp" - #include "../utility/enum_traits.hpp" #include "../utility/asserts.hpp" @@ -20,20 +19,23 @@ namespace lgrn * Ones are used as free IDs, and zeros are used as taken. This is because the bitwise operations * used are slightly faster at searching for ones. */ -template -class BitViewIdRegistry : private BitView +template +class BitViewIdRegistry : private BITVIEW_T { -public: - using id_int_t = underlying_int_type_t; - using base_t = BitView; - using IdIterator_t = BitPosIterator; +public: + + using Base_t = BITVIEW_T; + using Iterator_t = IdCastIterator; + using Sentinel_t = typename Base_t::ZerosSntl_t; + constexpr Base_t& bitview() noexcept { return static_cast(*this); } + constexpr Base_t const& bitview() const noexcept { return static_cast(*this); } BitViewIdRegistry() = default; - BitViewIdRegistry(base_t bitview) : base_t(bitview) {}; - BitViewIdRegistry(base_t bitview, [[maybe_unused]] ID_T) : base_t(bitview) {}; + BitViewIdRegistry(Base_t bitview) : Base_t(bitview) {}; + BitViewIdRegistry(Base_t bitview, [[maybe_unused]] ID_T) : Base_t(bitview) {}; /** * @brief Create a single ID @@ -56,12 +58,12 @@ class BitViewIdRegistry : private BitView /** * @return Max number of IDs that can be stored */ - constexpr std::size_t capacity() const noexcept { return base_t::size(); } + constexpr std::size_t capacity() const noexcept { return Base_t::size(); } /** * @return Current number of registered IDs, determined by counting bits */ - constexpr std::size_t size() const { return capacity() - base_t::count(); } + constexpr std::size_t size() const { return capacity() - Base_t::count(); } /** * @brief Remove an ID. This will mark it for reuse @@ -69,7 +71,7 @@ class BitViewIdRegistry : private BitView void remove(ID_T id) noexcept { LGRN_ASSERTMV(exists(id), "ID does not exist", std::size_t(id)); - base_t::set(id_int_t(id)); + Base_t::set(id_int_t(id)); } /** @@ -77,28 +79,26 @@ class BitViewIdRegistry : private BitView */ bool exists(ID_T id) const noexcept { - return (id_int_t(id) < capacity()) && ( ! base_t::test(id_int_t(id)) ); + return (id_int_t(id) < capacity()) && ( ! Base_t::test(id_int_t(id)) ); }; /** * @brief Iterate all existing IDs. Read-only */ - constexpr IdIterator_t begin() const noexcept + constexpr Iterator_t begin() const noexcept { - return IdIterator_t(std::begin(bitview().ints()), std::end(bitview().ints()), 0, 0); + return Iterator_t{ bitview().zeros().begin() }; } - constexpr typename IdIterator_t::Sentinel end() const noexcept { return {}; } + constexpr Sentinel_t end() const noexcept { return {}; } - constexpr base_t& bitview() noexcept { return static_cast(*this); } - constexpr base_t const& bitview() const noexcept { return static_cast(*this); } }; -template +template template -ITER_T BitViewIdRegistry::create(ITER_T first, SNTL_T last) +ITER_T BitViewIdRegistry::create(ITER_T first, SNTL_T last) { - auto const &ones = base_t::ones(); + auto const &ones = Base_t::ones(); auto onesFirst = ones.begin(); auto const &onesLast = ones.end(); @@ -107,7 +107,7 @@ ITER_T BitViewIdRegistry::create(ITER_T first, SNTL_T last) std::size_t const pos = *onesFirst; *first = ID_T(pos); - base_t::reset(pos); + Base_t::reset(pos); std::advance(onesFirst, 1); std::advance(first, 1); @@ -115,16 +115,16 @@ ITER_T BitViewIdRegistry::create(ITER_T first, SNTL_T last) return first; } -template -constexpr auto bitview_id_reg(IT_T first, ITB_T last, [[maybe_unused]] ID_T id) -{ - return BitViewIdRegistry(lgrn::bit_view(first, last), id); -} - -template -constexpr auto bitview_id_reg(RANGE_T& rRange, [[maybe_unused]] ID_T id = ID_T(0)) -{ - return bitview_id_reg(std::begin(rRange), std::end(rRange), ID_T(0)); -} +//template +//constexpr auto bitview_id_reg(IT_T first, ITB_T last, [[maybe_unused]] ID_T id) +//{ +// return BitViewIdRegistry(lgrn::bit_view(first, last), id); +//} + +//template +//constexpr auto bitview_id_reg(BITVIEW_T& rRange, [[maybe_unused]] ID_T id = ID_T(0)) +//{ +// return bitview_id_reg(std::begin(rRange), std::end(rRange), ID_T(0)); +//} } // namespace lgrn diff --git a/src/longeron/id_management/cast_iterator.hpp b/src/longeron/id_management/cast_iterator.hpp new file mode 100644 index 0000000..92cb103 --- /dev/null +++ b/src/longeron/id_management/cast_iterator.hpp @@ -0,0 +1,28 @@ +/** + * SPDX-License-Identifier: MIT + * SPDX-FileCopyrightText: 2024 Neal Nicdao + */ +#pragma once + +#include + +namespace lgrn +{ + +template +class IdCastIterator : public BASE_T +{ +public: + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = ID_T; + using pointer = void; + using reference = void; + + constexpr value_type operator*() const noexcept + { + return ID_T(BASE_T::operator*()); + } +}; + +} // namespace lgrn diff --git a/src/longeron/id_management/id_set_stl.hpp b/src/longeron/id_management/id_set_stl.hpp index a7dc3da..4f07e6b 100644 --- a/src/longeron/id_management/id_set_stl.hpp +++ b/src/longeron/id_management/id_set_stl.hpp @@ -6,6 +6,11 @@ #include "bitview_id_set.hpp" +#include "../containers/bit_view.hpp" +#include "../utility/bitmath.hpp" + +#include + #include namespace lgrn @@ -17,19 +22,19 @@ namespace lgrn * * No automatic reallocations. Use \c reserve(); */ -template -class IdSetStl : public BitViewIdSet, ID_T> +template > > +class IdSetStl : public BitViewIdSet { public: - using base_t = BitViewIdSet, ID_T>; - using bitview_t = typename base_t::base_t; + using Base_t = BitViewIdSet; + using bitview_t = typename Base_t::Base_t; - [[nodiscard]] constexpr auto& vec() noexcept { return base_t::bitview().ints(); } - [[nodiscard]] constexpr auto const& vec() const noexcept { return base_t::bitview().ints(); } + [[nodiscard]] constexpr auto& vec() noexcept { return Base_t::bitview().ints(); } + [[nodiscard]] constexpr auto const& vec() const noexcept { return Base_t::bitview().ints(); } void resize(std::size_t n) { - vec().resize(lgrn::div_ceil(n, base_t::bitview().int_bitsize()), 0); + vec().resize(lgrn::div_ceil(n, Base_t::bitview().int_bitsize()), 0); } }; diff --git a/src/longeron/id_management/registry_stl.hpp b/src/longeron/id_management/registry_stl.hpp index 58007fb..bccb5f7 100644 --- a/src/longeron/id_management/registry_stl.hpp +++ b/src/longeron/id_management/registry_stl.hpp @@ -18,17 +18,16 @@ namespace lgrn * std::vector internally. */ template > -class IdRegistryStl : private BitViewIdRegistry + typename BITVIEW_T = BitView< std::vector > > +class IdRegistryStl : private BitViewIdRegistry { public: - - using base_t = BitViewIdRegistry; - using bitview_t = typename base_t::base_t; + using Base_t = BitViewIdRegistry; + using BitView_t = typename Base_t::Base_t; class Generator { - using Iterator_t = BitPosIterator< typename bitview_t::iter_t, typename bitview_t::sntl_t, true >; + using OnesIter_t = typename BitView_t::OnesIter_t; public: Generator(IdRegistryStl &rRegistry) : iter{rRegistry.bitview().ones().begin()} @@ -40,22 +39,23 @@ class IdRegistryStl : private BitViewIdRegistry ID_T operator()() { return create(); } private: - Iterator_t iter; + OnesIter_t iter; IdRegistryStl &rRegistry; }; friend Generator; - IdRegistryStl() = default; - IdRegistryStl(base_t reg) : base_t(reg) { } + IdRegistryStl() noexcept = default; + IdRegistryStl(Base_t reg) : Base_t{ std::move(reg) } { } - using base_t::begin; - using base_t::bitview; - using base_t::capacity; - using base_t::end; - using base_t::exists; - using base_t::remove; - using base_t::size; + using Base_t::Base_t; + using Base_t::begin; + using Base_t::bitview; + using Base_t::capacity; + using Base_t::end; + using Base_t::exists; + using Base_t::remove; + using Base_t::size; /** * @brief Create a single ID @@ -94,11 +94,11 @@ class IdRegistryStl : private BitViewIdRegistry void reserve(std::size_t n) { // Resize with all new bits set, as 1 is for free Id - vec().resize(lgrn::div_ceil(n, base_t::bitview().int_bitsize()), ~uint64_t(0)); + vec().resize(lgrn::div_ceil(n, Base_t::bitview().int_bitsize()), ~uint64_t(0)); } - [[nodiscard]] constexpr auto& vec() noexcept { return base_t::bitview().ints(); } - [[nodiscard]] constexpr auto const& vec() const noexcept { return base_t::bitview().ints(); } + [[nodiscard]] constexpr auto& vec() noexcept { return Base_t::bitview().ints(); } + [[nodiscard]] constexpr auto const& vec() const noexcept { return Base_t::bitview().ints(); } private: @@ -121,13 +121,13 @@ ITER_T IdRegistryStl::create(ITER_T first, SNTL_T { if constexpr (NO_AUTO_RESIZE) { - return base_t::create(first, last); + return Base_t::create(first, last); } else { while (true) { - first = base_t::create(first, last); + first = Base_t::create(first, last); if (first == last) { @@ -145,7 +145,7 @@ ITER_T IdRegistryStl::create(ITER_T first, SNTL_T template ID_T IdRegistryStl::Generator::create() { - using Sentinel_t = typename Generator::Iterator_t::Sentinel; + using Sentinel_t = typename Generator::OnesIter_t::Sentinel; if (iter == Sentinel_t{}) // If out of space { diff --git a/src/longeron/id_management/span_map.hpp b/src/longeron/id_management/span_map.hpp deleted file mode 100644 index e69de29..0000000 diff --git a/test/id_management/registry.cpp b/test/id_management/registry.cpp index e96f14b..6a91c16 100644 --- a/test/id_management/registry.cpp +++ b/test/id_management/registry.cpp @@ -47,7 +47,7 @@ TEST(IdRegistry, ManageIds) ASSERT_TRUE( registry.exists(idC) ); ASSERT_EQ( registry.size(), 3 ); - std::array idsArray; + std::array idsArray{}; registry.create(std::begin(idsArray), std::end(idsArray)); for (Id id : idsArray)