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

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

cpp: added const variants for key extractors, public key extraction function to hash table for use one level above and tested the hash table with key value pairs

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