19#ifndef OPEN_CPP_UTILS_HASH_TABLE_H
20#define OPEN_CPP_UTILS_HASH_TABLE_H
30namespace open_cpp_utils
33template<
typename T,
class Hash = std::hash<T>,
class Alloc = std::allocator<T>>
47 using const_pointer =
const T*;
49 using const_reference =
const T&;
51 using hash_type = Hash;
52 using allocator_type =
typename std::allocator_traits<Alloc>::template rebind_alloc<_Node>;
54 using size_type = size_t;
55 using iterator_type =
class iterator;
58 using table_type = _Node*;
60 static constexpr node nullnode = std::integral_constant<node, -1>{};
71 _Node() : value(), psl(0) { }
72 _Node(const_reference v) : value(v), psl(0) { }
73 _Node(const_reference v,
int psl) : value(v), psl(psl) { }
75 bool operator==(
const _Node& b)
const
77 return value == b.value && psl == b.psl;
88 iterator(
hash_table* table, size_type idx) : table_(table), idx_(idx) { _next_index(); }
93 iterator& operator=(
const iterator& b) {
if(&b ==
this)
return *
this; table_ = b.table_; idx_ = b.idx_;
return *
this; }
94 iterator& operator=(
iterator&& b)
noexcept {
if(&b ==
this)
return *
this; table_ = b.table_; idx_ = b.idx_;
return *
this; };
96 iterator& operator++() { ++idx_; _next_index();
return *
this; }
97 iterator operator++(
int) {
iterator ret = *
this; ++idx_; _next_index();
return ret; }
99 bool operator==(
const iterator& o)
const =
default;
100 bool operator!=(
const iterator& o)
const =
default;
102 reference operator*() {
return table_->table_[idx_].value; }
103 pointer operator->() {
return &*table_->table_[idx_].value; }
105 const_reference operator*()
const {
return table_->table_[idx_].value; }
106 const_pointer operator->()
const {
return &*table_->table_[idx_].value; }
111 while(idx_ < table_->capacity_ && not table_->table_[idx_].value())
128 const_iterator& operator=(
const_iterator&& b)
noexcept {
if(&b ==
this)
return *
this; table_ = b.table_; idx_ = b.idx_;
return *
this; };
130 const_iterator& operator++() { ++idx_; _next_index();
return *
this; }
136 reference operator*()
const {
return table_->table_[idx_].value; }
137 pointer operator->()
const {
return &*table_->table_[idx_].value; }
140 void _next_index() {
while(idx_ < table_->capacity_ && not table_->table_[idx_].value()) ++idx_; }
147 iterator end() {
return iterator(
this, capacity_); }
149 const_iterator begin()
const {
return const_iterator(
this, 0); }
150 const_iterator end()
const {
return const_iterator(
this, capacity_); }
159 hash_table() : table_(nullptr), capacity_(0), size_(0), load_factor_(0.8), hash_(), alloc_() { }
160 hash_table(std::initializer_list<value_type> data);
161 hash_table(
const hash_table&);
162 hash_table(hash_table&&) =
default;
163 ~hash_table() { clear(); }
167 void reserve(
size_t size);
169 void insert(const_reference x);
170 void erase(const_reference x);
171 bool contains(const_reference x);
173 iterator find(const_reference x)
176 if(res == nullnode) res = capacity_;
177 return iterator(
this, res);
180 const_iterator find(const_reference x)
const
183 if(res == nullnode) res = capacity_;
184 return const_iterator(
this, res);
189 [[nodiscard]] size_type capacity()
const {
return capacity_; }
190 [[nodiscard]] size_type size()
const {
return size_; }
191 [[nodiscard]]
bool empty()
const {
return size_ == 0; }
192 [[nodiscard]]
double occupancy()
const {
return size_ /
static_cast<double>(capacity_); }
197 void _increase_capacity();
199 [[nodiscard]] node _hash(const_reference v)
const;
200 [[nodiscard]] node _find(const_reference x)
const;
201 [[nodiscard]] node _next(node n)
const {
return (n + 1) % capacity_; }
202 [[nodiscard]] node _prev(node n)
const {
return (n - 1 + capacity_) % capacity_; }
204 static size_type _next_prime(size_type x);
205 static size_type _prev_prime(size_type x);
212 size_type capacity_, size_;
215 allocator_type alloc_;
218template<
typename T,
class Hash,
class Alloc>
219hash_table<T, Hash, Alloc>::hash_table(std::initializer_list<value_type> data)
222 reserve(data.size());
223 for(value_type val : data) { insert(val); }
226template<
typename T,
class Hash,
class Alloc>
227hash_table<T, Hash, Alloc>::hash_table(
const hash_table& other)
229 , capacity_(other.capacity_)
236template<
typename T,
class Hash,
class Alloc>
237void hash_table<T, Hash, Alloc>::reserve(
size_t size)
239 table_type old = table_;
240 size_type old_capacity = capacity_;
241 capacity_ = _next_prime(size);
242 table_ = alloc_.allocate(capacity_);
243 memset(table_, 0, capacity_ *
sizeof(_Node));
246 for(size_type i = 0; i < old_capacity; ++i)
248 if(old[i].value()) insert(old[i].value);
251 alloc_.deallocate(old, old_capacity);
254template<
typename T,
class Hash,
class Alloc>
255void hash_table<T, Hash, Alloc>::clear()
257 alloc_.deallocate(table_, capacity_);
258 capacity_ = size_ = 0;
262template<
typename T,
class Hash,
class Alloc>
263void hash_table<T, Hash, Alloc>::insert(const_reference x)
265 if(occupancy() > load_factor_ || capacity_ == 0) _increase_capacity();
271 while(table_[idx].value())
273 _Node& node = table_[idx];
275 if(*node.value == x)
return;
279 int temp_psl = psl; psl = node.psl; node.psl = temp_psl;
280 T temp_val = std::move(node.value); node.value = std::move(val); val = std::move(temp_val);
287 std::construct_at(table_ + idx, val, psl);
291template<
typename T,
class Hash,
class Alloc>
292void hash_table<T, Hash, Alloc>::erase(const_reference x)
295 if(idx == nullnode)
return;
297 table_[idx].value.reset();
300 node prev = idx; idx = _next(idx);
301 while(table_[idx].value() && table_[idx].psl > 0)
303 _Node &a = table_[prev], &b = table_[idx];
305 --a.psl; prev = idx; idx = _next(idx);
309template<
typename T,
class Hash,
class Alloc>
310bool hash_table<T, Hash, Alloc>::contains(const_reference x)
312 return _find(x) != nullnode;
315template<
typename T,
class Hash,
class Alloc>
316void hash_table<T, Hash, Alloc>::_increase_capacity()
318 table_type old = table_;
319 size_type old_capacity = capacity_;
320 alloc_.deallocate(table_, capacity_);
321 capacity_ = _next_prime(capacity_);
322 table_ = alloc_.allocate(capacity_);
323 memset(table_, 0, capacity_ *
sizeof(_Node));
326 for(size_type i = 0; i < old_capacity; ++i)
328 if(old[i].value()) insert(old[i].value);
331 alloc_.deallocate(old, old_capacity);
334template<
typename T,
class Hash,
class Alloc>
335typename hash_table<T, Hash, Alloc>::node hash_table<T, Hash, Alloc>::_hash(const_reference v)
const
337 node x =
static_cast<node
>(hash_(v));
340 x *= UINT64_C(0xff51afd7ed558ccd);
342 x *= UINT64_C(0xc4ceb9fe1a85ec53);
345 return x % capacity_;
348template<
typename T,
class Hash,
class Alloc>
349typename hash_table<T, Hash, Alloc>::node hash_table<T, Hash, Alloc>::_find(const_reference x)
const
351 if(capacity_ == 0)
return nullnode;
355 while(table_[idx].value())
357 _Node& node = table_[idx];
359 if(node.psl > psl)
return nullnode;
360 if(*node.value == x)
return idx;
362 idx = _next(idx); ++psl;
368template<
typename T,
class Hash,
class Alloc>
369typename hash_table<T, Hash, Alloc>::size_type hash_table<T, Hash, Alloc>::_next_prime(size_type x)
371 size_type n = (x + 1) / 6;
377 if(!is_prime(x)) x = (n * 6) + 1;
378 if(!is_prime(x)) { ++n;
continue; }
379 return std::max(x, 7ull);
383template<
typename T,
class Hash,
class Alloc>
384typename hash_table<T, Hash, Alloc>::size_type hash_table<T, Hash, Alloc>::_prev_prime(size_type x)
386 size_type n = (x + 1) / 6;
392 if(!is_prime(x)) x = (n * 6) + 1;
393 if(!is_prime(x)) { --n;
continue; }
394 return std::max(x, 7ull);
Definition hash_table.h:120
Definition hash_table.h:86
Definition hash_table.h:35