source: mainline/uspace/lib/cpp/include/internal/hash_table.hpp@ 947ad139

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

cpp: fixed hash_table::head and changed hint_type to a more descriptive typedef called place_type

  • Property mode set to 100644
File size: 42.9 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 <limits>
36#include <memory>
37#include <tuple>
38#include <utility>
39
40namespace std::aux
41{
42 /**
43 * To save code, we're going to implement one hash table
44 * for both unordered_map and unordered_set. To do this,
45 * we create one inner hash table that is oblivious to its
46 * held type (and uses a key extractor to get the key from it)
47 * and two proxies that either use plain Key type or a pair
48 * of a key and a value.
49 * Note: I am aware the naming is very unimaginative here,
50 * not my strong side :)
51 */
52
53 template<class Key, class Value>
54 struct key_value_key_extractor
55 {
56 const Key& operator()(const pair<const Key, Value>& p) const noexcept
57 {
58 return p.first;
59 }
60 };
61
62 template<class Key>
63 struct key_no_value_key_extractor
64 {
65 Key& operator()(Key& k) const noexcept
66 {
67 return k;
68 }
69
70 const Key& operator()(const Key& k) const noexcept
71 {
72 return k;
73 }
74 };
75
76 template<class Value, class Size>
77 struct hash_table_bucket
78 {
79 /**
80 * Note: We use a doubly linked list because
81 * we need to use hints, which point to the
82 * element after the hinted spot.
83 */
84
85 list_node<Value>* head;
86
87 hash_table_bucket()
88 : head{}
89 { /* DUMMY BODY */ }
90
91 Size size() const noexcept
92 {
93 auto current = head;
94 Size res{};
95
96 do
97 {
98 ++res;
99 current = current->next;
100 }
101 while (current != head);
102
103 return res;
104 }
105
106 void append(list_node<Value>* node)
107 {
108 if (!head)
109 head = node;
110 else
111 head->append(node);
112 }
113
114 void prepend(list_node<Value>* node)
115 {
116 if (!head)
117 head = node;
118 else
119 head->prepend(node);
120 }
121
122 void clear()
123 {
124 if (!head)
125 return;
126
127 auto current = head;
128 do
129 {
130 auto tmp = current;
131 current = current->next;
132 delete tmp;
133 }
134 while (current != head);
135
136 head = nullptr;
137 }
138
139 ~hash_table_bucket()
140 {
141 clear();
142 }
143 };
144
145 struct hash_single_policy
146 {
147 template<class Table, class Key>
148 static typename Table::size_type count(const Table& table, const Key& key)
149 {
150 return table.find(key) == table.end() ? 0 : 1;
151 }
152
153 template<class Table, class Key>
154 static typename Table::place_type find_insertion_spot(const Table& table, const Key& key)
155 {
156 auto idx = table.get_bucket_idx_(key);
157 return make_tuple(
158 &table.table_[idx],
159 table.table_[idx].head,
160 idx
161 );
162 }
163
164 template<class Table, class Key>
165 static typename Table::size_type erase(Table& table, const Key& key)
166 {
167 auto idx = table.get_bucket_idx_(key);
168 auto head = table.table_[idx].head;
169 auto current = head;
170
171 do
172 {
173 if (table.keys_equal(key, current->value))
174 {
175 --table.size_;
176
177 if (current == head)
178 {
179 if (current->next != head)
180 table.table_[idx].head = current->next;
181 else
182 table.table_[idx].head = nullptr;
183 }
184
185 current->unlink();
186 delete current;
187
188 return 1;
189 }
190 else
191 current = current->next;
192 }
193 while (current != head);
194
195 return 0;
196 }
197
198 template<class Table, class Key>
199 static pair<
200 typename Table::iterator,
201 typename Table::iterator
202 > equal_range(Table& table, const Key& key)
203 {
204 auto it = table.find(key);
205 return make_pair(it, ++it);
206 }
207
208 template<class Table, class Key>
209 static pair<
210 typename Table::const_iterator,
211 typename Table::const_iterator
212 > equal_range_const(const Table& table, const Key& key)
213 { // Note: We cannot overload by return type, so we use a different name.
214 auto it = table.find(key);
215 return make_pair(it, ++it);
216 }
217
218 /**
219 * Note: We have to duplicate code for emplace, insert(const&)
220 * and insert(&&) here, because the node (which makes distinction
221 * between the arguments) is only created if the value isn't
222 * in the table already.
223 */
224
225 template<class Table, class... Args>
226 static pair<
227 typename Table::iterator, bool
228 > emplace(Table& table, Args&&... args)
229 {
230 using value_type = typename Table::value_type;
231 using node_type = typename Table::node_type;
232 using iterator = typename Table::iterator;
233
234 table.increment_size();
235
236 auto val = value_type{forward<Args>(args)...};
237 const auto& key = table.get_key(val);
238 auto [bucket, target, idx] = table.find_insertion_spot(key);
239
240 if (!bucket)
241 return make_pair(table.end(), false);
242
243 if (target && table.keys_equal(key, target->value))
244 {
245 table.decrement_size();
246
247 return make_pair(
248 iterator{
249 table.table(), idx, table.bucket_count(),
250 target
251 },
252 false
253 );
254 }
255 else
256 {
257 auto node = new node_type{move(val)};
258 bucket->prepend(node);
259
260 return make_pair(iterator{
261 table.table(), idx,
262 table.bucket_count(),
263 node
264 }, true);
265 }
266 }
267
268 template<class Table, class Value>
269 static pair<
270 typename Table::iterator, bool
271 > insert(Table& table, const Value& val)
272 {
273 using node_type = typename Table::node_type;
274 using iterator = typename Table::iterator;
275
276 table.increment_size();
277
278 const auto& key = table.get_key(val);
279 auto [bucket, target, idx] = table.find_insertion_spot(key);
280
281 if (!bucket)
282 return make_pair(table.end(), false);
283
284 if (target && table.keys_equal(key, target->value))
285 {
286 table.decrement_size();
287
288 return make_pair(
289 iterator{
290 table.table(), idx, table.bucket_count(),
291 target
292 },
293 false
294 );
295 }
296 else
297 {
298 auto node = new node_type{val};
299 bucket->prepend(node);
300
301 return make_pair(iterator{
302 table.table(), idx,
303 table.bucket_count(),
304 node
305 }, true);
306 }
307 }
308
309 template<class Table, class Value>
310 static pair<
311 typename Table::iterator, bool
312 > insert(Table& table, Value&& val)
313 {
314 using value_type = typename Table::value_type;
315 using node_type = typename Table::node_type;
316 using iterator = typename Table::iterator;
317
318 table.increment_size();
319
320 const auto& key = table.get_key(val);
321 auto [bucket, target, idx] = table.find_insertion_spot(key);
322
323 if (!bucket)
324 return make_pair(table.end(), false);
325
326 if (target && table.keys_equal(key, target->value))
327 {
328 table.decrement_size();
329
330 return make_pair(
331 iterator{
332 table.table(), idx, table.bucket_count(),
333 target
334 },
335 false
336 );
337 }
338 else
339 {
340 auto node = new node_type{forward<value_type>(val)};
341 bucket->prepend(node);
342
343 return make_pair(iterator{
344 table.table(), idx,
345 table.bucket_count(),
346 node
347 }, true);
348 }
349 }
350 };
351
352 struct hash_multi_policy
353 {
354 template<class Table, class Key>
355 static typename Table::size_type count(const Table& table, const Key& key)
356 {
357 auto head = table.table_[get_bucket_idx_(key)].head;
358 if (!head)
359 return 0;
360
361 auto current = head;
362 typename Table::size_type res = 0;
363 do
364 {
365 if (table.keys_equal(key, current->value))
366 ++res;
367
368 current = current->next;
369 }
370 while (current != head);
371
372 return res;
373 }
374
375 template<class Table, class Key>
376 static typename Table::place_type find_insertion_spot(const Table& table, const Key& key)
377 {
378 auto idx = table.get_bucket_idx_(key);
379 auto head = table.table_[idx].head;
380
381 if (head)
382 {
383 auto current = head;
384 do
385 {
386 if (table.keys_equal(key, current->value))
387 {
388 return make_tuple(
389 &table.table_[idx],
390 current,
391 idx
392 );
393 }
394
395 current = current->next;
396 } while (current != head);
397 }
398
399 return make_tuple(
400 &table.table_[idx],
401 table.table_[idx].head,
402 idx
403 );
404 }
405
406 template<class Table, class Key>
407 static typename Table::size_type erase(Table& table, const Key& key)
408 {
409 auto idx = table.get_bucket_idx_(key);
410 auto it = table.begin(it);
411 typename Table::size_type res{};
412
413 while (it != table.end(it))
414 {
415 if (table.keys_equal(key, *it))
416 {
417 while (table.keys_equal(key, *it))
418 {
419 auto node = it.node();
420 ++it;
421 ++res;
422
423 node.unlink();
424 delete node;
425 --table.size_;
426 }
427
428 // Elements with equal keys are next to each other.
429 return res;
430 }
431
432 ++it;
433 }
434
435 return res;
436 }
437
438 template<class Table, class Key>
439 static pair<
440 typename Table::iterator,
441 typename Table::iterator
442 > equal_range(Table& table, const Key& key)
443 {
444 auto first = table.find(key);
445 if (first == table.end())
446 return make_pair(table.end(), table.end());
447
448 auto last = first;
449 do
450 {
451 ++last;
452 } while (table.keys_equal(key, *last));
453
454 return make_pair(first, last);
455 }
456
457 template<class Table, class Key>
458 static pair<
459 typename Table::const_iterator,
460 typename Table::const_iterator
461 > equal_range_const(const Table& table, const Key& key)
462 {
463 auto first = table.find(key);
464 if (first == table.end())
465 return make_pair(table.end(), table.end());
466
467 auto last = first;
468 do
469 {
470 ++last;
471 } while (table.keys_equal(key, *last));
472
473 return make_pair(first, last);
474 }
475
476 template<class Table, class... Args>
477 static pair<
478 typename Table::iterator, bool
479 > emplace(Table& table, Args&&... args)
480 {
481 using node_type = typename Table::node_type;
482
483 auto node = new node_type{forward<Args>(args)...};
484
485 return insert(table, node);
486 }
487
488 template<class Table, class Value>
489 static pair<
490 typename Table::iterator, bool
491 > insert(Table& table, const Value& val)
492 {
493 using node_type = typename Table::node_type;
494
495 auto node = new node_type{val};
496
497 return insert(table, node);
498 }
499
500 template<class Table, class Value>
501 static pair<
502 typename Table::iterator, bool
503 > insert(Table& table, Value&& val)
504 {
505 using value_type = typename Table::value_type;
506 using node_type = typename Table::node_type;
507
508 auto node = new node_type{forward<value_type>(val)};
509
510 return insert(table, node);
511 }
512
513 template<class Table>
514 static pair<
515 typename Table::iterator, bool
516 > insert(Table& table, typename Table::node_type* node)
517 {
518 using iterator = typename Table::iterator;
519
520 table.increment_size();
521
522 const auto& key = table.get_key(node->value);
523 auto [bucket, target, idx] = table.find_insertion_spot(key);
524
525 if (!bucket)
526 return make_pair(table.end(), false);
527
528 if (target && table.keys_equal(key, target->value))
529 target->append(node);
530 else
531 bucket->prepend(node);
532
533 return make_pair(iterator{
534 table.table(), idx,
535 table.bucket_count(),
536 node
537 }, true);
538 }
539 };
540
541 template<class Value, class ConstReference, class ConstPointer, class Size>
542 class hash_table_const_iterator
543 {
544 public:
545 using value_type = Value;
546 using size_type = Size;
547 using const_reference = ConstReference;
548 using const_pointer = ConstPointer;
549 using difference_type = ptrdiff_t;
550
551 using iterator_category = forward_iterator_tag;
552
553 hash_table_const_iterator(const hash_table_bucket<value_type, size_type>* table = nullptr,
554 size_type idx = size_type{}, size_type max_idx = size_type{},
555 const list_node<value_type>* current = nullptr)
556 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
557 { /* DUMMY BODY */ }
558
559 hash_table_const_iterator(const hash_table_const_iterator&) = default;
560 hash_table_const_iterator& operator=(const hash_table_const_iterator&) = default;
561
562 const_reference operator*() const
563 {
564 return current_->value;
565 }
566
567 const_pointer operator->() const
568 {
569 return &current_->value;
570 }
571
572 hash_table_const_iterator& operator++()
573 {
574 current_ = current_->next;
575 if (current_ == table_[idx_].head)
576 {
577 if (idx_ < max_idx_)
578 {
579 while (!table_[++idx_].head)
580 { /* DUMMY BODY */ }
581
582 if (idx_ < max_idx_)
583 current_ = table_[idx_].head;
584 else
585 current_ = nullptr;
586 }
587 else
588 current_ = nullptr;
589 }
590
591 return *this;
592 }
593
594 hash_table_const_iterator operator++(int)
595 {
596 auto tmp_current = current_;
597 auto tmp_idx = idx_;
598
599 current_ = current_->next;
600 if (current_ == table_[idx_].head)
601 {
602 if (idx_ < max_idx_)
603 {
604 while (!table_[++idx_].head)
605 { /* DUMMY BODY */ }
606
607 if (idx_ < max_idx_)
608 current_ = table_[idx_].head;
609 else
610 current_ = nullptr;
611 }
612 else
613 current_ = nullptr;
614 }
615
616 return hash_table_const_iterator{
617 table_, tmp_idx, max_idx_, tmp_current
618 };
619 }
620
621 list_node<value_type>* node()
622 {
623 return const_cast<list_node<value_type>*>(current_);
624 }
625
626 const list_node<value_type>* node() const
627 {
628 return current_;
629 }
630
631 size_type idx() const
632 {
633 return idx_;
634 }
635
636 private:
637 const hash_table_bucket<value_type, size_type>* table_;
638 size_type idx_;
639 size_type max_idx_;
640 const list_node<value_type>* current_;
641 };
642
643 template<class Value, class ConstRef, class ConstPtr, class Size>
644 bool operator==(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
645 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
646 {
647 return lhs.node() == rhs.node();
648 }
649
650 template<class Value, class ConstRef, class ConstPtr, class Size>
651 bool operator!=(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
652 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
653 {
654 return !(lhs == rhs);
655 }
656
657 template<class Value, class Reference, class Pointer, class Size>
658 class hash_table_iterator
659 {
660 public:
661 using value_type = Value;
662 using size_type = Size;
663 using reference = Reference;
664 using pointer = Pointer;
665 using difference_type = ptrdiff_t;
666
667 using iterator_category = forward_iterator_tag;
668
669 hash_table_iterator(hash_table_bucket<value_type, size_type>* table = nullptr,
670 size_type idx = size_type{}, size_type max_idx = size_type{},
671 list_node<value_type>* current = nullptr)
672 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
673 { /* DUMMY BODY */ }
674
675 hash_table_iterator(const hash_table_iterator&) = default;
676 hash_table_iterator& operator=(const hash_table_iterator&) = default;
677
678 reference operator*()
679 {
680 return current_->value;
681 }
682
683 pointer operator->()
684 {
685 return &current_->value;
686 }
687
688 hash_table_iterator& operator++()
689 {
690 current_ = current_->next;
691 if (current_ == table_[idx_].head)
692 {
693 if (idx_ < max_idx_)
694 {
695 while (!table_[++idx_].head)
696 { /* DUMMY BODY */ }
697
698 if (idx_ < max_idx_)
699 current_ = table_[idx_].head;
700 else
701 current_ = nullptr;
702 }
703 else
704 current_ = nullptr;
705 }
706
707 return *this;
708 }
709
710 hash_table_iterator operator++(int)
711 {
712 auto tmp_current = current_;
713 auto tmp_idx = idx_;
714
715 current_ = current_->next;
716 if (current_ == table_[idx_].head)
717 {
718 if (idx_ < max_idx_)
719 {
720 while (!table_[++idx_].head)
721 { /* DUMMY BODY */ }
722
723 if (idx_ < max_idx_)
724 current_ = table_[idx_].head;
725 else
726 current_ = nullptr;
727 }
728 else
729 current_ = nullptr;
730 }
731
732 return hash_table_iterator{
733 table_, tmp_idx, max_idx_, tmp_current
734 };
735 }
736
737 list_node<value_type>* node()
738 {
739 return current_;
740 }
741
742 const list_node<value_type>* node() const
743 {
744 return current_;
745 }
746
747 size_type idx() const
748 {
749 return idx_;
750 }
751
752 template<class ConstRef, class ConstPtr>
753 operator hash_table_const_iterator<
754 Value, ConstRef, ConstPtr, Size
755 >() const
756 {
757 return hash_table_const_iterator<value_type, ConstRef, ConstPtr, size_type>{
758 table_, idx_, max_idx_, current_
759 };
760 }
761
762 private:
763 hash_table_bucket<value_type, size_type>* table_;
764 size_type idx_;
765 size_type max_idx_;
766 list_node<value_type>* current_;
767 };
768
769 template<class Value, class Ref, class Ptr, class Size>
770 bool operator==(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
771 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
772 {
773 return lhs.node() == rhs.node();
774 }
775
776 template<class Value, class Ref, class Ptr, class Size>
777 bool operator!=(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
778 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
779 {
780 return !(lhs == rhs);
781 }
782
783 template<class Value, class ConstReference, class ConstPointer>
784 class hash_table_const_local_iterator
785 {
786 public:
787 using value_type = Value;
788 using const_reference = ConstReference;
789 using const_pointer = ConstPointer;
790 using difference_type = ptrdiff_t;
791
792 using iterator_category = forward_iterator_tag;
793
794 // TODO: requirement for forward iterator is default constructibility, fix others!
795 hash_table_const_local_iterator(const list_node<value_type>* head = nullptr,
796 const list_node<value_type>* current = nullptr)
797 : head_{head}, current_{current}
798 { /* DUMMY BODY */ }
799
800 hash_table_const_local_iterator(const hash_table_const_local_iterator&) = default;
801 hash_table_const_local_iterator& operator=(const hash_table_const_local_iterator&) = default;
802
803 const_reference operator*() const
804 {
805 return current_->value;
806 }
807
808 const_pointer operator->() const
809 {
810 return &current_->value;
811 }
812
813 hash_table_const_local_iterator& operator++()
814 {
815 current_ = current_->next;
816 if (current_ == head_)
817 current_ = nullptr;
818
819 return *this;
820 }
821
822 hash_table_const_local_iterator operator++(int)
823 {
824 auto tmp = current_;
825 current_ = current_->next;
826 if (current_ == head_)
827 current_ = nullptr;
828
829 return hash_table_const_local_iterator{head_, tmp};
830 }
831
832
833 list_node<value_type>* node()
834 {
835 return const_cast<list_node<value_type>*>(current_);
836 }
837
838 const list_node<value_type>* node() const
839 {
840 return current_;
841 }
842
843 private:
844 const list_node<value_type>* head_;
845 const list_node<value_type>* current_;
846 };
847
848 template<class Value, class ConstRef, class ConstPtr>
849 bool operator==(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
850 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
851 {
852 return lhs.node() == rhs.node();
853 }
854
855 template<class Value, class ConstRef, class ConstPtr>
856 bool operator!=(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
857 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
858 {
859 return !(lhs == rhs);
860 }
861
862 template<class Value, class Reference, class Pointer>
863 class hash_table_local_iterator
864 {
865 public:
866 using value_type = Value;
867 using reference = Reference;
868 using pointer = Pointer;
869 using difference_type = ptrdiff_t;
870
871 using iterator_category = forward_iterator_tag;
872
873 hash_table_local_iterator(list_node<value_type>* head = nullptr,
874 list_node<value_type>* current = nullptr)
875 : head_{head}, current_{current}
876 { /* DUMMY BODY */ }
877
878 hash_table_local_iterator(const hash_table_local_iterator&) = default;
879 hash_table_local_iterator& operator=(const hash_table_local_iterator&) = default;
880
881 reference operator*()
882 {
883 return current_->value;
884 }
885
886 pointer operator->()
887 {
888 return &current_->value;
889 }
890
891 hash_table_local_iterator& operator++()
892 {
893 current_ = current_->next;
894 if (current_ == head_)
895 current_ = nullptr;
896
897 return *this;
898 }
899
900 hash_table_local_iterator operator++(int)
901 {
902 auto tmp = current_;
903 current_ = current_->next;
904 if (current_ == head_)
905 current_ = nullptr;
906
907 return hash_table_local_iterator{head_, tmp};
908 }
909
910 list_node<value_type>* node()
911 {
912 return current_;
913 }
914
915 const list_node<value_type>* node() const
916 {
917 return current_;
918 }
919
920 template<class ConstRef, class ConstPtr>
921 operator hash_table_const_local_iterator<
922 Value, ConstRef, ConstPtr
923 >() const
924 {
925 return hash_table_const_local_iterator<
926 value_type, ConstRef, ConstPtr
927 >{head_, current_};
928 }
929
930 private:
931 list_node<value_type>* head_;
932 list_node<value_type>* current_;
933 };
934
935 template<class Value, class Ref, class Ptr>
936 bool operator==(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
937 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
938 {
939 return lhs.node() == rhs.node();
940 }
941
942 template<class Value, class Ref, class Ptr>
943 bool operator!=(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
944 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
945 {
946 return !(lhs == rhs);
947 }
948
949 template<
950 class Value, class Key, class KeyExtractor,
951 class Hasher, class KeyEq,
952 class Alloc, class Size,
953 class Iterator, class ConstIterator,
954 class LocalIterator, class ConstLocalIterator,
955 class Policy
956 >
957 class hash_table
958 {
959 public:
960 using value_type = Value;
961 using key_type = Key;
962 using size_type = Size;
963 using allocator_type = Alloc;
964 using key_equal = KeyEq;
965 using hasher = Hasher;
966 using key_extract = KeyExtractor;
967
968 using iterator = Iterator;
969 using const_iterator = ConstIterator;
970 using local_iterator = LocalIterator;
971 using const_local_iterator = ConstLocalIterator;
972
973 using node_type = list_node<value_type>;
974
975 using place_type = tuple<
976 hash_table_bucket<value_type, size_type>*,
977 list_node<value_type>*, size_type
978 >;
979
980 hash_table(size_type buckets, float max_load_factor = 1.f)
981 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
982 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
983 key_extractor_{}, max_load_factor_{max_load_factor}
984 { /* DUMMY BODY */ }
985
986 hash_table(size_type buckets, const hasher& hf, const key_equal& eql,
987 float max_load_factor = 1.f)
988 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
989 bucket_count_{buckets}, size_{}, hasher_{hf}, key_eq_{eql},
990 key_extractor_{}, max_load_factor_{max_load_factor}
991 { /* DUMMY BODY */ }
992
993 hash_table(const hash_table& other)
994 : hash_table{other.bucket_count_, other.hasher_, other.key_eq_,
995 other.max_load_factor_}
996 {
997 for (const auto& x: other)
998 {
999 auto spot = find_insertion_spot(key_extractor_(x));
1000 insert(spot, x);
1001 }
1002 }
1003
1004 hash_table(hash_table&& other)
1005 : table_{other.table_}, bucket_count_{other.bucket_count_},
1006 size_{other.size_}, hasher_{move(other.hasher_)},
1007 key_eq_{move(other.key_eq_)}, key_extractor_{move(other.key_extractor_)},
1008 max_load_factor_{other.max_load_factor_}
1009 {
1010 other.table_ = nullptr;
1011 other.bucket_count_ = size_type{};
1012 other.size_ = size_type{};
1013 other.max_load_factor_ = 1.f;
1014 }
1015
1016 hash_table& operator=(const hash_table& other)
1017 {
1018 hash_table tmp{other};
1019 tmp.swap(*this);
1020
1021 return *this;
1022 }
1023
1024 hash_table& operator=(hash_table&& other)
1025 {
1026 hash_table tmp{move(other)};
1027 tmp.swap(*this);
1028
1029 return *this;
1030 }
1031
1032 bool empty() const noexcept
1033 {
1034 return size_ == 0;
1035 }
1036
1037 size_type size() const noexcept
1038 {
1039 return size_;
1040 }
1041
1042 template<class Allocator>
1043 size_type max_size(Allocator& alloc)
1044 {
1045 return allocator_traits<Allocator>::max_size(alloc);
1046 }
1047
1048 iterator begin() noexcept
1049 {
1050 return iterator{
1051 table_, size_type{}, bucket_count_,
1052 table_[0].head
1053 };
1054 }
1055
1056 const_iterator begin() const noexcept
1057 {
1058 return cbegin();
1059 }
1060
1061 iterator end() noexcept
1062 {
1063 return iterator{};
1064 }
1065
1066 const_iterator end() const noexcept
1067 {
1068 return cend();
1069 }
1070
1071 const_iterator cbegin() const noexcept
1072 {
1073 return const_iterator{
1074 table_, size_type{}, bucket_count_,
1075 table_[0].head
1076 };
1077 }
1078
1079 const_iterator cend() const noexcept
1080 {
1081 return const_iterator{};
1082 }
1083
1084 template<class... Args>
1085 pair<iterator, bool> emplace(Args&&... args)
1086 {
1087 return Policy::emplace(*this, forward<Args>(args)...);
1088 }
1089
1090 pair<iterator, bool> insert(const value_type& val)
1091 {
1092 return Policy::insert(*this, val);
1093 }
1094
1095 pair<iterator, bool> insert(value_type&& val)
1096 {
1097 return Policy::insert(*this, forward<value_type>(val));
1098 }
1099
1100 size_type erase(const key_type& key)
1101 {
1102 return Policy::erase(*this, key);
1103 }
1104
1105 iterator erase(const_iterator it)
1106 {
1107 auto node = it.node();
1108 auto idx = it.idx();
1109
1110 /**
1111 * Note: This way we will continue on the next bucket
1112 * if this is the last element in its bucket.
1113 */
1114 iterator res{table_, idx, size_, node};
1115 ++res;
1116
1117 if (table_[idx].head == node)
1118 {
1119 if (node->next != node)
1120 table_[idx].head = node->next;
1121 else
1122 table_[idx].head = nullptr;
1123 }
1124
1125 node->unlink();
1126 delete node;
1127
1128 return res;
1129 }
1130
1131 void clear() noexcept
1132 {
1133 for (size_type i = 0; i < bucket_count_; ++i)
1134 table_[i].clear();
1135 size_ = size_type{};
1136 }
1137
1138 void swap(hash_table& other)
1139 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
1140 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
1141 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
1142 {
1143 std::swap(table_, other.table_);
1144 std::swap(bucket_count_, other.bucket_count_);
1145 std::swap(size_, other.size_);
1146 std::swap(hasher_, other.hasher_);
1147 std::swap(key_eq_, other.key_eq_);
1148 std::swap(max_load_factor_, other.max_load_factor_);
1149 }
1150
1151 hasher hash_function() const
1152 {
1153 return hasher_;
1154 }
1155
1156 key_equal key_eq() const
1157 {
1158 return key_eq_;
1159 }
1160
1161 iterator find(const key_type& key)
1162 {
1163 auto idx = get_bucket_idx_(key);
1164 auto head = table_[idx].head;
1165
1166 if (!head)
1167 return end();
1168
1169 auto current = head;
1170 do
1171 {
1172 if (key_eq_(key, key_extractor_(current->value)))
1173 return iterator{table_, idx, size_, current};
1174 current = current->next;
1175 }
1176 while (current != head);
1177
1178 return end();
1179 }
1180
1181 const_iterator find(const key_type& key) const
1182 {
1183 auto idx = get_bucket_idx_(key);
1184 auto head = table_[idx].head;
1185
1186 if (!head)
1187 return end();
1188
1189 auto current = head;
1190 do
1191 {
1192 if (key_eq_(key, key_extractor_(current->value)))
1193 return iterator{table_, idx, size_, current};
1194 current = current->next;
1195 }
1196 while (current != head);
1197
1198 return end();
1199 }
1200
1201 size_type count(const key_type& key) const
1202 {
1203 return Policy::count(*this, key);
1204 }
1205
1206 pair<iterator, iterator> equal_range(const key_type& key)
1207 {
1208 return Policy::equal_range(*this, key);
1209 }
1210
1211 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
1212 {
1213 return Policy::equal_range_const(*this, key);
1214 }
1215
1216 size_type bucket_count() const noexcept
1217 {
1218 return bucket_count_;
1219 }
1220
1221 size_type max_bucket_count() const noexcept
1222 {
1223 return numeric_limits<size_type>::max() /
1224 sizeof(hash_table_bucket<value_type, size_type>);
1225 }
1226
1227 size_type bucket_size(size_type n) const
1228 {
1229 return table_[n].size();
1230 }
1231
1232 size_type bucket(const key_type& key) const
1233 {
1234 return get_bucket_idx_(key);
1235 }
1236
1237 local_iterator begin(size_type n)
1238 {
1239 return local_iterator{table_[n].head, table_[n].head};
1240 }
1241
1242 const_local_iterator begin(size_type n) const
1243 {
1244 return cbegin(n);
1245 }
1246
1247 local_iterator end(size_type n)
1248 {
1249 return local_iterator{};
1250 }
1251
1252 const_local_iterator end(size_type n) const
1253 {
1254 return cend(n);
1255 }
1256
1257 const_local_iterator cbegin(size_type n) const
1258 {
1259 return const_local_iterator{table_[n].head, table_[n].head};
1260 }
1261
1262 const_local_iterator cend(size_type n) const
1263 {
1264 return const_local_iterator{};
1265 }
1266
1267 float load_factor() const noexcept
1268 {
1269 return size_ / static_cast<float>(bucket_count_);
1270 }
1271
1272 float max_load_factor() const noexcept
1273 {
1274 return max_load_factor_;
1275 }
1276
1277 void max_load_factor(float factor)
1278 {
1279 if (factor > 0.f)
1280 max_load_factor_ = factor;
1281
1282 rehash_if_needed();
1283 }
1284
1285 void rehash(size_type count)
1286 {
1287 if (count < size_ / max_load_factor_)
1288 count = size_ / max_load_factor_;
1289
1290 /**
1291 * Note: If an exception is thrown, there
1292 * is no effect. Since this is the only
1293 * place where an exception (no mem) can
1294 * be thrown and no changes to this have been
1295 * made, we're ok.
1296 */
1297 hash_table new_table{count, max_load_factor_};
1298
1299 for (std::size_t i = 0; i < bucket_count_; ++i)
1300 {
1301 auto head = table_[i].head;
1302 if (!head)
1303 continue;
1304
1305 auto current = head;
1306
1307 do
1308 {
1309 auto next = current->next;
1310
1311 current->next = current;
1312 current->prev = current;
1313
1314 auto where = Policy::find_insertion_spot(
1315 new_table, key_extractor_(current->value)
1316 );
1317
1318 /**
1319 * Note: We're rehashing, so we know each
1320 * key can be inserted.
1321 */
1322 auto new_bucket = get<0>(where);
1323 auto new_successor = get<1>(where);
1324
1325 if (new_successor)
1326 new_successor->append(current);
1327 else
1328 new_bucket->append(current);
1329
1330 current = next;
1331 } while (current != head);
1332
1333 table_[i].head = nullptr;
1334 }
1335
1336 new_table.size_ = size_;
1337 swap(new_table);
1338
1339 delete[] new_table.table_;
1340 new_table.table_ = nullptr;
1341 }
1342
1343 void reserve(size_type count)
1344 {
1345 rehash(count / max_load_factor_ + 1);
1346 }
1347
1348 bool is_eq_to(const hash_table& other) const
1349 {
1350 // TODO: implement
1351 return false;
1352 }
1353
1354 ~hash_table()
1355 {
1356 // Lists are deleted in ~hash_table_bucket.
1357 if (table_)
1358 delete[] table_;
1359 }
1360
1361 place_type find_insertion_spot(const key_type& key) const
1362 {
1363 return Policy::find_insertion_spot(*this, key);
1364 }
1365
1366 place_type find_insertion_spot(key_type&& key) const
1367 {
1368 return Policy::find_insertion_spot(*this, key);
1369 }
1370
1371 const key_type& get_key(const value_type& val) const
1372 {
1373 return key_extractor_(val);
1374 }
1375
1376 bool keys_equal(const key_type& key, const value_type& val)
1377 {
1378 return key_eq_(key, key_extractor_(val));
1379 }
1380
1381 bool keys_equal(const key_type& key, const value_type& val) const
1382 {
1383 return key_eq_(key, key_extractor_(val));
1384 }
1385
1386 hash_table_bucket<value_type, size_type>* table()
1387 {
1388 return table_;
1389 }
1390
1391 hash_table_bucket<value_type, size_type>* head(size_type idx)
1392 {
1393 if (idx < bucket_count_)
1394 return table_[idx]->head;
1395 else
1396 return nullptr;
1397 }
1398
1399 void rehash_if_needed()
1400 {
1401 if (size_ > max_load_factor_ * bucket_count_)
1402 rehash(bucket_count_ * bucket_count_growth_factor_);
1403 }
1404
1405 void increment_size()
1406 {
1407 ++size_;
1408
1409 rehash_if_needed();
1410 }
1411
1412 void decrement_size()
1413 {
1414 --size_;
1415 }
1416
1417 private:
1418 hash_table_bucket<value_type, size_type>* table_;
1419 size_type bucket_count_;
1420 size_type size_;
1421 hasher hasher_;
1422 key_equal key_eq_;
1423 key_extract key_extractor_;
1424 float max_load_factor_;
1425
1426 static constexpr float bucket_count_growth_factor_{1.25};
1427
1428 size_type get_bucket_idx_(const key_type& key) const
1429 {
1430 return hasher_(key) % bucket_count_;
1431 }
1432
1433 friend Policy;
1434 };
1435}
1436
1437#endif
Note: See TracBrowser for help on using the repository browser.