source: mainline/uspace/lib/cpp/include/internal/hash_table.hpp@ 3be3752

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 3be3752 was 3be3752, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: added aux functions and rehashing to insertion when needed

  • Property mode set to 100644
File size: 35.5 KB
Line 
1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
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.
16 *
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.
27 */
28
29#ifndef LIBCPP_INTERNAL_HASH_TABLE
30#define LIBCPP_INTERNAL_HASH_TABLE
31
32#include <cstdlib>
33#include <internal/list.hpp>
34#include <iterator>
35#include <memory>
36#include <tuple>
37#include <utility>
38
39namespace std::aux
40{
41 /**
42 * To save code, we're going to implement one hash table
43 * for both unordered_map and unordered_set. To do this,
44 * we create one inner hash table that is oblivious to its
45 * held type (and uses a key extractor to get the key from it)
46 * and two proxies that either use plain Key type or a pair
47 * of a key and a value.
48 * Note: I am aware the naming is very unimaginative here,
49 * not my strong side :)
50 */
51
52 template<class Key, class Value>
53 struct key_value_key_extractor
54 {
55 const Key& operator()(const pair<const Key, Value>& p) const noexcept
56 {
57 return p.first;
58 }
59 };
60
61 template<class Key>
62 struct key_no_value_key_extractor
63 {
64 Key& operator()(Key& k) const noexcept
65 {
66 return k;
67 }
68
69 const Key& operator()(const Key& k) const noexcept
70 {
71 return k;
72 }
73 };
74
75 template<class Value, class Size>
76 struct hash_table_bucket
77 {
78 /**
79 * Note: We use a doubly linked list because
80 * we need to use hints, which point to the
81 * element after the hinted spot.
82 */
83
84 list_node<Value>* head;
85
86 hash_table_bucket()
87 : head{}
88 { /* DUMMY BODY */ }
89
90 Size size() const noexcept
91 {
92 auto current = head;
93 Size res{};
94
95 do
96 {
97 ++res;
98 current = current->next;
99 }
100 while (current != head);
101
102 return res;
103 }
104
105 void append(list_node<Value>* node)
106 {
107 if (!head)
108 head = node;
109 else
110 head->append(node);
111 }
112
113 void clear()
114 {
115 if (!head)
116 return;
117
118 auto current = head;
119 do
120 {
121 auto tmp = current;
122 current = current->next;
123 delete tmp;
124 }
125 while (current != head);
126
127 head = nullptr;
128 }
129
130 ~hash_table_bucket()
131 {
132 clear();
133 }
134 };
135
136 struct hash_single_policy
137 {
138 // TODO: umap/uset operations
139
140 template<class Table, class Key>
141 static typename Table::size_type count(const Table& table, const Key& key)
142 {
143 return table.find(key) == table.end() ? 0 : 1;
144 }
145
146 template<class Table, class Key>
147 static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key)
148 {
149 auto idx = table.get_bucket_idx_(key);
150 return make_tuple(
151 &table.table_[idx],
152 table.table_[idx].head,
153 idx
154 );
155 }
156
157 template<class Table, class Key>
158 static typename Table::size_type erase(Table& table, const Key& key)
159 {
160 auto idx = table.get_bucket_idx_(key);
161 auto head = table.table_[idx].head;
162 auto current = head;
163
164 do
165 {
166 if (table.key_eq_(key, table.key_extractor_(current->value)))
167 {
168 --table.size_;
169
170 if (current == head)
171 {
172 if (current->next != head)
173 table.table_[idx].head = current->next;
174 else
175 table.table_[idx].head = nullptr;
176 }
177
178 current->unlink();
179 delete current;
180
181 return 1;
182 }
183 else
184 current = current->next;
185 }
186 while (current != head);
187
188 return 0;
189 }
190
191 template<class Table, class Key>
192 static pair<
193 typename Table::iterator,
194 typename Table::iterator
195 > equal_range(Table& table, const Key& key)
196 {
197 auto it = table.find(key);
198 return make_pair(it, ++it);
199 }
200
201 template<class Table, class Key>
202 static pair<
203 typename Table::const_iterator,
204 typename Table::const_iterator
205 > equal_range_const(Table& table, const Key& key)
206 { // Note: We cannot overload by return type, so we use a different name.
207 auto it = table.find(key);
208 return make_pair(it, ++it);
209 }
210 };
211
212 struct hash_multi_policy
213 {
214 // TODO: umultimap/umultiset operations
215
216 template<class Table, class Key>
217 static typename Table::size_type count(const Table& table, const Key& key)
218 {
219 auto head = table.table_[get_bucket_idx_(key)].head;
220 if (!head)
221 return 0;
222
223 auto current = head;
224 typename Table::size_type res = 0;
225 do
226 {
227 if (table.key_eq_(key, table.key_extractor_(current->value)))
228 ++res;
229
230 current = current->next;
231 }
232 while (current != head);
233
234 return res;
235 }
236
237 template<class Table, class Key>
238 static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key)
239 {
240 // TODO: find the right bucket, in that, find the right
241 // link (if key is already in it, place the new copy
242 // next to it, otherwise just return head)
243 }
244
245 template<class Table, class Key>
246 static typename Table::size_type erase(Table& table, const Key& key)
247 {
248 // TODO: erase all items with given key
249 }
250
251 template<class Table, class Key>
252 static pair<
253 typename Table::iterator,
254 typename Table::iterator
255 > equal_range(Table& table, const Key& key)
256 {
257 // TODO: implement
258 }
259
260 template<class Table, class Key>
261 static pair<
262 typename Table::const_iterator,
263 typename Table::const_iterator
264 > equal_range_const(Table& table, const Key& key)
265 {
266 // TODO: implement
267 }
268 };
269
270 template<class Value, class ConstReference, class ConstPointer, class Size>
271 class hash_table_const_iterator
272 {
273 public:
274 using value_type = Value;
275 using size_type = Size;
276 using const_reference = ConstReference;
277 using const_pointer = ConstPointer;
278 using difference_type = ptrdiff_t;
279
280 using iterator_category = forward_iterator_tag;
281
282 hash_table_const_iterator(const hash_table_bucket<value_type, size_type>* table = nullptr,
283 size_type idx = size_type{}, size_type max_idx = size_type{},
284 const list_node<value_type>* current = nullptr)
285 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
286 { /* DUMMY BODY */ }
287
288 hash_table_const_iterator(const hash_table_const_iterator&) = default;
289 hash_table_const_iterator& operator=(const hash_table_const_iterator&) = default;
290
291 const_reference operator*() const
292 {
293 return current_->value;
294 }
295
296 const_pointer operator->() const
297 {
298 return &current_->value;
299 }
300
301 hash_table_const_iterator& operator++()
302 {
303 current_ = current_->next;
304 if (current_ == table_[idx_].head)
305 {
306 if (idx_ < max_idx_)
307 {
308 while (!table_[++idx_].head)
309 { /* DUMMY BODY */ }
310
311 if (idx_ < max_idx_)
312 current_ = table_[idx_].head;
313 else
314 current_ = nullptr;
315 }
316 else
317 current_ = nullptr;
318 }
319
320 return *this;
321 }
322
323 hash_table_const_iterator operator++(int)
324 {
325 auto tmp_current = current_;
326 auto tmp_idx = idx_;
327
328 current_ = current_->next;
329 if (current_ == table_[idx_].head)
330 {
331 if (idx_ < max_idx_)
332 {
333 while (!table_[++idx_].head)
334 { /* DUMMY BODY */ }
335
336 if (idx_ < max_idx_)
337 current_ = table_[idx_].head;
338 else
339 current_ = nullptr;
340 }
341 else
342 current_ = nullptr;
343 }
344
345 return hash_table_const_iterator{
346 table_, tmp_idx, max_idx_, tmp_current
347 };
348 }
349
350 list_node<value_type>* node()
351 {
352 return const_cast<list_node<value_type>*>(current_);
353 }
354
355 const list_node<value_type>* node() const
356 {
357 return current_;
358 }
359
360 size_type idx() const
361 {
362 return idx_;
363 }
364
365 private:
366 const hash_table_bucket<value_type, size_type>* table_;
367 size_type idx_;
368 size_type max_idx_;
369 const list_node<value_type>* current_;
370 };
371
372 template<class Value, class ConstRef, class ConstPtr, class Size>
373 bool operator==(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
374 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
375 {
376 return lhs.node() == rhs.node();
377 }
378
379 template<class Value, class ConstRef, class ConstPtr, class Size>
380 bool operator!=(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
381 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
382 {
383 return !(lhs == rhs);
384 }
385
386 template<class Value, class Reference, class Pointer, class Size>
387 class hash_table_iterator
388 {
389 public:
390 using value_type = Value;
391 using size_type = Size;
392 using reference = Reference;
393 using pointer = Pointer;
394 using difference_type = ptrdiff_t;
395
396 using iterator_category = forward_iterator_tag;
397
398 hash_table_iterator(hash_table_bucket<value_type, size_type>* table = nullptr,
399 size_type idx = size_type{}, size_type max_idx = size_type{},
400 list_node<value_type>* current = nullptr)
401 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
402 { /* DUMMY BODY */ }
403
404 hash_table_iterator(const hash_table_iterator&) = default;
405 hash_table_iterator& operator=(const hash_table_iterator&) = default;
406
407 reference operator*()
408 {
409 return current_->value;
410 }
411
412 pointer operator->()
413 {
414 return &current_->value;
415 }
416
417 hash_table_iterator& operator++()
418 {
419 current_ = current_->next;
420 if (current_ == table_[idx_].head)
421 {
422 if (idx_ < max_idx_)
423 {
424 while (!table_[++idx_].head)
425 { /* DUMMY BODY */ }
426
427 if (idx_ < max_idx_)
428 current_ = table_[idx_].head;
429 else
430 current_ = nullptr;
431 }
432 else
433 current_ = nullptr;
434 }
435
436 return *this;
437 }
438
439 hash_table_iterator operator++(int)
440 {
441 auto tmp_current = current_;
442 auto tmp_idx = idx_;
443
444 current_ = current_->next;
445 if (current_ == table_[idx_].head)
446 {
447 if (idx_ < max_idx_)
448 {
449 while (!table_[++idx_].head)
450 { /* DUMMY BODY */ }
451
452 if (idx_ < max_idx_)
453 current_ = table_[idx_].head;
454 else
455 current_ = nullptr;
456 }
457 else
458 current_ = nullptr;
459 }
460
461 return hash_table_iterator{
462 table_, tmp_idx, max_idx_, tmp_current
463 };
464 }
465
466 list_node<value_type>* node()
467 {
468 return current_;
469 }
470
471 const list_node<value_type>* node() const
472 {
473 return current_;
474 }
475
476 size_type idx() const
477 {
478 return idx_;
479 }
480
481 template<class ConstRef, class ConstPtr>
482 operator hash_table_const_iterator<
483 Value, ConstRef, ConstPtr, Size
484 >() const
485 {
486 return hash_table_const_iterator<value_type, ConstRef, ConstPtr, size_type>{
487 table_, idx_, max_idx_, current_
488 };
489 }
490
491 private:
492 hash_table_bucket<value_type, size_type>* table_;
493 size_type idx_;
494 size_type max_idx_;
495 list_node<value_type>* current_;
496 };
497
498 template<class Value, class Ref, class Ptr, class Size>
499 bool operator==(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
500 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
501 {
502 return lhs.node() == rhs.node();
503 }
504
505 template<class Value, class Ref, class Ptr, class Size>
506 bool operator!=(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
507 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
508 {
509 return !(lhs == rhs);
510 }
511
512 template<class Value, class ConstReference, class ConstPointer>
513 class hash_table_const_local_iterator
514 {
515 public:
516 using value_type = Value;
517 using const_reference = ConstReference;
518 using const_pointer = ConstPointer;
519 using difference_type = ptrdiff_t;
520
521 using iterator_category = forward_iterator_tag;
522
523 // TODO: requirement for forward iterator is default constructibility, fix others!
524 hash_table_const_local_iterator(const list_node<value_type>* head = nullptr,
525 const list_node<value_type>* current = nullptr)
526 : head_{head}, current_{current}
527 { /* DUMMY BODY */ }
528
529 hash_table_const_local_iterator(const hash_table_const_local_iterator&) = default;
530 hash_table_const_local_iterator& operator=(const hash_table_const_local_iterator&) = default;
531
532 const_reference operator*() const
533 {
534 return current_->value;
535 }
536
537 const_pointer operator->() const
538 {
539 return &current_->value;
540 }
541
542 hash_table_const_local_iterator& operator++()
543 {
544 current_ = current_->next;
545 if (current_ == head_)
546 current_ = nullptr;
547
548 return *this;
549 }
550
551 hash_table_const_local_iterator operator++(int)
552 {
553 auto tmp = current_;
554 current_ = current_->next;
555 if (current_ == head_)
556 current_ = nullptr;
557
558 return hash_table_const_local_iterator{head_, tmp};
559 }
560
561
562 list_node<value_type>* node()
563 {
564 return const_cast<list_node<value_type>*>(current_);
565 }
566
567 const list_node<value_type>* node() const
568 {
569 return current_;
570 }
571
572 private:
573 const list_node<value_type>* head_;
574 const list_node<value_type>* current_;
575 };
576
577 template<class Value, class ConstRef, class ConstPtr>
578 bool operator==(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
579 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
580 {
581 return lhs.node() == rhs.node();
582 }
583
584 template<class Value, class ConstRef, class ConstPtr>
585 bool operator!=(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
586 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
587 {
588 return !(lhs == rhs);
589 }
590
591 template<class Value, class Reference, class Pointer>
592 class hash_table_local_iterator
593 {
594 public:
595 using value_type = Value;
596 using reference = Reference;
597 using pointer = Pointer;
598 using difference_type = ptrdiff_t;
599
600 using iterator_category = forward_iterator_tag;
601
602 hash_table_local_iterator(list_node<value_type>* head = nullptr,
603 list_node<value_type>* current = nullptr)
604 : head_{head}, current_{current}
605 { /* DUMMY BODY */ }
606
607 hash_table_local_iterator(const hash_table_local_iterator&) = default;
608 hash_table_local_iterator& operator=(const hash_table_local_iterator&) = default;
609
610 reference operator*()
611 {
612 return current_->value;
613 }
614
615 pointer operator->()
616 {
617 return &current_->value;
618 }
619
620 hash_table_local_iterator& operator++()
621 {
622 current_ = current_->next;
623 if (current_ == head_)
624 current_ = nullptr;
625
626 return *this;
627 }
628
629 hash_table_local_iterator operator++(int)
630 {
631 auto tmp = current_;
632 current_ = current_->next;
633 if (current_ == head_)
634 current_ = nullptr;
635
636 return hash_table_local_iterator{head_, tmp};
637 }
638
639 list_node<value_type>* node()
640 {
641 return current_;
642 }
643
644 const list_node<value_type>* node() const
645 {
646 return current_;
647 }
648
649 template<class ConstRef, class ConstPtr>
650 operator hash_table_const_local_iterator<
651 Value, ConstRef, ConstPtr
652 >() const
653 {
654 return hash_table_const_local_iterator<
655 value_type, ConstRef, ConstPtr
656 >{head_, current_};
657 }
658
659 private:
660 list_node<value_type>* head_;
661 list_node<value_type>* current_;
662 };
663
664 template<class Value, class Ref, class Ptr>
665 bool operator==(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
666 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
667 {
668 return lhs.node() == rhs.node();
669 }
670
671 template<class Value, class Ref, class Ptr>
672 bool operator!=(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
673 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
674 {
675 return !(lhs == rhs);
676 }
677
678 template<
679 class Value, class Key, class KeyExtractor,
680 class Hasher, class KeyEq,
681 class Alloc, class Size,
682 class Iterator, class ConstIterator,
683 class LocalIterator, class ConstLocalIterator,
684 class Policy
685 >
686 class hash_table
687 {
688 public:
689 using value_type = Value;
690 using key_type = Key;
691 using size_type = Size;
692 using allocator_type = Alloc;
693 using key_equal = KeyEq;
694 using hasher = Hasher;
695 using key_extract = KeyExtractor;
696
697 using iterator = Iterator;
698 using const_iterator = ConstIterator;
699 using local_iterator = LocalIterator;
700 using const_local_iterator = ConstLocalIterator;
701
702 using node_type = list_node<value_type>;
703
704 using hint_type = tuple<
705 hash_table_bucket<value_type, size_type>*,
706 list_node<value_type>*, size_type
707 >;
708
709 hash_table(size_type buckets, float max_load_factor = 1.f)
710 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
711 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
712 key_extractor_{}, max_load_factor_{max_load_factor}
713 { /* DUMMY BODY */ }
714
715 hash_table(size_type buckets, const hasher& hf, const key_equal& eql,
716 float max_load_factor = 1.f)
717 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
718 bucket_count_{buckets}, size_{}, hasher_{hf}, key_eq_{eql},
719 key_extractor_{}, max_load_factor_{max_load_factor}
720 { /* DUMMY BODY */ }
721
722 // TODO: copy/move constructor/assignment operator
723
724 bool empty() const noexcept
725 {
726 return size_ == 0;
727 }
728
729 size_type size() const noexcept
730 {
731 return size_;
732 }
733
734 template<class Allocator>
735 size_type max_size(Allocator& alloc)
736 {
737 return allocator_traits<Allocator>::max_size(alloc);
738 }
739
740 iterator begin() noexcept
741 {
742 return iterator{
743 table_, size_type{}, bucket_count_,
744 table_[0].head
745 };
746 }
747
748 const_iterator begin() const noexcept
749 {
750 return cbegin();
751 }
752
753 iterator end() noexcept
754 {
755 return iterator{};
756 }
757
758 const_iterator end() const noexcept
759 {
760 return cend();
761 }
762
763 const_iterator cbegin() const noexcept
764 {
765 return const_iterator{
766 table_, size_type{}, bucket_count_,
767 table_[0].head
768 };
769 }
770
771 const_iterator cend() const noexcept
772 {
773 return const_iterator{};
774 }
775
776 template<class Allocator, class... Args>
777 void emplace(const hint_type& where, Allocator& alloc, Args&&... args)
778 {
779 if (!hint_ok_(where))
780 return;
781
782 auto node = new list_node<value_type>{forward<Args&&>(args)...};
783 if (get<1>(where) == nullptr) // Append here will create a new head.
784 get<0>(where)->append(node);
785 else // Prepending before an exact position is common in the standard.
786 get<1>(where)->prepend(node);
787
788 ++size_;
789
790 rehash_if_needed();
791 }
792
793 void insert(const hint_type& where, const value_type& val)
794 {
795 if (!hint_ok_(where))
796 return;
797
798 auto node = new list_node<value_type>{val};
799 if (get<1>(where) == nullptr)
800 get<0>(where)->append(node);
801 else
802 get<1>(where)->prepend(node);
803
804 ++size_;
805
806 rehash_if_needed();
807 }
808
809 void insert(const hint_type& where, value_type&& val)
810 {
811 if (!hint_ok_(where))
812 return;
813
814 auto node = new list_node<value_type>{forward<value_type>(val)};
815 if (get<1>(where) == nullptr)
816 get<0>(where)->append(node);
817 else
818 get<1>(where)->prepend(node);
819
820 ++size_;
821
822 rehash_if_needed();
823 }
824
825 size_type erase(const key_type& key)
826 {
827 return Policy::erase(*this, key);
828 }
829
830 iterator erase(const_iterator it)
831 {
832 auto node = it.node();
833 auto idx = it.idx();
834
835 /**
836 * Note: This way we will continue on the next bucket
837 * if this is the last element in its bucket.
838 */
839 iterator res{table_, idx, size_, node};
840 ++res;
841
842 if (table_[idx].head == node)
843 {
844 if (node->next != node)
845 table_[idx].head = node->next;
846 else
847 table_[idx].head = nullptr;
848 }
849
850 node->unlink();
851 delete node;
852
853 return res;
854 }
855
856 void clear() noexcept
857 {
858 for (size_type i = 0; i < bucket_count_; ++i)
859 table_[i].clear();
860 size_ = size_type{};
861 }
862
863 void swap(hash_table& other)
864 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
865 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
866 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
867 {
868 std::swap(table_, other.table_);
869 std::swap(bucket_count_, other.bucket_count_);
870 std::swap(size_, other.size_);
871 std::swap(hasher_, other.hasher_);
872 std::swap(key_eq_, other.key_eq_);
873 std::swap(max_load_factor_, other.max_load_factor_);
874 }
875
876 hasher hash_function() const
877 {
878 return hasher_;
879 }
880
881 key_equal key_eq() const
882 {
883 return key_eq_;
884 }
885
886 iterator find(const key_type& key)
887 {
888 auto idx = get_bucket_idx_(key);
889 auto head = table_[idx].head;
890
891 if (!head)
892 return end();
893
894 auto current = head;
895 do
896 {
897 if (key_eq_(key, key_extractor_(current->value)))
898 return iterator{table_, idx, size_, current};
899 current = current->next;
900 }
901 while (current != head);
902
903 return end();
904 }
905
906 const_iterator find(const key_type& key) const
907 {
908 auto idx = get_bucket_idx_(key);
909 auto head = table_[idx].head;
910
911 if (!head)
912 return end();
913
914 auto current = head;
915 do
916 {
917 if (key_eq_(key, key_extractor_(current->value)))
918 return iterator{table_, idx, size_, current};
919 current = current->next;
920 }
921 while (current != head);
922
923 return end();
924 }
925
926 size_type count(const key_type& key) const
927 {
928 return Policy::count(*this, key);
929 }
930
931 pair<iterator, iterator> equal_range(const key_type& key)
932 {
933 return Policy::equal_range(*this, key);
934 }
935
936 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
937 {
938 return Policy::equal_range_const(*this, key);
939 }
940
941 size_type bucket_count() const noexcept
942 {
943 return bucket_count_;
944 }
945
946 size_type max_bucket_count() const noexcept
947 {
948 // TODO: implement
949 return 0;
950 }
951
952 size_type bucket_size(size_type n) const
953 {
954 return table_[n].size();
955 }
956
957 size_type bucket(const key_type& key) const
958 {
959 return get_bucket_idx_(key);
960 }
961
962 local_iterator begin(size_type n)
963 {
964 return local_iterator{table_[n].head, table_[n].head};
965 }
966
967 const_local_iterator begin(size_type n) const
968 {
969 return cbegin(n);
970 }
971
972 local_iterator end(size_type n)
973 {
974 return local_iterator{};
975 }
976
977 const_local_iterator end(size_type n) const
978 {
979 return cend(n);
980 }
981
982 const_local_iterator cbegin(size_type n) const
983 {
984 return const_local_iterator{table_[n].head, table_[n].head};
985 }
986
987 const_local_iterator cend(size_type n) const
988 {
989 return const_local_iterator{};
990 }
991
992 float load_factor() const noexcept
993 {
994 return size_ / static_cast<float>(bucket_count_);
995 }
996
997 float max_load_factor() const noexcept
998 {
999 return max_load_factor_;
1000 }
1001
1002 void max_load_factor(float factor)
1003 {
1004 /**
1005 * Note: According to the standard, this function
1006 * can have no effect.
1007 */
1008 // TODO: change max factor and rehash if needed
1009 }
1010
1011 void rehash(size_type count)
1012 {
1013 if (count < size_ / max_load_factor_)
1014 count = size_ / max_load_factor_;
1015
1016 // Note: This is the only place where an exception can be
1017 // thrown (no mem) in this function, so wrap it
1018 // in try-catch-rethrow.
1019 hash_table new_table{count, max_load_factor_};
1020
1021 for (std::size_t i = 0; i < bucket_count_; ++i)
1022 {
1023 auto head = table_[i].head;
1024 if (!head)
1025 continue;
1026
1027 auto current = head;
1028
1029 do
1030 {
1031 auto next = current->next;
1032
1033 current->next = current;
1034 current->prev = current;
1035
1036 auto where = Policy::find_insertion_spot(
1037 new_table, key_extractor_(current->value)
1038 );
1039
1040 /**
1041 * Note: We're rehashing, so we know each
1042 * key can be inserted.
1043 */
1044 auto new_bucket = get<0>(where);
1045 auto new_successor = get<1>(where);
1046
1047 if (new_successor)
1048 new_successor->append(current);
1049 else
1050 new_bucket->append(current);
1051
1052 current = next;
1053 } while (current != head);
1054
1055 table_[i].head = nullptr;
1056 }
1057
1058 new_table.size_ = size_;
1059 swap(new_table);
1060
1061 delete[] new_table.table_;
1062 new_table.table_ = nullptr;
1063 }
1064
1065 void reserve(size_type count)
1066 {
1067 rehash(count / max_load_factor_ + 1);
1068 }
1069
1070 bool is_eq_to(hash_table& other)
1071 {
1072 // TODO: implement
1073 return false;
1074 }
1075
1076 ~hash_table()
1077 {
1078 // Lists are deleted in ~hash_table_bucket.
1079 if (table_)
1080 delete[] table_;
1081 }
1082
1083 void set_hint(const_iterator hint)
1084 {
1085 // TODO: hint_ should be a ptr and we extract it here,
1086 // then set it to nullptr after each operation
1087 }
1088
1089 hint_type find_insertion_spot(const key_type& key)
1090 {
1091 return Policy::find_insertion_spot(*this, key);
1092 }
1093
1094 hint_type find_insertion_spot(key_type&& key)
1095 {
1096 return Policy::find_insertion_spot(*this, key);
1097 }
1098
1099 const key_type& get_key(const value_type& val)
1100 {
1101 return key_extractor_(val);
1102 }
1103
1104 bool keys_equal(const key_type& key, const value_type& val)
1105 {
1106 return key_eq_(key, key_extractor_(val));
1107 }
1108
1109 hash_table_bucket<value_type, size_type>* table()
1110 {
1111 return table_;
1112 }
1113
1114 hash_table_bucket<value_type, size_type>* head(size_type idx)
1115 {
1116 if (idx < bucket_count_)
1117 return &table_[idx];
1118 else
1119 return nullptr;
1120 }
1121
1122 void rehash_if_needed()
1123 {
1124 if (size_ > max_load_factor_ * bucket_count_)
1125 rehash(bucket_count_ * bucket_count_growth_factor_);
1126 }
1127
1128 void increment_size()
1129 {
1130 ++size_;
1131 }
1132
1133 private:
1134 hash_table_bucket<value_type, size_type>* table_;
1135 size_type bucket_count_;
1136 size_type size_;
1137 hasher hasher_;
1138 key_equal key_eq_;
1139 key_extract key_extractor_;
1140 float max_load_factor_;
1141
1142 static constexpr float bucket_count_growth_factor_{1.25};
1143
1144 size_type get_bucket_idx_(const key_type& key) const
1145 {
1146 return hasher_(key) % bucket_count_;
1147 }
1148
1149 bool hint_ok_(const hint_type& hint)
1150 {
1151 // TODO: pass this to the policy, because the multi policy
1152 // will need to check if a similar key is close,
1153 // that is something like:
1154 // return get<1>(hint)->prev->key == key || !bucket.contains(key)
1155 // TODO: also, make it public and make hint usage one level above?
1156 // (since we already have insert with decisive hint)
1157 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
1158 }
1159
1160 // Praise C++11 for this.
1161 friend Policy;
1162 };
1163}
1164
1165#endif
Note: See TracBrowser for help on using the repository browser.