diff --git a/src/entt/entity/runtime_view.hpp b/src/entt/entity/runtime_view.hpp index ddb2b6d85..84a067338 100644 --- a/src/entt/entity/runtime_view.hpp +++ b/src/entt/entity/runtime_view.hpp @@ -126,6 +126,12 @@ class basic_runtime_view { static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector; + [[nodiscard]] auto offset() const noexcept { + ENTT_ASSERT(!pools.empty(), "Invalid view"); + const auto &leading = *pools.front(); + return (leading.policy() == deletion_policy::swap_only) ? leading.free_list() : leading.size(); + } + public: /*! @brief Allocator type. */ using allocator_type = Allocator; @@ -133,6 +139,8 @@ public: using entity_type = typename Type::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; /*! @brief Common type among all storage types. */ using common_type = Type; /*! @brief Bidirectional iterator type. */ @@ -219,10 +227,10 @@ public: * @return This runtime view. */ basic_runtime_view &iterate(common_type &base) { - if(pools.empty() || !(base.size() < pools[0u]->size())) { + if(pools.empty() || !(base.size() < pools.front()->size())) { pools.push_back(&base); } else { - pools.push_back(std::exchange(pools[0u], &base)); + pools.push_back(std::exchange(pools.front(), &base)); } return *this; @@ -243,7 +251,7 @@ public: * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const { - return pools.empty() ? size_type{} : pools.front()->size(); + return pools.empty() ? size_type{} : offset(); } /** @@ -255,7 +263,7 @@ public: * @return An iterator to the first entity that has the given elements. */ [[nodiscard]] iterator begin() const { - return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; + return pools.empty() ? iterator{} : iterator{pools, filter, pools.front()->end() - static_cast(offset())}; } /** @@ -265,7 +273,7 @@ public: * given elements. */ [[nodiscard]] iterator end() const { - return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; + return pools.empty() ? iterator{} : iterator{pools, filter, pools.front()->end()}; } /** @@ -284,7 +292,8 @@ public: [[nodiscard]] bool contains(const entity_type entt) const { return !pools.empty() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) - && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); + && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }) + && pools.front()->index(entt) < offset(); } /** diff --git a/test/entt/entity/runtime_view.cpp b/test/entt/entity/runtime_view.cpp index af711234d..53997be78 100644 --- a/test/entt/entity/runtime_view.cpp +++ b/test/entt/entity/runtime_view.cpp @@ -73,6 +73,27 @@ TYPED_TEST(RuntimeView, Functionalities) { ASSERT_EQ(view.begin(), view.end()); } +TYPED_TEST(RuntimeView, InvalidView) { + using runtime_view_type = typename TestFixture::type; + + runtime_view_type view{}; + + ASSERT_FALSE(view); + + ASSERT_EQ(view.size_hint(), 0u); + ASSERT_FALSE(view.contains(entt::null)); + + ASSERT_EQ(view.begin(), typename decltype(view)::iterator{}); + ASSERT_EQ(view.begin(), view.end()); + + view.each([](const entt::entity) { FAIL(); }); + + entt::storage storage; + view.iterate(storage); + + ASSERT_TRUE(view); +} + TYPED_TEST(RuntimeView, Constructors) { using runtime_view_type = typename TestFixture::type; @@ -516,3 +537,36 @@ TYPED_TEST(RuntimeView, StorageEntityWithExclude) { ASSERT_EQ(entt, entity[1u]); }); } + +TYPED_TEST(RuntimeView, StorageEntityExcludeOnly) { + using runtime_view_type = typename TestFixture::type; + + std::tuple, entt::storage> storage{}; + const std::array entity{std::get<0>(storage).generate(), std::get<0>(storage).generate(), std::get<0>(storage).generate()}; + runtime_view_type view{}; + + std::get<1>(storage).emplace(entity[2u]); + + std::get<0>(storage).erase(entity[0u]); + std::get<0>(storage).bump(entity[0u]); + + view.iterate(std::get<0>(storage)).exclude(std::get<1>(storage)); + + ASSERT_FALSE(view.contains(entity[0u])); + ASSERT_TRUE(view.contains(entity[1u])); + ASSERT_FALSE(view.contains(entity[2u])); + + ASSERT_EQ(view.size_hint(), 2u); + ASSERT_NE(view.begin(), view.end()); + + ASSERT_EQ(std::distance(view.begin(), view.end()), 1); + ASSERT_EQ(*view.begin(), entity[1u]); + + for(auto entt: view) { + ASSERT_EQ(entt, entity[1u]); + } + + view.each([&entity](auto entt) { + ASSERT_EQ(entt, entity[1u]); + }); +}