C++: resolve ambiguous naming
[helenos.git] / uspace / lib / cpp / include / __bits / adt / hash_table.hpp
blob173c116a11f6aaf5a94ad2823e5b16e2043a6dc4
1 /*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #ifndef LIBCPP_BITS_ADT_HASH_TABLE
30 #define LIBCPP_BITS_ADT_HASH_TABLE
32 #include <__bits/adt/list_node.hpp>
33 #include <__bits/adt/key_extractors.hpp>
34 #include <__bits/adt/hash_table_iterators.hpp>
35 #include <__bits/adt/hash_table_policies.hpp>
36 #include <cstdlib>
37 #include <iterator>
38 #include <limits>
39 #include <memory>
40 #include <tuple>
41 #include <utility>
43 namespace std::aux
45 /**
46 * To save code, we're going to implement one hash table
47 * for both unordered_map and unordered_set. To do this,
48 * we create one inner hash table that is oblivious to its
49 * held type (and uses a key extractor to get the key from it)
50 * and two proxies that either use plain Key type or a pair
51 * of a key and a value.
52 * Additionally, we will use policies to represent both single
53 * and multi variants of these containers at once.
54 * Note: I am aware the naming is very unimaginative here,
55 * not my strong side :)
58 template<
59 class Value, class Key, class KeyExtractor,
60 class Hasher, class KeyEq,
61 class Alloc, class Size,
62 class Iterator, class ConstIterator,
63 class LocalIterator, class ConstLocalIterator,
64 class Policy
66 class hash_table
68 public:
69 using value_type = Value;
70 using key_type = Key;
71 using size_type = Size;
72 using allocator_type = Alloc;
73 using key_equal = KeyEq;
74 using hasher = Hasher;
75 using key_extract = KeyExtractor;
77 using iterator = Iterator;
78 using const_iterator = ConstIterator;
79 using local_iterator = LocalIterator;
80 using const_local_iterator = ConstLocalIterator;
82 using node_type = list_node<value_type>;
84 using place_type = tuple<
85 hash_table_bucket<value_type, size_type>*,
86 list_node<value_type>*, size_type
89 hash_table(size_type buckets, float max_load_factor = 1.f)
90 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
91 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
92 key_extractor_{}, max_load_factor_{max_load_factor}
93 { /* DUMMY BODY */ }
95 hash_table(size_type buckets, const hasher& hf, const key_equal& eql,
96 float max_load_factor = 1.f)
97 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
98 bucket_count_{buckets}, size_{}, hasher_{hf}, key_eq_{eql},
99 key_extractor_{}, max_load_factor_{max_load_factor}
100 { /* DUMMY BODY */ }
102 hash_table(const hash_table& other)
103 : hash_table{other.bucket_count_, other.hasher_, other.key_eq_,
104 other.max_load_factor_}
106 for (const auto& x: other)
107 insert(x);
110 hash_table(hash_table&& other)
111 : table_{other.table_}, bucket_count_{other.bucket_count_},
112 size_{other.size_}, hasher_{move(other.hasher_)},
113 key_eq_{move(other.key_eq_)}, key_extractor_{move(other.key_extractor_)},
114 max_load_factor_{other.max_load_factor_}
116 other.table_ = nullptr;
117 other.bucket_count_ = size_type{};
118 other.size_ = size_type{};
119 other.max_load_factor_ = 1.f;
122 hash_table& operator=(const hash_table& other)
124 hash_table tmp{other};
125 tmp.swap(*this);
127 return *this;
130 hash_table& operator=(hash_table&& other)
132 hash_table tmp{move(other)};
133 tmp.swap(*this);
135 return *this;
138 bool empty() const noexcept
140 return size_ == 0;
143 size_type size() const noexcept
145 return size_;
148 size_type max_size(allocator_type& alloc)
150 return allocator_traits<allocator_type>::max_size(alloc);
153 iterator begin() noexcept
155 auto idx = first_filled_bucket_();
156 return iterator{
157 table_, idx, bucket_count_,
158 table_[idx].head
162 const_iterator begin() const noexcept
164 return cbegin();
167 iterator end() noexcept
169 return iterator{};
172 const_iterator end() const noexcept
174 return cend();
177 const_iterator cbegin() const noexcept
179 auto idx = first_filled_bucket_();
180 return const_iterator{
181 table_, idx, bucket_count_,
182 table_[idx].head
186 const_iterator cend() const noexcept
188 return const_iterator{};
191 template<class... Args>
192 auto emplace(Args&&... args)
194 return Policy::emplace(*this, forward<Args>(args)...);
197 auto insert(const value_type& val)
199 return Policy::insert(*this, val);
202 auto insert(value_type&& val)
204 return Policy::insert(*this, forward<value_type>(val));
207 size_type erase(const key_type& key)
209 return Policy::erase(*this, key);
212 iterator erase(const_iterator it)
214 if (it == cend())
215 return end();
217 auto node = it.node();
218 auto idx = it.idx();
221 * Note: This way we will continue on the next bucket
222 * if this is the last element in its bucket.
224 iterator res{table_, idx, size_, node};
225 ++res;
227 if (table_[idx].head == node)
229 if (node->next != node)
230 table_[idx].head = node->next;
231 else
232 table_[idx].head = nullptr;
234 --size_;
236 node->unlink();
237 delete node;
239 if (empty())
240 return end();
241 else
242 return res;
245 void clear() noexcept
247 for (size_type i = 0; i < bucket_count_; ++i)
248 table_[i].clear();
249 size_ = size_type{};
252 void swap(hash_table& other)
253 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
254 noexcept(std::swap(declval<Hasher&>(), declval<Hasher&>())) &&
255 noexcept(std::swap(declval<KeyEq&>(), declval<KeyEq&>())))
257 std::swap(table_, other.table_);
258 std::swap(bucket_count_, other.bucket_count_);
259 std::swap(size_, other.size_);
260 std::swap(hasher_, other.hasher_);
261 std::swap(key_eq_, other.key_eq_);
262 std::swap(max_load_factor_, other.max_load_factor_);
265 hasher hash_function() const
267 return hasher_;
270 key_equal key_eq() const
272 return key_eq_;
275 iterator find(const key_type& key)
277 auto idx = get_bucket_idx_(key);
278 auto head = table_[idx].head;
280 if (!head)
281 return end();
283 auto current = head;
286 if (key_eq_(key, key_extractor_(current->value)))
287 return iterator{table_, idx, size_, current};
288 current = current->next;
290 while (current && current != head);
292 return end();
295 const_iterator find(const key_type& key) const
297 auto idx = get_bucket_idx_(key);
298 auto head = table_[idx].head;
300 if (!head)
301 return end();
303 auto current = head;
306 if (key_eq_(key, key_extractor_(current->value)))
307 return iterator{table_, idx, size_, current};
308 current = current->next;
310 while (current != head);
312 return end();
315 size_type count(const key_type& key) const
317 return Policy::count(*this, key);
320 pair<iterator, iterator> equal_range(const key_type& key)
322 return Policy::equal_range(*this, key);
325 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
327 return Policy::equal_range_const(*this, key);
330 size_type bucket_count() const noexcept
332 return bucket_count_;
335 size_type max_bucket_count() const noexcept
337 return numeric_limits<size_type>::max() /
338 sizeof(hash_table_bucket<value_type, size_type>);
341 size_type bucket_size(size_type n) const
343 return table_[n].size();
346 size_type bucket(const key_type& key) const
348 return get_bucket_idx_(key);
351 local_iterator begin(size_type n)
353 return local_iterator{table_[n].head, table_[n].head};
356 const_local_iterator begin(size_type n) const
358 return cbegin(n);
361 local_iterator end(size_type n)
363 return local_iterator{};
366 const_local_iterator end(size_type n) const
368 return cend(n);
371 const_local_iterator cbegin(size_type n) const
373 return const_local_iterator{table_[n].head, table_[n].head};
376 const_local_iterator cend(size_type n) const
378 return const_local_iterator{};
381 float load_factor() const noexcept
383 return size_ / static_cast<float>(bucket_count_);
386 float max_load_factor() const noexcept
388 return max_load_factor_;
391 void max_load_factor(float factor)
393 if (factor > 0.f)
394 max_load_factor_ = factor;
396 rehash_if_needed();
399 void rehash(size_type count)
401 if (count < size_ / max_load_factor_)
402 count = size_ / max_load_factor_;
405 * Note: If an exception is thrown, there
406 * is no effect. Since this is the only
407 * place where an exception (no mem) can
408 * be thrown and no changes to this have been
409 * made, we're ok.
411 hash_table new_table{count, max_load_factor_};
413 for (std::size_t i = 0; i < bucket_count_; ++i)
415 auto head = table_[i].head;
416 if (!head)
417 continue;
419 auto current = head;
423 auto next = current->next;
425 current->next = current;
426 current->prev = current;
428 auto where = Policy::find_insertion_spot(
429 new_table, key_extractor_(current->value)
433 * Note: We're rehashing, so we know each
434 * key can be inserted.
436 auto new_bucket = get<0>(where);
437 auto new_successor = get<1>(where);
439 if (new_successor)
440 new_successor->append(current);
441 else
442 new_bucket->append(current);
444 current = next;
445 } while (current != head);
447 table_[i].head = nullptr;
450 new_table.size_ = size_;
451 swap(new_table);
453 delete[] new_table.table_;
454 new_table.table_ = nullptr;
457 void reserve(size_type count)
459 rehash(count / max_load_factor_ + 1);
462 bool is_eq_to(const hash_table& other) const
464 if (size() != other.size())
465 return false;
467 auto it = begin();
468 while (it != end())
471 * For each key K we will check how many
472 * instances of K are there in the table.
473 * Then we will check if the count for K
474 * is equal to that amount.
477 size_type cnt{};
478 auto tmp = it;
480 while (key_eq_(key_extractor_(*it), key_extractor_(*tmp)))
482 ++cnt;
483 if (++tmp == end())
484 break;
487 auto other_cnt = other.count(key_extractor_(*it));
488 if (cnt != other_cnt)
489 return false;
491 it = tmp; // tmp is one past *it's key.
494 return true;
497 ~hash_table()
499 // Lists are deleted in ~hash_table_bucket.
500 if (table_)
501 delete[] table_;
504 place_type find_insertion_spot(const key_type& key) const
506 return Policy::find_insertion_spot(*this, key);
509 place_type find_insertion_spot(key_type&& key) const
511 return Policy::find_insertion_spot(*this, key);
514 const key_type& get_key(const value_type& val) const
516 return key_extractor_(val);
519 bool keys_equal(const key_type& key, const value_type& val)
521 return key_eq_(key, key_extractor_(val));
524 bool keys_equal(const key_type& key, const value_type& val) const
526 return key_eq_(key, key_extractor_(val));
529 hash_table_bucket<value_type, size_type>* table()
531 return table_;
534 hash_table_bucket<value_type, size_type>* head(size_type idx)
536 if (idx < bucket_count_)
537 return table_[idx]->head;
538 else
539 return nullptr;
542 void rehash_if_needed()
544 if (size_ > max_load_factor_ * bucket_count_)
545 rehash(bucket_count_ * bucket_count_growth_factor_);
548 void increment_size()
550 ++size_;
552 rehash_if_needed();
555 void decrement_size()
557 --size_;
560 private:
561 hash_table_bucket<value_type, size_type>* table_;
562 size_type bucket_count_;
563 size_type size_;
564 hasher hasher_;
565 key_equal key_eq_;
566 key_extract key_extractor_;
567 float max_load_factor_;
569 static constexpr float bucket_count_growth_factor_{1.25};
571 size_type get_bucket_idx_(const key_type& key) const
573 return hasher_(key) % bucket_count_;
576 size_type first_filled_bucket_() const
578 size_type res{};
579 while (res < bucket_count_)
581 if (table_[res].head)
582 return res;
583 ++res;
587 * Note: This is used for iterators,
588 * so we need to return a valid index.
589 * But since table_[0].head is nullptr
590 * we know that if we return 0 the
591 * created iterator will test as equal
592 * to end().
594 return 0;
597 friend Policy;
601 #endif