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

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

cpp: added a constructor and getters needed by unordered_map and others

  • Property mode set to 100644
File size: 34.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 <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 hash_table(size_type buckets, const hasher& hf, const key_equal& eql,
719 float max_load_factor = 1.f)
720 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
721 bucket_count_{buckets}, size_{}, hasher_{hf}, key_eq_{eql},
722 key_extractor_{}, max_load_factor_{max_load_factor}
723 { /* DUMMY BODY */ }
724
725 // TODO: copy/move constructor/assignment operator
726
727 bool empty() const noexcept
728 {
729 return size_ == 0;
730 }
731
732 size_type size() const noexcept
733 {
734 return size_;
735 }
736
737 template<class Allocator>
738 size_type max_size(Allocator& alloc)
739 {
740 return allocator_traits<Allocator>::max_size(alloc);
741 }
742
743 iterator begin() noexcept
744 {
745 return iterator{
746 table_, size_type{}, bucket_count_,
747 table_[0].head
748 };
749 }
750
751 const_iterator begin() const noexcept
752 {
753 return cbegin();
754 }
755
756 iterator end() noexcept
757 {
758 return iterator{};
759 }
760
761 const_iterator end() const noexcept
762 {
763 return cend();
764 }
765
766 const_iterator cbegin() const noexcept
767 {
768 return const_iterator{
769 table_, size_type{}, bucket_count_,
770 table_[0].head
771 };
772 }
773
774 const_iterator cend() const noexcept
775 {
776 return const_iterator{};
777 }
778
779 template<class Allocator, class... Args>
780 void emplace(const hint_type& where, Allocator& alloc, Args&&... args)
781 {
782 if (!hint_ok_(where))
783 return;
784
785 auto node = new list_node<value_type>{forward<Args&&>(args)...};
786 if (get<1>(where) == nullptr) // Append here will create a new head.
787 get<0>(where)->append(node);
788 else // Prepending before an exact position is common in the standard.
789 get<1>(where)->prepend(node);
790
791 ++size_;
792 // TODO: if we go over max load factor, rehash
793 }
794
795 void insert(const hint_type& where, const value_type& val)
796 {
797 if (!hint_ok_(where))
798 return;
799
800 auto node = new list_node<value_type>{val};
801 if (get<1>(where) == nullptr)
802 get<0>(where)->append(node);
803 else
804 get<1>(where)->prepend(node);
805
806 ++size_;
807 // TODO: if we go over max load factor, rehash
808 }
809
810 void insert(const hint_type& where, value_type&& val)
811 {
812 if (!hint_ok_(where))
813 return;
814
815 auto node = new list_node<value_type>{forward<value_type>(val)};
816 if (get<1>(where) == nullptr)
817 get<0>(where)->append(node);
818 else
819 get<1>(where)->prepend(node);
820
821 ++size_;
822 // TODO: if we go over max load factor, rehash
823 }
824
825 size_type erase(const key_type& key)
826 {
827 return Policy::erase(*this, key);
828 }
829
830 iterator erase(const_iterator it)
831 {
832 auto node = it.node();
833 auto idx = it.idx();
834
835 /**
836 * Note: This way we will continue on the next bucket
837 * if this is the last element in its bucket.
838 */
839 iterator res{table_, idx, size_, node};
840 ++res;
841
842 if (table_[idx].head == node)
843 {
844 if (node->next != node)
845 table_[idx].head = node->next;
846 else
847 table_[idx].head = nullptr;
848 }
849
850 node->unlink();
851 delete node;
852
853 return res;
854 }
855
856 void clear() noexcept
857 {
858 for (size_type i = 0; i < bucket_count_; ++i)
859 table_[i].clear();
860 size_ = size_type{};
861 }
862
863 void swap(hash_table& other)
864 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
865 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
866 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
867 {
868 std::swap(table_, other.table_);
869 std::swap(bucket_count_, other.bucket_count_);
870 std::swap(size_, other.size_);
871 std::swap(hasher_, other.hasher_);
872 std::swap(key_eq_, other.key_eq_);
873 std::swap(max_load_factor_, other.max_load_factor_);
874 }
875
876 hasher hash_function() const
877 {
878 return hasher_;
879 }
880
881 key_equal key_eq() const
882 {
883 return key_eq_;
884 }
885
886 iterator find(const key_type& key)
887 {
888 auto idx = get_bucket_idx_(key);
889 auto head = table_[idx].head;
890
891 if (!head)
892 return end();
893
894 auto current = head;
895 do
896 {
897 if (key_eq_(key, key_extractor_(current->value)))
898 return iterator{table_, idx, size_, current};
899 current = current->next;
900 }
901 while (current != head);
902
903 return end();
904 }
905
906 const_iterator find(const key_type& key) const
907 {
908 auto idx = get_bucket_idx_(key);
909 auto head = table_[idx].head;
910
911 if (!head)
912 return end();
913
914 auto current = head;
915 do
916 {
917 if (key_eq_(key, key_extractor_(current->value)))
918 return iterator{table_, idx, size_, current};
919 current = current->next;
920 }
921 while (current != head);
922
923 return end();
924 }
925
926 size_type count(const key_type& key) const
927 {
928 return Policy::count(*this, key);
929 }
930
931 pair<iterator, iterator> equal_range(const key_type& key)
932 {
933 return Policy::equal_range(*this, key);
934 }
935
936 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
937 {
938 return Policy::equal_range_const(*this, key);
939 }
940
941 size_type bucket_count() const noexcept
942 {
943 return bucket_count_;
944 }
945
946 size_type max_bucket_count() const noexcept
947 {
948 // TODO: implement
949 return 0;
950 }
951
952 size_type bucket_size(size_type n) const
953 {
954 return table_[n].size();
955 }
956
957 size_type bucket(const key_type& key) const
958 {
959 return get_bucket_idx_(key);
960 }
961
962 local_iterator begin(size_type n)
963 {
964 return local_iterator{table_[n].head, table_[n].head};
965 }
966
967 const_local_iterator begin(size_type n) const
968 {
969 return cbegin(n);
970 }
971
972 local_iterator end(size_type n)
973 {
974 return local_iterator{};
975 }
976
977 const_local_iterator end(size_type n) const
978 {
979 return cend(n);
980 }
981
982 const_local_iterator cbegin(size_type n) const
983 {
984 return const_local_iterator{table_[n].head, table_[n].head};
985 }
986
987 const_local_iterator cend(size_type n) const
988 {
989 return const_local_iterator{};
990 }
991
992 float load_factor() const noexcept
993 {
994 return size_ / static_cast<float>(bucket_count_);
995 }
996
997 float max_load_factor() const noexcept
998 {
999 return max_load_factor_;
1000 }
1001
1002 void max_load_factor(float factor)
1003 {
1004 /**
1005 * Note: According to the standard, this function
1006 * can have no effect.
1007 */
1008 // TODO: change max factor and rehash if needed
1009 }
1010
1011 void rehash(size_type count)
1012 {
1013 if (count < size_ / max_load_factor_)
1014 count = size_ / max_load_factor_;
1015
1016 // Note: This is the only place where an exception can be
1017 // thrown (no mem) in this function, so wrap it
1018 // in try-catch-rethrow.
1019 hash_table new_table{count, max_load_factor_};
1020
1021 for (std::size_t i = 0; i < bucket_count_; ++i)
1022 {
1023 auto head = table_[i].head;
1024 if (!head)
1025 continue;
1026
1027 auto current = head;
1028
1029 do
1030 {
1031 auto next = current->next;
1032
1033 current->next = current;
1034 current->prev = current;
1035
1036 auto where = Policy::find_insertion_spot(
1037 new_table, key_extractor_(current->value)
1038 );
1039
1040 /**
1041 * Note: We're rehashing, so we know each
1042 * key can be inserted.
1043 */
1044 auto new_bucket = get<0>(where);
1045 auto new_successor = get<1>(where);
1046
1047 if (new_successor)
1048 new_successor->append(current);
1049 else
1050 new_bucket->append(current);
1051
1052 current = next;
1053 } while (current != head);
1054
1055 table_[i].head = nullptr;
1056 }
1057
1058 new_table.size_ = size_;
1059 swap(new_table);
1060
1061 delete[] new_table.table_;
1062 new_table.table_ = nullptr;
1063 }
1064
1065 void reserve(size_type count)
1066 {
1067 rehash(count / max_load_factor_ + 1);
1068 }
1069
1070 bool is_eq_to(hash_table& other)
1071 {
1072 // TODO: implement
1073 return false;
1074 }
1075
1076 ~hash_table()
1077 {
1078 // Lists are deleted in ~hash_table_bucket.
1079 if (table_)
1080 delete[] table_;
1081 }
1082
1083 void set_hint(const_iterator hint)
1084 {
1085 // TODO: hint_ should be a ptr and we extract it here,
1086 // then set it to nullptr after each operation
1087 }
1088
1089 hint_type find_insertion_spot(const key_type& key)
1090 {
1091 return Policy::find_insertion_spot(*this, key);
1092 }
1093
1094 hint_type find_insertion_spot(key_type&& key)
1095 {
1096 return Policy::find_insertion_spot(*this, key);
1097 }
1098
1099 const key_type& get_key(const value_type& val)
1100 {
1101 return key_extractor_(val);
1102 }
1103
1104 hasher hash_function() const
1105 {
1106 return hasher_;
1107 }
1108
1109 key_equal key_eq() const
1110 {
1111 return key_eq_;
1112 }
1113
1114 /* private: */
1115 hash_table_bucket<value_type, size_type>* table_;
1116 size_type bucket_count_;
1117 size_type size_;
1118 hasher hasher_;
1119 key_equal key_eq_;
1120 key_extract key_extractor_;
1121 float max_load_factor_;
1122
1123 size_type get_bucket_idx_(const key_type& key) const
1124 {
1125 return hasher_(key) % bucket_count_;
1126 }
1127
1128 bool hint_ok_(const hint_type& hint)
1129 {
1130 // TODO: pass this to the policy, because the multi policy
1131 // will need to check if a similar key is close,
1132 // that is something like:
1133 // return get<1>(hint)->prev->key == key || !bucket.contains(key)
1134 // TODO: also, make it public and make hint usage one level above?
1135 // (since we already have insert with decisive hint)
1136 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
1137 }
1138
1139 // Praise C++11 for this.
1140 friend Policy;
1141 };
1142}
1143
1144#endif
Note: See TracBrowser for help on using the repository browser.