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

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

cpp: added a prepend function to table bucket, this helps us keep equal keys in multi containers together

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