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

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

cpp: fixed semantic errors, added support functions for higher level containers

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