From fc1b59cee222ccfd4c73daf7c368d860a0381068 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 28 Dec 2018 08:03:02 +0700 Subject: [PATCH] sparse indexer for sparse_set and sparse_map --- ecs.hpp | 124 ++++++++++++++++++++++++++++++++++---------------- ecs_tests.cpp | 56 +++++++++++++++++++++-- 2 files changed, 139 insertions(+), 41 deletions(-) diff --git a/ecs.hpp b/ecs.hpp index d563f26..fd3676e 100644 --- a/ecs.hpp +++ b/ecs.hpp @@ -159,6 +159,33 @@ namespace ecs_hpp } } +// ----------------------------------------------------------------------------- +// +// detail::sparse_indexer +// +// ----------------------------------------------------------------------------- + +namespace ecs_hpp +{ + namespace detail + { + template < typename T + , bool = std::is_unsigned::value && sizeof(T) <= sizeof(std::size_t) > + struct sparse_unsigned_indexer { + std::size_t operator()(const T v) const noexcept { + return static_cast(v); + } + }; + + template < typename T > + struct sparse_unsigned_indexer {}; + + template < typename T > + struct sparse_indexer + : public sparse_unsigned_indexer {}; + } +} + // ----------------------------------------------------------------------------- // // detail::sparse_set @@ -169,12 +196,10 @@ namespace ecs_hpp { namespace detail { - template < typename T > + template < typename T + , typename Indexer = sparse_indexer > class sparse_set final { public: - static_assert( - std::is_unsigned::value, - "sparse_set can contain an unsigned integers only"); using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; public: @@ -205,27 +230,42 @@ namespace ecs_hpp return cbegin() + static_cast
(size_); } public: - bool insert(const T v) { + sparse_set(const Indexer& indexer = Indexer()) + : indexer_(indexer) {} + + bool insert(T&& v) { if ( has(v) ) { return false; } - if ( v >= capacity_ ) { - reserve(new_capacity_for_(v + 1u)); + const std::size_t vi = indexer_(v); + if ( vi >= capacity_ ) { + reserve(new_capacity_for_(vi + 1u)); } - dense_[size_] = v; - sparse_[v] = size_; + dense_[size_] = std::move(v); + sparse_[vi] = size_; ++size_; return true; } - bool unordered_erase(const T v) noexcept { + bool insert(const T& v) { + return insert(T(v)); + } + + template < typename... Args > + bool emplace(Args&&... args) { + return insert(T(std::forward(args)...)); + } + + bool unordered_erase(const T& v) + noexcept(std::is_nothrow_move_assignable::value) + { if ( !has(v) ) { return false; } - const std::size_t index = sparse_[v]; - const T last = dense_[size_ - 1u]; - dense_[index] = last; - sparse_[last] = index; + const std::size_t vi = indexer_(v); + const std::size_t index = sparse_[vi]; + dense_[index] = std::move(dense_[size_ - 1u]); + sparse_[indexer_(dense_[index])] = index; --size_; return true; } @@ -234,29 +274,31 @@ namespace ecs_hpp size_ = 0u; } - bool has(const T v) const noexcept { - return v < capacity_ - && sparse_[v] < size_ - && dense_[sparse_[v]] == v; + bool has(const T& v) const noexcept { + const std::size_t vi = indexer_(v); + return vi < capacity_ + && sparse_[vi] < size_ + && dense_[sparse_[vi]] == v; } - const_iterator find(const T v) const noexcept { + const_iterator find(const T& v) const noexcept { + const std::size_t vi = indexer_(v); return has(v) - ? begin() + sparse_[v] + ? begin() + sparse_[vi] : end(); } - std::size_t get_index(const T v) const { + std::size_t get_index(const T& v) const { const auto p = find_index(v); if ( p.second ) { return p.first; } - throw std::out_of_range("sparse_set"); + throw std::out_of_range("sparse_set"); } - std::pair find_index(const T v) const noexcept { + std::pair find_index(const T& v) const noexcept { return has(v) - ? std::make_pair(sparse_[v], true) + ? std::make_pair(sparse_[indexer_(v)], true) : std::make_pair(std::size_t(-1), false); } @@ -299,6 +341,7 @@ namespace ecs_hpp return std::max(capacity_ * 2u, nsize); } private: + Indexer indexer_; std::vector dense_; std::vector sparse_; std::size_t size_{0u}; @@ -317,12 +360,11 @@ namespace ecs_hpp { namespace detail { - template < typename K, typename T > + template < typename K + , typename T + , typename Indexer = sparse_indexer > class sparse_map final { public: - static_assert( - std::is_unsigned::value, - "sparse_map can contain unsigned integers keys only"); using iterator = typename std::vector::iterator; using const_iterator = typename std::vector::const_iterator; public: @@ -350,7 +392,10 @@ namespace ecs_hpp return keys_.cend(); } public: - bool insert(const K k, const T& v) { + sparse_map(const Indexer& indexer = Indexer()) + : keys_(indexer) {} + + bool insert(const K& k, const T& v) { if ( keys_.has(k) ) { return false; } @@ -363,7 +408,7 @@ namespace ecs_hpp } } - bool insert(const K k, T&& v) { + bool insert(const K& k, T&& v) { if ( keys_.has(k) ) { return false; } @@ -377,7 +422,7 @@ namespace ecs_hpp } template < typename... Args > - bool emplace(const K k, Args&&... args) { + bool emplace(const K& k, Args&&... args) { if ( keys_.has(k) ) { return false; } @@ -390,7 +435,10 @@ namespace ecs_hpp } } - bool unordered_erase(const K k) { + std::enable_if_t< + std::is_nothrow_move_assignable::value, + bool> + unordered_erase(const K& k) { if ( !keys_.has(k) ) { return false; } @@ -406,26 +454,26 @@ namespace ecs_hpp values_.clear(); } - bool has(const K k) const noexcept { + bool has(const K& k) const noexcept { return keys_.has(k); } - T& get_value(const K k) { + T& get_value(const K& k) { return values_[keys_.get_index(k)]; } - const T& get_value(const K k) const { + const T& get_value(const K& k) const { return values_[keys_.get_index(k)]; } - T* find_value(const K k) noexcept { + T* find_value(const K& k) noexcept { const auto ip = keys_.find_index(k); return ip.second ? &values_[ip.first] : nullptr; } - const T* find_value(const K k) const noexcept { + const T* find_value(const K& k) const noexcept { const auto ip = keys_.find_index(k); return ip.second ? &values_[ip.first] @@ -453,7 +501,7 @@ namespace ecs_hpp return values_.capacity(); } private: - sparse_set keys_; + sparse_set keys_; std::vector values_; }; } diff --git a/ecs_tests.cpp b/ecs_tests.cpp index 48dfc79..c6afe4b 100644 --- a/ecs_tests.cpp +++ b/ecs_tests.cpp @@ -37,6 +37,19 @@ namespace return l.x == r.x && l.y == r.y; } + + struct mult_indexer { + template < typename T > + std::size_t operator()(const T& v) const noexcept { + return static_cast(v * 2); + } + }; + + struct position_c_indexer { + std::size_t operator()(const position_c& v) const noexcept { + return static_cast(v.x); + } + }; } TEST_CASE("detail") { @@ -70,7 +83,7 @@ TEST_CASE("detail") { SECTION("sparse_set") { using namespace ecs::detail; { - sparse_set s; + sparse_set s{mult_indexer{}}; REQUIRE(s.empty()); REQUIRE_FALSE(s.size()); @@ -84,7 +97,7 @@ TEST_CASE("detail") { REQUIRE_FALSE(s.empty()); REQUIRE(s.size() == 1u); - REQUIRE(s.capacity() == 43u); + REQUIRE(s.capacity() == 85u); REQUIRE(s.has(42u)); REQUIRE_FALSE(s.has(84u)); @@ -108,7 +121,7 @@ TEST_CASE("detail") { REQUIRE_FALSE(s.has(84u)); REQUIRE(s.empty()); REQUIRE_FALSE(s.size()); - REQUIRE(s.capacity() == 43u * 2); + REQUIRE(s.capacity() == 85u * 2); s.insert(42u); s.insert(84u); @@ -133,6 +146,22 @@ TEST_CASE("detail") { REQUIRE_THROWS(s.get_index(42u)); REQUIRE(s.get_index(84u) == 0u); } + { + sparse_set s{position_c_indexer()}; + REQUIRE(s.insert(position_c(1,2))); + REQUIRE_FALSE(s.insert(position_c(1,2))); + REQUIRE(s.has(position_c(1,2))); + REQUIRE(s.emplace(3,4)); + REQUIRE(s.has(position_c(3,4))); + REQUIRE(s.get_index(position_c(1,2)) == 0); + REQUIRE(s.get_index(position_c(3,4)) == 1); + REQUIRE(s.find_index(position_c(1,2)).first == 0); + REQUIRE(s.find_index(position_c(3,4)).first == 1); + REQUIRE(s.find_index(position_c(1,2)).second); + REQUIRE(s.find_index(position_c(3,4)).second); + REQUIRE(s.unordered_erase(position_c(1,2))); + REQUIRE(s.get_index(position_c(3,4)) == 0); + } } SECTION("sparse_map") { using namespace ecs::detail; @@ -210,6 +239,27 @@ TEST_CASE("detail") { REQUIRE_FALSE(m.has(42u)); REQUIRE_FALSE(m.has(84u)); } + { + struct obj_t { + int x; + obj_t(int nx) : x(nx) {} + }; + + sparse_map s{position_c_indexer()}; + REQUIRE(s.insert(position_c(1,2), obj_t{1})); + REQUIRE_FALSE(s.insert(position_c(1,2), obj_t{1})); + REQUIRE(s.has(position_c(1,2))); + REQUIRE(s.emplace(position_c(3,4), obj_t{3})); + REQUIRE(s.has(position_c(3,4))); + REQUIRE(s.get_value(position_c(1,2)).x == 1); + REQUIRE(s.get_value(position_c(3,4)).x == 3); + REQUIRE(s.find_value(position_c(1,2))->x == 1); + REQUIRE(s.find_value(position_c(3,4))->x == 3); + REQUIRE(s.find_value(position_c(1,2))); + REQUIRE(s.find_value(position_c(3,4))); + REQUIRE(s.unordered_erase(position_c(1,2))); + REQUIRE(s.get_value(position_c(3,4)).x == 3); + } } }