Change hashmap is_valid to contains, separate implementation to separate file. Add iterator constructors to string_view
This commit is contained in:
parent
54c8e24fab
commit
53378eb6c3
@ -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 <climits> //CHAR_BIT
|
||||
#include <cstddef> //size_t, ptrdiff_t
|
||||
#include <utility> //pair
|
||||
#include <type_traits> //decay
|
||||
#include <initializer_list>
|
||||
|
||||
@ -65,9 +64,8 @@ namespace rexy::cx{
|
||||
|
||||
|
||||
private:
|
||||
array<mapped_type,N> m_values; //perfect hash table
|
||||
array<value_type,N> m_elements; //perfect hash table
|
||||
array<size_type,N> m_g; //'salt' values for indexing into the perfect hash table
|
||||
array<size_type,N> m_key_hashes; //full hash values for keys to verify good index values
|
||||
|
||||
public:
|
||||
constexpr hashmap(const value_type(&elements)[N])
|
||||
@ -83,121 +81,14 @@ namespace rexy::cx{
|
||||
constexpr const_reference operator[](U&& u)const noexcept;
|
||||
|
||||
template<class U, class UHash = hash<std::decay_t<U>>>
|
||||
constexpr bool is_valid(U&& u)const noexcept;
|
||||
constexpr bool contains(U&& u)const noexcept;
|
||||
|
||||
};
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N])
|
||||
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
|
||||
std::is_nothrow_copy_constructible<value_type>::value &&
|
||||
std::is_nothrow_move_assignable<mapped_type>::value &&
|
||||
std::is_nothrow_invocable<Hash,Key,size_t>::value)
|
||||
{
|
||||
array<vector<value_type,N>,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<bool,N> pass_slots_used;
|
||||
vector<size_type,N> 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<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_values[d & ~single_bucket_bit];
|
||||
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
|
||||
}
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const noexcept -> const_reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_values[d & ~single_bucket_bit];
|
||||
return m_values[UHash{}(std::forward<U>(key), d) % max_size];
|
||||
}
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr bool hashmap<Key,Value,N,Hash>::is_valid(U&& key)const noexcept{
|
||||
const auto hashval = UHash{}(std::forward<U>(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<U>(key), d) % max_size] == hashval;
|
||||
}
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash = hash<Key>>
|
||||
constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){
|
||||
return hashmap<Key,Value,N,Hash>(list);
|
||||
}
|
||||
}
|
||||
|
||||
#include "hashmap.tpp"
|
||||
|
||||
#ifdef REXY_STRING_BASE_HPP
|
||||
#include "../string_hash.hpp"
|
||||
#endif
|
||||
|
||||
139
include/rexy/cx/hashmap.tpp
Normal file
139
include/rexy/cx/hashmap.tpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef REXY_CX_HASHMAP_TPP
|
||||
#define REXY_CX_HASHMAP_TPP
|
||||
|
||||
#include <utility> //move, forward
|
||||
|
||||
namespace rexy::cx{
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
constexpr hashmap<Key,Value,N,Hash>::hashmap(const value_type(&elements)[N])
|
||||
noexcept(std::is_nothrow_default_constructible<value_type>::value &&
|
||||
std::is_nothrow_copy_constructible<value_type>::value &&
|
||||
std::is_nothrow_move_assignable<mapped_type>::value &&
|
||||
std::is_nothrow_invocable<Hash,Key,size_t>::value)
|
||||
{
|
||||
array<vector<value_type,N>,N> buckets;
|
||||
array<size_type,N> 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<bool,N> pass_slots_used;
|
||||
vector<size_type,N> 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<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)noexcept -> reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_elements[d & ~single_bucket_bit].value;
|
||||
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value;
|
||||
}
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr auto hashmap<Key,Value,N,Hash>::operator[](U&& key)const noexcept -> const_reference{
|
||||
auto d = m_g[UHash{}(std::forward<U>(key), 0) % max_size];
|
||||
if(d & single_bucket_bit)
|
||||
return m_elements[d & ~single_bucket_bit].value;
|
||||
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].value;
|
||||
}
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash>
|
||||
template<class U, class UHash>
|
||||
constexpr bool hashmap<Key,Value,N,Hash>::contains(U&& key)const noexcept{
|
||||
const auto hashval = UHash{}(std::forward<U>(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<U>(key);
|
||||
}
|
||||
return m_elements[UHash{}(std::forward<U>(key), d) % max_size].key == std::forward<U>(key);
|
||||
}
|
||||
|
||||
template<class Key, class Value, size_t N, class Hash = hash<Key>>
|
||||
constexpr auto make_hashmap(const typename hashmap<Key,Value,N,Hash>::value_type(&list)[N]){
|
||||
return hashmap<Key,Value,N,Hash>(list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -32,22 +32,22 @@ namespace rexy::detail{
|
||||
template<class Iter, class Compare>
|
||||
constexpr Iter qs_partition(Iter left, Iter right, const Compare& cmp)
|
||||
noexcept(std::is_nothrow_invocable<Compare,decltype(*left),decltype(*right)>::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<class Iter, class Op>
|
||||
|
||||
@ -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<Char>& s)noexcept;
|
||||
constexpr string_view(string_view&& s)noexcept;
|
||||
template<class InIter>
|
||||
constexpr string_view(InIter start, InIter fin)noexcept;
|
||||
REXY_CPP20_CONSTEXPR ~string_view(void)noexcept = default;
|
||||
|
||||
constexpr string_view& operator=(const_pointer c)noexcept;
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include <utility> //forward, move, swap, etc
|
||||
#include <cstdlib> //memcpy
|
||||
#include <cstring> //strlen, strcpy
|
||||
#include <memory> //to_address
|
||||
|
||||
#include "utility.hpp" //max
|
||||
#include "detail/string_appender.hpp"
|
||||
@ -374,9 +375,17 @@ namespace rexy{
|
||||
constexpr string_view<Char>::string_view(const string_view& s)noexcept:
|
||||
string_view(s.get_long_ptr(), s.get_long_length()){}
|
||||
template<class Char>
|
||||
constexpr string_view<Char>::string_view(const string_base<Char>& s)noexcept:
|
||||
string_view(s.c_str(), s.length()){}
|
||||
template<class Char>
|
||||
constexpr string_view<Char>::string_view(string_view&& s)noexcept:
|
||||
string_view(s.get_long_ptr(), s.get_long_length()){}
|
||||
template<class Char>
|
||||
template<class InIter>
|
||||
constexpr string_view<Char>::string_view(InIter start, InIter fin)noexcept:
|
||||
string_view(std::to_address(start), fin - start){}
|
||||
|
||||
template<class Char>
|
||||
constexpr string_view<Char>& string_view<Char>::operator=(const string_view& s)noexcept{
|
||||
this->set_long_ptr(const_cast<pointer>(s.get_long_ptr()));
|
||||
this->set_long_length(s.get_long_length());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user