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

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

cpp: added rehash, reserve, find for hash_table and also equal range for single hash table

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