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
RevLine 
[e29ce3d]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>
[871cfe0c]34#include <iterator>
[e29ce3d]35#include <memory>
[871cfe0c]36#include <tuple>
[e29ce3d]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
[871cfe0c]52 template<class Key, class Value>
[e29ce3d]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
[871cfe0c]70 template<class Value, class Size>
71 struct hash_table_bucket
[e29ce3d]72 {
[871cfe0c]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
[1d5424a]81 hash_table_bucket()
82 : head{}
83 { /* DUMMY BODY */ }
84
[871cfe0c]85 Size size() const noexcept
[e29ce3d]86 {
[7320ca6]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;
[871cfe0c]98 }
99
100 void append(list_node<Value>* node)
101 {
102 if (!head)
103 head = node;
104 else
105 head->append(node);
106 }
107
[7320ca6]108 void clear()
109 {
[1d5424a]110 if (!head)
111 return;
112
[7320ca6]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
[871cfe0c]125 ~hash_table_bucket()
126 {
[7320ca6]127 clear();
[e29ce3d]128 }
129 };
130
131 struct hash_single_policy
132 {
133 // TODO: umap/uset operations
[871cfe0c]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 }
[e9027b5]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 }
[1d5424a]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 }
[e29ce3d]205 };
206
207 struct hash_multi_policy
208 {
209 // TODO: umultimap/umultiset operations
210
[871cfe0c]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;
[e29ce3d]217
[871cfe0c]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;
[e29ce3d]224
[871cfe0c]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)
[e29ce3d]234 {
[871cfe0c]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)
[e29ce3d]238 }
[e9027b5]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 }
[1d5424a]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 }
[e29ce3d]263 };
264
[871cfe0c]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
[e9027b5]277 hash_table_const_iterator(const hash_table_bucket<value_type, size_type>* table = nullptr,
[871cfe0c]278 size_type idx = size_type{}, size_type max_idx = size_type{},
[e9027b5]279 const list_node<value_type>* current = nullptr)
[871cfe0c]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 {
[e9027b5]347 return const_cast<list_node<value_type>*>(current_);
[871cfe0c]348 }
349
350 const list_node<value_type>* node() const
351 {
352 return current_;
353 }
354
[e9027b5]355 size_type idx() const
356 {
357 return idx_;
358 }
359
[871cfe0c]360 private:
[e9027b5]361 const hash_table_bucket<value_type, size_type>* table_;
[871cfe0c]362 size_type idx_;
363 size_type max_idx_;
[e9027b5]364 const list_node<value_type>* current_;
[871cfe0c]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
[e9027b5]471 size_type idx() const
472 {
473 return idx_;
474 }
475
[871cfe0c]476 template<class ConstRef, class ConstPtr>
477 operator hash_table_const_iterator<
478 Value, ConstRef, ConstPtr, Size
479 >() const
480 {
[e9027b5]481 return hash_table_const_iterator<value_type, ConstRef, ConstPtr, size_type>{
[871cfe0c]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!
[e9027b5]519 hash_table_const_local_iterator(const list_node<value_type>* head = nullptr,
520 const list_node<value_type>* current = nullptr)
[871cfe0c]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
[e9027b5]556
[871cfe0c]557 list_node<value_type>* node()
558 {
[e9027b5]559 return const_cast<list_node<value_type>*>(current_);
[871cfe0c]560 }
561
562 const list_node<value_type>* node() const
563 {
564 return current_;
565 }
566
567 private:
[e9027b5]568 const list_node<value_type>* head_;
569 const list_node<value_type>* current_;
[871cfe0c]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 {
[e9027b5]649 return hash_table_const_local_iterator<
650 value_type, ConstRef, ConstPtr
651 >{head_, current_};
[871cfe0c]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 }
[e29ce3d]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,
[871cfe0c]678 class LocalIterator, class ConstLocalIterator,
[e29ce3d]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
[871cfe0c]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)
[1d5424a]703 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
[871cfe0c]704 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
705 key_extractor_{}, max_load_factor_{max_load_factor}
706 { /* DUMMY BODY */ }
[e29ce3d]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 {
[871cfe0c]726 return iterator{
727 table_, size_type{}, bucket_count_,
728 table_[0].head
729 };
[e29ce3d]730 }
731
732 const_iterator begin() const noexcept
733 {
[871cfe0c]734 return cbegin();
[e29ce3d]735 }
736
737 iterator end() noexcept
738 {
[871cfe0c]739 return iterator{};
[e29ce3d]740 }
741
742 const_iterator end() const noexcept
743 {
[871cfe0c]744 return cend();
[e29ce3d]745 }
746
747 const_iterator cbegin() const noexcept
748 {
[871cfe0c]749 return const_iterator{
750 table_, size_type{}, bucket_count_,
751 table_[0].head
752 };
[e29ce3d]753 }
754
755 const_iterator cend() const noexcept
756 {
[871cfe0c]757 return const_iterator{};
[e29ce3d]758 }
759
760 template<class Allocator, class... Args>
[1d5424a]761 void emplace(const hint_type& where, Allocator& alloc, Args&&... args)
[e29ce3d]762 {
[1d5424a]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
[e29ce3d]774 }
775
[871cfe0c]776 void insert(const hint_type& where, const value_type& val)
[e29ce3d]777 {
[871cfe0c]778 if (!hint_ok_(where))
779 return;
780
781 auto node = new list_node<value_type>{val};
[1d5424a]782 if (get<1>(where) == nullptr)
[871cfe0c]783 get<0>(where)->append(node);
[1d5424a]784 else
[871cfe0c]785 get<1>(where)->prepend(node);
786
787 ++size_;
[e9027b5]788 // TODO: if we go over max load factor, rehash
[871cfe0c]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_;
[e9027b5]803 // TODO: if we go over max load factor, rehash
[e29ce3d]804 }
805
806 size_type erase(const key_type& key)
807 {
[e9027b5]808 return Policy::erase(*this, key);
[e29ce3d]809 }
810
811 iterator erase(const_iterator it)
812 {
[e9027b5]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;
[e29ce3d]835 }
836
837 void clear() noexcept
838 {
[7320ca6]839 for (size_type i = 0; i < bucket_count_; ++i)
840 table_[i].clear();
[871cfe0c]841 size_ = size_type{};
[e29ce3d]842 }
843
844 void swap(hash_table& other)
[1d5424a]845 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
[e29ce3d]846 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
847 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
848 {
[871cfe0c]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_);
[e29ce3d]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 {
[1d5424a]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();
[e29ce3d]885 }
886
887 const_iterator find(const key_type& key) const
888 {
[1d5424a]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();
[e29ce3d]905 }
906
907 size_type count(const key_type& key) const
908 {
[871cfe0c]909 return Policy::count(*this, key);
[e29ce3d]910 }
911
912 pair<iterator, iterator> equal_range(const key_type& key)
913 {
[1d5424a]914 return Policy::equal_range(*this, key);
[e29ce3d]915 }
916
917 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
918 {
[1d5424a]919 return Policy::equal_range_const(*this, key);
[e29ce3d]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 {
[871cfe0c]935 return table_[n].size();
[e29ce3d]936 }
937
938 size_type bucket(const key_type& key) const
939 {
[871cfe0c]940 return get_bucket_idx_(key);
[e29ce3d]941 }
942
943 local_iterator begin(size_type n)
944 {
[871cfe0c]945 return local_iterator{table_[n].head, table_[n].head};
[e29ce3d]946 }
947
948 const_local_iterator begin(size_type n) const
949 {
[871cfe0c]950 return cbegin(n);
[e29ce3d]951 }
952
[871cfe0c]953 local_iterator end(size_type n)
[e29ce3d]954 {
[871cfe0c]955 return local_iterator{};
956 }
957
958 const_local_iterator end(size_type n) const
959 {
960 return cend(n);
[e29ce3d]961 }
962
963 const_local_iterator cbegin(size_type n) const
964 {
[871cfe0c]965 return const_local_iterator{table_[n].head, table_[n].head};
[e29ce3d]966 }
967
968 const_local_iterator cend(size_type n) const
969 {
[871cfe0c]970 return const_local_iterator{};
[e29ce3d]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 */
[1d5424a]989 // TODO: change max factor and rehash if needed
[e29ce3d]990 }
991
[1d5424a]992 void rehash(size_type count)
[e29ce3d]993 {
[1d5424a]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;
[e29ce3d]1044 }
1045
[1d5424a]1046 void reserve(size_type count)
[e29ce3d]1047 {
[1d5424a]1048 rehash(count / max_load_factor_ + 1);
[e29ce3d]1049 }
1050
1051 bool is_eq_to(hash_table& other)
1052 {
1053 // TODO: implement
[871cfe0c]1054 return false;
[e29ce3d]1055 }
1056
1057 ~hash_table()
1058 {
1059 // Lists are deleted in ~hash_table_bucket.
[1d5424a]1060 if (table_)
1061 delete[] table_;
[e29ce3d]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
[871cfe0c]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: */
[e29ce3d]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_;
[871cfe0c]1086 key_extract key_extractor_;
[e29ce3d]1087 float max_load_factor_;
1088
[871cfe0c]1089 size_type get_bucket_idx_(const key_type& key) const
1090 {
1091 return hasher_(key) % bucket_count_;
1092 }
[e29ce3d]1093
[871cfe0c]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)
[1d5424a]1100 // TODO: also, make it public and make hint usage one level above?
1101 // (since we already have insert with decisive hint)
[871cfe0c]1102 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
1103 }
1104
1105 // Praise C++11 for this.
1106 friend Policy;
[e29ce3d]1107 };
[871cfe0c]1108}
[e29ce3d]1109
1110#endif
Note: See TracBrowser for help on using the repository browser.