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

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

cpp: removed unnecessary key_extractor_ uses, rendunant increments that can be avoided with do/while instead of while, added keys_equal for constant context

  • Property mode set to 100644
File size: 39.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 <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::hint_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 struct hash_multi_policy
220 {
221 template<class Table, class Key>
222 static typename Table::size_type count(const Table& table, const Key& key)
223 {
224 auto head = table.table_[get_bucket_idx_(key)].head;
225 if (!head)
226 return 0;
227
228 auto current = head;
229 typename Table::size_type res = 0;
230 do
231 {
232 if (table.keys_equal(key, current->value))
233 ++res;
234
235 current = current->next;
236 }
237 while (current != head);
238
239 return res;
240 }
241
242 template<class Table, class Key>
243 static typename Table::hint_type find_insertion_spot(const Table& table, const Key& key)
244 {
245 auto idx = table.get_bucket_idx_(key);
246 auto head = table.table_[idx].head;
247
248 if (head)
249 {
250 auto current = head;
251 do
252 {
253 if (table.keys_equal(key, current->value))
254 {
255 return make_tuple(
256 &table.table_[idx],
257 current,
258 idx
259 );
260 }
261
262 current = current->next;
263 } while (current != head);
264 }
265
266 return make_tuple(
267 &table.table_[idx],
268 table.table_[idx].head,
269 idx
270 );
271 }
272
273 template<class Table, class Key>
274 static typename Table::size_type erase(Table& table, const Key& key)
275 {
276 auto idx = table.get_bucket_idx_(key);
277 auto it = table.begin(it);
278 typename Table::size_type res{};
279
280 while (it != table.end(it))
281 {
282 if (table.keys_equal(key, *it))
283 {
284 while (table.keys_equal(key, *it))
285 {
286 auto node = it.node();
287 ++it;
288 ++res;
289
290 node.unlink();
291 delete node;
292 --table.size_;
293 }
294
295 // Elements with equal keys are next to each other.
296 return res;
297 }
298
299 ++it;
300 }
301
302 return res;
303 }
304
305 template<class Table, class Key>
306 static pair<
307 typename Table::iterator,
308 typename Table::iterator
309 > equal_range(Table& table, const Key& key)
310 {
311 auto first = table.find(key);
312 if (first == table.end())
313 return make_pair(table.end(), table.end());
314
315 auto last = first;
316 do
317 {
318 ++last;
319 } while (table.keys_equal(key, *last));
320
321 return make_pair(first, last);
322 }
323
324 template<class Table, class Key>
325 static pair<
326 typename Table::const_iterator,
327 typename Table::const_iterator
328 > equal_range_const(const Table& table, const Key& key)
329 {
330 auto first = table.find(key);
331 if (first == table.end())
332 return make_pair(table.end(), table.end());
333
334 auto last = first;
335 do
336 {
337 ++last;
338 } while (table.keys_equal(key, *last));
339
340 return make_pair(first, last);
341 }
342 };
343
344 template<class Value, class ConstReference, class ConstPointer, class Size>
345 class hash_table_const_iterator
346 {
347 public:
348 using value_type = Value;
349 using size_type = Size;
350 using const_reference = ConstReference;
351 using const_pointer = ConstPointer;
352 using difference_type = ptrdiff_t;
353
354 using iterator_category = forward_iterator_tag;
355
356 hash_table_const_iterator(const hash_table_bucket<value_type, size_type>* table = nullptr,
357 size_type idx = size_type{}, size_type max_idx = size_type{},
358 const list_node<value_type>* current = nullptr)
359 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
360 { /* DUMMY BODY */ }
361
362 hash_table_const_iterator(const hash_table_const_iterator&) = default;
363 hash_table_const_iterator& operator=(const hash_table_const_iterator&) = default;
364
365 const_reference operator*() const
366 {
367 return current_->value;
368 }
369
370 const_pointer operator->() const
371 {
372 return &current_->value;
373 }
374
375 hash_table_const_iterator& operator++()
376 {
377 current_ = current_->next;
378 if (current_ == table_[idx_].head)
379 {
380 if (idx_ < max_idx_)
381 {
382 while (!table_[++idx_].head)
383 { /* DUMMY BODY */ }
384
385 if (idx_ < max_idx_)
386 current_ = table_[idx_].head;
387 else
388 current_ = nullptr;
389 }
390 else
391 current_ = nullptr;
392 }
393
394 return *this;
395 }
396
397 hash_table_const_iterator operator++(int)
398 {
399 auto tmp_current = current_;
400 auto tmp_idx = idx_;
401
402 current_ = current_->next;
403 if (current_ == table_[idx_].head)
404 {
405 if (idx_ < max_idx_)
406 {
407 while (!table_[++idx_].head)
408 { /* DUMMY BODY */ }
409
410 if (idx_ < max_idx_)
411 current_ = table_[idx_].head;
412 else
413 current_ = nullptr;
414 }
415 else
416 current_ = nullptr;
417 }
418
419 return hash_table_const_iterator{
420 table_, tmp_idx, max_idx_, tmp_current
421 };
422 }
423
424 list_node<value_type>* node()
425 {
426 return const_cast<list_node<value_type>*>(current_);
427 }
428
429 const list_node<value_type>* node() const
430 {
431 return current_;
432 }
433
434 size_type idx() const
435 {
436 return idx_;
437 }
438
439 private:
440 const hash_table_bucket<value_type, size_type>* table_;
441 size_type idx_;
442 size_type max_idx_;
443 const list_node<value_type>* current_;
444 };
445
446 template<class Value, class ConstRef, class ConstPtr, class Size>
447 bool operator==(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
448 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
449 {
450 return lhs.node() == rhs.node();
451 }
452
453 template<class Value, class ConstRef, class ConstPtr, class Size>
454 bool operator!=(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
455 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
456 {
457 return !(lhs == rhs);
458 }
459
460 template<class Value, class Reference, class Pointer, class Size>
461 class hash_table_iterator
462 {
463 public:
464 using value_type = Value;
465 using size_type = Size;
466 using reference = Reference;
467 using pointer = Pointer;
468 using difference_type = ptrdiff_t;
469
470 using iterator_category = forward_iterator_tag;
471
472 hash_table_iterator(hash_table_bucket<value_type, size_type>* table = nullptr,
473 size_type idx = size_type{}, size_type max_idx = size_type{},
474 list_node<value_type>* current = nullptr)
475 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
476 { /* DUMMY BODY */ }
477
478 hash_table_iterator(const hash_table_iterator&) = default;
479 hash_table_iterator& operator=(const hash_table_iterator&) = default;
480
481 reference operator*()
482 {
483 return current_->value;
484 }
485
486 pointer operator->()
487 {
488 return &current_->value;
489 }
490
491 hash_table_iterator& operator++()
492 {
493 current_ = current_->next;
494 if (current_ == table_[idx_].head)
495 {
496 if (idx_ < max_idx_)
497 {
498 while (!table_[++idx_].head)
499 { /* DUMMY BODY */ }
500
501 if (idx_ < max_idx_)
502 current_ = table_[idx_].head;
503 else
504 current_ = nullptr;
505 }
506 else
507 current_ = nullptr;
508 }
509
510 return *this;
511 }
512
513 hash_table_iterator operator++(int)
514 {
515 auto tmp_current = current_;
516 auto tmp_idx = idx_;
517
518 current_ = current_->next;
519 if (current_ == table_[idx_].head)
520 {
521 if (idx_ < max_idx_)
522 {
523 while (!table_[++idx_].head)
524 { /* DUMMY BODY */ }
525
526 if (idx_ < max_idx_)
527 current_ = table_[idx_].head;
528 else
529 current_ = nullptr;
530 }
531 else
532 current_ = nullptr;
533 }
534
535 return hash_table_iterator{
536 table_, tmp_idx, max_idx_, tmp_current
537 };
538 }
539
540 list_node<value_type>* node()
541 {
542 return current_;
543 }
544
545 const list_node<value_type>* node() const
546 {
547 return current_;
548 }
549
550 size_type idx() const
551 {
552 return idx_;
553 }
554
555 template<class ConstRef, class ConstPtr>
556 operator hash_table_const_iterator<
557 Value, ConstRef, ConstPtr, Size
558 >() const
559 {
560 return hash_table_const_iterator<value_type, ConstRef, ConstPtr, size_type>{
561 table_, idx_, max_idx_, current_
562 };
563 }
564
565 private:
566 hash_table_bucket<value_type, size_type>* table_;
567 size_type idx_;
568 size_type max_idx_;
569 list_node<value_type>* current_;
570 };
571
572 template<class Value, class Ref, class Ptr, class Size>
573 bool operator==(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
574 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
575 {
576 return lhs.node() == rhs.node();
577 }
578
579 template<class Value, class Ref, class Ptr, class Size>
580 bool operator!=(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
581 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
582 {
583 return !(lhs == rhs);
584 }
585
586 template<class Value, class ConstReference, class ConstPointer>
587 class hash_table_const_local_iterator
588 {
589 public:
590 using value_type = Value;
591 using const_reference = ConstReference;
592 using const_pointer = ConstPointer;
593 using difference_type = ptrdiff_t;
594
595 using iterator_category = forward_iterator_tag;
596
597 // TODO: requirement for forward iterator is default constructibility, fix others!
598 hash_table_const_local_iterator(const list_node<value_type>* head = nullptr,
599 const list_node<value_type>* current = nullptr)
600 : head_{head}, current_{current}
601 { /* DUMMY BODY */ }
602
603 hash_table_const_local_iterator(const hash_table_const_local_iterator&) = default;
604 hash_table_const_local_iterator& operator=(const hash_table_const_local_iterator&) = default;
605
606 const_reference operator*() const
607 {
608 return current_->value;
609 }
610
611 const_pointer operator->() const
612 {
613 return &current_->value;
614 }
615
616 hash_table_const_local_iterator& operator++()
617 {
618 current_ = current_->next;
619 if (current_ == head_)
620 current_ = nullptr;
621
622 return *this;
623 }
624
625 hash_table_const_local_iterator operator++(int)
626 {
627 auto tmp = current_;
628 current_ = current_->next;
629 if (current_ == head_)
630 current_ = nullptr;
631
632 return hash_table_const_local_iterator{head_, tmp};
633 }
634
635
636 list_node<value_type>* node()
637 {
638 return const_cast<list_node<value_type>*>(current_);
639 }
640
641 const list_node<value_type>* node() const
642 {
643 return current_;
644 }
645
646 private:
647 const list_node<value_type>* head_;
648 const list_node<value_type>* current_;
649 };
650
651 template<class Value, class ConstRef, class ConstPtr>
652 bool operator==(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
653 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
654 {
655 return lhs.node() == rhs.node();
656 }
657
658 template<class Value, class ConstRef, class ConstPtr>
659 bool operator!=(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
660 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
661 {
662 return !(lhs == rhs);
663 }
664
665 template<class Value, class Reference, class Pointer>
666 class hash_table_local_iterator
667 {
668 public:
669 using value_type = Value;
670 using reference = Reference;
671 using pointer = Pointer;
672 using difference_type = ptrdiff_t;
673
674 using iterator_category = forward_iterator_tag;
675
676 hash_table_local_iterator(list_node<value_type>* head = nullptr,
677 list_node<value_type>* current = nullptr)
678 : head_{head}, current_{current}
679 { /* DUMMY BODY */ }
680
681 hash_table_local_iterator(const hash_table_local_iterator&) = default;
682 hash_table_local_iterator& operator=(const hash_table_local_iterator&) = default;
683
684 reference operator*()
685 {
686 return current_->value;
687 }
688
689 pointer operator->()
690 {
691 return &current_->value;
692 }
693
694 hash_table_local_iterator& operator++()
695 {
696 current_ = current_->next;
697 if (current_ == head_)
698 current_ = nullptr;
699
700 return *this;
701 }
702
703 hash_table_local_iterator operator++(int)
704 {
705 auto tmp = current_;
706 current_ = current_->next;
707 if (current_ == head_)
708 current_ = nullptr;
709
710 return hash_table_local_iterator{head_, tmp};
711 }
712
713 list_node<value_type>* node()
714 {
715 return current_;
716 }
717
718 const list_node<value_type>* node() const
719 {
720 return current_;
721 }
722
723 template<class ConstRef, class ConstPtr>
724 operator hash_table_const_local_iterator<
725 Value, ConstRef, ConstPtr
726 >() const
727 {
728 return hash_table_const_local_iterator<
729 value_type, ConstRef, ConstPtr
730 >{head_, current_};
731 }
732
733 private:
734 list_node<value_type>* head_;
735 list_node<value_type>* current_;
736 };
737
738 template<class Value, class Ref, class Ptr>
739 bool operator==(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
740 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
741 {
742 return lhs.node() == rhs.node();
743 }
744
745 template<class Value, class Ref, class Ptr>
746 bool operator!=(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
747 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
748 {
749 return !(lhs == rhs);
750 }
751
752 template<
753 class Value, class Key, class KeyExtractor,
754 class Hasher, class KeyEq,
755 class Alloc, class Size,
756 class Iterator, class ConstIterator,
757 class LocalIterator, class ConstLocalIterator,
758 class Policy
759 >
760 class hash_table
761 {
762 public:
763 using value_type = Value;
764 using key_type = Key;
765 using size_type = Size;
766 using allocator_type = Alloc;
767 using key_equal = KeyEq;
768 using hasher = Hasher;
769 using key_extract = KeyExtractor;
770
771 using iterator = Iterator;
772 using const_iterator = ConstIterator;
773 using local_iterator = LocalIterator;
774 using const_local_iterator = ConstLocalIterator;
775
776 using node_type = list_node<value_type>;
777
778 using hint_type = tuple<
779 hash_table_bucket<value_type, size_type>*,
780 list_node<value_type>*, size_type
781 >;
782
783 hash_table(size_type buckets, float max_load_factor = 1.f)
784 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
785 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
786 key_extractor_{}, max_load_factor_{max_load_factor}
787 { /* DUMMY BODY */ }
788
789 hash_table(size_type buckets, const hasher& hf, const key_equal& eql,
790 float max_load_factor = 1.f)
791 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
792 bucket_count_{buckets}, size_{}, hasher_{hf}, key_eq_{eql},
793 key_extractor_{}, max_load_factor_{max_load_factor}
794 { /* DUMMY BODY */ }
795
796 hash_table(const hash_table& other)
797 : hash_table{other.bucket_count_, other.hasher_, other.key_eq_,
798 other.max_load_factor_}
799 {
800 for (const auto& x: other)
801 {
802 auto spot = find_insertion_spot(key_extractor_(x));
803 insert(spot, x);
804 }
805 }
806
807 hash_table(hash_table&& other)
808 : table_{other.table_}, bucket_count_{other.bucket_count_},
809 size_{other.size_}, hasher_{move(other.hasher_)},
810 key_eq_{move(other.key_eq_)}, key_extractor_{move(other.key_extractor_)},
811 max_load_factor_{other.max_load_factor_}
812 {
813 other.table_ = nullptr;
814 other.bucket_count_ = size_type{};
815 other.size_ = size_type{};
816 other.max_load_factor_ = 1.f;
817 }
818
819 hash_table& operator=(const hash_table& other)
820 {
821 hash_table tmp{other};
822 tmp.swap(*this);
823
824 return *this;
825 }
826
827 hash_table& operator=(hash_table&& other)
828 {
829 hash_table tmp{move(other)};
830 tmp.swap(*this);
831
832 return *this;
833 }
834
835 bool empty() const noexcept
836 {
837 return size_ == 0;
838 }
839
840 size_type size() const noexcept
841 {
842 return size_;
843 }
844
845 template<class Allocator>
846 size_type max_size(Allocator& alloc)
847 {
848 return allocator_traits<Allocator>::max_size(alloc);
849 }
850
851 iterator begin() noexcept
852 {
853 return iterator{
854 table_, size_type{}, bucket_count_,
855 table_[0].head
856 };
857 }
858
859 const_iterator begin() const noexcept
860 {
861 return cbegin();
862 }
863
864 iterator end() noexcept
865 {
866 return iterator{};
867 }
868
869 const_iterator end() const noexcept
870 {
871 return cend();
872 }
873
874 const_iterator cbegin() const noexcept
875 {
876 return const_iterator{
877 table_, size_type{}, bucket_count_,
878 table_[0].head
879 };
880 }
881
882 const_iterator cend() const noexcept
883 {
884 return const_iterator{};
885 }
886
887 template<class Allocator, class... Args>
888 void emplace(const hint_type& where, Allocator& alloc, Args&&... args)
889 {
890 if (!hint_ok_(where))
891 return;
892
893 auto node = new list_node<value_type>{forward<Args&&>(args)...};
894 if (get<1>(where) == nullptr) // Append here will create a new head.
895 get<0>(where)->append(node);
896 else // Prepending before an exact position is common in the standard.
897 get<1>(where)->prepend(node);
898
899 ++size_;
900
901 rehash_if_needed();
902 }
903
904 void insert(const hint_type& where, const value_type& val)
905 {
906 if (!hint_ok_(where))
907 return;
908
909 auto node = new list_node<value_type>{val};
910 if (get<1>(where) == nullptr)
911 get<0>(where)->append(node);
912 else
913 get<1>(where)->prepend(node);
914
915 ++size_;
916
917 rehash_if_needed();
918 }
919
920 void insert(const hint_type& where, value_type&& val)
921 {
922 if (!hint_ok_(where))
923 return;
924
925 auto node = new list_node<value_type>{forward<value_type>(val)};
926 if (get<1>(where) == nullptr)
927 get<0>(where)->append(node);
928 else
929 get<1>(where)->prepend(node);
930
931 ++size_;
932
933 rehash_if_needed();
934 }
935
936 size_type erase(const key_type& key)
937 {
938 return Policy::erase(*this, key);
939 }
940
941 iterator erase(const_iterator it)
942 {
943 auto node = it.node();
944 auto idx = it.idx();
945
946 /**
947 * Note: This way we will continue on the next bucket
948 * if this is the last element in its bucket.
949 */
950 iterator res{table_, idx, size_, node};
951 ++res;
952
953 if (table_[idx].head == node)
954 {
955 if (node->next != node)
956 table_[idx].head = node->next;
957 else
958 table_[idx].head = nullptr;
959 }
960
961 node->unlink();
962 delete node;
963
964 return res;
965 }
966
967 void clear() noexcept
968 {
969 for (size_type i = 0; i < bucket_count_; ++i)
970 table_[i].clear();
971 size_ = size_type{};
972 }
973
974 void swap(hash_table& other)
975 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
976 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
977 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
978 {
979 std::swap(table_, other.table_);
980 std::swap(bucket_count_, other.bucket_count_);
981 std::swap(size_, other.size_);
982 std::swap(hasher_, other.hasher_);
983 std::swap(key_eq_, other.key_eq_);
984 std::swap(max_load_factor_, other.max_load_factor_);
985 }
986
987 hasher hash_function() const
988 {
989 return hasher_;
990 }
991
992 key_equal key_eq() const
993 {
994 return key_eq_;
995 }
996
997 iterator find(const key_type& key)
998 {
999 auto idx = get_bucket_idx_(key);
1000 auto head = table_[idx].head;
1001
1002 if (!head)
1003 return end();
1004
1005 auto current = head;
1006 do
1007 {
1008 if (key_eq_(key, key_extractor_(current->value)))
1009 return iterator{table_, idx, size_, current};
1010 current = current->next;
1011 }
1012 while (current != head);
1013
1014 return end();
1015 }
1016
1017 const_iterator find(const key_type& key) const
1018 {
1019 auto idx = get_bucket_idx_(key);
1020 auto head = table_[idx].head;
1021
1022 if (!head)
1023 return end();
1024
1025 auto current = head;
1026 do
1027 {
1028 if (key_eq_(key, key_extractor_(current->value)))
1029 return iterator{table_, idx, size_, current};
1030 current = current->next;
1031 }
1032 while (current != head);
1033
1034 return end();
1035 }
1036
1037 size_type count(const key_type& key) const
1038 {
1039 return Policy::count(*this, key);
1040 }
1041
1042 pair<iterator, iterator> equal_range(const key_type& key)
1043 {
1044 return Policy::equal_range(*this, key);
1045 }
1046
1047 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
1048 {
1049 return Policy::equal_range_const(*this, key);
1050 }
1051
1052 size_type bucket_count() const noexcept
1053 {
1054 return bucket_count_;
1055 }
1056
1057 size_type max_bucket_count() const noexcept
1058 {
1059 return numeric_limits<size_type>::max() /
1060 sizeof(hash_table_bucket<value_type, size_type>);
1061 }
1062
1063 size_type bucket_size(size_type n) const
1064 {
1065 return table_[n].size();
1066 }
1067
1068 size_type bucket(const key_type& key) const
1069 {
1070 return get_bucket_idx_(key);
1071 }
1072
1073 local_iterator begin(size_type n)
1074 {
1075 return local_iterator{table_[n].head, table_[n].head};
1076 }
1077
1078 const_local_iterator begin(size_type n) const
1079 {
1080 return cbegin(n);
1081 }
1082
1083 local_iterator end(size_type n)
1084 {
1085 return local_iterator{};
1086 }
1087
1088 const_local_iterator end(size_type n) const
1089 {
1090 return cend(n);
1091 }
1092
1093 const_local_iterator cbegin(size_type n) const
1094 {
1095 return const_local_iterator{table_[n].head, table_[n].head};
1096 }
1097
1098 const_local_iterator cend(size_type n) const
1099 {
1100 return const_local_iterator{};
1101 }
1102
1103 float load_factor() const noexcept
1104 {
1105 return size_ / static_cast<float>(bucket_count_);
1106 }
1107
1108 float max_load_factor() const noexcept
1109 {
1110 return max_load_factor_;
1111 }
1112
1113 void max_load_factor(float factor)
1114 {
1115 if (factor > 0.f)
1116 max_load_factor_ = factor;
1117
1118 rehash_if_needed();
1119 }
1120
1121 void rehash(size_type count)
1122 {
1123 if (count < size_ / max_load_factor_)
1124 count = size_ / max_load_factor_;
1125
1126 /**
1127 * Note: If an exception is thrown, there
1128 * is no effect. Since this is the only
1129 * place where an exception (no mem) can
1130 * be thrown and no changes to this have been
1131 * made, we're ok.
1132 */
1133 hash_table new_table{count, max_load_factor_};
1134
1135 for (std::size_t i = 0; i < bucket_count_; ++i)
1136 {
1137 auto head = table_[i].head;
1138 if (!head)
1139 continue;
1140
1141 auto current = head;
1142
1143 do
1144 {
1145 auto next = current->next;
1146
1147 current->next = current;
1148 current->prev = current;
1149
1150 auto where = Policy::find_insertion_spot(
1151 new_table, key_extractor_(current->value)
1152 );
1153
1154 /**
1155 * Note: We're rehashing, so we know each
1156 * key can be inserted.
1157 */
1158 auto new_bucket = get<0>(where);
1159 auto new_successor = get<1>(where);
1160
1161 if (new_successor)
1162 new_successor->append(current);
1163 else
1164 new_bucket->append(current);
1165
1166 current = next;
1167 } while (current != head);
1168
1169 table_[i].head = nullptr;
1170 }
1171
1172 new_table.size_ = size_;
1173 swap(new_table);
1174
1175 delete[] new_table.table_;
1176 new_table.table_ = nullptr;
1177 }
1178
1179 void reserve(size_type count)
1180 {
1181 rehash(count / max_load_factor_ + 1);
1182 }
1183
1184 bool is_eq_to(const hash_table& other)
1185 {
1186 // TODO: implement
1187 return false;
1188 }
1189
1190 ~hash_table()
1191 {
1192 // Lists are deleted in ~hash_table_bucket.
1193 if (table_)
1194 delete[] table_;
1195 }
1196
1197 hint_type find_insertion_spot(const key_type& key)
1198 {
1199 return Policy::find_insertion_spot(*this, key);
1200 }
1201
1202 hint_type find_insertion_spot(key_type&& key)
1203 {
1204 return Policy::find_insertion_spot(*this, key);
1205 }
1206
1207 const key_type& get_key(const value_type& val)
1208 {
1209 return key_extractor_(val);
1210 }
1211
1212 bool keys_equal(const key_type& key, const value_type& val)
1213 {
1214 return key_eq_(key, key_extractor_(val));
1215 }
1216
1217 bool keys_equal(const key_type& key, const value_type& val) const
1218 {
1219 return key_eq_(key, key_extractor_(val));
1220 }
1221
1222 hash_table_bucket<value_type, size_type>* table()
1223 {
1224 return table_;
1225 }
1226
1227 hash_table_bucket<value_type, size_type>* head(size_type idx)
1228 {
1229 if (idx < bucket_count_)
1230 return &table_[idx];
1231 else
1232 return nullptr;
1233 }
1234
1235 void rehash_if_needed()
1236 {
1237 if (size_ > max_load_factor_ * bucket_count_)
1238 rehash(bucket_count_ * bucket_count_growth_factor_);
1239 }
1240
1241 void increment_size()
1242 {
1243 ++size_;
1244
1245 rehash_if_needed();
1246 }
1247
1248 void decrement_size()
1249 {
1250 --size_;
1251 }
1252
1253 node_type* find_node_or_return_head(const key_type& key,
1254 const hash_table_bucket<value_type, size_type>& bucket)
1255 {
1256 if (bucket.head)
1257 {
1258 auto head = bucket.head;
1259 auto current = bucket.head;
1260
1261 do
1262 {
1263 if (keys_equal(key, current->value))
1264 return current;
1265 else
1266 current = current->next;
1267 }
1268 while (current != head);
1269
1270 return head;
1271 }
1272 else
1273 return nullptr;
1274 }
1275
1276 private:
1277 hash_table_bucket<value_type, size_type>* table_;
1278 size_type bucket_count_;
1279 size_type size_;
1280 hasher hasher_;
1281 key_equal key_eq_;
1282 key_extract key_extractor_;
1283 float max_load_factor_;
1284
1285 static constexpr float bucket_count_growth_factor_{1.25};
1286
1287 size_type get_bucket_idx_(const key_type& key) const
1288 {
1289 return hasher_(key) % bucket_count_;
1290 }
1291
1292 bool hint_ok_(const hint_type& hint)
1293 {
1294 // TODO: pass this to the policy, because the multi policy
1295 // will need to check if a similar key is close,
1296 // that is something like:
1297 // return get<1>(hint)->prev->key == key || !bucket.contains(key)
1298 // TODO: also, make it public and make hint usage one level above?
1299 // (since we already have insert with decisive hint)
1300 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
1301 }
1302
1303 // Praise C++11 for this.
1304 friend Policy;
1305 };
1306}
1307
1308#endif
Note: See TracBrowser for help on using the repository browser.