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

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

cpp: removed no longer needed todos

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