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

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

cpp: removed the allocator trick, map allocator is for the pair, not the value

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