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

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

cpp: implemented multi policy operations, fixed constness of some parameters

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