diff --git a/bytell_hash_map.hpp b/bytell_hash_map.hpp index 34d8593..9c9a246 100644 --- a/bytell_hash_map.hpp +++ b/bytell_hash_map.hpp @@ -15,12 +15,6 @@ #include #include -#ifdef _MSC_VER -#define SKA_NOINLINE(...) __declspec(noinline) __VA_ARGS__ -#else -#define SKA_NOINLINE(...) __VA_ARGS__ __attribute__((noinline)) -#endif - namespace ska { @@ -413,10 +407,10 @@ class sherwood_v8_table : private ByteAlloc, private Hasher, private Equal size_t index = hash_object(key); size_t num_slots_minus_one = this->num_slots_minus_one; BlockPointer entries = this->entries; + index = hash_policy.index_for_hash(index, num_slots_minus_one); bool first = true; for (;;) { - index = hash_policy.index_for_hash(index, num_slots_minus_one); size_t block_index = index / BlockSize; int index_in_block = index % BlockSize; BlockPointer block = entries + block_index; @@ -433,6 +427,7 @@ class sherwood_v8_table : private ByteAlloc, private Hasher, private Equal if (to_next_index == 0) return end(); index += Constants::jump_distances[to_next_index]; + index = hash_policy.keep_in_range(index, num_slots_minus_one); } } inline const_iterator find(const FindKey & key) const @@ -467,10 +462,10 @@ class sherwood_v8_table : private ByteAlloc, private Hasher, private Equal size_t index = hash_object(key); size_t num_slots_minus_one = this->num_slots_minus_one; BlockPointer entries = this->entries; + index = hash_policy.index_for_hash(index, num_slots_minus_one); bool first = true; for (;;) { - index = hash_policy.index_for_hash(index, num_slots_minus_one); size_t block_index = index / BlockSize; int index_in_block = index % BlockSize; BlockPointer block = entries + block_index; @@ -487,6 +482,7 @@ class sherwood_v8_table : private ByteAlloc, private Hasher, private Equal if (to_next_index == 0) return emplace_new_key({ index, block }, std::forward(key), std::forward(args)...); index += Constants::jump_distances[to_next_index]; + index = hash_policy.keep_in_range(index, num_slots_minus_one); } } diff --git a/flat_hash_map.hpp b/flat_hash_map.hpp index c83d9f3..33fc221 100644 --- a/flat_hash_map.hpp +++ b/flat_hash_map.hpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -24,6 +23,7 @@ namespace ska { struct prime_number_hash_policy; struct power_of_two_hash_policy; +struct fibonacci_hash_policy; namespace detailv3 { @@ -268,7 +268,7 @@ template using void_t = void; template struct HashPolicySelector { - typedef prime_number_hash_policy type; + typedef fibonacci_hash_policy type; }; template struct HashPolicySelector> @@ -634,11 +634,10 @@ class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal return; int8_t new_max_lookups = compute_max_lookups(num_buckets); EntryPointer new_buckets(AllocatorTraits::allocate(*this, num_buckets + new_max_lookups)); - for (EntryPointer it = new_buckets, real_end = it + static_cast(num_buckets + new_max_lookups - 1); it != real_end; ++it) - { + EntryPointer special_end_item = new_buckets + static_cast(num_buckets + new_max_lookups - 1); + for (EntryPointer it = new_buckets; it != special_end_item; ++it) it->distance_from_desired = -1; - } - new_buckets[num_buckets + new_max_lookups - 1].distance_from_desired = Entry::special_end_value; + special_end_item->distance_from_desired = Entry::special_end_value; std::swap(entries, new_buckets); std::swap(num_slots_minus_one, num_buckets); --num_slots_minus_one; @@ -683,6 +682,8 @@ class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal iterator erase(const_iterator begin_it, const_iterator end_it) { + if (begin_it == end_it) + return { begin_it.current }; for (EntryPointer it = begin_it.current, end = end_it.current; it != end; ++it) { if (it->has_value()) @@ -753,7 +754,7 @@ class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal } size_t bucket_count() const { - return num_slots_minus_one + 1; + return num_slots_minus_one ? num_slots_minus_one + 1 : 0; } size_type max_bucket_count() const { @@ -823,7 +824,7 @@ class sherwood_v3_table : private EntryAlloc, private Hasher, private Equal SKA_NOINLINE(std::pair) emplace_new_key(int8_t distance_from_desired, EntryPointer current_entry, Key && key, Args &&... args) { using std::swap; - if (num_slots_minus_one == 0 || distance_from_desired == max_lookups || static_cast(num_elements + 1) / static_cast(bucket_count()) > _max_load_factor) + if (num_slots_minus_one == 0 || distance_from_desired == max_lookups || num_elements + 1 > (num_slots_minus_one + 1) * static_cast(_max_load_factor)) { grow(); return emplace(std::forward(key), std::forward(args)...); @@ -1264,6 +1265,35 @@ struct power_of_two_hash_policy }; +struct fibonacci_hash_policy +{ + size_t index_for_hash(size_t hash, size_t /*num_slots_minus_one*/) const + { + return (11400714819323198485ull * hash) >> shift; + } + size_t keep_in_range(size_t index, size_t num_slots_minus_one) const + { + return index & num_slots_minus_one; + } + + int8_t next_size_over(size_t & size) const + { + size = detailv3::next_power_of_two(size); + return 64 - detailv3::log2(size); + } + void commit(int8_t shift) + { + this->shift = shift; + } + void reset() + { + shift = 63; + } + +private: + int8_t shift = 63; +}; + template, typename E = std::equal_to, typename A = std::allocator > > class flat_hash_map : public detailv3::sherwood_v3_table @@ -1299,11 +1329,11 @@ class flat_hash_map { } - V & operator[](const K & key) + inline V & operator[](const K & key) { return emplace(key, convertible_to_value()).first->second; } - V & operator[](K && key) + inline V & operator[](K && key) { return emplace(std::move(key), convertible_to_value()).first->second; } @@ -1327,6 +1357,32 @@ class flat_hash_map { return emplace(key_type(), convertible_to_value()); } + template + std::pair insert_or_assign(const key_type & key, M && m) + { + auto emplace_result = emplace(key, std::forward(m)); + if (!emplace_result.second) + emplace_result.first->second = std::forward(m); + return emplace_result; + } + template + std::pair insert_or_assign(key_type && key, M && m) + { + auto emplace_result = emplace(std::move(key), std::forward(m)); + if (!emplace_result.second) + emplace_result.first->second = std::forward(m); + return emplace_result; + } + template + typename Table::iterator insert_or_assign(typename Table::const_iterator, const key_type & key, M && m) + { + return insert_or_assign(key, std::forward(m)).first; + } + template + typename Table::iterator insert_or_assign(typename Table::const_iterator, key_type && key, M && m) + { + return insert_or_assign(std::move(key), std::forward(m)).first; + } friend bool operator==(const flat_hash_map & lhs, const flat_hash_map & rhs) { diff --git a/unordered_map.hpp b/unordered_map.hpp new file mode 100644 index 0000000..5e6e876 --- /dev/null +++ b/unordered_map.hpp @@ -0,0 +1,885 @@ +// Copyright Malte Skarupke 2017. +// Distributed under the Boost Software License, Version 1.0. +// (See http://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "flat_hash_map.hpp" + +namespace ska +{ + +namespace detailv10 +{ +template +struct sherwood_v10_entry +{ + sherwood_v10_entry() + { + } + ~sherwood_v10_entry() + { + } + + using EntryPointer = typename std::allocator_traits::template rebind_alloc>::pointer; + + EntryPointer next = nullptr; + union + { + T value; + }; + + static EntryPointer * empty_pointer() + { + static EntryPointer result[3] = { EntryPointer(nullptr) + ptrdiff_t(1), nullptr, nullptr }; + return result + 1; + } +}; + +using ska::detailv3::functor_storage; +using ska::detailv3::KeyOrValueHasher; +using ska::detailv3::KeyOrValueEquality; +using ska::detailv3::AssignIfTrue; +using ska::detailv3::HashPolicySelector; + +template +class sherwood_v10_table : private EntryAlloc, private Hasher, private Equal, private BucketAllocator +{ + using Entry = detailv10::sherwood_v10_entry; + using AllocatorTraits = std::allocator_traits; + using BucketAllocatorTraits = std::allocator_traits; + using EntryPointer = typename AllocatorTraits::pointer; + struct convertible_to_iterator; + +public: + + using value_type = T; + using size_type = size_t; + using difference_type = std::ptrdiff_t; + using hasher = ArgumentHash; + using key_equal = ArgumentEqual; + using allocator_type = EntryAlloc; + using reference = value_type &; + using const_reference = const value_type &; + using pointer = value_type *; + using const_pointer = const value_type *; + + sherwood_v10_table() + { + } + explicit sherwood_v10_table(size_type bucket_count, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : EntryAlloc(alloc), Hasher(hash), Equal(equal), BucketAllocator(alloc) + { + rehash(bucket_count); + } + sherwood_v10_table(size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v10_table(bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + sherwood_v10_table(size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v10_table(bucket_count, hash, ArgumentEqual(), alloc) + { + } + explicit sherwood_v10_table(const ArgumentAlloc & alloc) + : EntryAlloc(alloc), BucketAllocator(alloc) + { + } + template + sherwood_v10_table(It first, It last, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : sherwood_v10_table(bucket_count, hash, equal, alloc) + { + insert(first, last); + } + template + sherwood_v10_table(It first, It last, size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v10_table(first, last, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + template + sherwood_v10_table(It first, It last, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v10_table(first, last, bucket_count, hash, ArgumentEqual(), alloc) + { + } + sherwood_v10_table(std::initializer_list il, size_type bucket_count = 0, const ArgumentHash & hash = ArgumentHash(), const ArgumentEqual & equal = ArgumentEqual(), const ArgumentAlloc & alloc = ArgumentAlloc()) + : sherwood_v10_table(bucket_count, hash, equal, alloc) + { + if (bucket_count == 0) + reserve(il.size()); + insert(il.begin(), il.end()); + } + sherwood_v10_table(std::initializer_list il, size_type bucket_count, const ArgumentAlloc & alloc) + : sherwood_v10_table(il, bucket_count, ArgumentHash(), ArgumentEqual(), alloc) + { + } + sherwood_v10_table(std::initializer_list il, size_type bucket_count, const ArgumentHash & hash, const ArgumentAlloc & alloc) + : sherwood_v10_table(il, bucket_count, hash, ArgumentEqual(), alloc) + { + } + sherwood_v10_table(const sherwood_v10_table & other) + : sherwood_v10_table(other, AllocatorTraits::select_on_container_copy_construction(other.get_allocator())) + { + } + sherwood_v10_table(const sherwood_v10_table & other, const ArgumentAlloc & alloc) + : EntryAlloc(alloc), Hasher(other), Equal(other), BucketAllocator(alloc), _max_load_factor(other._max_load_factor) + { + try + { + rehash_for_other_container(other); + insert(other.begin(), other.end()); + } + catch(...) + { + clear(); + deallocate_data(); + throw; + } + } + sherwood_v10_table(sherwood_v10_table && other) noexcept + : EntryAlloc(std::move(other)), Hasher(std::move(other)), Equal(std::move(other)), BucketAllocator(std::move(other)), _max_load_factor(other._max_load_factor) + { + swap_pointers(other); + } + sherwood_v10_table(sherwood_v10_table && other, const ArgumentAlloc & alloc) noexcept + : EntryAlloc(alloc), Hasher(std::move(other)), Equal(std::move(other)), BucketAllocator(alloc), _max_load_factor(other._max_load_factor) + { + swap_pointers(other); + } + sherwood_v10_table & operator=(const sherwood_v10_table & other) + { + if (this == std::addressof(other)) + return *this; + + clear(); + static_assert(AllocatorTraits::propagate_on_container_copy_assignment::value == BucketAllocatorTraits::propagate_on_container_copy_assignment::value, "The allocators have to behave the same way"); + if (AllocatorTraits::propagate_on_container_copy_assignment::value) + { + if (static_cast(*this) != static_cast(other) || static_cast(*this) != static_cast(other)) + { + reset_to_empty_state(); + } + AssignIfTrue()(*this, other); + AssignIfTrue()(*this, other); + } + _max_load_factor = other._max_load_factor; + static_cast(*this) = other; + static_cast(*this) = other; + rehash_for_other_container(other); + insert(other.begin(), other.end()); + return *this; + } + sherwood_v10_table & operator=(sherwood_v10_table && other) noexcept + { + static_assert(AllocatorTraits::propagate_on_container_move_assignment::value == BucketAllocatorTraits::propagate_on_container_move_assignment::value, "The allocators have to behave the same way"); + if (this == std::addressof(other)) + return *this; + else if (AllocatorTraits::propagate_on_container_move_assignment::value) + { + clear(); + reset_to_empty_state(); + AssignIfTrue()(*this, std::move(other)); + AssignIfTrue()(*this, std::move(other)); + swap_pointers(other); + } + else if (static_cast(*this) == static_cast(other) && static_cast(*this) == static_cast(other)) + { + swap_pointers(other); + } + else + { + clear(); + _max_load_factor = other._max_load_factor; + rehash_for_other_container(other); + for (T & elem : other) + emplace(std::move(elem)); + other.clear(); + } + static_cast(*this) = std::move(other); + static_cast(*this) = std::move(other); + return *this; + } + ~sherwood_v10_table() + { + clear(); + deallocate_data(); + } + + const allocator_type & get_allocator() const + { + return static_cast(*this); + } + const ArgumentEqual & key_eq() const + { + return static_cast(*this); + } + const ArgumentHash & hash_function() const + { + return static_cast(*this); + } + + template + struct templated_iterator + { + templated_iterator() + { + } + templated_iterator(EntryPointer element, EntryPointer * bucket) + : current_element(element), current_bucket(bucket) + { + } + + EntryPointer current_element = nullptr; + EntryPointer * current_bucket = nullptr; + + using iterator_category = std::forward_iterator_tag; + using value_type = ValueType; + using difference_type = ptrdiff_t; + using pointer = ValueType *; + using reference = ValueType &; + + friend bool operator==(const templated_iterator & lhs, const templated_iterator & rhs) + { + return lhs.current_element == rhs.current_element; + } + friend bool operator!=(const templated_iterator & lhs, const templated_iterator & rhs) + { + return !(lhs == rhs); + } + + templated_iterator & operator++() + { + if (!current_element->next) + { + do + { + --current_bucket; + } + while (!*current_bucket); + current_element = *current_bucket; + } + else + current_element = current_element->next; + return *this; + } + templated_iterator operator++(int) + { + templated_iterator copy(*this); + ++*this; + return copy; + } + + ValueType & operator*() const + { + return current_element->value; + } + ValueType * operator->() const + { + return std::addressof(current_element->value); + } + + operator templated_iterator() const + { + return { current_element, current_bucket }; + } + }; + using iterator = templated_iterator; + using const_iterator = templated_iterator; + + iterator begin() + { + EntryPointer * end = entries - 1; + for (EntryPointer * it = entries + num_slots_minus_one; it != end; --it) + { + if (*it) + return { *it, it }; + } + return { *end, end }; + } + const_iterator begin() const + { + return const_cast(this)->begin(); + } + const_iterator cbegin() const + { + return begin(); + } + iterator end() + { + EntryPointer * end = entries - 1; + return { *end, end }; + } + const_iterator end() const + { + EntryPointer * end = entries - 1; + return { *end, end }; + } + const_iterator cend() const + { + return end(); + } + + iterator find(const FindKey & key) + { + size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + EntryPointer * bucket = entries + ptrdiff_t(index); + for (EntryPointer it = *bucket; it; it = it->next) + { + if (compares_equal(key, it->value)) + return { it, bucket }; + } + return end(); + } + const_iterator find(const FindKey & key) const + { + return const_cast(this)->find(key); + } + size_t count(const FindKey & key) const + { + return find(key) == end() ? 0 : 1; + } + std::pair equal_range(const FindKey & key) + { + iterator found = find(key); + if (found == end()) + return { found, found }; + else + return { found, std::next(found) }; + } + std::pair equal_range(const FindKey & key) const + { + const_iterator found = find(key); + if (found == end()) + return { found, found }; + else + return { found, std::next(found) }; + } + + template + std::pair emplace(Key && key, Args &&... args) + { + size_t index = hash_policy.index_for_hash(hash_object(key), num_slots_minus_one); + EntryPointer * bucket = entries + ptrdiff_t(index); + for (EntryPointer it = *bucket; it; it = it->next) + { + if (compares_equal(key, it->value)) + return { { it, bucket }, false }; + } + return emplace_new_key(bucket, std::forward(key), std::forward(args)...); + } + + std::pair insert(const value_type & value) + { + return emplace(value); + } + std::pair insert(value_type && value) + { + return emplace(std::move(value)); + } + template + iterator emplace_hint(const_iterator, Args &&... args) + { + return emplace(std::forward(args)...).first; + } + iterator insert(const_iterator, const value_type & value) + { + return emplace(value).first; + } + iterator insert(const_iterator, value_type && value) + { + return emplace(std::move(value)).first; + } + + template + void insert(It begin, It end) + { + for (; begin != end; ++begin) + { + emplace(*begin); + } + } + void insert(std::initializer_list il) + { + insert(il.begin(), il.end()); + } + + void rehash(size_t num_buckets) + { + num_buckets = std::max(num_buckets, static_cast(std::ceil(num_elements / static_cast(_max_load_factor)))); + if (num_buckets == 0) + { + reset_to_empty_state(); + return; + } + auto new_prime_index = hash_policy.next_size_over(num_buckets); + if (num_buckets == bucket_count()) + return; + EntryPointer * new_buckets(&*BucketAllocatorTraits::allocate(*this, num_buckets + 1)); + EntryPointer * end_it = new_buckets + static_cast(num_buckets + 1); + *new_buckets = EntryPointer(nullptr) + ptrdiff_t(1); + ++new_buckets; + std::fill(new_buckets, end_it, nullptr); + std::swap(entries, new_buckets); + std::swap(num_slots_minus_one, num_buckets); + --num_slots_minus_one; + hash_policy.commit(new_prime_index); + if (!num_buckets) + return; + + for (EntryPointer * it = new_buckets, * end = it + static_cast(num_buckets + 1); it != end; ++it) + { + for (EntryPointer e = *it; e;) + { + EntryPointer next = e->next; + size_t index = hash_policy.index_for_hash(hash_object(e->value), num_slots_minus_one); + EntryPointer & new_slot = entries[index]; + e->next = new_slot; + new_slot = e; + e = next; + } + } + BucketAllocatorTraits::deallocate(*this, new_buckets - 1, num_buckets + 2); + } + + void reserve(size_t num_elements) + { + if (!num_elements) + return; + num_elements = static_cast(std::ceil(num_elements / static_cast(_max_load_factor))); + if (num_elements > bucket_count()) + rehash(num_elements); + } + + // the return value is a type that can be converted to an iterator + // the reason for doing this is that it's not free to find the + // iterator pointing at the next element. if you care about the + // next iterator, turn the return value into an iterator + convertible_to_iterator erase(const_iterator to_erase) + { + --num_elements; + AllocatorTraits::destroy(*this, std::addressof(to_erase.current_element->value)); + EntryPointer * previous = to_erase.current_bucket; + while (*previous != to_erase.current_element) + { + previous = &(*previous)->next; + } + *previous = to_erase.current_element->next; + AllocatorTraits::deallocate(*this, to_erase.current_element, 1); + return { *previous, to_erase.current_bucket }; + } + + convertible_to_iterator erase(const_iterator begin_it, const_iterator end_it) + { + while (begin_it.current_bucket != end_it.current_bucket) + { + begin_it = erase(begin_it); + } + EntryPointer * bucket = begin_it.current_bucket; + EntryPointer * previous = bucket; + while (*previous != begin_it.current_element) + previous = &(*previous)->next; + while (*previous != end_it.current_element) + { + --num_elements; + EntryPointer entry = *previous; + AllocatorTraits::destroy(*this, std::addressof(entry->value)); + *previous = entry->next; + AllocatorTraits::deallocate(*this, entry, 1); + } + return { *previous, bucket }; + } + + size_t erase(const FindKey & key) + { + auto found = find(key); + if (found == end()) + return 0; + else + { + erase(found); + return 1; + } + } + + void clear() + { + if (!num_slots_minus_one) + return; + for (EntryPointer * it = entries, * end = it + static_cast(num_slots_minus_one + 1); it != end; ++it) + { + for (EntryPointer e = *it; e;) + { + EntryPointer next = e->next; + AllocatorTraits::destroy(*this, std::addressof(e->value)); + AllocatorTraits::deallocate(*this, e, 1); + e = next; + } + *it = nullptr; + } + num_elements = 0; + } + + void swap(sherwood_v10_table & other) + { + using std::swap; + swap_pointers(other); + swap(static_cast(*this), static_cast(other)); + swap(static_cast(*this), static_cast(other)); + if (AllocatorTraits::propagate_on_container_swap::value) + swap(static_cast(*this), static_cast(other)); + if (BucketAllocatorTraits::propagate_on_container_swap::value) + swap(static_cast(*this), static_cast(other)); + } + + size_t size() const + { + return num_elements; + } + size_t max_size() const + { + return (AllocatorTraits::max_size(*this)) / sizeof(Entry); + } + size_t bucket_count() const + { + return num_slots_minus_one + 1; + } + size_type max_bucket_count() const + { + return (AllocatorTraits::max_size(*this) - 1) / sizeof(Entry); + } + size_t bucket(const FindKey & key) const + { + return hash_policy.template index_for_hash<0>(hash_object(key), num_slots_minus_one); + } + float load_factor() const + { + size_t buckets = bucket_count(); + if (buckets) + return static_cast(num_elements) / bucket_count(); + else + return 0; + } + void max_load_factor(float value) + { + _max_load_factor = value; + } + float max_load_factor() const + { + return _max_load_factor; + } + + bool empty() const + { + return num_elements == 0; + } + +private: + EntryPointer * entries = Entry::empty_pointer(); + size_t num_slots_minus_one = 0; + typename HashPolicySelector::type hash_policy; + float _max_load_factor = 1.0f; + size_t num_elements = 0; + + void rehash_for_other_container(const sherwood_v10_table & other) + { + reserve(other.size()); + } + + void swap_pointers(sherwood_v10_table & other) + { + using std::swap; + swap(hash_policy, other.hash_policy); + swap(entries, other.entries); + swap(num_slots_minus_one, other.num_slots_minus_one); + swap(num_elements, other.num_elements); + swap(_max_load_factor, other._max_load_factor); + } + + template + SKA_NOINLINE(std::pair) emplace_new_key(EntryPointer * bucket, Args &&... args) + { + using std::swap; + if (is_full()) + { + grow(); + return emplace(std::forward(args)...); + } + else + { + EntryPointer new_entry = AllocatorTraits::allocate(*this, 1); + try + { + AllocatorTraits::construct(*this, std::addressof(new_entry->value), std::forward(args)...); + } + catch(...) + { + AllocatorTraits::deallocate(*this, new_entry, 1); + throw; + } + ++num_elements; + new_entry->next = *bucket; + *bucket = new_entry; + return { { new_entry, bucket }, true }; + } + } + + bool is_full() const + { + if (!num_slots_minus_one) + return true; + else + return num_elements + 1 > (num_slots_minus_one + 1) * static_cast(_max_load_factor); + } + + void grow() + { + rehash(std::max(size_t(4), 2 * bucket_count())); + } + + void deallocate_data() + { + if (entries != Entry::empty_pointer()) + { + BucketAllocatorTraits::deallocate(*this, entries - 1, num_slots_minus_one + 2); + } + } + + void reset_to_empty_state() + { + deallocate_data(); + entries = Entry::empty_pointer(); + num_slots_minus_one = 0; + hash_policy.reset(); + } + + template + size_t hash_object(const U & key) + { + return static_cast(*this)(key); + } + template + size_t hash_object(const U & key) const + { + return static_cast(*this)(key); + } + template + bool compares_equal(const L & lhs, const R & rhs) + { + return static_cast(*this)(lhs, rhs); + } + + struct convertible_to_iterator + { + EntryPointer element; + EntryPointer * bucket; + + operator iterator() + { + if (element) + return { element, bucket }; + else + { + do + { + --bucket; + } + while (!*bucket); + return { *bucket, bucket }; + } + } + operator const_iterator() + { + if (element) + return { element, bucket }; + else + { + do + { + --bucket; + } + while (!*bucket); + return { *bucket, bucket }; + } + } + }; +}; +} + + +template, typename E = std::equal_to, typename A = std::allocator > > +class unordered_map + : public detailv10::sherwood_v10_table + < + std::pair, + K, + H, + detailv10::KeyOrValueHasher, H>, + E, + detailv10::KeyOrValueEquality, E>, + A, + typename std::allocator_traits::template rebind_alloc, A>>, + typename std::allocator_traits::template rebind_alloc::template rebind_traits, A>>::pointer> + > +{ + using Table = detailv10::sherwood_v10_table + < + std::pair, + K, + H, + detailv10::KeyOrValueHasher, H>, + E, + detailv10::KeyOrValueEquality, E>, + A, + typename std::allocator_traits::template rebind_alloc, A>>, + typename std::allocator_traits::template rebind_alloc::template rebind_traits, A>>::pointer> + >; +public: + + using key_type = K; + using mapped_type = V; + + using Table::Table; + unordered_map() + { + } + + V & operator[](const K & key) + { + return emplace(key, convertible_to_value()).first->second; + } + V & operator[](K && key) + { + return emplace(std::move(key), convertible_to_value()).first->second; + } + V & at(const K & key) + { + auto found = this->find(key); + if (found == this->end()) + throw std::out_of_range("Argument passed to at() was not in the map."); + return found->second; + } + const V & at(const K & key) const + { + auto found = this->find(key); + if (found == this->end()) + throw std::out_of_range("Argument passed to at() was not in the map."); + return found->second; + } + + using Table::emplace; + std::pair emplace() + { + return emplace(key_type(), convertible_to_value()); + } + + friend bool operator==(const unordered_map & lhs, const unordered_map & rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (const typename Table::value_type & value : lhs) + { + auto found = rhs.find(value.first); + if (found == rhs.end()) + return false; + else if (value.second != found->second) + return false; + } + return true; + } + friend bool operator!=(const unordered_map & lhs, const unordered_map & rhs) + { + return !(lhs == rhs); + } + +private: + struct convertible_to_value + { + operator V() const + { + return V(); + } + }; +}; + +template, typename E = std::equal_to, typename A = std::allocator > +class unordered_set + : public detailv10::sherwood_v10_table + < + T, + T, + H, + detailv10::functor_storage, + E, + detailv10::functor_storage, + A, + typename std::allocator_traits::template rebind_alloc>, + typename std::allocator_traits::template rebind_alloc::template rebind_traits>::pointer> + > +{ + using Table = detailv10::sherwood_v10_table + < + T, + T, + H, + detailv10::functor_storage, + E, + detailv10::functor_storage, + A, + typename std::allocator_traits::template rebind_alloc>, + typename std::allocator_traits::template rebind_alloc::template rebind_traits>::pointer> + >; +public: + + using key_type = T; + + using Table::Table; + unordered_set() + { + } + + template + std::pair emplace(Args &&... args) + { + return Table::emplace(T(std::forward(args)...)); + } + std::pair emplace(const key_type & arg) + { + return Table::emplace(arg); + } + std::pair emplace(key_type & arg) + { + return Table::emplace(arg); + } + std::pair emplace(const key_type && arg) + { + return Table::emplace(std::move(arg)); + } + std::pair emplace(key_type && arg) + { + return Table::emplace(std::move(arg)); + } + + friend bool operator==(const unordered_set & lhs, const unordered_set & rhs) + { + if (lhs.size() != rhs.size()) + return false; + for (const T & value : lhs) + { + if (rhs.find(value) == rhs.end()) + return false; + } + return true; + } + friend bool operator!=(const unordered_set & lhs, const unordered_set & rhs) + { + return !(lhs == rhs); + } +}; + +} // end namespace ska