diff options
Diffstat (limited to 'deps/include/entt/container')
| -rw-r--r-- | deps/include/entt/container/dense_map.hpp | 1043 | ||||
| -rw-r--r-- | deps/include/entt/container/dense_set.hpp | 927 | ||||
| -rw-r--r-- | deps/include/entt/container/fwd.hpp | 38 | ||||
| -rw-r--r-- | deps/include/entt/container/table.hpp | 460 |
4 files changed, 2468 insertions, 0 deletions
diff --git a/deps/include/entt/container/dense_map.hpp b/deps/include/entt/container/dense_map.hpp new file mode 100644 index 0000000..4674761 --- /dev/null +++ b/deps/include/entt/container/dense_map.hpp @@ -0,0 +1,1043 @@ +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
+#define ENTT_CONTAINER_DENSE_MAP_HPP
+
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "../config/config.h"
+#include "../core/compressed_pair.hpp"
+#include "../core/iterator.hpp"
+#include "../core/memory.hpp"
+#include "../core/bit.hpp"
+#include "../core/type_traits.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/*! @cond TURN_OFF_DOXYGEN */
+namespace internal {
+
+template<typename Key, typename Type>
+struct dense_map_node final {
+ using value_type = std::pair<Key, Type>;
+
+ template<typename... Args>
+ dense_map_node(const std::size_t pos, Args &&...args)
+ : next{pos},
+ element{std::forward<Args>(args)...} {}
+
+ template<typename Allocator, typename... Args>
+ dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args)
+ : next{pos},
+ element{entt::make_obj_using_allocator<value_type>(allocator, std::forward<Args>(args)...)} {}
+
+ template<typename Allocator>
+ dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other)
+ : next{other.next},
+ element{entt::make_obj_using_allocator<value_type>(allocator, other.element)} {}
+
+ template<typename Allocator>
+ dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other)
+ : next{other.next},
+ element{entt::make_obj_using_allocator<value_type>(allocator, std::move(other.element))} {}
+
+ std::size_t next;
+ value_type element;
+};
+
+template<typename It>
+class dense_map_iterator final {
+ template<typename>
+ friend class dense_map_iterator;
+
+ using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+ using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+ using value_type = std::pair<first_type, second_type>;
+ using pointer = input_iterator_pointer<value_type>;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::input_iterator_tag;
+ using iterator_concept = std::random_access_iterator_tag;
+
+ constexpr dense_map_iterator() noexcept
+ : it{} {}
+
+ constexpr dense_map_iterator(const It iter) noexcept
+ : it{iter} {}
+
+ template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+ constexpr dense_map_iterator(const dense_map_iterator<Other> &other) noexcept
+ : it{other.it} {}
+
+ constexpr dense_map_iterator &operator++() noexcept {
+ return ++it, *this;
+ }
+
+ constexpr dense_map_iterator operator++(int) noexcept {
+ dense_map_iterator orig = *this;
+ return ++(*this), orig;
+ }
+
+ constexpr dense_map_iterator &operator--() noexcept {
+ return --it, *this;
+ }
+
+ constexpr dense_map_iterator operator--(int) noexcept {
+ dense_map_iterator orig = *this;
+ return operator--(), orig;
+ }
+
+ constexpr dense_map_iterator &operator+=(const difference_type value) noexcept {
+ it += value;
+ return *this;
+ }
+
+ constexpr dense_map_iterator operator+(const difference_type value) const noexcept {
+ dense_map_iterator copy = *this;
+ return (copy += value);
+ }
+
+ constexpr dense_map_iterator &operator-=(const difference_type value) noexcept {
+ return (*this += -value);
+ }
+
+ constexpr dense_map_iterator operator-(const difference_type value) const noexcept {
+ return (*this + -value);
+ }
+
+ [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
+ return {it[value].element.first, it[value].element.second};
+ }
+
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
+ return operator*();
+ }
+
+ [[nodiscard]] constexpr reference operator*() const noexcept {
+ return operator[](0);
+ }
+
+ template<typename Lhs, typename Rhs>
+ friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
+
+ template<typename Lhs, typename Rhs>
+ friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
+
+ template<typename Lhs, typename Rhs>
+ friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
+
+private:
+ It it;
+};
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return lhs.it - rhs.it;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return lhs.it == rhs.it;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return lhs.it < rhs.it;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return rhs < lhs;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return !(lhs > rhs);
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_map_local_iterator final {
+ template<typename>
+ friend class dense_map_local_iterator;
+
+ using first_type = decltype(std::as_const(std::declval<It>()->element.first));
+ using second_type = decltype((std::declval<It>()->element.second));
+
+public:
+ using value_type = std::pair<first_type, second_type>;
+ using pointer = input_iterator_pointer<value_type>;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::input_iterator_tag;
+ using iterator_concept = std::forward_iterator_tag;
+
+ constexpr dense_map_local_iterator() noexcept
+ : it{},
+ offset{} {}
+
+ constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept
+ : it{iter},
+ offset{pos} {}
+
+ template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+ constexpr dense_map_local_iterator(const dense_map_local_iterator<Other> &other) noexcept
+ : it{other.it},
+ offset{other.offset} {}
+
+ constexpr dense_map_local_iterator &operator++() noexcept {
+ return offset = it[offset].next, *this;
+ }
+
+ constexpr dense_map_local_iterator operator++(int) noexcept {
+ dense_map_local_iterator orig = *this;
+ return ++(*this), orig;
+ }
+
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
+ return operator*();
+ }
+
+ [[nodiscard]] constexpr reference operator*() const noexcept {
+ return {it[offset].element.first, it[offset].element.second};
+ }
+
+ [[nodiscard]] constexpr std::size_t index() const noexcept {
+ return offset;
+ }
+
+private:
+ It it;
+ std::size_t offset;
+};
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
+ return lhs.index() == rhs.index();
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+} // namespace internal
+/*! @endcond */
+
+/**
+ * @brief Associative container for key-value pairs with unique keys.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on the hash of its key. Keys with the same hash
+ * code appear in the same bucket.
+ *
+ * @tparam Key Key type of the associative container.
+ * @tparam Type Mapped type of the associative container.
+ * @tparam Hash Type of function to use to hash the keys.
+ * @tparam KeyEqual Type of function to use to compare the keys for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Key, typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_map {
+ static constexpr float default_threshold = 0.875f;
+ static constexpr std::size_t minimum_capacity = 8u;
+
+ using node_type = internal::dense_map_node<Key, Type>;
+ using alloc_traits = std::allocator_traits<Allocator>;
+ static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type");
+ using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+ using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+ template<typename Other>
+ [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
+ return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
+ }
+
+ template<typename Other>
+ [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
+ for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+ if(packed.second()(it->first, key)) {
+ return begin() + static_cast<typename iterator::difference_type>(it.index());
+ }
+ }
+
+ return end();
+ }
+
+ template<typename Other>
+ [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
+ for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+ if(packed.second()(it->first, key)) {
+ return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+ }
+ }
+
+ return cend();
+ }
+
+ template<typename Other, typename... Args>
+ [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) {
+ const auto index = key_to_bucket(key);
+
+ if(auto it = constrained_find(key, index); it != end()) {
+ return std::make_pair(it, false);
+ }
+
+ packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward<Other>(key)), std::forward_as_tuple(std::forward<Args>(args)...));
+ sparse.first()[index] = packed.first().size() - 1u;
+ rehash_if_required();
+
+ return std::make_pair(--end(), true);
+ }
+
+ template<typename Other, typename Arg>
+ [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) {
+ const auto index = key_to_bucket(key);
+
+ if(auto it = constrained_find(key, index); it != end()) {
+ it->second = std::forward<Arg>(value);
+ return std::make_pair(it, false);
+ }
+
+ packed.first().emplace_back(sparse.first()[index], std::forward<Other>(key), std::forward<Arg>(value));
+ sparse.first()[index] = packed.first().size() - 1u;
+ rehash_if_required();
+
+ return std::make_pair(--end(), true);
+ }
+
+ void move_and_pop(const std::size_t pos) {
+ if(const auto last = size() - 1u; pos != last) {
+ size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
+ packed.first()[pos] = std::move(packed.first().back());
+ for(; *curr != last; curr = &packed.first()[*curr].next) {}
+ *curr = pos;
+ }
+
+ packed.first().pop_back();
+ }
+
+ void rehash_if_required() {
+ if(size() > (bucket_count() * max_load_factor())) {
+ rehash(bucket_count() * 2u);
+ }
+ }
+
+public:
+ /*! @brief Allocator type. */
+ using allocator_type = Allocator;
+ /*! @brief Key type of the container. */
+ using key_type = Key;
+ /*! @brief Mapped type of the container. */
+ using mapped_type = Type;
+ /*! @brief Key-value type of the container. */
+ using value_type = std::pair<const Key, Type>;
+ /*! @brief Unsigned integer type. */
+ using size_type = std::size_t;
+ /*! @brief Type of function to use to hash the keys. */
+ using hasher = Hash;
+ /*! @brief Type of function to use to compare the keys for equality. */
+ using key_equal = KeyEqual;
+ /*! @brief Input iterator type. */
+ using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
+ /*! @brief Constant input iterator type. */
+ using const_iterator = internal::dense_map_iterator<typename packed_container_type::const_iterator>;
+ /*! @brief Input iterator type. */
+ using local_iterator = internal::dense_map_local_iterator<typename packed_container_type::iterator>;
+ /*! @brief Constant input iterator type. */
+ using const_local_iterator = internal::dense_map_local_iterator<typename packed_container_type::const_iterator>;
+
+ /*! @brief Default constructor. */
+ dense_map()
+ : dense_map{minimum_capacity} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator.
+ * @param allocator The allocator to use.
+ */
+ explicit dense_map(const allocator_type &allocator)
+ : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator and user
+ * supplied minimal number of buckets.
+ * @param cnt Minimal number of buckets.
+ * @param allocator The allocator to use.
+ */
+ dense_map(const size_type cnt, const allocator_type &allocator)
+ : dense_map{cnt, hasher{}, key_equal{}, allocator} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator, hash
+ * function and user supplied minimal number of buckets.
+ * @param cnt Minimal number of buckets.
+ * @param hash Hash function to use.
+ * @param allocator The allocator to use.
+ */
+ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator)
+ : dense_map{cnt, hash, key_equal{}, allocator} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator, hash
+ * function, compare function and user supplied minimal number of buckets.
+ * @param cnt Minimal number of buckets.
+ * @param hash Hash function to use.
+ * @param equal Compare function to use.
+ * @param allocator The allocator to use.
+ */
+ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+ : sparse{allocator, hash},
+ packed{allocator, equal} {
+ rehash(cnt);
+ }
+
+ /*! @brief Default copy constructor. */
+ dense_map(const dense_map &) = default;
+
+ /**
+ * @brief Allocator-extended copy constructor.
+ * @param other The instance to copy from.
+ * @param allocator The allocator to use.
+ */
+ dense_map(const dense_map &other, const allocator_type &allocator)
+ : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+ packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+ threshold{other.threshold} {}
+
+ /*! @brief Default move constructor. */
+ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
+
+ /**
+ * @brief Allocator-extended move constructor.
+ * @param other The instance to move from.
+ * @param allocator The allocator to use.
+ */
+ dense_map(dense_map &&other, const allocator_type &allocator)
+ : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+ packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+ threshold{other.threshold} {}
+
+ /*! @brief Default destructor. */
+ ~dense_map() noexcept = default;
+
+ /**
+ * @brief Default copy assignment operator.
+ * @return This container.
+ */
+ dense_map &operator=(const dense_map &) = default;
+
+ /**
+ * @brief Default move assignment operator.
+ * @return This container.
+ */
+ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
+
+ /**
+ * @brief Returns the associated allocator.
+ * @return The associated allocator.
+ */
+ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+ return sparse.first().get_allocator();
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning.
+ *
+ * If the array is empty, the returned iterator will be equal to `end()`.
+ *
+ * @return An iterator to the first instance of the internal array.
+ */
+ [[nodiscard]] const_iterator cbegin() const noexcept {
+ return packed.first().begin();
+ }
+
+ /*! @copydoc cbegin */
+ [[nodiscard]] const_iterator begin() const noexcept {
+ return cbegin();
+ }
+
+ /*! @copydoc begin */
+ [[nodiscard]] iterator begin() noexcept {
+ return packed.first().begin();
+ }
+
+ /**
+ * @brief Returns an iterator to the end.
+ * @return An iterator to the element following the last instance of the
+ * internal array.
+ */
+ [[nodiscard]] const_iterator cend() const noexcept {
+ return packed.first().end();
+ }
+
+ /*! @copydoc cend */
+ [[nodiscard]] const_iterator end() const noexcept {
+ return cend();
+ }
+
+ /*! @copydoc end */
+ [[nodiscard]] iterator end() noexcept {
+ return packed.first().end();
+ }
+
+ /**
+ * @brief Checks whether a container is empty.
+ * @return True if the container is empty, false otherwise.
+ */
+ [[nodiscard]] bool empty() const noexcept {
+ return packed.first().empty();
+ }
+
+ /**
+ * @brief Returns the number of elements in a container.
+ * @return Number of elements in a container.
+ */
+ [[nodiscard]] size_type size() const noexcept {
+ return packed.first().size();
+ }
+
+ /**
+ * @brief Returns the maximum possible number of elements.
+ * @return Maximum possible number of elements.
+ */
+ [[nodiscard]] size_type max_size() const noexcept {
+ return packed.first().max_size();
+ }
+
+ /*! @brief Clears the container. */
+ void clear() noexcept {
+ sparse.first().clear();
+ packed.first().clear();
+ rehash(0u);
+ }
+
+ /**
+ * @brief Inserts an element into the container, if the key does not exist.
+ * @param value A key-value pair eventually convertible to the value type.
+ * @return A pair consisting of an iterator to the inserted element (or to
+ * the element that prevented the insertion) and a bool denoting whether the
+ * insertion took place.
+ */
+ std::pair<iterator, bool> insert(const value_type &value) {
+ return insert_or_do_nothing(value.first, value.second);
+ }
+
+ /*! @copydoc insert */
+ std::pair<iterator, bool> insert(value_type &&value) {
+ return insert_or_do_nothing(std::move(value.first), std::move(value.second));
+ }
+
+ /**
+ * @copydoc insert
+ * @tparam Arg Type of the key-value pair to insert into the container.
+ */
+ template<typename Arg>
+ std::enable_if_t<std::is_constructible_v<value_type, Arg &&>, std::pair<iterator, bool>>
+ insert(Arg &&value) {
+ return insert_or_do_nothing(std::forward<Arg>(value).first, std::forward<Arg>(value).second);
+ }
+
+ /**
+ * @brief Inserts elements into the container, if their keys do not exist.
+ * @tparam It Type of input iterator.
+ * @param first An iterator to the first element of the range of elements.
+ * @param last An iterator past the last element of the range of elements.
+ */
+ template<typename It>
+ void insert(It first, It last) {
+ for(; first != last; ++first) {
+ insert(*first);
+ }
+ }
+
+ /**
+ * @brief Inserts an element into the container or assigns to the current
+ * element if the key already exists.
+ * @tparam Arg Type of the value to insert or assign.
+ * @param key A key used both to look up and to insert if not found.
+ * @param value A value to insert or assign.
+ * @return A pair consisting of an iterator to the element and a bool
+ * denoting whether the insertion took place.
+ */
+ template<typename Arg>
+ std::pair<iterator, bool> insert_or_assign(const key_type &key, Arg &&value) {
+ return insert_or_overwrite(key, std::forward<Arg>(value));
+ }
+
+ /*! @copydoc insert_or_assign */
+ template<typename Arg>
+ std::pair<iterator, bool> insert_or_assign(key_type &&key, Arg &&value) {
+ return insert_or_overwrite(std::move(key), std::forward<Arg>(value));
+ }
+
+ /**
+ * @brief Constructs an element in-place, if the key does not exist.
+ *
+ * The element is also constructed when the container already has the key,
+ * in which case the newly constructed object is destroyed immediately.
+ *
+ * @tparam Args Types of arguments to forward to the constructor of the
+ * element.
+ * @param args Arguments to forward to the constructor of the element.
+ * @return A pair consisting of an iterator to the inserted element (or to
+ * the element that prevented the insertion) and a bool denoting whether the
+ * insertion took place.
+ */
+ template<typename... Args>
+ std::pair<iterator, bool> emplace([[maybe_unused]] Args &&...args) {
+ if constexpr(sizeof...(Args) == 0u) {
+ return insert_or_do_nothing(key_type{});
+ } else if constexpr(sizeof...(Args) == 1u) {
+ return insert_or_do_nothing(std::forward<Args>(args).first..., std::forward<Args>(args).second...);
+ } else if constexpr(sizeof...(Args) == 2u) {
+ return insert_or_do_nothing(std::forward<Args>(args)...);
+ } else {
+ auto &node = packed.first().emplace_back(packed.first().size(), std::forward<Args>(args)...);
+ const auto index = key_to_bucket(node.element.first);
+
+ if(auto it = constrained_find(node.element.first, index); it != end()) {
+ packed.first().pop_back();
+ return std::make_pair(it, false);
+ }
+
+ std::swap(node.next, sparse.first()[index]);
+ rehash_if_required();
+
+ return std::make_pair(--end(), true);
+ }
+ }
+
+ /**
+ * @brief Inserts in-place if the key does not exist, does nothing if the
+ * key exists.
+ * @tparam Args Types of arguments to forward to the constructor of the
+ * element.
+ * @param key A key used both to look up and to insert if not found.
+ * @param args Arguments to forward to the constructor of the element.
+ * @return A pair consisting of an iterator to the inserted element (or to
+ * the element that prevented the insertion) and a bool denoting whether the
+ * insertion took place.
+ */
+ template<typename... Args>
+ std::pair<iterator, bool> try_emplace(const key_type &key, Args &&...args) {
+ return insert_or_do_nothing(key, std::forward<Args>(args)...);
+ }
+
+ /*! @copydoc try_emplace */
+ template<typename... Args>
+ std::pair<iterator, bool> try_emplace(key_type &&key, Args &&...args) {
+ return insert_or_do_nothing(std::move(key), std::forward<Args>(args)...);
+ }
+
+ /**
+ * @brief Removes an element from a given position.
+ * @param pos An iterator to the element to remove.
+ * @return An iterator following the removed element.
+ */
+ iterator erase(const_iterator pos) {
+ const auto diff = pos - cbegin();
+ erase(pos->first);
+ return begin() + diff;
+ }
+
+ /**
+ * @brief Removes the given elements from a container.
+ * @param first An iterator to the first element of the range of elements.
+ * @param last An iterator past the last element of the range of elements.
+ * @return An iterator following the last removed element.
+ */
+ iterator erase(const_iterator first, const_iterator last) {
+ const auto dist = first - cbegin();
+
+ for(auto from = last - cbegin(); from != dist; --from) {
+ erase(packed.first()[from - 1u].element.first);
+ }
+
+ return (begin() + dist);
+ }
+
+ /**
+ * @brief Removes the element associated with a given key.
+ * @param key A key value of an element to remove.
+ * @return Number of elements removed (either 0 or 1).
+ */
+ size_type erase(const key_type &key) {
+ for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
+ if(packed.second()(packed.first()[*curr].element.first, key)) {
+ const auto index = *curr;
+ *curr = packed.first()[*curr].next;
+ move_and_pop(index);
+ return 1u;
+ }
+ }
+
+ return 0u;
+ }
+
+ /**
+ * @brief Exchanges the contents with those of a given container.
+ * @param other Container to exchange the content with.
+ */
+ void swap(dense_map &other) {
+ using std::swap;
+ swap(sparse, other.sparse);
+ swap(packed, other.packed);
+ swap(threshold, other.threshold);
+ }
+
+ /**
+ * @brief Accesses a given element with bounds checking.
+ * @param key A key of an element to find.
+ * @return A reference to the mapped value of the requested element.
+ */
+ [[nodiscard]] mapped_type &at(const key_type &key) {
+ auto it = find(key);
+ ENTT_ASSERT(it != end(), "Invalid key");
+ return it->second;
+ }
+
+ /*! @copydoc at */
+ [[nodiscard]] const mapped_type &at(const key_type &key) const {
+ auto it = find(key);
+ ENTT_ASSERT(it != cend(), "Invalid key");
+ return it->second;
+ }
+
+ /**
+ * @brief Accesses or inserts a given element.
+ * @param key A key of an element to find or insert.
+ * @return A reference to the mapped value of the requested element.
+ */
+ [[nodiscard]] mapped_type &operator[](const key_type &key) {
+ return insert_or_do_nothing(key).first->second;
+ }
+
+ /**
+ * @brief Accesses or inserts a given element.
+ * @param key A key of an element to find or insert.
+ * @return A reference to the mapped value of the requested element.
+ */
+ [[nodiscard]] mapped_type &operator[](key_type &&key) {
+ return insert_or_do_nothing(std::move(key)).first->second;
+ }
+
+ /**
+ * @brief Returns the number of elements matching a key (either 1 or 0).
+ * @param key Key value of an element to search for.
+ * @return Number of elements matching the key (either 1 or 0).
+ */
+ [[nodiscard]] size_type count(const key_type &key) const {
+ return find(key) != end();
+ }
+
+ /**
+ * @brief Returns the number of elements matching a key (either 1 or 0).
+ * @tparam Other Type of the key value of an element to search for.
+ * @param key Key value of an element to search for.
+ * @return Number of elements matching the key (either 1 or 0).
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
+ count(const Other &key) const {
+ return find(key) != end();
+ }
+
+ /**
+ * @brief Finds an element with a given key.
+ * @param key Key value of an element to search for.
+ * @return An iterator to an element with the given key. If no such element
+ * is found, a past-the-end iterator is returned.
+ */
+ [[nodiscard]] iterator find(const key_type &key) {
+ return constrained_find(key, key_to_bucket(key));
+ }
+
+ /*! @copydoc find */
+ [[nodiscard]] const_iterator find(const key_type &key) const {
+ return constrained_find(key, key_to_bucket(key));
+ }
+
+ /**
+ * @brief Finds an element with a key that compares _equivalent_ to a given
+ * key.
+ * @tparam Other Type of the key value of an element to search for.
+ * @param key Key value of an element to search for.
+ * @return An iterator to an element with the given key. If no such element
+ * is found, a past-the-end iterator is returned.
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+ find(const Other &key) {
+ return constrained_find(key, key_to_bucket(key));
+ }
+
+ /*! @copydoc find */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+ find(const Other &key) const {
+ return constrained_find(key, key_to_bucket(key));
+ }
+
+ /**
+ * @brief Returns a range containing all elements with a given key.
+ * @param key Key value of an element to search for.
+ * @return A pair of iterators pointing to the first element and past the
+ * last element of the range.
+ */
+ [[nodiscard]] std::pair<iterator, iterator> equal_range(const key_type &key) {
+ const auto it = find(key);
+ return {it, it + !(it == end())};
+ }
+
+ /*! @copydoc equal_range */
+ [[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const key_type &key) const {
+ const auto it = find(key);
+ return {it, it + !(it == cend())};
+ }
+
+ /**
+ * @brief Returns a range containing all elements that compare _equivalent_
+ * to a given key.
+ * @tparam Other Type of an element to search for.
+ * @param key Key value of an element to search for.
+ * @return A pair of iterators pointing to the first element and past the
+ * last element of the range.
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
+ equal_range(const Other &key) {
+ const auto it = find(key);
+ return {it, it + !(it == end())};
+ }
+
+ /*! @copydoc equal_range */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
+ equal_range(const Other &key) const {
+ const auto it = find(key);
+ return {it, it + !(it == cend())};
+ }
+
+ /**
+ * @brief Checks if the container contains an element with a given key.
+ * @param key Key value of an element to search for.
+ * @return True if there is such an element, false otherwise.
+ */
+ [[nodiscard]] bool contains(const key_type &key) const {
+ return (find(key) != cend());
+ }
+
+ /**
+ * @brief Checks if the container contains an element with a key that
+ * compares _equivalent_ to a given value.
+ * @tparam Other Type of the key value of an element to search for.
+ * @param key Key value of an element to search for.
+ * @return True if there is such an element, false otherwise.
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+ contains(const Other &key) const {
+ return (find(key) != cend());
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the beginning of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+ return {packed.first().begin(), sparse.first()[index]};
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the beginning of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator begin(const size_type index) const {
+ return cbegin(index);
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the beginning of the given bucket.
+ */
+ [[nodiscard]] local_iterator begin(const size_type index) {
+ return {packed.first().begin(), sparse.first()[index]};
+ }
+
+ /**
+ * @brief Returns an iterator to the end of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the end of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+ return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+ }
+
+ /**
+ * @brief Returns an iterator to the end of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the end of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator end(const size_type index) const {
+ return cend(index);
+ }
+
+ /**
+ * @brief Returns an iterator to the end of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the end of the given bucket.
+ */
+ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+ return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+ }
+
+ /**
+ * @brief Returns the number of buckets.
+ * @return The number of buckets.
+ */
+ [[nodiscard]] size_type bucket_count() const {
+ return sparse.first().size();
+ }
+
+ /**
+ * @brief Returns the maximum number of buckets.
+ * @return The maximum number of buckets.
+ */
+ [[nodiscard]] size_type max_bucket_count() const {
+ return sparse.first().max_size();
+ }
+
+ /**
+ * @brief Returns the number of elements in a given bucket.
+ * @param index The index of the bucket to examine.
+ * @return The number of elements in the given bucket.
+ */
+ [[nodiscard]] size_type bucket_size(const size_type index) const {
+ return static_cast<size_type>(std::distance(begin(index), end(index)));
+ }
+
+ /**
+ * @brief Returns the bucket for a given key.
+ * @param key The value of the key to examine.
+ * @return The bucket for the given key.
+ */
+ [[nodiscard]] size_type bucket(const key_type &key) const {
+ return key_to_bucket(key);
+ }
+
+ /**
+ * @brief Returns the average number of elements per bucket.
+ * @return The average number of elements per bucket.
+ */
+ [[nodiscard]] float load_factor() const {
+ return size() / static_cast<float>(bucket_count());
+ }
+
+ /**
+ * @brief Returns the maximum average number of elements per bucket.
+ * @return The maximum average number of elements per bucket.
+ */
+ [[nodiscard]] float max_load_factor() const {
+ return threshold;
+ }
+
+ /**
+ * @brief Sets the desired maximum average number of elements per bucket.
+ * @param value A desired maximum average number of elements per bucket.
+ */
+ void max_load_factor(const float value) {
+ ENTT_ASSERT(value > 0.f, "Invalid load factor");
+ threshold = value;
+ rehash(0u);
+ }
+
+ /**
+ * @brief Reserves at least the specified number of buckets and regenerates
+ * the hash table.
+ * @param cnt New number of buckets.
+ */
+ void rehash(const size_type cnt) {
+ auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
+ const auto cap = static_cast<size_type>(size() / max_load_factor());
+ value = value > cap ? value : cap;
+
+ if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+ sparse.first().resize(sz);
+
+ for(auto &&elem: sparse.first()) {
+ elem = (std::numeric_limits<size_type>::max)();
+ }
+
+ for(size_type pos{}, last = size(); pos < last; ++pos) {
+ const auto index = key_to_bucket(packed.first()[pos].element.first);
+ packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
+ }
+ }
+ }
+
+ /**
+ * @brief Reserves space for at least the specified number of elements and
+ * regenerates the hash table.
+ * @param cnt New number of elements.
+ */
+ void reserve(const size_type cnt) {
+ packed.first().reserve(cnt);
+ rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
+ }
+
+ /**
+ * @brief Returns the function used to hash the keys.
+ * @return The function used to hash the keys.
+ */
+ [[nodiscard]] hasher hash_function() const {
+ return sparse.second();
+ }
+
+ /**
+ * @brief Returns the function used to compare keys for equality.
+ * @return The function used to compare keys for equality.
+ */
+ [[nodiscard]] key_equal key_eq() const {
+ return packed.second();
+ }
+
+private:
+ compressed_pair<sparse_container_type, hasher> sparse;
+ compressed_pair<packed_container_type, key_equal> packed;
+ float threshold{default_threshold};
+};
+
+} // namespace entt
+
+/*! @cond TURN_OFF_DOXYGEN */
+namespace std {
+
+template<typename Key, typename Value, typename Allocator>
+struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
+ : std::true_type {};
+
+} // namespace std
+/*! @endcond */
+
+#endif
diff --git a/deps/include/entt/container/dense_set.hpp b/deps/include/entt/container/dense_set.hpp new file mode 100644 index 0000000..d996dfd --- /dev/null +++ b/deps/include/entt/container/dense_set.hpp @@ -0,0 +1,927 @@ +#ifndef ENTT_CONTAINER_DENSE_SET_HPP
+#define ENTT_CONTAINER_DENSE_SET_HPP
+
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include <vector>
+#include "../config/config.h"
+#include "../core/bit.hpp"
+#include "../core/compressed_pair.hpp"
+#include "../core/type_traits.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/*! @cond TURN_OFF_DOXYGEN */
+namespace internal {
+
+template<typename It>
+class dense_set_iterator final {
+ template<typename>
+ friend class dense_set_iterator;
+
+public:
+ using value_type = typename It::value_type::second_type;
+ using pointer = const value_type *;
+ using reference = const value_type &;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::random_access_iterator_tag;
+
+ constexpr dense_set_iterator() noexcept
+ : it{} {}
+
+ constexpr dense_set_iterator(const It iter) noexcept
+ : it{iter} {}
+
+ template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+ constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
+ : it{other.it} {}
+
+ constexpr dense_set_iterator &operator++() noexcept {
+ return ++it, *this;
+ }
+
+ constexpr dense_set_iterator operator++(int) noexcept {
+ dense_set_iterator orig = *this;
+ return ++(*this), orig;
+ }
+
+ constexpr dense_set_iterator &operator--() noexcept {
+ return --it, *this;
+ }
+
+ constexpr dense_set_iterator operator--(int) noexcept {
+ dense_set_iterator orig = *this;
+ return operator--(), orig;
+ }
+
+ constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
+ it += value;
+ return *this;
+ }
+
+ constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
+ dense_set_iterator copy = *this;
+ return (copy += value);
+ }
+
+ constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
+ return (*this += -value);
+ }
+
+ constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
+ return (*this + -value);
+ }
+
+ [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
+ return it[value].second;
+ }
+
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
+ return std::addressof(operator[](0));
+ }
+
+ [[nodiscard]] constexpr reference operator*() const noexcept {
+ return operator[](0);
+ }
+
+ template<typename Lhs, typename Rhs>
+ friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
+
+ template<typename Lhs, typename Rhs>
+ friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
+
+ template<typename Lhs, typename Rhs>
+ friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
+
+private:
+ It it;
+};
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return lhs.it - rhs.it;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return lhs.it == rhs.it;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return lhs.it < rhs.it;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return rhs < lhs;
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return !(lhs > rhs);
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+template<typename It>
+class dense_set_local_iterator final {
+ template<typename>
+ friend class dense_set_local_iterator;
+
+public:
+ using value_type = typename It::value_type::second_type;
+ using pointer = const value_type *;
+ using reference = const value_type &;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
+ constexpr dense_set_local_iterator() noexcept
+ : it{},
+ offset{} {}
+
+ constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
+ : it{iter},
+ offset{pos} {}
+
+ template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
+ constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
+ : it{other.it},
+ offset{other.offset} {}
+
+ constexpr dense_set_local_iterator &operator++() noexcept {
+ return offset = it[offset].first, *this;
+ }
+
+ constexpr dense_set_local_iterator operator++(int) noexcept {
+ dense_set_local_iterator orig = *this;
+ return ++(*this), orig;
+ }
+
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
+ return std::addressof(it[offset].second);
+ }
+
+ [[nodiscard]] constexpr reference operator*() const noexcept {
+ return *operator->();
+ }
+
+ [[nodiscard]] constexpr std::size_t index() const noexcept {
+ return offset;
+ }
+
+private:
+ It it;
+ std::size_t offset;
+};
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
+ return lhs.index() == rhs.index();
+}
+
+template<typename Lhs, typename Rhs>
+[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+} // namespace internal
+/*! @endcond */
+
+/**
+ * @brief Associative container for unique objects of a given type.
+ *
+ * Internally, elements are organized into buckets. Which bucket an element is
+ * placed into depends entirely on its hash. Elements with the same hash code
+ * appear in the same bucket.
+ *
+ * @tparam Type Value type of the associative container.
+ * @tparam Hash Type of function to use to hash the values.
+ * @tparam KeyEqual Type of function to use to compare the values for equality.
+ * @tparam Allocator Type of allocator used to manage memory and elements.
+ */
+template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
+class dense_set {
+ static constexpr float default_threshold = 0.875f;
+ static constexpr std::size_t minimum_capacity = 8u;
+
+ using node_type = std::pair<std::size_t, Type>;
+ using alloc_traits = std::allocator_traits<Allocator>;
+ static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
+ using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
+ using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
+
+ template<typename Other>
+ [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept {
+ return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
+ }
+
+ template<typename Other>
+ [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
+ for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
+ if(packed.second()(*it, value)) {
+ return begin() + static_cast<typename iterator::difference_type>(it.index());
+ }
+ }
+
+ return end();
+ }
+
+ template<typename Other>
+ [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
+ for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
+ if(packed.second()(*it, value)) {
+ return cbegin() + static_cast<typename iterator::difference_type>(it.index());
+ }
+ }
+
+ return cend();
+ }
+
+ template<typename Other>
+ [[nodiscard]] auto insert_or_do_nothing(Other &&value) {
+ const auto index = value_to_bucket(value);
+
+ if(auto it = constrained_find(value, index); it != end()) {
+ return std::make_pair(it, false);
+ }
+
+ packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
+ sparse.first()[index] = packed.first().size() - 1u;
+ rehash_if_required();
+
+ return std::make_pair(--end(), true);
+ }
+
+ void move_and_pop(const std::size_t pos) {
+ if(const auto last = size() - 1u; pos != last) {
+ size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
+ packed.first()[pos] = std::move(packed.first().back());
+ for(; *curr != last; curr = &packed.first()[*curr].first) {}
+ *curr = pos;
+ }
+
+ packed.first().pop_back();
+ }
+
+ void rehash_if_required() {
+ if(size() > (bucket_count() * max_load_factor())) {
+ rehash(bucket_count() * 2u);
+ }
+ }
+
+public:
+ /*! @brief Allocator type. */
+ using allocator_type = Allocator;
+ /*! @brief Key type of the container. */
+ using key_type = Type;
+ /*! @brief Value type of the container. */
+ using value_type = Type;
+ /*! @brief Unsigned integer type. */
+ using size_type = std::size_t;
+ /*! @brief Type of function to use to hash the elements. */
+ using hasher = Hash;
+ /*! @brief Type of function to use to compare the elements for equality. */
+ using key_equal = KeyEqual;
+ /*! @brief Random access iterator type. */
+ using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
+ /*! @brief Constant random access iterator type. */
+ using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
+ /*! @brief Reverse iterator type. */
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ /*! @brief Constant reverse iterator type. */
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ /*! @brief Forward iterator type. */
+ using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
+ /*! @brief Constant forward iterator type. */
+ using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
+
+ /*! @brief Default constructor. */
+ dense_set()
+ : dense_set{minimum_capacity} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator.
+ * @param allocator The allocator to use.
+ */
+ explicit dense_set(const allocator_type &allocator)
+ : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator and user
+ * supplied minimal number of buckets.
+ * @param cnt Minimal number of buckets.
+ * @param allocator The allocator to use.
+ */
+ dense_set(const size_type cnt, const allocator_type &allocator)
+ : dense_set{cnt, hasher{}, key_equal{}, allocator} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator, hash
+ * function and user supplied minimal number of buckets.
+ * @param cnt Minimal number of buckets.
+ * @param hash Hash function to use.
+ * @param allocator The allocator to use.
+ */
+ dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
+ : dense_set{cnt, hash, key_equal{}, allocator} {}
+
+ /**
+ * @brief Constructs an empty container with a given allocator, hash
+ * function, compare function and user supplied minimal number of buckets.
+ * @param cnt Minimal number of buckets.
+ * @param hash Hash function to use.
+ * @param equal Compare function to use.
+ * @param allocator The allocator to use.
+ */
+ explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
+ : sparse{allocator, hash},
+ packed{allocator, equal} {
+ rehash(cnt);
+ }
+
+ /*! @brief Default copy constructor. */
+ dense_set(const dense_set &) = default;
+
+ /**
+ * @brief Allocator-extended copy constructor.
+ * @param other The instance to copy from.
+ * @param allocator The allocator to use.
+ */
+ dense_set(const dense_set &other, const allocator_type &allocator)
+ : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
+ packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
+ threshold{other.threshold} {}
+
+ /*! @brief Default move constructor. */
+ dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
+
+ /**
+ * @brief Allocator-extended move constructor.
+ * @param other The instance to move from.
+ * @param allocator The allocator to use.
+ */
+ dense_set(dense_set &&other, const allocator_type &allocator)
+ : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
+ packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
+ threshold{other.threshold} {}
+
+ /*! @brief Default destructor. */
+ ~dense_set() noexcept = default;
+
+ /**
+ * @brief Default copy assignment operator.
+ * @return This container.
+ */
+ dense_set &operator=(const dense_set &) = default;
+
+ /**
+ * @brief Default move assignment operator.
+ * @return This container.
+ */
+ dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> && std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
+
+ /**
+ * @brief Returns the associated allocator.
+ * @return The associated allocator.
+ */
+ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
+ return sparse.first().get_allocator();
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning.
+ *
+ * If the array is empty, the returned iterator will be equal to `end()`.
+ *
+ * @return An iterator to the first instance of the internal array.
+ */
+ [[nodiscard]] const_iterator cbegin() const noexcept {
+ return packed.first().begin();
+ }
+
+ /*! @copydoc cbegin */
+ [[nodiscard]] const_iterator begin() const noexcept {
+ return cbegin();
+ }
+
+ /*! @copydoc begin */
+ [[nodiscard]] iterator begin() noexcept {
+ return packed.first().begin();
+ }
+
+ /**
+ * @brief Returns an iterator to the end.
+ * @return An iterator to the element following the last instance of the
+ * internal array.
+ */
+ [[nodiscard]] const_iterator cend() const noexcept {
+ return packed.first().end();
+ }
+
+ /*! @copydoc cend */
+ [[nodiscard]] const_iterator end() const noexcept {
+ return cend();
+ }
+
+ /*! @copydoc end */
+ [[nodiscard]] iterator end() noexcept {
+ return packed.first().end();
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the beginning.
+ *
+ * If the array is empty, the returned iterator will be equal to `rend()`.
+ *
+ * @return An iterator to the first instance of the reversed internal array.
+ */
+ [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
+ return std::make_reverse_iterator(cend());
+ }
+
+ /*! @copydoc crbegin */
+ [[nodiscard]] const_reverse_iterator rbegin() const noexcept {
+ return crbegin();
+ }
+
+ /*! @copydoc rbegin */
+ [[nodiscard]] reverse_iterator rbegin() noexcept {
+ return std::make_reverse_iterator(end());
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the end.
+ * @return An iterator to the element following the last instance of the
+ * reversed internal array.
+ */
+ [[nodiscard]] const_reverse_iterator crend() const noexcept {
+ return std::make_reverse_iterator(cbegin());
+ }
+
+ /*! @copydoc crend */
+ [[nodiscard]] const_reverse_iterator rend() const noexcept {
+ return crend();
+ }
+
+ /*! @copydoc rend */
+ [[nodiscard]] reverse_iterator rend() noexcept {
+ return std::make_reverse_iterator(begin());
+ }
+
+ /**
+ * @brief Checks whether a container is empty.
+ * @return True if the container is empty, false otherwise.
+ */
+ [[nodiscard]] bool empty() const noexcept {
+ return packed.first().empty();
+ }
+
+ /**
+ * @brief Returns the number of elements in a container.
+ * @return Number of elements in a container.
+ */
+ [[nodiscard]] size_type size() const noexcept {
+ return packed.first().size();
+ }
+
+ /**
+ * @brief Returns the maximum possible number of elements.
+ * @return Maximum possible number of elements.
+ */
+ [[nodiscard]] size_type max_size() const noexcept {
+ return packed.first().max_size();
+ }
+
+ /*! @brief Clears the container. */
+ void clear() noexcept {
+ sparse.first().clear();
+ packed.first().clear();
+ rehash(0u);
+ }
+
+ /**
+ * @brief Inserts an element into the container, if it does not exist.
+ * @param value An element to insert into the container.
+ * @return A pair consisting of an iterator to the inserted element (or to
+ * the element that prevented the insertion) and a bool denoting whether the
+ * insertion took place.
+ */
+ std::pair<iterator, bool> insert(const value_type &value) {
+ return insert_or_do_nothing(value);
+ }
+
+ /*! @copydoc insert */
+ std::pair<iterator, bool> insert(value_type &&value) {
+ return insert_or_do_nothing(std::move(value));
+ }
+
+ /**
+ * @brief Inserts elements into the container, if they do not exist.
+ * @tparam It Type of input iterator.
+ * @param first An iterator to the first element of the range of elements.
+ * @param last An iterator past the last element of the range of elements.
+ */
+ template<typename It>
+ void insert(It first, It last) {
+ for(; first != last; ++first) {
+ insert(*first);
+ }
+ }
+
+ /**
+ * @brief Constructs an element in-place, if it does not exist.
+ *
+ * The element is also constructed when the container already has the key,
+ * in which case the newly constructed object is destroyed immediately.
+ *
+ * @tparam Args Types of arguments to forward to the constructor of the
+ * element.
+ * @param args Arguments to forward to the constructor of the element.
+ * @return A pair consisting of an iterator to the inserted element (or to
+ * the element that prevented the insertion) and a bool denoting whether the
+ * insertion took place.
+ */
+ template<typename... Args>
+ std::pair<iterator, bool> emplace(Args &&...args) {
+ if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::decay_t<Args>, value_type>)) {
+ return insert_or_do_nothing(std::forward<Args>(args)...);
+ } else {
+ auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
+ const auto index = value_to_bucket(node.second);
+
+ if(auto it = constrained_find(node.second, index); it != end()) {
+ packed.first().pop_back();
+ return std::make_pair(it, false);
+ }
+
+ std::swap(node.first, sparse.first()[index]);
+ rehash_if_required();
+
+ return std::make_pair(--end(), true);
+ }
+ }
+
+ /**
+ * @brief Removes an element from a given position.
+ * @param pos An iterator to the element to remove.
+ * @return An iterator following the removed element.
+ */
+ iterator erase(const_iterator pos) {
+ const auto diff = pos - cbegin();
+ erase(*pos);
+ return begin() + diff;
+ }
+
+ /**
+ * @brief Removes the given elements from a container.
+ * @param first An iterator to the first element of the range of elements.
+ * @param last An iterator past the last element of the range of elements.
+ * @return An iterator following the last removed element.
+ */
+ iterator erase(const_iterator first, const_iterator last) {
+ const auto dist = first - cbegin();
+
+ for(auto from = last - cbegin(); from != dist; --from) {
+ erase(packed.first()[from - 1u].second);
+ }
+
+ return (begin() + dist);
+ }
+
+ /**
+ * @brief Removes the element associated with a given value.
+ * @param value Value of an element to remove.
+ * @return Number of elements removed (either 0 or 1).
+ */
+ size_type erase(const value_type &value) {
+ for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
+ if(packed.second()(packed.first()[*curr].second, value)) {
+ const auto index = *curr;
+ *curr = packed.first()[*curr].first;
+ move_and_pop(index);
+ return 1u;
+ }
+ }
+
+ return 0u;
+ }
+
+ /**
+ * @brief Exchanges the contents with those of a given container.
+ * @param other Container to exchange the content with.
+ */
+ void swap(dense_set &other) {
+ using std::swap;
+ swap(sparse, other.sparse);
+ swap(packed, other.packed);
+ swap(threshold, other.threshold);
+ }
+
+ /**
+ * @brief Returns the number of elements matching a value (either 1 or 0).
+ * @param key Key value of an element to search for.
+ * @return Number of elements matching the key (either 1 or 0).
+ */
+ [[nodiscard]] size_type count(const value_type &key) const {
+ return find(key) != end();
+ }
+
+ /**
+ * @brief Returns the number of elements matching a key (either 1 or 0).
+ * @tparam Other Type of the key value of an element to search for.
+ * @param key Key value of an element to search for.
+ * @return Number of elements matching the key (either 1 or 0).
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
+ count(const Other &key) const {
+ return find(key) != end();
+ }
+
+ /**
+ * @brief Finds an element with a given value.
+ * @param value Value of an element to search for.
+ * @return An iterator to an element with the given value. If no such
+ * element is found, a past-the-end iterator is returned.
+ */
+ [[nodiscard]] iterator find(const value_type &value) {
+ return constrained_find(value, value_to_bucket(value));
+ }
+
+ /*! @copydoc find */
+ [[nodiscard]] const_iterator find(const value_type &value) const {
+ return constrained_find(value, value_to_bucket(value));
+ }
+
+ /**
+ * @brief Finds an element that compares _equivalent_ to a given value.
+ * @tparam Other Type of an element to search for.
+ * @param value Value of an element to search for.
+ * @return An iterator to an element with the given value. If no such
+ * element is found, a past-the-end iterator is returned.
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
+ find(const Other &value) {
+ return constrained_find(value, value_to_bucket(value));
+ }
+
+ /*! @copydoc find */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
+ find(const Other &value) const {
+ return constrained_find(value, value_to_bucket(value));
+ }
+
+ /**
+ * @brief Returns a range containing all elements with a given value.
+ * @param value Value of an element to search for.
+ * @return A pair of iterators pointing to the first element and past the
+ * last element of the range.
+ */
+ [[nodiscard]] std::pair<iterator, iterator> equal_range(const value_type &value) {
+ const auto it = find(value);
+ return {it, it + !(it == end())};
+ }
+
+ /*! @copydoc equal_range */
+ [[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
+ const auto it = find(value);
+ return {it, it + !(it == cend())};
+ }
+
+ /**
+ * @brief Returns a range containing all elements that compare _equivalent_
+ * to a given value.
+ * @tparam Other Type of an element to search for.
+ * @param value Value of an element to search for.
+ * @return A pair of iterators pointing to the first element and past the
+ * last element of the range.
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
+ equal_range(const Other &value) {
+ const auto it = find(value);
+ return {it, it + !(it == end())};
+ }
+
+ /*! @copydoc equal_range */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
+ equal_range(const Other &value) const {
+ const auto it = find(value);
+ return {it, it + !(it == cend())};
+ }
+
+ /**
+ * @brief Checks if the container contains an element with a given value.
+ * @param value Value of an element to search for.
+ * @return True if there is such an element, false otherwise.
+ */
+ [[nodiscard]] bool contains(const value_type &value) const {
+ return (find(value) != cend());
+ }
+
+ /**
+ * @brief Checks if the container contains an element that compares
+ * _equivalent_ to a given value.
+ * @tparam Other Type of an element to search for.
+ * @param value Value of an element to search for.
+ * @return True if there is such an element, false otherwise.
+ */
+ template<typename Other>
+ [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
+ contains(const Other &value) const {
+ return (find(value) != cend());
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the beginning of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator cbegin(const size_type index) const {
+ return {packed.first().begin(), sparse.first()[index]};
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the beginning of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator begin(const size_type index) const {
+ return cbegin(index);
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the beginning of the given bucket.
+ */
+ [[nodiscard]] local_iterator begin(const size_type index) {
+ return {packed.first().begin(), sparse.first()[index]};
+ }
+
+ /**
+ * @brief Returns an iterator to the end of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the end of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
+ return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+ }
+
+ /**
+ * @brief Returns an iterator to the end of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the end of the given bucket.
+ */
+ [[nodiscard]] const_local_iterator end(const size_type index) const {
+ return cend(index);
+ }
+
+ /**
+ * @brief Returns an iterator to the end of a given bucket.
+ * @param index An index of a bucket to access.
+ * @return An iterator to the end of the given bucket.
+ */
+ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
+ return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
+ }
+
+ /**
+ * @brief Returns the number of buckets.
+ * @return The number of buckets.
+ */
+ [[nodiscard]] size_type bucket_count() const {
+ return sparse.first().size();
+ }
+
+ /**
+ * @brief Returns the maximum number of buckets.
+ * @return The maximum number of buckets.
+ */
+ [[nodiscard]] size_type max_bucket_count() const {
+ return sparse.first().max_size();
+ }
+
+ /**
+ * @brief Returns the number of elements in a given bucket.
+ * @param index The index of the bucket to examine.
+ * @return The number of elements in the given bucket.
+ */
+ [[nodiscard]] size_type bucket_size(const size_type index) const {
+ return static_cast<size_type>(std::distance(begin(index), end(index)));
+ }
+
+ /**
+ * @brief Returns the bucket for a given element.
+ * @param value The value of the element to examine.
+ * @return The bucket for the given element.
+ */
+ [[nodiscard]] size_type bucket(const value_type &value) const {
+ return value_to_bucket(value);
+ }
+
+ /**
+ * @brief Returns the average number of elements per bucket.
+ * @return The average number of elements per bucket.
+ */
+ [[nodiscard]] float load_factor() const {
+ return size() / static_cast<float>(bucket_count());
+ }
+
+ /**
+ * @brief Returns the maximum average number of elements per bucket.
+ * @return The maximum average number of elements per bucket.
+ */
+ [[nodiscard]] float max_load_factor() const {
+ return threshold;
+ }
+
+ /**
+ * @brief Sets the desired maximum average number of elements per bucket.
+ * @param value A desired maximum average number of elements per bucket.
+ */
+ void max_load_factor(const float value) {
+ ENTT_ASSERT(value > 0.f, "Invalid load factor");
+ threshold = value;
+ rehash(0u);
+ }
+
+ /**
+ * @brief Reserves at least the specified number of buckets and regenerates
+ * the hash table.
+ * @param cnt New number of buckets.
+ */
+ void rehash(const size_type cnt) {
+ auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
+ const auto cap = static_cast<size_type>(size() / max_load_factor());
+ value = value > cap ? value : cap;
+
+ if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
+ sparse.first().resize(sz);
+
+ for(auto &&elem: sparse.first()) {
+ elem = (std::numeric_limits<size_type>::max)();
+ }
+
+ for(size_type pos{}, last = size(); pos < last; ++pos) {
+ const auto index = value_to_bucket(packed.first()[pos].second);
+ packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
+ }
+ }
+ }
+
+ /**
+ * @brief Reserves space for at least the specified number of elements and
+ * regenerates the hash table.
+ * @param cnt New number of elements.
+ */
+ void reserve(const size_type cnt) {
+ packed.first().reserve(cnt);
+ rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
+ }
+
+ /**
+ * @brief Returns the function used to hash the elements.
+ * @return The function used to hash the elements.
+ */
+ [[nodiscard]] hasher hash_function() const {
+ return sparse.second();
+ }
+
+ /**
+ * @brief Returns the function used to compare elements for equality.
+ * @return The function used to compare elements for equality.
+ */
+ [[nodiscard]] key_equal key_eq() const {
+ return packed.second();
+ }
+
+private:
+ compressed_pair<sparse_container_type, hasher> sparse;
+ compressed_pair<packed_container_type, key_equal> packed;
+ float threshold{default_threshold};
+};
+
+} // namespace entt
+
+#endif
diff --git a/deps/include/entt/container/fwd.hpp b/deps/include/entt/container/fwd.hpp new file mode 100644 index 0000000..8b6375d --- /dev/null +++ b/deps/include/entt/container/fwd.hpp @@ -0,0 +1,38 @@ +#ifndef ENTT_CONTAINER_FWD_HPP
+#define ENTT_CONTAINER_FWD_HPP
+
+#include <functional>
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace entt {
+
+template<
+ typename Key,
+ typename Type,
+ typename = std::hash<Key>,
+ typename = std::equal_to<>,
+ typename = std::allocator<std::pair<const Key, Type>>>
+class dense_map;
+
+template<
+ typename Type,
+ typename = std::hash<Type>,
+ typename = std::equal_to<>,
+ typename = std::allocator<Type>>
+class dense_set;
+
+template<typename...>
+class basic_table;
+
+/**
+ * @brief Alias declaration for the most common use case.
+ * @tparam Type Element types.
+ */
+template<typename... Type>
+using table = basic_table<std::vector<Type>...>;
+
+} // namespace entt
+
+#endif
diff --git a/deps/include/entt/container/table.hpp b/deps/include/entt/container/table.hpp new file mode 100644 index 0000000..3c8e777 --- /dev/null +++ b/deps/include/entt/container/table.hpp @@ -0,0 +1,460 @@ +#ifndef ENTT_CONTAINER_TABLE_HPP
+#define ENTT_CONTAINER_TABLE_HPP
+
+#include <cstddef>
+#include <iterator>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+#include "../config/config.h"
+#include "../core/iterator.hpp"
+#include "fwd.hpp"
+
+namespace entt {
+
+/*! @cond TURN_OFF_DOXYGEN */
+namespace internal {
+
+template<typename... It>
+class table_iterator {
+ template<typename...>
+ friend class table_iterator;
+
+public:
+ using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...));
+ using pointer = input_iterator_pointer<value_type>;
+ using reference = value_type;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::input_iterator_tag;
+ using iterator_concept = std::random_access_iterator_tag;
+
+ constexpr table_iterator() noexcept
+ : it{} {}
+
+ constexpr table_iterator(It... from) noexcept
+ : it{from...} {}
+
+ template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
+ constexpr table_iterator(const table_iterator<Other...> &other) noexcept
+ : table_iterator{std::get<Other>(other.it)...} {}
+
+ constexpr table_iterator &operator++() noexcept {
+ return (++std::get<It>(it), ...), *this;
+ }
+
+ constexpr table_iterator operator++(int) noexcept {
+ table_iterator orig = *this;
+ return ++(*this), orig;
+ }
+
+ constexpr table_iterator &operator--() noexcept {
+ return (--std::get<It>(it), ...), *this;
+ }
+
+ constexpr table_iterator operator--(int) noexcept {
+ table_iterator orig = *this;
+ return operator--(), orig;
+ }
+
+ constexpr table_iterator &operator+=(const difference_type value) noexcept {
+ return ((std::get<It>(it) += value), ...), *this;
+ }
+
+ constexpr table_iterator operator+(const difference_type value) const noexcept {
+ table_iterator copy = *this;
+ return (copy += value);
+ }
+
+ constexpr table_iterator &operator-=(const difference_type value) noexcept {
+ return (*this += -value);
+ }
+
+ constexpr table_iterator operator-(const difference_type value) const noexcept {
+ return (*this + -value);
+ }
+
+ [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
+ return std::forward_as_tuple(std::get<It>(it)[value]...);
+ }
+
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
+ return {operator[](0)};
+ }
+
+ [[nodiscard]] constexpr reference operator*() const noexcept {
+ return operator[](0);
+ }
+
+ template<typename... Lhs, typename... Rhs>
+ friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
+
+ template<typename... Lhs, typename... Rhs>
+ friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
+
+ template<typename... Lhs, typename... Rhs>
+ friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
+
+private:
+ std::tuple<It...> it;
+};
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return std::get<0>(lhs.it) - std::get<0>(rhs.it);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return std::get<0>(lhs.it) == std::get<0>(rhs.it);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return !(lhs == rhs);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return std::get<0>(lhs.it) < std::get<0>(rhs.it);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return rhs < lhs;
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return !(lhs > rhs);
+}
+
+template<typename... Lhs, typename... Rhs>
+[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
+ return !(lhs < rhs);
+}
+
+} // namespace internal
+/*! @endcond */
+
+/**
+ * @brief Basic table implementation.
+ *
+ * Internal data structures arrange elements to maximize performance. There are
+ * no guarantees that objects are returned in the insertion order when iterate
+ * a table. Do not make assumption on the order in any case.
+ *
+ * @tparam Container Sequence container row types.
+ */
+template<typename... Container>
+class basic_table {
+ using container_type = std::tuple<Container...>;
+
+public:
+ /*! @brief Unsigned integer type. */
+ using size_type = std::size_t;
+ /*! @brief Input iterator type. */
+ using iterator = internal::table_iterator<typename Container::iterator...>;
+ /*! @brief Constant input iterator type. */
+ using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
+ /*! @brief Reverse iterator type. */
+ using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
+ /*! @brief Constant reverse iterator type. */
+ using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>;
+
+ /*! @brief Default constructor. */
+ basic_table()
+ : payload{} {
+ }
+
+ /**
+ * @brief Copy constructs the underlying containers.
+ * @param container The containers to copy from.
+ */
+ explicit basic_table(const Container &...container) noexcept
+ : payload{container...} {
+ ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
+ }
+
+ /**
+ * @brief Move constructs the underlying containers.
+ * @param container The containers to move from.
+ */
+ explicit basic_table(Container &&...container) noexcept
+ : payload{std::move(container)...} {
+ ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
+ }
+
+ /*! @brief Default copy constructor, deleted on purpose. */
+ basic_table(const basic_table &) = delete;
+
+ /**
+ * @brief Move constructor.
+ * @param other The instance to move from.
+ */
+ basic_table(basic_table &&other) noexcept
+ : payload{std::move(other.payload)} {}
+
+ /**
+ * @brief Constructs the underlying containers using a given allocator.
+ * @tparam Allocator Type of allocator.
+ * @param allocator A valid allocator.
+ */
+ template<typename Allocator>
+ explicit basic_table(const Allocator &allocator)
+ : payload{Container{allocator}...} {}
+
+ /**
+ * @brief Copy constructs the underlying containers using a given allocator.
+ * @tparam Allocator Type of allocator.
+ * @param container The containers to copy from.
+ * @param allocator A valid allocator.
+ */
+ template<class Allocator>
+ basic_table(const Container &...container, const Allocator &allocator) noexcept
+ : payload{Container{container, allocator}...} {
+ ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
+ }
+
+ /**
+ * @brief Move constructs the underlying containers using a given allocator.
+ * @tparam Allocator Type of allocator.
+ * @param container The containers to move from.
+ * @param allocator A valid allocator.
+ */
+ template<class Allocator>
+ basic_table(Container &&...container, const Allocator &allocator) noexcept
+ : payload{Container{std::move(container), allocator}...} {
+ ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
+ }
+
+ /**
+ * @brief Allocator-extended move constructor.
+ * @tparam Allocator Type of allocator.
+ * @param other The instance to move from.
+ * @param allocator The allocator to use.
+ */
+ template<class Allocator>
+ basic_table(basic_table &&other, const Allocator &allocator)
+ : payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {}
+
+ /*! @brief Default destructor. */
+ ~basic_table() noexcept = default;
+
+ /**
+ * @brief Default copy assignment operator, deleted on purpose.
+ * @return This container.
+ */
+ basic_table &operator=(const basic_table &) = delete;
+
+ /**
+ * @brief Move assignment operator.
+ * @param other The instance to move from.
+ * @return This container.
+ */
+ basic_table &operator=(basic_table &&other) noexcept {
+ payload = std::move(other.payload);
+ return *this;
+ }
+
+ /**
+ * @brief Exchanges the contents with those of a given table.
+ * @param other Table to exchange the content with.
+ */
+ void swap(basic_table &other) {
+ using std::swap;
+ swap(payload, other.payload);
+ }
+
+ /**
+ * @brief Increases the capacity of a table.
+ *
+ * If the new capacity is greater than the current capacity, new storage is
+ * allocated, otherwise the method does nothing.
+ *
+ * @param cap Desired capacity.
+ */
+ void reserve(const size_type cap) {
+ (std::get<Container>(payload).reserve(cap), ...);
+ }
+
+ /**
+ * @brief Returns the number of rows that a table has currently allocated
+ * space for.
+ * @return Capacity of the table.
+ */
+ [[nodiscard]] size_type capacity() const noexcept {
+ return std::get<0>(payload).capacity();
+ }
+
+ /*! @brief Requests the removal of unused capacity. */
+ void shrink_to_fit() {
+ (std::get<Container>(payload).shrink_to_fit(), ...);
+ }
+
+ /**
+ * @brief Returns the number of rows in a table.
+ * @return Number of rows.
+ */
+ [[nodiscard]] size_type size() const noexcept {
+ return std::get<0>(payload).size();
+ }
+
+ /**
+ * @brief Checks whether a table is empty.
+ * @return True if the table is empty, false otherwise.
+ */
+ [[nodiscard]] bool empty() const noexcept {
+ return std::get<0>(payload).empty();
+ }
+
+ /**
+ * @brief Returns an iterator to the beginning.
+ *
+ * If the table is empty, the returned iterator will be equal to `end()`.
+ *
+ * @return An iterator to the first row of the table.
+ */
+ [[nodiscard]] const_iterator cbegin() const noexcept {
+ return {std::get<Container>(payload).cbegin()...};
+ }
+
+ /*! @copydoc cbegin */
+ [[nodiscard]] const_iterator begin() const noexcept {
+ return cbegin();
+ }
+
+ /*! @copydoc begin */
+ [[nodiscard]] iterator begin() noexcept {
+ return {std::get<Container>(payload).begin()...};
+ }
+
+ /**
+ * @brief Returns an iterator to the end.
+ * @return An iterator to the element following the last row of the table.
+ */
+ [[nodiscard]] const_iterator cend() const noexcept {
+ return {std::get<Container>(payload).cend()...};
+ }
+
+ /*! @copydoc cend */
+ [[nodiscard]] const_iterator end() const noexcept {
+ return cend();
+ }
+
+ /*! @copydoc end */
+ [[nodiscard]] iterator end() noexcept {
+ return {std::get<Container>(payload).end()...};
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the beginning.
+ *
+ * If the table is empty, the returned iterator will be equal to `rend()`.
+ *
+ * @return An iterator to the first row of the reversed table.
+ */
+ [[nodiscard]] const_reverse_iterator crbegin() const noexcept {
+ return {std::get<Container>(payload).crbegin()...};
+ }
+
+ /*! @copydoc crbegin */
+ [[nodiscard]] const_reverse_iterator rbegin() const noexcept {
+ return crbegin();
+ }
+
+ /*! @copydoc rbegin */
+ [[nodiscard]] reverse_iterator rbegin() noexcept {
+ return {std::get<Container>(payload).rbegin()...};
+ }
+
+ /**
+ * @brief Returns a reverse iterator to the end.
+ * @return An iterator to the element following the last row of the reversed
+ * table.
+ */
+ [[nodiscard]] const_reverse_iterator crend() const noexcept {
+ return {std::get<Container>(payload).crend()...};
+ }
+
+ /*! @copydoc crend */
+ [[nodiscard]] const_reverse_iterator rend() const noexcept {
+ return crend();
+ }
+
+ /*! @copydoc rend */
+ [[nodiscard]] reverse_iterator rend() noexcept {
+ return {std::get<Container>(payload).rend()...};
+ }
+
+ /**
+ * @brief Appends a row to the end of a table.
+ * @tparam Args Types of arguments to use to construct the row data.
+ * @param args Parameters to use to construct the row data.
+ * @return A reference to the newly created row data.
+ */
+ template<typename... Args>
+ std::tuple<typename Container::value_type &...> emplace(Args &&...args) {
+ if constexpr(sizeof...(Args) == 0u) {
+ return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...);
+ } else {
+ return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...);
+ }
+ }
+
+ /**
+ * @brief Removes a row from a table.
+ * @param pos An iterator to the row to remove.
+ * @return An iterator following the removed row.
+ */
+ iterator erase(const_iterator pos) {
+ const auto diff = pos - begin();
+ return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...};
+ }
+
+ /**
+ * @brief Removes a row from a table.
+ * @param pos Index of the row to remove.
+ */
+ void erase(const size_type pos) {
+ ENTT_ASSERT(pos < size(), "Index out of bounds");
+ erase(begin() + static_cast<typename const_iterator::difference_type>(pos));
+ }
+
+ /**
+ * @brief Returns the row data at specified location.
+ * @param pos The row for which to return the data.
+ * @return The row data at specified location.
+ */
+ [[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const {
+ ENTT_ASSERT(pos < size(), "Index out of bounds");
+ return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
+ }
+
+ /*! @copydoc operator[] */
+ [[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) {
+ ENTT_ASSERT(pos < size(), "Index out of bounds");
+ return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
+ }
+
+ /*! @brief Clears a table. */
+ void clear() {
+ (std::get<Container>(payload).clear(), ...);
+ }
+
+private:
+ container_type payload;
+};
+
+} // namespace entt
+
+/*! @cond TURN_OFF_DOXYGEN */
+namespace std {
+
+template<typename... Container, typename Allocator>
+struct uses_allocator<entt::basic_table<Container...>, Allocator>
+ : std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
+
+} // namespace std
+/*! @endcond */
+
+#endif
|
