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

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

cpp: added copy/move constructor/assignment to the hash table

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