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

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

cpp: added const variants for key extractors, public key extraction function to hash table for use one level above and tested the hash table with key value pairs

  • Property mode set to 100644
File size: 34.3 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
718 bool empty() const noexcept
719 {
720 return size_ == 0;
721 }
722
723 size_type size() const noexcept
724 {
725 return size_;
726 }
727
728 template<class Allocator>
729 size_type max_size(Allocator& alloc)
730 {
731 return allocator_traits<Allocator>::max_size(alloc);
732 }
733
734 iterator begin() noexcept
735 {
[871cfe0c]736 return iterator{
737 table_, size_type{}, bucket_count_,
738 table_[0].head
739 };
[e29ce3d]740 }
741
742 const_iterator begin() const noexcept
743 {
[871cfe0c]744 return cbegin();
[e29ce3d]745 }
746
747 iterator end() noexcept
748 {
[871cfe0c]749 return iterator{};
[e29ce3d]750 }
751
752 const_iterator end() const noexcept
753 {
[871cfe0c]754 return cend();
[e29ce3d]755 }
756
757 const_iterator cbegin() const noexcept
758 {
[871cfe0c]759 return const_iterator{
760 table_, size_type{}, bucket_count_,
761 table_[0].head
762 };
[e29ce3d]763 }
764
765 const_iterator cend() const noexcept
766 {
[871cfe0c]767 return const_iterator{};
[e29ce3d]768 }
769
770 template<class Allocator, class... Args>
[1d5424a]771 void emplace(const hint_type& where, Allocator& alloc, Args&&... args)
[e29ce3d]772 {
[1d5424a]773 if (!hint_ok_(where))
774 return;
775
776 auto node = new list_node<value_type>{forward<Args&&>(args)...};
777 if (get<1>(where) == nullptr) // Append here will create a new head.
778 get<0>(where)->append(node);
779 else // Prepending before an exact position is common in the standard.
780 get<1>(where)->prepend(node);
781
782 ++size_;
783 // TODO: if we go over max load factor, rehash
[e29ce3d]784 }
785
[871cfe0c]786 void insert(const hint_type& where, const value_type& val)
[e29ce3d]787 {
[871cfe0c]788 if (!hint_ok_(where))
789 return;
790
791 auto node = new list_node<value_type>{val};
[1d5424a]792 if (get<1>(where) == nullptr)
[871cfe0c]793 get<0>(where)->append(node);
[1d5424a]794 else
[871cfe0c]795 get<1>(where)->prepend(node);
796
797 ++size_;
[e9027b5]798 // TODO: if we go over max load factor, rehash
[871cfe0c]799 }
800
801 void insert(const hint_type& where, value_type&& val)
802 {
803 if (!hint_ok_(where))
804 return;
805
806 auto node = new list_node<value_type>{forward<value_type>(val)};
807 if (get<1>(where) == nullptr)
808 get<0>(where)->append(node);
809 else
810 get<1>(where)->prepend(node);
811
812 ++size_;
[e9027b5]813 // TODO: if we go over max load factor, rehash
[e29ce3d]814 }
815
816 size_type erase(const key_type& key)
817 {
[e9027b5]818 return Policy::erase(*this, key);
[e29ce3d]819 }
820
821 iterator erase(const_iterator it)
822 {
[e9027b5]823 auto node = it.node();
824 auto idx = it.idx();
825
826 /**
827 * Note: This way we will continue on the next bucket
828 * if this is the last element in its bucket.
829 */
830 iterator res{table_, idx, size_, node};
831 ++res;
832
833 if (table_[idx].head == node)
834 {
835 if (node->next != node)
836 table_[idx].head = node->next;
837 else
838 table_[idx].head = nullptr;
839 }
840
841 node->unlink();
842 delete node;
843
844 return res;
[e29ce3d]845 }
846
847 void clear() noexcept
848 {
[7320ca6]849 for (size_type i = 0; i < bucket_count_; ++i)
850 table_[i].clear();
[871cfe0c]851 size_ = size_type{};
[e29ce3d]852 }
853
854 void swap(hash_table& other)
[1d5424a]855 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
[e29ce3d]856 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
857 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
858 {
[871cfe0c]859 std::swap(table_, other.table_);
860 std::swap(bucket_count_, other.bucket_count_);
861 std::swap(size_, other.size_);
862 std::swap(hasher_, other.hasher_);
863 std::swap(key_eq_, other.key_eq_);
864 std::swap(max_load_factor_, other.max_load_factor_);
[e29ce3d]865 }
866
867 hasher hash_function() const
868 {
869 return hasher_;
870 }
871
872 key_equal key_eq() const
873 {
874 return key_eq_;
875 }
876
877 iterator find(const key_type& key)
878 {
[1d5424a]879 auto idx = get_bucket_idx_(key);
880 auto head = table_[idx].head;
881
882 if (!head)
883 return end();
884
885 auto current = head;
886 do
887 {
888 if (key_eq_(key, key_extractor_(current->value)))
889 return iterator{table_, idx, size_, current};
890 current = current->next;
891 }
892 while (current != head);
893
894 return end();
[e29ce3d]895 }
896
897 const_iterator find(const key_type& key) const
898 {
[1d5424a]899 auto idx = get_bucket_idx_(key);
900 auto head = table_[idx].head;
901
902 if (!head)
903 return end();
904
905 auto current = head;
906 do
907 {
908 if (key_eq_(key, key_extractor_(current->value)))
909 return iterator{table_, idx, size_, current};
910 current = current->next;
911 }
912 while (current != head);
913
914 return end();
[e29ce3d]915 }
916
917 size_type count(const key_type& key) const
918 {
[871cfe0c]919 return Policy::count(*this, key);
[e29ce3d]920 }
921
922 pair<iterator, iterator> equal_range(const key_type& key)
923 {
[1d5424a]924 return Policy::equal_range(*this, key);
[e29ce3d]925 }
926
927 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
928 {
[1d5424a]929 return Policy::equal_range_const(*this, key);
[e29ce3d]930 }
931
932 size_type bucket_count() const noexcept
933 {
934 return bucket_count_;
935 }
936
937 size_type max_bucket_count() const noexcept
938 {
939 // TODO: implement
940 return 0;
941 }
942
943 size_type bucket_size(size_type n) const
944 {
[871cfe0c]945 return table_[n].size();
[e29ce3d]946 }
947
948 size_type bucket(const key_type& key) const
949 {
[871cfe0c]950 return get_bucket_idx_(key);
[e29ce3d]951 }
952
953 local_iterator begin(size_type n)
954 {
[871cfe0c]955 return local_iterator{table_[n].head, table_[n].head};
[e29ce3d]956 }
957
958 const_local_iterator begin(size_type n) const
959 {
[871cfe0c]960 return cbegin(n);
[e29ce3d]961 }
962
[871cfe0c]963 local_iterator end(size_type n)
[e29ce3d]964 {
[871cfe0c]965 return local_iterator{};
966 }
967
968 const_local_iterator end(size_type n) const
969 {
970 return cend(n);
[e29ce3d]971 }
972
973 const_local_iterator cbegin(size_type n) const
974 {
[871cfe0c]975 return const_local_iterator{table_[n].head, table_[n].head};
[e29ce3d]976 }
977
978 const_local_iterator cend(size_type n) const
979 {
[871cfe0c]980 return const_local_iterator{};
[e29ce3d]981 }
982
983 float load_factor() const noexcept
984 {
985 return size_ / static_cast<float>(bucket_count_);
986 }
987
988 float max_load_factor() const noexcept
989 {
990 return max_load_factor_;
991 }
992
993 void max_load_factor(float factor)
994 {
995 /**
996 * Note: According to the standard, this function
997 * can have no effect.
998 */
[1d5424a]999 // TODO: change max factor and rehash if needed
[e29ce3d]1000 }
1001
[1d5424a]1002 void rehash(size_type count)
[e29ce3d]1003 {
[1d5424a]1004 if (count < size_ / max_load_factor_)
1005 count = size_ / max_load_factor_;
1006
1007 // Note: This is the only place where an exception can be
1008 // thrown (no mem) in this function, so wrap it
1009 // in try-catch-rethrow.
1010 hash_table new_table{count, max_load_factor_};
1011
1012 for (std::size_t i = 0; i < bucket_count_; ++i)
1013 {
1014 auto head = table_[i].head;
1015 if (!head)
1016 continue;
1017
1018 auto current = head;
1019
1020 do
1021 {
1022 auto next = current->next;
1023
1024 current->next = current;
1025 current->prev = current;
1026
1027 auto where = Policy::find_insertion_spot(
1028 new_table, key_extractor_(current->value)
1029 );
1030
1031 /**
1032 * Note: We're rehashing, so we know each
1033 * key can be inserted.
1034 */
1035 auto new_bucket = get<0>(where);
1036 auto new_successor = get<1>(where);
1037
1038 if (new_successor)
1039 new_successor->append(current);
1040 else
1041 new_bucket->append(current);
1042
1043 current = next;
1044 } while (current != head);
1045
1046 table_[i].head = nullptr;
1047 }
1048
1049 new_table.size_ = size_;
1050 swap(new_table);
1051
1052 delete[] new_table.table_;
1053 new_table.table_ = nullptr;
[e29ce3d]1054 }
1055
[1d5424a]1056 void reserve(size_type count)
[e29ce3d]1057 {
[1d5424a]1058 rehash(count / max_load_factor_ + 1);
[e29ce3d]1059 }
1060
1061 bool is_eq_to(hash_table& other)
1062 {
1063 // TODO: implement
[871cfe0c]1064 return false;
[e29ce3d]1065 }
1066
1067 ~hash_table()
1068 {
1069 // Lists are deleted in ~hash_table_bucket.
[1d5424a]1070 if (table_)
1071 delete[] table_;
[e29ce3d]1072 }
1073
1074 void set_hint(const_iterator hint)
1075 {
1076 // TODO: hint_ should be a ptr and we extract it here,
1077 // then set it to nullptr after each operation
1078 }
1079
[871cfe0c]1080 hint_type find_insertion_spot(const key_type& key)
1081 {
1082 return Policy::find_insertion_spot(*this, key);
1083 }
1084
1085 hint_type find_insertion_spot(key_type&& key)
1086 {
1087 return Policy::find_insertion_spot(*this, key);
1088 }
1089
[875788a8]1090 const key_type& get_key(const value_type& val)
1091 {
1092 return key_extractor_(val);
1093 }
1094
[871cfe0c]1095 /* private: */
[e29ce3d]1096 hash_table_bucket<value_type, size_type>* table_;
1097 size_type bucket_count_;
1098 size_type size_;
1099 hasher hasher_;
1100 key_equal key_eq_;
[871cfe0c]1101 key_extract key_extractor_;
[e29ce3d]1102 float max_load_factor_;
1103
[871cfe0c]1104 size_type get_bucket_idx_(const key_type& key) const
1105 {
1106 return hasher_(key) % bucket_count_;
1107 }
[e29ce3d]1108
[871cfe0c]1109 bool hint_ok_(const hint_type& hint)
1110 {
1111 // TODO: pass this to the policy, because the multi policy
1112 // will need to check if a similar key is close,
1113 // that is something like:
1114 // return get<1>(hint)->prev->key == key || !bucket.contains(key)
[1d5424a]1115 // TODO: also, make it public and make hint usage one level above?
1116 // (since we already have insert with decisive hint)
[871cfe0c]1117 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
1118 }
1119
1120 // Praise C++11 for this.
1121 friend Policy;
[e29ce3d]1122 };
[871cfe0c]1123}
[e29ce3d]1124
1125#endif
Note: See TracBrowser for help on using the repository browser.