diff --git a/include/rexy/cx/hashmap.hpp b/include/rexy/cx/hashmap.hpp index a5f51f6..888819e 100644 --- a/include/rexy/cx/hashmap.hpp +++ b/include/rexy/cx/hashmap.hpp @@ -1,6 +1,6 @@ /** This file is a part of rexy's general purpose library - Copyright (C) 2020 rexy712 + Copyright (C) 2020-2022 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ #include //CHAR_BIT #include //size_t, ptrdiff_t -#include //pair #include //decay #include @@ -65,9 +64,8 @@ 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 + array m_elements; //perfect hash table + array m_g; //'salt' values for indexing into the perfect hash table public: constexpr hashmap(const value_type(&elements)[N]) @@ -83,121 +81,14 @@ namespace rexy::cx{ constexpr const_reference operator[](U&& u)const noexcept; template>> - constexpr bool is_valid(U&& u)const noexcept; + constexpr bool contains(U&& u)const noexcept; }; - template - 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; - size_type current_bucket = 0; - - //place all keys into buckets - for(auto& element : elements){ - buckets[Hash{}(element.key, 0) % max_size].push_back(element); - } - - //sort the buckets based on size, largest first - quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{ - return left.size() > right.size(); - }); - - //for each bucket, try different values of 'd' to try to find a hash that doesn't collide - for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){ - auto& bucket = buckets[current_bucket]; - //only handle buckets containing collisions - 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] || m_key_hashes[slot] != 0){ - //slot already in use, try another value for 'd' - ++d; - i = 0; - pass_slots_used.fill(false); - pass_slots.clear(); - }else{ - //slot is good to go - pass_slots_used[slot] = true; - pass_slots.push_back(slot); - ++i; - } - } - //store the successful value of 'd' at index of the first hash for this bucket - 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); - m_key_hashes[pass_slots[i]] = hashval; - } - } - - //Handle remaining single value buckets - size_type next_free_slot = 0; - - for(;current_bucket < buckets.size();++current_bucket){ - auto& bucket = buckets[current_bucket]; - if(bucket.size() == 0) - break; - - 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); - m_key_hashes[next_free_slot] = hashval; - } - } - - //no key checks. give a correct key or get a random answer :) - template - template - constexpr auto hashmap::operator[](U&& key)noexcept -> reference{ - auto d = m_g[UHash{}(std::forward(key), 0) % max_size]; - if(d & single_bucket_bit) - return m_values[d & ~single_bucket_bit]; - return m_values[UHash{}(std::forward(key), d) % max_size]; - } - template - template - constexpr auto hashmap::operator[](U&& key)const noexcept -> const_reference{ - auto d = m_g[UHash{}(std::forward(key), 0) % max_size]; - if(d & single_bucket_bit) - return m_values[d & ~single_bucket_bit]; - 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]){ - return hashmap(list); - } } +#include "hashmap.tpp" + #ifdef REXY_STRING_BASE_HPP #include "../string_hash.hpp" #endif diff --git a/include/rexy/cx/hashmap.tpp b/include/rexy/cx/hashmap.tpp new file mode 100644 index 0000000..687239a --- /dev/null +++ b/include/rexy/cx/hashmap.tpp @@ -0,0 +1,139 @@ +/** + This file is a part of rexy's general purpose library + Copyright (C) 2020-2022 rexy712 + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef REXY_CX_HASHMAP_TPP +#define REXY_CX_HASHMAP_TPP + +#include //move, forward + +namespace rexy::cx{ + + template + 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 key_hashes; //full hash values for keys to verify good index values + size_type current_bucket = 0; + + //place all keys into buckets + for(auto& element : elements){ + buckets[Hash{}(element.key, 0) % max_size].push_back(element); + } + + //sort the buckets based on size, largest first + quicksort(buckets.begin(), buckets.end(), [](auto&& left, auto&& right) -> bool{ + return left.size() > right.size(); + }); + + //for each bucket, try different values of 'd' to try to find a hash that doesn't collide + for(current_bucket = 0;current_bucket < buckets.size();++current_bucket){ + auto& bucket = buckets[current_bucket]; + //only handle buckets containing collisions + 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] || key_hashes[slot] != 0){ + //slot already in use, try another value for 'd' + ++d; + i = 0; + pass_slots_used.fill(false); + pass_slots.clear(); + }else{ + //slot is good to go + pass_slots_used[slot] = true; + pass_slots.push_back(slot); + ++i; + } + } + //store the successful value of 'd' at index of the first hash for this bucket + 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_elements[pass_slots[i]] = std::move(bucket[i]); + key_hashes[pass_slots[i]] = hashval; + } + } + + //Handle remaining single value buckets + size_type next_free_slot = 0; + + for(;current_bucket < buckets.size();++current_bucket){ + auto& bucket = buckets[current_bucket]; + if(bucket.size() == 0) + break; + + const auto hashval = Hash{}(bucket[0].key, 0); + + for(;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_elements[next_free_slot] = std::move(bucket[0]); + key_hashes[next_free_slot] = hashval; + } + } + + //no key checks. give a correct key or get a random answer :) + template + template + constexpr auto hashmap::operator[](U&& key)noexcept -> reference{ + auto d = m_g[UHash{}(std::forward(key), 0) % max_size]; + if(d & single_bucket_bit) + return m_elements[d & ~single_bucket_bit].value; + return m_elements[UHash{}(std::forward(key), d) % max_size].value; + } + template + template + constexpr auto hashmap::operator[](U&& key)const noexcept -> const_reference{ + auto d = m_g[UHash{}(std::forward(key), 0) % max_size]; + if(d & single_bucket_bit) + return m_elements[d & ~single_bucket_bit].value; + return m_elements[UHash{}(std::forward(key), d) % max_size].value; + } + + template + template + constexpr bool hashmap::contains(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_elements[d & ~single_bucket_bit].key == std::forward(key); + } + return m_elements[UHash{}(std::forward(key), d) % max_size].key == std::forward(key); + } + + template> + constexpr auto make_hashmap(const typename hashmap::value_type(&list)[N]){ + return hashmap(list); + } + +} + +#endif diff --git a/include/rexy/detail/algorithm.hpp b/include/rexy/detail/algorithm.hpp index d26db74..1e5c561 100644 --- a/include/rexy/detail/algorithm.hpp +++ b/include/rexy/detail/algorithm.hpp @@ -32,22 +32,22 @@ namespace rexy::detail{ template constexpr Iter qs_partition(Iter left, Iter right, const Compare& cmp) noexcept(std::is_nothrow_invocable::value && - noexcept(swap(*left,*right))) + noexcept(::rexy::swap(*left,*right))) { auto range = right - left; auto pivot = left + (range / 2); auto value = *pivot; //move pivot value all the way to the right side to preserve it - swap(*pivot, *right); + ::rexy::swap(*pivot, *right); for(auto it = left;it != right;++it){ if(cmp(*it, value)){ - swap(*left, *it); + ::rexy::swap(*left, *it); ++left; } } //move pivot value back to proper position - swap(*left, *right); + ::rexy::swap(*left, *right); return left; } template diff --git a/include/rexy/string_base.hpp b/include/rexy/string_base.hpp index 3f4e9ca..de514f3 100644 --- a/include/rexy/string_base.hpp +++ b/include/rexy/string_base.hpp @@ -373,7 +373,10 @@ namespace rexy{ constexpr string_view(const_pointer str, size_type len)noexcept; constexpr string_view(const_pointer c)noexcept; constexpr string_view(const string_view& s)noexcept; + constexpr string_view(const string_base& s)noexcept; constexpr string_view(string_view&& s)noexcept; + template + constexpr string_view(InIter start, InIter fin)noexcept; REXY_CPP20_CONSTEXPR ~string_view(void)noexcept = default; constexpr string_view& operator=(const_pointer c)noexcept; diff --git a/include/rexy/string_base.tpp b/include/rexy/string_base.tpp index 23ba3b9..8b60247 100644 --- a/include/rexy/string_base.tpp +++ b/include/rexy/string_base.tpp @@ -22,6 +22,7 @@ #include //forward, move, swap, etc #include //memcpy #include //strlen, strcpy +#include //to_address #include "utility.hpp" //max #include "detail/string_appender.hpp" @@ -374,9 +375,17 @@ namespace rexy{ constexpr string_view::string_view(const string_view& s)noexcept: string_view(s.get_long_ptr(), s.get_long_length()){} template + constexpr string_view::string_view(const string_base& s)noexcept: + string_view(s.c_str(), s.length()){} + template constexpr string_view::string_view(string_view&& s)noexcept: string_view(s.get_long_ptr(), s.get_long_length()){} template + template + constexpr string_view::string_view(InIter start, InIter fin)noexcept: + string_view(std::to_address(start), fin - start){} + + template constexpr string_view& string_view::operator=(const string_view& s)noexcept{ this->set_long_ptr(const_cast(s.get_long_ptr())); this->set_long_length(s.get_long_length());