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

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

cpp: added a constructor and getters needed by unordered_map and others

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