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

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

cpp: removed unnecessary key_extractor_ uses, rendunant increments that can be avoided with do/while instead of while, added keys_equal for constant context

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