|
|
@@ -1,7 +1,6 @@
|
|
|
-#ifndef ENTT_CONTAINER_DENSE_HASH_SET_HPP
|
|
|
-#define ENTT_CONTAINER_DENSE_HASH_SET_HPP
|
|
|
+#ifndef ENTT_CONTAINER_DENSE_SET_HPP
|
|
|
+#define ENTT_CONTAINER_DENSE_SET_HPP
|
|
|
|
|
|
-#include <algorithm>
|
|
|
#include <cmath>
|
|
|
#include <cstddef>
|
|
|
#include <functional>
|
|
|
@@ -27,177 +26,167 @@ namespace entt {
|
|
|
|
|
|
namespace internal {
|
|
|
|
|
|
-template<typename Type>
|
|
|
-struct dense_hash_set_node final {
|
|
|
- template<typename... Args>
|
|
|
- dense_hash_set_node(const std::size_t pos, Args &&...args)
|
|
|
- : next{pos},
|
|
|
- element{std::forward<Args>(args)...} {}
|
|
|
-
|
|
|
- std::size_t next;
|
|
|
- Type element;
|
|
|
-};
|
|
|
-
|
|
|
template<typename It>
|
|
|
-class dense_hash_set_iterator {
|
|
|
- friend dense_hash_set_iterator<const std::remove_pointer_t<It> *>;
|
|
|
-
|
|
|
- using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
|
|
|
+class dense_set_iterator final {
|
|
|
+ template<typename>
|
|
|
+ friend class dense_set_iterator;
|
|
|
|
|
|
public:
|
|
|
- using value_type = typename iterator_traits::value_type;
|
|
|
- using pointer = typename iterator_traits::pointer;
|
|
|
- using reference = typename iterator_traits::reference;
|
|
|
- using difference_type = typename iterator_traits::difference_type;
|
|
|
+ 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;
|
|
|
|
|
|
- dense_hash_set_iterator() ENTT_NOEXCEPT = default;
|
|
|
+ constexpr dense_set_iterator() noexcept
|
|
|
+ : it{} {}
|
|
|
|
|
|
- dense_hash_set_iterator(const It iter) ENTT_NOEXCEPT
|
|
|
+ constexpr dense_set_iterator(const It iter) noexcept
|
|
|
: it{iter} {}
|
|
|
|
|
|
- template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
|
|
|
- dense_hash_set_iterator(const dense_hash_set_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
|
|
|
+ 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} {}
|
|
|
|
|
|
- dense_hash_set_iterator &operator++() ENTT_NOEXCEPT {
|
|
|
+ constexpr dense_set_iterator &operator++() noexcept {
|
|
|
return ++it, *this;
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator operator++(int) ENTT_NOEXCEPT {
|
|
|
- dense_hash_set_iterator orig = *this;
|
|
|
+ constexpr dense_set_iterator operator++(int) noexcept {
|
|
|
+ dense_set_iterator orig = *this;
|
|
|
return ++(*this), orig;
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator &operator--() ENTT_NOEXCEPT {
|
|
|
+ constexpr dense_set_iterator &operator--() noexcept {
|
|
|
return --it, *this;
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator operator--(int) ENTT_NOEXCEPT {
|
|
|
- dense_hash_set_iterator orig = *this;
|
|
|
+ constexpr dense_set_iterator operator--(int) noexcept {
|
|
|
+ dense_set_iterator orig = *this;
|
|
|
return operator--(), orig;
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
|
|
+ constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
|
|
|
it += value;
|
|
|
return *this;
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
|
|
- dense_hash_set_iterator copy = *this;
|
|
|
+ constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
|
|
|
+ dense_set_iterator copy = *this;
|
|
|
return (copy += value);
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
|
|
+ constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
|
|
|
return (*this += -value);
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
|
|
+ constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
|
|
|
return (*this + -value);
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] reference operator[](const difference_type value) const {
|
|
|
- return it[value].element;
|
|
|
+ [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
|
|
+ return it[value].second;
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] pointer operator->() const {
|
|
|
- return std::addressof(it->element);
|
|
|
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
|
|
|
+ return std::addressof(it->second);
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] reference operator*() const {
|
|
|
+ [[nodiscard]] constexpr reference operator*() const noexcept {
|
|
|
return *operator->();
|
|
|
}
|
|
|
|
|
|
- template<typename ILhs, typename IRhs>
|
|
|
- friend auto operator-(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
|
|
|
+ template<typename Lhs, typename Rhs>
|
|
|
+ friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
|
|
|
|
|
- template<typename ILhs, typename IRhs>
|
|
|
- friend bool operator==(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_NOEXCEPT;
|
|
|
+ template<typename Lhs, typename Rhs>
|
|
|
+ friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
|
|
|
|
|
- template<typename ILhs, typename IRhs>
|
|
|
- friend bool operator<(const dense_hash_set_iterator<ILhs> &, const dense_hash_set_iterator<IRhs> &) ENTT_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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] auto operator-(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator==(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator!=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator<(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator>(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator<=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator>=(const dense_hash_set_iterator<ILhs> &lhs, const dense_hash_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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_hash_set_local_iterator {
|
|
|
- friend dense_hash_set_local_iterator<const std::remove_pointer_t<It> *>;
|
|
|
-
|
|
|
- using iterator_traits = std::iterator_traits<decltype(std::addressof(std::as_const(std::declval<It>()->element)))>;
|
|
|
+class dense_set_local_iterator final {
|
|
|
+ template<typename>
|
|
|
+ friend class dense_set_local_iterator;
|
|
|
|
|
|
public:
|
|
|
- using value_type = typename iterator_traits::value_type;
|
|
|
- using pointer = typename iterator_traits::pointer;
|
|
|
- using reference = typename iterator_traits::reference;
|
|
|
- using difference_type = typename iterator_traits::difference_type;
|
|
|
+ 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;
|
|
|
|
|
|
- dense_hash_set_local_iterator() ENTT_NOEXCEPT = default;
|
|
|
+ constexpr dense_set_local_iterator() noexcept
|
|
|
+ : it{},
|
|
|
+ offset{} {}
|
|
|
|
|
|
- dense_hash_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT
|
|
|
+ constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
|
|
|
: it{iter},
|
|
|
offset{pos} {}
|
|
|
|
|
|
- template<bool Const = std::is_const_v<std::remove_pointer_t<It>>, typename = std::enable_if_t<Const>>
|
|
|
- dense_hash_set_local_iterator(const dense_hash_set_local_iterator<std::remove_const_t<std::remove_pointer_t<It>> *> &other)
|
|
|
+ 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} {}
|
|
|
|
|
|
- dense_hash_set_local_iterator &operator++() ENTT_NOEXCEPT {
|
|
|
- return offset = it[offset].next, *this;
|
|
|
+ constexpr dense_set_local_iterator &operator++() noexcept {
|
|
|
+ return offset = it[offset].first, *this;
|
|
|
}
|
|
|
|
|
|
- dense_hash_set_local_iterator operator++(int) ENTT_NOEXCEPT {
|
|
|
- dense_hash_set_local_iterator orig = *this;
|
|
|
+ constexpr dense_set_local_iterator operator++(int) noexcept {
|
|
|
+ dense_set_local_iterator orig = *this;
|
|
|
return ++(*this), orig;
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] pointer operator->() const {
|
|
|
- return std::addressof(it[offset].element);
|
|
|
+ [[nodiscard]] constexpr pointer operator->() const noexcept {
|
|
|
+ return std::addressof(it[offset].second);
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] reference operator*() const {
|
|
|
+ [[nodiscard]] constexpr reference operator*() const noexcept {
|
|
|
return *operator->();
|
|
|
}
|
|
|
|
|
|
- [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
|
|
|
+ [[nodiscard]] constexpr std::size_t index() const noexcept {
|
|
|
return offset;
|
|
|
}
|
|
|
|
|
|
@@ -206,13 +195,13 @@ private:
|
|
|
std::size_t offset;
|
|
|
};
|
|
|
|
|
|
-template<typename ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator==(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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 ILhs, typename IRhs>
|
|
|
-[[nodiscard]] bool operator!=(const dense_hash_set_local_iterator<ILhs> &lhs, const dense_hash_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
|
|
+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);
|
|
|
}
|
|
|
|
|
|
@@ -236,27 +225,26 @@ template<typename ILhs, typename IRhs>
|
|
|
* @tparam Allocator Type of allocator used to manage memory and elements.
|
|
|
*/
|
|
|
template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
|
|
-class dense_hash_set final {
|
|
|
+class dense_set {
|
|
|
static constexpr float default_threshold = 0.875f;
|
|
|
static constexpr std::size_t minimum_capacity = 8u;
|
|
|
|
|
|
- using allocator_traits = std::allocator_traits<Allocator>;
|
|
|
- using alloc = typename allocator_traits::template rebind_alloc<Type>;
|
|
|
- using alloc_traits = typename std::allocator_traits<alloc>;
|
|
|
-
|
|
|
- using node_type = internal::dense_hash_set_node<Type>;
|
|
|
+ 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>>;
|
|
|
|
|
|
- [[nodiscard]] std::size_t hash_to_bucket(const std::size_t hash) const ENTT_NOEXCEPT {
|
|
|
- return fast_mod(hash, bucket_count());
|
|
|
+ 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() + it.index();
|
|
|
+ return begin() + static_cast<typename iterator::difference_type>(it.index());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -267,61 +255,45 @@ class dense_hash_set final {
|
|
|
[[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() + it.index();
|
|
|
+ return cbegin() + static_cast<typename iterator::difference_type>(it.index());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return cend();
|
|
|
}
|
|
|
|
|
|
- template<typename Arg>
|
|
|
- [[nodiscard]] auto get_or_emplace(Arg &&arg) {
|
|
|
- const auto hash = sparse.second()(arg);
|
|
|
- auto index = hash_to_bucket(hash);
|
|
|
+ template<typename Other>
|
|
|
+ [[nodiscard]] auto insert_or_do_nothing(Other &&value) {
|
|
|
+ const auto index = value_to_bucket(value);
|
|
|
|
|
|
- if(auto it = constrained_find(arg, index); it != end()) {
|
|
|
+ if(auto it = constrained_find(value, index); it != end()) {
|
|
|
return std::make_pair(it, false);
|
|
|
}
|
|
|
|
|
|
- if(const auto count = size() + 1u; count > (bucket_count() * max_load_factor())) {
|
|
|
- rehash(bucket_count() * 2u);
|
|
|
- index = hash_to_bucket(hash);
|
|
|
- }
|
|
|
-
|
|
|
- packed.first().emplace_back(sparse.first()[index], std::forward<Arg>(arg));
|
|
|
- // update goes after emplace to enforce exception guarantees
|
|
|
- sparse.first()[index] = size() - 1u;
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
- template<typename Other>
|
|
|
- bool do_erase(const Other &value) {
|
|
|
- for(size_type *curr = sparse.first().data() + bucket(value); *curr != std::numeric_limits<size_type>::max(); curr = &packed.first()[*curr].next) {
|
|
|
- if(packed.second()(packed.first()[*curr].element, value)) {
|
|
|
- const auto index = *curr;
|
|
|
- *curr = packed.first()[*curr].next;
|
|
|
- move_and_pop(index);
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
void move_and_pop(const std::size_t pos) {
|
|
|
if(const auto last = size() - 1u; pos != last) {
|
|
|
- size_type *curr = sparse.first().data() + bucket(packed.first().back().element);
|
|
|
- for(; *curr != last; curr = &packed.first()[*curr].next) {}
|
|
|
- *curr = pos;
|
|
|
-
|
|
|
- // basic exception guarantees when value type has a throwing move constructor
|
|
|
+ size_type *curr = sparse.first().data() + 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 Key type of the container. */
|
|
|
using key_type = Type;
|
|
|
@@ -336,179 +308,194 @@ public:
|
|
|
/*! @brief Allocator type. */
|
|
|
using allocator_type = Allocator;
|
|
|
/*! @brief Random access iterator type. */
|
|
|
- using iterator = internal::dense_hash_set_iterator<typename packed_container_type::pointer>;
|
|
|
+ using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
|
|
|
/*! @brief Constant random access iterator type. */
|
|
|
- using const_iterator = internal::dense_hash_set_iterator<typename packed_container_type::const_pointer>;
|
|
|
+ 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_hash_set_local_iterator<typename packed_container_type::pointer>;
|
|
|
+ using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
|
|
|
/*! @brief Constant forward iterator type. */
|
|
|
- using const_local_iterator = internal::dense_hash_set_local_iterator<typename packed_container_type::const_pointer>;
|
|
|
+ using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
|
|
|
|
|
|
/*! @brief Default constructor. */
|
|
|
- dense_hash_set()
|
|
|
- : dense_hash_set(minimum_capacity) {}
|
|
|
+ dense_set()
|
|
|
+ : dense_set{minimum_capacity} {}
|
|
|
|
|
|
/**
|
|
|
* @brief Constructs an empty container with a given allocator.
|
|
|
* @param allocator The allocator to use.
|
|
|
*/
|
|
|
- explicit dense_hash_set(const allocator_type &allocator)
|
|
|
- : dense_hash_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
|
|
|
+ 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 bucket_count Minimal number of buckets.
|
|
|
+ * @param cnt Minimal number of buckets.
|
|
|
* @param allocator The allocator to use.
|
|
|
*/
|
|
|
- dense_hash_set(const size_type bucket_count, const allocator_type &allocator)
|
|
|
- : dense_hash_set{bucket_count, hasher{}, key_equal{}, allocator} {}
|
|
|
+ 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 bucket_count Minimal number of buckets.
|
|
|
+ * @param cnt Minimal number of buckets.
|
|
|
* @param hash Hash function to use.
|
|
|
* @param allocator The allocator to use.
|
|
|
*/
|
|
|
- dense_hash_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
|
|
|
- : dense_hash_set{bucket_count, hash, key_equal{}, allocator} {}
|
|
|
+ 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 bucket_count 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_hash_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type())
|
|
|
+ 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},
|
|
|
threshold{default_threshold} {
|
|
|
- rehash(bucket_count);
|
|
|
+ rehash(cnt);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * @brief Copy constructor.
|
|
|
- * @param other The instance to copy from.
|
|
|
- */
|
|
|
- dense_hash_set(const dense_hash_set &other)
|
|
|
- : dense_hash_set{other, alloc_traits::select_on_container_copy_construction(other.get_allocator())} {}
|
|
|
+ /*! @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_hash_set(const dense_hash_set &other, const allocator_type &allocator)
|
|
|
- : sparse{sparse_container_type{other.sparse.first(), allocator}, other.sparse.second()},
|
|
|
- // cannot copy the container directly due to a nasty issue of apple clang :(
|
|
|
- packed{packed_container_type{other.packed.first().begin(), other.packed.first().end(), allocator}, other.packed.second()},
|
|
|
- threshold{other.threshold} {
|
|
|
- }
|
|
|
+ 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.
|
|
|
- * @param other The instance to move from.
|
|
|
- */
|
|
|
- dense_hash_set(dense_hash_set &&other) ENTT_NOEXCEPT = default;
|
|
|
+ /*! @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_hash_set(dense_hash_set &&other, const allocator_type &allocator) ENTT_NOEXCEPT
|
|
|
- : sparse{sparse_container_type{std::move(other.sparse.first()), allocator}, std::move(other.sparse.second())},
|
|
|
- // cannot move the container directly due to a nasty issue of apple clang :(
|
|
|
- packed{packed_container_type{std::make_move_iterator(other.packed.first().begin()), std::make_move_iterator(other.packed.first().end()), allocator}, std::move(other.packed.second())},
|
|
|
+ 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_hash_set() = default;
|
|
|
-
|
|
|
/**
|
|
|
- * @brief Copy assignment operator.
|
|
|
- * @param other The instance to copy from.
|
|
|
+ * @brief Default copy assignment operator.
|
|
|
* @return This container.
|
|
|
*/
|
|
|
- dense_hash_set &operator=(const dense_hash_set &other) {
|
|
|
- threshold = other.threshold;
|
|
|
- sparse.first().clear();
|
|
|
- packed.first().clear();
|
|
|
- rehash(other.bucket_count());
|
|
|
- packed.first().reserve(other.packed.first().size());
|
|
|
- insert(other.cbegin(), other.cend());
|
|
|
- return *this;
|
|
|
- }
|
|
|
+ dense_set &operator=(const dense_set &) = default;
|
|
|
|
|
|
/**
|
|
|
* @brief Default move assignment operator.
|
|
|
- * @param other The instance to move from.
|
|
|
* @return This container.
|
|
|
*/
|
|
|
- dense_hash_set &operator=(dense_hash_set &&other) ENTT_NOEXCEPT = default;
|
|
|
+ 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 ENTT_NOEXCEPT {
|
|
|
+ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
|
|
return sparse.first().get_allocator();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Returns an iterator to the beginning.
|
|
|
*
|
|
|
- * The returned iterator points to the first instance of the internal array.
|
|
|
* 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 ENTT_NOEXCEPT {
|
|
|
- return packed.first().data();
|
|
|
+ [[nodiscard]] const_iterator cbegin() const noexcept {
|
|
|
+ return packed.first().begin();
|
|
|
}
|
|
|
|
|
|
/*! @copydoc cbegin */
|
|
|
- [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
|
|
+ [[nodiscard]] const_iterator begin() const noexcept {
|
|
|
return cbegin();
|
|
|
}
|
|
|
|
|
|
/*! @copydoc begin */
|
|
|
- [[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
|
|
- return packed.first().data();
|
|
|
+ [[nodiscard]] iterator begin() noexcept {
|
|
|
+ return packed.first().begin();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @brief Returns an iterator to the end.
|
|
|
- *
|
|
|
- * The returned iterator points to the element following the last instance
|
|
|
- * of the internal array. Attempting to dereference the returned iterator
|
|
|
- * results in undefined behavior.
|
|
|
- *
|
|
|
* @return An iterator to the element following the last instance of the
|
|
|
* internal array.
|
|
|
*/
|
|
|
- [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
|
|
- return packed.first().data() + size();
|
|
|
+ [[nodiscard]] const_iterator cend() const noexcept {
|
|
|
+ return packed.first().end();
|
|
|
}
|
|
|
|
|
|
/*! @copydoc cend */
|
|
|
- [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
|
|
+ [[nodiscard]] const_iterator end() const noexcept {
|
|
|
return cend();
|
|
|
}
|
|
|
|
|
|
/*! @copydoc end */
|
|
|
- [[nodiscard]] iterator end() ENTT_NOEXCEPT {
|
|
|
- return packed.first().data() + size();
|
|
|
+ [[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 ENTT_NOEXCEPT {
|
|
|
+ [[nodiscard]] bool empty() const noexcept {
|
|
|
return packed.first().empty();
|
|
|
}
|
|
|
|
|
|
@@ -516,12 +503,20 @@ public:
|
|
|
* @brief Returns the number of elements in a container.
|
|
|
* @return Number of elements in a container.
|
|
|
*/
|
|
|
- [[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
|
|
+ [[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() ENTT_NOEXCEPT {
|
|
|
+ void clear() noexcept {
|
|
|
sparse.first().clear();
|
|
|
packed.first().clear();
|
|
|
rehash(0u);
|
|
|
@@ -535,12 +530,12 @@ public:
|
|
|
* insertion took place.
|
|
|
*/
|
|
|
std::pair<iterator, bool> insert(const value_type &value) {
|
|
|
- return emplace(value);
|
|
|
+ return insert_or_do_nothing(value);
|
|
|
}
|
|
|
|
|
|
/*! @copydoc insert */
|
|
|
std::pair<iterator, bool> insert(value_type &&value) {
|
|
|
- return emplace(std::move(value));
|
|
|
+ return insert_or_do_nothing(std::move(value));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -552,12 +547,16 @@ public:
|
|
|
template<typename It>
|
|
|
void insert(It first, It last) {
|
|
|
for(; first != last; ++first) {
|
|
|
- emplace(*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.
|
|
|
@@ -567,10 +566,21 @@ public:
|
|
|
*/
|
|
|
template<typename... Args>
|
|
|
std::pair<iterator, bool> emplace(Args &&...args) {
|
|
|
- if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_const_t<std::remove_reference_t<Args>>, value_type>)) {
|
|
|
- return get_or_emplace(std::forward<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 {
|
|
|
- return get_or_emplace(value_type{std::forward<Args>(args)...});
|
|
|
+ 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);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -580,9 +590,9 @@ public:
|
|
|
* @return An iterator following the removed element.
|
|
|
*/
|
|
|
iterator erase(const_iterator pos) {
|
|
|
- const auto dist = std::distance(cbegin(), pos);
|
|
|
+ const auto diff = pos - cbegin();
|
|
|
erase(*pos);
|
|
|
- return begin() + dist;
|
|
|
+ return begin() + diff;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -592,13 +602,13 @@ public:
|
|
|
* @return An iterator following the last removed element.
|
|
|
*/
|
|
|
iterator erase(const_iterator first, const_iterator last) {
|
|
|
- const auto dist = std::distance(cbegin(), first);
|
|
|
+ const auto dist = first - cbegin();
|
|
|
|
|
|
- for(auto rfirst = std::make_reverse_iterator(last), rlast = std::make_reverse_iterator(first); rfirst != rlast; ++rfirst) {
|
|
|
- erase(*rfirst);
|
|
|
+ for(auto from = last - cbegin(); from != dist; --from) {
|
|
|
+ erase(packed.first()[from - 1u].second);
|
|
|
}
|
|
|
|
|
|
- return dist > static_cast<decltype(dist)>(size()) ? end() : (begin() + dist);
|
|
|
+ return (begin() + dist);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -607,20 +617,50 @@ public:
|
|
|
* @return Number of elements removed (either 0 or 1).
|
|
|
*/
|
|
|
size_type erase(const value_type &value) {
|
|
|
- return do_erase(value);
|
|
|
+ for(size_type *curr = sparse.first().data() + 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_hash_set &other) {
|
|
|
+ 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.
|
|
|
@@ -628,12 +668,12 @@ public:
|
|
|
* element is found, a past-the-end iterator is returned.
|
|
|
*/
|
|
|
[[nodiscard]] iterator find(const value_type &value) {
|
|
|
- return constrained_find(value, bucket(value));
|
|
|
+ return constrained_find(value, value_to_bucket(value));
|
|
|
}
|
|
|
|
|
|
/*! @copydoc find */
|
|
|
[[nodiscard]] const_iterator find(const value_type &value) const {
|
|
|
- return constrained_find(value, bucket(value));
|
|
|
+ return constrained_find(value, value_to_bucket(value));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -646,14 +686,54 @@ public:
|
|
|
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, bucket(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, bucket(value));
|
|
|
+ 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())};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -684,7 +764,7 @@ public:
|
|
|
* @return An iterator to the beginning of the given bucket.
|
|
|
*/
|
|
|
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
|
|
|
- return {packed.first().data(), sparse.first()[index]};
|
|
|
+ return {packed.first().begin(), sparse.first()[index]};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -702,7 +782,7 @@ public:
|
|
|
* @return An iterator to the beginning of the given bucket.
|
|
|
*/
|
|
|
[[nodiscard]] local_iterator begin(const size_type index) {
|
|
|
- return {packed.first().data(), sparse.first()[index]};
|
|
|
+ return {packed.first().begin(), sparse.first()[index]};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -711,7 +791,7 @@ public:
|
|
|
* @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().data(), std::numeric_limits<size_type>::max()};
|
|
|
+ return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -719,7 +799,7 @@ public:
|
|
|
* @param index An index of a bucket to access.
|
|
|
* @return An iterator to the end of the given bucket.
|
|
|
*/
|
|
|
- [[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
|
|
|
+ [[nodiscard]] const_local_iterator end(const size_type index) const {
|
|
|
return cend(index);
|
|
|
}
|
|
|
|
|
|
@@ -729,7 +809,7 @@ public:
|
|
|
* @return An iterator to the end of the given bucket.
|
|
|
*/
|
|
|
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
|
|
- return {packed.first().data(), std::numeric_limits<size_type>::max()};
|
|
|
+ return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -763,7 +843,7 @@ public:
|
|
|
* @return The bucket for the given element.
|
|
|
*/
|
|
|
[[nodiscard]] size_type bucket(const value_type &value) const {
|
|
|
- return hash_to_bucket(sparse.second()(value));
|
|
|
+ return value_to_bucket(value);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -795,19 +875,23 @@ public:
|
|
|
/**
|
|
|
* @brief Reserves at least the specified number of buckets and regenerates
|
|
|
* the hash table.
|
|
|
- * @param count New number of buckets.
|
|
|
+ * @param cnt New number of buckets.
|
|
|
*/
|
|
|
- void rehash(const size_type count) {
|
|
|
- auto value = (std::max)(count, minimum_capacity);
|
|
|
- value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
|
|
|
+ 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);
|
|
|
- std::fill(sparse.first().begin(), sparse.first().end(), std::numeric_limits<size_type>::max());
|
|
|
+
|
|
|
+ for(auto &&elem: sparse.first()) {
|
|
|
+ elem = (std::numeric_limits<size_type>::max)();
|
|
|
+ }
|
|
|
|
|
|
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
|
|
- const auto index = bucket(packed.first()[pos].element);
|
|
|
- packed.first()[pos].next = std::exchange(sparse.first()[index], pos);
|
|
|
+ const auto index = value_to_bucket(packed.first()[pos].second);
|
|
|
+ packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -815,11 +899,11 @@ public:
|
|
|
/**
|
|
|
* @brief Reserves space for at least the specified number of elements and
|
|
|
* regenerates the hash table.
|
|
|
- * @param count New number of elements.
|
|
|
+ * @param cnt New number of elements.
|
|
|
*/
|
|
|
- void reserve(const size_type count) {
|
|
|
- packed.first().reserve(count);
|
|
|
- rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
|
|
|
+ void reserve(const size_type cnt) {
|
|
|
+ packed.first().reserve(cnt);
|
|
|
+ rehash(static_cast<size_type>(std::ceil(cnt / max_load_factor())));
|
|
|
}
|
|
|
|
|
|
/**
|