diff --git a/include/rexy/cx/hashmap.hpp b/include/rexy/cx/hashmap.hpp index 01dd2ae..e3ace57 100644 --- a/include/rexy/cx/hashmap.hpp +++ b/include/rexy/cx/hashmap.hpp @@ -67,21 +67,34 @@ namespace rexy::cx{ private: array m_values; //perfect hash table array m_g; //'salt' values for indexing into the perfect hash table + array m_key_hashes; //full hash values for keys to verify good index values public: - constexpr hashmap(const value_type(&elements)[N]); + constexpr hashmap(const value_type(&elements)[N]) + noexcept(std::is_nothrow_default_constructible::value && + std::is_nothrow_copy_constructible::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_invocable::value); //no key checks. give a correct key or get a random answer :) template>> constexpr reference operator[](U&& u)noexcept; template>> constexpr const_reference operator[](U&& u)const noexcept; + + template>> + constexpr bool is_valid(U&& u)const noexcept; + }; template - constexpr hashmap::hashmap(const value_type(&elements)[N]){ + constexpr hashmap::hashmap(const value_type(&elements)[N]) + noexcept(std::is_nothrow_default_constructible::value && + std::is_nothrow_copy_constructible::value && + std::is_nothrow_move_assignable::value && + std::is_nothrow_invocable::value) + { array,N> buckets; - array slots_used; size_type current_bucket = 0; //place all keys into buckets @@ -101,13 +114,15 @@ namespace rexy::cx{ if(bucket.size() <= 1) break; + const auto hashval = Hash{}(bucket[0].key, 0); + array pass_slots_used; vector pass_slots; size_type d = 1; for(size_type i = 0;i < bucket.size();){ size_type slot = Hash{}(bucket[i].key, d) % max_size; - if(pass_slots_used[slot] || slots_used[slot]){ + if(pass_slots_used[slot] || m_key_hashes[slot] != 0){ //slot already in use, try another value for 'd' ++d; i = 0; @@ -121,12 +136,12 @@ namespace rexy::cx{ } } //store the successful value of 'd' at index of the first hash for this bucket - m_g[Hash{}(bucket[0].key, 0) % max_size] = d; + m_g[hashval % max_size] = d; //take the value from the temporary bucket into the permanent slot for(size_type i = 0;i < bucket.size();++i){ m_values[pass_slots[i]] = std::move(bucket[i].value); - slots_used[pass_slots[i]] = true; + m_key_hashes[pass_slots[i]] = hashval; } } @@ -137,11 +152,14 @@ namespace rexy::cx{ auto& bucket = buckets[current_bucket]; if(bucket.size() == 0) break; - for(;slots_used[next_free_slot];++next_free_slot); + + const auto hashval = Hash{}(bucket[0].key, 0); + + for(;m_key_hashes[next_free_slot] != 0;++next_free_slot); m_g[Hash{}(bucket[0].key, 0) % max_size] = (next_free_slot | single_bucket_bit); m_values[next_free_slot] = std::move(bucket[0].value); - slots_used[next_free_slot] = true; + m_key_hashes[next_free_slot] = hashval; } } @@ -163,7 +181,16 @@ namespace rexy::cx{ return m_values[UHash{}(std::forward(key), d) % max_size]; } - + template + template + constexpr bool hashmap::is_valid(U&& key)const noexcept{ + const auto hashval = UHash{}(std::forward(key), 0); + const auto d = m_g[hashval % max_size]; + if(d & single_bucket_bit){ + return m_key_hashes[d & ~single_bucket_bit] == hashval; + } + return m_key_hashes[UHash{}(std::forward(key), d) % max_size] == hashval; + } template> constexpr auto make_hashmap(const typename hashmap::value_type(&list)[N]){