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

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

cpp: fixed hash_table::head and changed hint_type to a more descriptive typedef called place_type

  • Property mode set to 100644
File size: 42.9 KB
RevLine 
[e29ce3d]1/*
2 * Copyright (c) 2018 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_INTERNAL_HASH_TABLE
30#define LIBCPP_INTERNAL_HASH_TABLE
31
32#include <cstdlib>
33#include <internal/list.hpp>
[871cfe0c]34#include <iterator>
[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>
[947ad139]154 static typename Table::place_type find_insertion_spot(const Table& table, const Key& key)
[871cfe0c]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 }
[492377a]217
218 /**
219 * Note: We have to duplicate code for emplace, insert(const&)
220 * and insert(&&) here, because the node (which makes distinction
221 * between the arguments) is only created if the value isn't
222 * in the table already.
223 */
224
225 template<class Table, class... Args>
226 static pair<
227 typename Table::iterator, bool
228 > emplace(Table& table, Args&&... args)
229 {
230 using value_type = typename Table::value_type;
231 using node_type = typename Table::node_type;
232 using iterator = typename Table::iterator;
233
234 table.increment_size();
235
236 auto val = value_type{forward<Args>(args)...};
237 const auto& key = table.get_key(val);
238 auto [bucket, target, idx] = table.find_insertion_spot(key);
239
240 if (!bucket)
241 return make_pair(table.end(), false);
242
243 if (target && table.keys_equal(key, target->value))
244 {
245 table.decrement_size();
246
247 return make_pair(
248 iterator{
249 table.table(), idx, table.bucket_count(),
250 target
251 },
252 false
253 );
254 }
255 else
256 {
257 auto node = new node_type{move(val)};
258 bucket->prepend(node);
259
260 return make_pair(iterator{
261 table.table(), idx,
262 table.bucket_count(),
263 node
264 }, true);
265 }
266 }
267
268 template<class Table, class Value>
269 static pair<
270 typename Table::iterator, bool
271 > insert(Table& table, const Value& val)
272 {
273 using node_type = typename Table::node_type;
274 using iterator = typename Table::iterator;
275
276 table.increment_size();
277
278 const auto& key = table.get_key(val);
279 auto [bucket, target, idx] = table.find_insertion_spot(key);
280
281 if (!bucket)
282 return make_pair(table.end(), false);
283
284 if (target && table.keys_equal(key, target->value))
285 {
286 table.decrement_size();
287
288 return make_pair(
289 iterator{
290 table.table(), idx, table.bucket_count(),
291 target
292 },
293 false
294 );
295 }
296 else
297 {
298 auto node = new node_type{val};
299 bucket->prepend(node);
300
301 return make_pair(iterator{
302 table.table(), idx,
303 table.bucket_count(),
304 node
305 }, true);
306 }
307 }
308
309 template<class Table, class Value>
310 static pair<
311 typename Table::iterator, bool
312 > insert(Table& table, Value&& val)
313 {
314 using value_type = typename Table::value_type;
315 using node_type = typename Table::node_type;
316 using iterator = typename Table::iterator;
317
318 table.increment_size();
319
320 const auto& key = table.get_key(val);
321 auto [bucket, target, idx] = table.find_insertion_spot(key);
322
323 if (!bucket)
324 return make_pair(table.end(), false);
325
326 if (target && table.keys_equal(key, target->value))
327 {
328 table.decrement_size();
329
330 return make_pair(
331 iterator{
332 table.table(), idx, table.bucket_count(),
333 target
334 },
335 false
336 );
337 }
338 else
339 {
340 auto node = new node_type{forward<value_type>(val)};
341 bucket->prepend(node);
342
343 return make_pair(iterator{
344 table.table(), idx,
345 table.bucket_count(),
346 node
347 }, true);
348 }
349 }
[e29ce3d]350 };
351
352 struct hash_multi_policy
353 {
[871cfe0c]354 template<class Table, class Key>
355 static typename Table::size_type count(const Table& table, const Key& key)
356 {
357 auto head = table.table_[get_bucket_idx_(key)].head;
358 if (!head)
359 return 0;
[e29ce3d]360
[871cfe0c]361 auto current = head;
362 typename Table::size_type res = 0;
363 do
364 {
[99bf4c4]365 if (table.keys_equal(key, current->value))
[871cfe0c]366 ++res;
[e29ce3d]367
[871cfe0c]368 current = current->next;
369 }
370 while (current != head);
371
372 return res;
373 }
374
375 template<class Table, class Key>
[947ad139]376 static typename Table::place_type find_insertion_spot(const Table& table, const Key& key)
[e29ce3d]377 {
[7f379fe]378 auto idx = table.get_bucket_idx_(key);
379 auto head = table.table_[idx].head;
380
381 if (head)
382 {
383 auto current = head;
384 do
385 {
[99bf4c4]386 if (table.keys_equal(key, current->value))
[7f379fe]387 {
388 return make_tuple(
389 &table.table_[idx],
390 current,
391 idx
392 );
393 }
394
395 current = current->next;
396 } while (current != head);
397 }
398
399 return make_tuple(
400 &table.table_[idx],
401 table.table_[idx].head,
402 idx
403 );
[e29ce3d]404 }
[e9027b5]405
406 template<class Table, class Key>
407 static typename Table::size_type erase(Table& table, const Key& key)
408 {
[7f379fe]409 auto idx = table.get_bucket_idx_(key);
410 auto it = table.begin(it);
411 typename Table::size_type res{};
412
413 while (it != table.end(it))
414 {
[99bf4c4]415 if (table.keys_equal(key, *it))
[7f379fe]416 {
[99bf4c4]417 while (table.keys_equal(key, *it))
[7f379fe]418 {
419 auto node = it.node();
420 ++it;
421 ++res;
422
423 node.unlink();
424 delete node;
425 --table.size_;
426 }
427
428 // Elements with equal keys are next to each other.
429 return res;
430 }
431
432 ++it;
433 }
434
435 return res;
[e9027b5]436 }
[1d5424a]437
438 template<class Table, class Key>
439 static pair<
440 typename Table::iterator,
441 typename Table::iterator
[99bf4c4]442 > equal_range(Table& table, const Key& key)
[1d5424a]443 {
[7f379fe]444 auto first = table.find(key);
445 if (first == table.end())
[cfeeb61]446 return make_pair(table.end(), table.end());
[7f379fe]447
448 auto last = first;
[99bf4c4]449 do
450 {
[7f379fe]451 ++last;
[99bf4c4]452 } while (table.keys_equal(key, *last));
[7f379fe]453
454 return make_pair(first, last);
[1d5424a]455 }
456
457 template<class Table, class Key>
458 static pair<
459 typename Table::const_iterator,
460 typename Table::const_iterator
[7f379fe]461 > equal_range_const(const Table& table, const Key& key)
[1d5424a]462 {
[7f379fe]463 auto first = table.find(key);
464 if (first == table.end())
[cfeeb61]465 return make_pair(table.end(), table.end());
[7f379fe]466
467 auto last = first;
[99bf4c4]468 do
469 {
[7f379fe]470 ++last;
[99bf4c4]471 } while (table.keys_equal(key, *last));
[7f379fe]472
473 return make_pair(first, last);
[1d5424a]474 }
[492377a]475
476 template<class Table, class... Args>
477 static pair<
478 typename Table::iterator, bool
479 > emplace(Table& table, Args&&... args)
480 {
481 using node_type = typename Table::node_type;
482
483 auto node = new node_type{forward<Args>(args)...};
484
485 return insert(table, node);
486 }
487
488 template<class Table, class Value>
489 static pair<
490 typename Table::iterator, bool
491 > insert(Table& table, const Value& val)
492 {
493 using node_type = typename Table::node_type;
494
495 auto node = new node_type{val};
496
497 return insert(table, node);
498 }
499
500 template<class Table, class Value>
501 static pair<
502 typename Table::iterator, bool
503 > insert(Table& table, Value&& val)
504 {
505 using value_type = typename Table::value_type;
506 using node_type = typename Table::node_type;
507
508 auto node = new node_type{forward<value_type>(val)};
509
510 return insert(table, node);
511 }
512
513 template<class Table>
514 static pair<
515 typename Table::iterator, bool
516 > insert(Table& table, typename Table::node_type* node)
517 {
518 using iterator = typename Table::iterator;
519
520 table.increment_size();
521
522 const auto& key = table.get_key(node->value);
523 auto [bucket, target, idx] = table.find_insertion_spot(key);
524
525 if (!bucket)
526 return make_pair(table.end(), false);
527
528 if (target && table.keys_equal(key, target->value))
529 target->append(node);
530 else
531 bucket->prepend(node);
532
533 return make_pair(iterator{
534 table.table(), idx,
535 table.bucket_count(),
536 node
537 }, true);
538 }
[e29ce3d]539 };
540
[871cfe0c]541 template<class Value, class ConstReference, class ConstPointer, class Size>
542 class hash_table_const_iterator
543 {
544 public:
545 using value_type = Value;
546 using size_type = Size;
547 using const_reference = ConstReference;
548 using const_pointer = ConstPointer;
549 using difference_type = ptrdiff_t;
550
551 using iterator_category = forward_iterator_tag;
552
[e9027b5]553 hash_table_const_iterator(const hash_table_bucket<value_type, size_type>* table = nullptr,
[871cfe0c]554 size_type idx = size_type{}, size_type max_idx = size_type{},
[e9027b5]555 const list_node<value_type>* current = nullptr)
[871cfe0c]556 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
557 { /* DUMMY BODY */ }
558
559 hash_table_const_iterator(const hash_table_const_iterator&) = default;
560 hash_table_const_iterator& operator=(const hash_table_const_iterator&) = default;
561
562 const_reference operator*() const
563 {
564 return current_->value;
565 }
566
567 const_pointer operator->() const
568 {
569 return &current_->value;
570 }
571
572 hash_table_const_iterator& operator++()
573 {
574 current_ = current_->next;
575 if (current_ == table_[idx_].head)
576 {
577 if (idx_ < max_idx_)
578 {
579 while (!table_[++idx_].head)
580 { /* DUMMY BODY */ }
581
582 if (idx_ < max_idx_)
583 current_ = table_[idx_].head;
584 else
585 current_ = nullptr;
586 }
587 else
588 current_ = nullptr;
589 }
590
591 return *this;
592 }
593
594 hash_table_const_iterator operator++(int)
595 {
596 auto tmp_current = current_;
597 auto tmp_idx = idx_;
598
599 current_ = current_->next;
600 if (current_ == table_[idx_].head)
601 {
602 if (idx_ < max_idx_)
603 {
604 while (!table_[++idx_].head)
605 { /* DUMMY BODY */ }
606
607 if (idx_ < max_idx_)
608 current_ = table_[idx_].head;
609 else
610 current_ = nullptr;
611 }
612 else
613 current_ = nullptr;
614 }
615
616 return hash_table_const_iterator{
617 table_, tmp_idx, max_idx_, tmp_current
618 };
619 }
620
621 list_node<value_type>* node()
622 {
[e9027b5]623 return const_cast<list_node<value_type>*>(current_);
[871cfe0c]624 }
625
626 const list_node<value_type>* node() const
627 {
628 return current_;
629 }
630
[e9027b5]631 size_type idx() const
632 {
633 return idx_;
634 }
635
[871cfe0c]636 private:
[e9027b5]637 const hash_table_bucket<value_type, size_type>* table_;
[871cfe0c]638 size_type idx_;
639 size_type max_idx_;
[e9027b5]640 const list_node<value_type>* current_;
[871cfe0c]641 };
642
643 template<class Value, class ConstRef, class ConstPtr, class Size>
644 bool operator==(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
645 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
646 {
647 return lhs.node() == rhs.node();
648 }
649
650 template<class Value, class ConstRef, class ConstPtr, class Size>
651 bool operator!=(const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& lhs,
652 const hash_table_const_iterator<Value, ConstRef, ConstPtr, Size>& rhs)
653 {
654 return !(lhs == rhs);
655 }
656
657 template<class Value, class Reference, class Pointer, class Size>
658 class hash_table_iterator
659 {
660 public:
661 using value_type = Value;
662 using size_type = Size;
663 using reference = Reference;
664 using pointer = Pointer;
665 using difference_type = ptrdiff_t;
666
667 using iterator_category = forward_iterator_tag;
668
669 hash_table_iterator(hash_table_bucket<value_type, size_type>* table = nullptr,
670 size_type idx = size_type{}, size_type max_idx = size_type{},
671 list_node<value_type>* current = nullptr)
672 : table_{table}, idx_{idx}, max_idx_{max_idx}, current_{current}
673 { /* DUMMY BODY */ }
674
675 hash_table_iterator(const hash_table_iterator&) = default;
676 hash_table_iterator& operator=(const hash_table_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_iterator& operator++()
689 {
690 current_ = current_->next;
691 if (current_ == table_[idx_].head)
692 {
693 if (idx_ < max_idx_)
694 {
695 while (!table_[++idx_].head)
696 { /* DUMMY BODY */ }
697
698 if (idx_ < max_idx_)
699 current_ = table_[idx_].head;
700 else
701 current_ = nullptr;
702 }
703 else
704 current_ = nullptr;
705 }
706
707 return *this;
708 }
709
710 hash_table_iterator operator++(int)
711 {
712 auto tmp_current = current_;
713 auto tmp_idx = idx_;
714
715 current_ = current_->next;
716 if (current_ == table_[idx_].head)
717 {
718 if (idx_ < max_idx_)
719 {
720 while (!table_[++idx_].head)
721 { /* DUMMY BODY */ }
722
723 if (idx_ < max_idx_)
724 current_ = table_[idx_].head;
725 else
726 current_ = nullptr;
727 }
728 else
729 current_ = nullptr;
730 }
731
732 return hash_table_iterator{
733 table_, tmp_idx, max_idx_, tmp_current
734 };
735 }
736
737 list_node<value_type>* node()
738 {
739 return current_;
740 }
741
742 const list_node<value_type>* node() const
743 {
744 return current_;
745 }
746
[e9027b5]747 size_type idx() const
748 {
749 return idx_;
750 }
751
[871cfe0c]752 template<class ConstRef, class ConstPtr>
753 operator hash_table_const_iterator<
754 Value, ConstRef, ConstPtr, Size
755 >() const
756 {
[e9027b5]757 return hash_table_const_iterator<value_type, ConstRef, ConstPtr, size_type>{
[871cfe0c]758 table_, idx_, max_idx_, current_
759 };
760 }
761
762 private:
763 hash_table_bucket<value_type, size_type>* table_;
764 size_type idx_;
765 size_type max_idx_;
766 list_node<value_type>* current_;
767 };
768
769 template<class Value, class Ref, class Ptr, class Size>
770 bool operator==(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
771 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
772 {
773 return lhs.node() == rhs.node();
774 }
775
776 template<class Value, class Ref, class Ptr, class Size>
777 bool operator!=(const hash_table_iterator<Value, Ref, Ptr, Size>& lhs,
778 const hash_table_iterator<Value, Ref, Ptr, Size>& rhs)
779 {
780 return !(lhs == rhs);
781 }
782
783 template<class Value, class ConstReference, class ConstPointer>
784 class hash_table_const_local_iterator
785 {
786 public:
787 using value_type = Value;
788 using const_reference = ConstReference;
789 using const_pointer = ConstPointer;
790 using difference_type = ptrdiff_t;
791
792 using iterator_category = forward_iterator_tag;
793
794 // TODO: requirement for forward iterator is default constructibility, fix others!
[e9027b5]795 hash_table_const_local_iterator(const list_node<value_type>* head = nullptr,
796 const list_node<value_type>* current = nullptr)
[871cfe0c]797 : head_{head}, current_{current}
798 { /* DUMMY BODY */ }
799
800 hash_table_const_local_iterator(const hash_table_const_local_iterator&) = default;
801 hash_table_const_local_iterator& operator=(const hash_table_const_local_iterator&) = default;
802
803 const_reference operator*() const
804 {
805 return current_->value;
806 }
807
808 const_pointer operator->() const
809 {
810 return &current_->value;
811 }
812
813 hash_table_const_local_iterator& operator++()
814 {
815 current_ = current_->next;
816 if (current_ == head_)
817 current_ = nullptr;
818
819 return *this;
820 }
821
822 hash_table_const_local_iterator operator++(int)
823 {
824 auto tmp = current_;
825 current_ = current_->next;
826 if (current_ == head_)
827 current_ = nullptr;
828
829 return hash_table_const_local_iterator{head_, tmp};
830 }
831
[e9027b5]832
[871cfe0c]833 list_node<value_type>* node()
834 {
[e9027b5]835 return const_cast<list_node<value_type>*>(current_);
[871cfe0c]836 }
837
838 const list_node<value_type>* node() const
839 {
840 return current_;
841 }
842
843 private:
[e9027b5]844 const list_node<value_type>* head_;
845 const list_node<value_type>* current_;
[871cfe0c]846 };
847
848 template<class Value, class ConstRef, class ConstPtr>
849 bool operator==(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
850 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
851 {
852 return lhs.node() == rhs.node();
853 }
854
855 template<class Value, class ConstRef, class ConstPtr>
856 bool operator!=(const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& lhs,
857 const hash_table_const_local_iterator<Value, ConstRef, ConstPtr>& rhs)
858 {
859 return !(lhs == rhs);
860 }
861
862 template<class Value, class Reference, class Pointer>
863 class hash_table_local_iterator
864 {
865 public:
866 using value_type = Value;
867 using reference = Reference;
868 using pointer = Pointer;
869 using difference_type = ptrdiff_t;
870
871 using iterator_category = forward_iterator_tag;
872
873 hash_table_local_iterator(list_node<value_type>* head = nullptr,
874 list_node<value_type>* current = nullptr)
875 : head_{head}, current_{current}
876 { /* DUMMY BODY */ }
877
878 hash_table_local_iterator(const hash_table_local_iterator&) = default;
879 hash_table_local_iterator& operator=(const hash_table_local_iterator&) = default;
880
881 reference operator*()
882 {
883 return current_->value;
884 }
885
886 pointer operator->()
887 {
888 return &current_->value;
889 }
890
891 hash_table_local_iterator& operator++()
892 {
893 current_ = current_->next;
894 if (current_ == head_)
895 current_ = nullptr;
896
897 return *this;
898 }
899
900 hash_table_local_iterator operator++(int)
901 {
902 auto tmp = current_;
903 current_ = current_->next;
904 if (current_ == head_)
905 current_ = nullptr;
906
907 return hash_table_local_iterator{head_, tmp};
908 }
909
910 list_node<value_type>* node()
911 {
912 return current_;
913 }
914
915 const list_node<value_type>* node() const
916 {
917 return current_;
918 }
919
920 template<class ConstRef, class ConstPtr>
921 operator hash_table_const_local_iterator<
922 Value, ConstRef, ConstPtr
923 >() const
924 {
[e9027b5]925 return hash_table_const_local_iterator<
926 value_type, ConstRef, ConstPtr
927 >{head_, current_};
[871cfe0c]928 }
929
930 private:
931 list_node<value_type>* head_;
932 list_node<value_type>* current_;
933 };
934
935 template<class Value, class Ref, class Ptr>
936 bool operator==(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
937 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
938 {
939 return lhs.node() == rhs.node();
940 }
941
942 template<class Value, class Ref, class Ptr>
943 bool operator!=(const hash_table_local_iterator<Value, Ref, Ptr>& lhs,
944 const hash_table_local_iterator<Value, Ref, Ptr>& rhs)
945 {
946 return !(lhs == rhs);
947 }
[e29ce3d]948
949 template<
950 class Value, class Key, class KeyExtractor,
951 class Hasher, class KeyEq,
952 class Alloc, class Size,
953 class Iterator, class ConstIterator,
[871cfe0c]954 class LocalIterator, class ConstLocalIterator,
[e29ce3d]955 class Policy
956 >
957 class hash_table
958 {
959 public:
960 using value_type = Value;
961 using key_type = Key;
962 using size_type = Size;
963 using allocator_type = Alloc;
964 using key_equal = KeyEq;
965 using hasher = Hasher;
966 using key_extract = KeyExtractor;
967
968 using iterator = Iterator;
969 using const_iterator = ConstIterator;
970 using local_iterator = LocalIterator;
971 using const_local_iterator = ConstLocalIterator;
972
[86b3ae98]973 using node_type = list_node<value_type>;
974
[947ad139]975 using place_type = tuple<
[871cfe0c]976 hash_table_bucket<value_type, size_type>*,
977 list_node<value_type>*, size_type
978 >;
979
980 hash_table(size_type buckets, float max_load_factor = 1.f)
[1d5424a]981 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
[871cfe0c]982 bucket_count_{buckets}, size_{}, hasher_{}, key_eq_{},
983 key_extractor_{}, max_load_factor_{max_load_factor}
984 { /* DUMMY BODY */ }
[e29ce3d]985
[8ec1cd2]986 hash_table(size_type buckets, const hasher& hf, const key_equal& eql,
987 float max_load_factor = 1.f)
988 : table_{new hash_table_bucket<value_type, size_type>[buckets]()},
989 bucket_count_{buckets}, size_{}, hasher_{hf}, key_eq_{eql},
990 key_extractor_{}, max_load_factor_{max_load_factor}
991 { /* DUMMY BODY */ }
992
[b9076db]993 hash_table(const hash_table& other)
994 : hash_table{other.bucket_count_, other.hasher_, other.key_eq_,
995 other.max_load_factor_}
996 {
997 for (const auto& x: other)
998 {
999 auto spot = find_insertion_spot(key_extractor_(x));
1000 insert(spot, x);
1001 }
1002 }
1003
1004 hash_table(hash_table&& other)
1005 : table_{other.table_}, bucket_count_{other.bucket_count_},
1006 size_{other.size_}, hasher_{move(other.hasher_)},
1007 key_eq_{move(other.key_eq_)}, key_extractor_{move(other.key_extractor_)},
1008 max_load_factor_{other.max_load_factor_}
1009 {
1010 other.table_ = nullptr;
1011 other.bucket_count_ = size_type{};
1012 other.size_ = size_type{};
1013 other.max_load_factor_ = 1.f;
1014 }
1015
1016 hash_table& operator=(const hash_table& other)
1017 {
1018 hash_table tmp{other};
1019 tmp.swap(*this);
1020
1021 return *this;
1022 }
1023
1024 hash_table& operator=(hash_table&& other)
1025 {
1026 hash_table tmp{move(other)};
1027 tmp.swap(*this);
1028
1029 return *this;
1030 }
[8ec1cd2]1031
[e29ce3d]1032 bool empty() const noexcept
1033 {
1034 return size_ == 0;
1035 }
1036
1037 size_type size() const noexcept
1038 {
1039 return size_;
1040 }
1041
1042 template<class Allocator>
1043 size_type max_size(Allocator& alloc)
1044 {
1045 return allocator_traits<Allocator>::max_size(alloc);
1046 }
1047
1048 iterator begin() noexcept
1049 {
[871cfe0c]1050 return iterator{
1051 table_, size_type{}, bucket_count_,
1052 table_[0].head
1053 };
[e29ce3d]1054 }
1055
1056 const_iterator begin() const noexcept
1057 {
[871cfe0c]1058 return cbegin();
[e29ce3d]1059 }
1060
1061 iterator end() noexcept
1062 {
[871cfe0c]1063 return iterator{};
[e29ce3d]1064 }
1065
1066 const_iterator end() const noexcept
1067 {
[871cfe0c]1068 return cend();
[e29ce3d]1069 }
1070
1071 const_iterator cbegin() const noexcept
1072 {
[871cfe0c]1073 return const_iterator{
1074 table_, size_type{}, bucket_count_,
1075 table_[0].head
1076 };
[e29ce3d]1077 }
1078
1079 const_iterator cend() const noexcept
1080 {
[871cfe0c]1081 return const_iterator{};
[e29ce3d]1082 }
1083
[492377a]1084 template<class... Args>
1085 pair<iterator, bool> emplace(Args&&... args)
[e29ce3d]1086 {
[492377a]1087 return Policy::emplace(*this, forward<Args>(args)...);
[e29ce3d]1088 }
1089
[492377a]1090 pair<iterator, bool> insert(const value_type& val)
[e29ce3d]1091 {
[492377a]1092 return Policy::insert(*this, val);
[871cfe0c]1093 }
1094
[492377a]1095 pair<iterator, bool> insert(value_type&& val)
[871cfe0c]1096 {
[492377a]1097 return Policy::insert(*this, forward<value_type>(val));
[e29ce3d]1098 }
1099
1100 size_type erase(const key_type& key)
1101 {
[e9027b5]1102 return Policy::erase(*this, key);
[e29ce3d]1103 }
1104
1105 iterator erase(const_iterator it)
1106 {
[e9027b5]1107 auto node = it.node();
1108 auto idx = it.idx();
1109
1110 /**
1111 * Note: This way we will continue on the next bucket
1112 * if this is the last element in its bucket.
1113 */
1114 iterator res{table_, idx, size_, node};
1115 ++res;
1116
1117 if (table_[idx].head == node)
1118 {
1119 if (node->next != node)
1120 table_[idx].head = node->next;
1121 else
1122 table_[idx].head = nullptr;
1123 }
1124
1125 node->unlink();
1126 delete node;
1127
1128 return res;
[e29ce3d]1129 }
1130
1131 void clear() noexcept
1132 {
[7320ca6]1133 for (size_type i = 0; i < bucket_count_; ++i)
1134 table_[i].clear();
[871cfe0c]1135 size_ = size_type{};
[e29ce3d]1136 }
1137
1138 void swap(hash_table& other)
[1d5424a]1139 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
[e29ce3d]1140 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
1141 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
1142 {
[871cfe0c]1143 std::swap(table_, other.table_);
1144 std::swap(bucket_count_, other.bucket_count_);
1145 std::swap(size_, other.size_);
1146 std::swap(hasher_, other.hasher_);
1147 std::swap(key_eq_, other.key_eq_);
1148 std::swap(max_load_factor_, other.max_load_factor_);
[e29ce3d]1149 }
1150
1151 hasher hash_function() const
1152 {
1153 return hasher_;
1154 }
1155
1156 key_equal key_eq() const
1157 {
1158 return key_eq_;
1159 }
1160
1161 iterator find(const key_type& key)
1162 {
[1d5424a]1163 auto idx = get_bucket_idx_(key);
1164 auto head = table_[idx].head;
1165
1166 if (!head)
1167 return end();
1168
1169 auto current = head;
1170 do
1171 {
1172 if (key_eq_(key, key_extractor_(current->value)))
1173 return iterator{table_, idx, size_, current};
1174 current = current->next;
1175 }
1176 while (current != head);
1177
1178 return end();
[e29ce3d]1179 }
1180
1181 const_iterator find(const key_type& key) const
1182 {
[1d5424a]1183 auto idx = get_bucket_idx_(key);
1184 auto head = table_[idx].head;
1185
1186 if (!head)
1187 return end();
1188
1189 auto current = head;
1190 do
1191 {
1192 if (key_eq_(key, key_extractor_(current->value)))
1193 return iterator{table_, idx, size_, current};
1194 current = current->next;
1195 }
1196 while (current != head);
1197
1198 return end();
[e29ce3d]1199 }
1200
1201 size_type count(const key_type& key) const
1202 {
[871cfe0c]1203 return Policy::count(*this, key);
[e29ce3d]1204 }
1205
1206 pair<iterator, iterator> equal_range(const key_type& key)
1207 {
[1d5424a]1208 return Policy::equal_range(*this, key);
[e29ce3d]1209 }
1210
1211 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
1212 {
[1d5424a]1213 return Policy::equal_range_const(*this, key);
[e29ce3d]1214 }
1215
1216 size_type bucket_count() const noexcept
1217 {
1218 return bucket_count_;
1219 }
1220
1221 size_type max_bucket_count() const noexcept
1222 {
[db628a0]1223 return numeric_limits<size_type>::max() /
1224 sizeof(hash_table_bucket<value_type, size_type>);
[e29ce3d]1225 }
1226
1227 size_type bucket_size(size_type n) const
1228 {
[871cfe0c]1229 return table_[n].size();
[e29ce3d]1230 }
1231
1232 size_type bucket(const key_type& key) const
1233 {
[871cfe0c]1234 return get_bucket_idx_(key);
[e29ce3d]1235 }
1236
1237 local_iterator begin(size_type n)
1238 {
[871cfe0c]1239 return local_iterator{table_[n].head, table_[n].head};
[e29ce3d]1240 }
1241
1242 const_local_iterator begin(size_type n) const
1243 {
[871cfe0c]1244 return cbegin(n);
[e29ce3d]1245 }
1246
[871cfe0c]1247 local_iterator end(size_type n)
[e29ce3d]1248 {
[871cfe0c]1249 return local_iterator{};
1250 }
1251
1252 const_local_iterator end(size_type n) const
1253 {
1254 return cend(n);
[e29ce3d]1255 }
1256
1257 const_local_iterator cbegin(size_type n) const
1258 {
[871cfe0c]1259 return const_local_iterator{table_[n].head, table_[n].head};
[e29ce3d]1260 }
1261
1262 const_local_iterator cend(size_type n) const
1263 {
[871cfe0c]1264 return const_local_iterator{};
[e29ce3d]1265 }
1266
1267 float load_factor() const noexcept
1268 {
1269 return size_ / static_cast<float>(bucket_count_);
1270 }
1271
1272 float max_load_factor() const noexcept
1273 {
1274 return max_load_factor_;
1275 }
1276
1277 void max_load_factor(float factor)
1278 {
[e912cdf]1279 if (factor > 0.f)
1280 max_load_factor_ = factor;
1281
1282 rehash_if_needed();
[e29ce3d]1283 }
1284
[1d5424a]1285 void rehash(size_type count)
[e29ce3d]1286 {
[1d5424a]1287 if (count < size_ / max_load_factor_)
1288 count = size_ / max_load_factor_;
1289
[b9076db]1290 /**
1291 * Note: If an exception is thrown, there
1292 * is no effect. Since this is the only
1293 * place where an exception (no mem) can
1294 * be thrown and no changes to this have been
1295 * made, we're ok.
1296 */
[1d5424a]1297 hash_table new_table{count, max_load_factor_};
1298
1299 for (std::size_t i = 0; i < bucket_count_; ++i)
1300 {
1301 auto head = table_[i].head;
1302 if (!head)
1303 continue;
1304
1305 auto current = head;
1306
1307 do
1308 {
1309 auto next = current->next;
1310
1311 current->next = current;
1312 current->prev = current;
1313
1314 auto where = Policy::find_insertion_spot(
1315 new_table, key_extractor_(current->value)
1316 );
1317
1318 /**
1319 * Note: We're rehashing, so we know each
1320 * key can be inserted.
1321 */
1322 auto new_bucket = get<0>(where);
1323 auto new_successor = get<1>(where);
1324
1325 if (new_successor)
1326 new_successor->append(current);
1327 else
1328 new_bucket->append(current);
1329
1330 current = next;
1331 } while (current != head);
1332
1333 table_[i].head = nullptr;
1334 }
1335
1336 new_table.size_ = size_;
1337 swap(new_table);
1338
1339 delete[] new_table.table_;
1340 new_table.table_ = nullptr;
[e29ce3d]1341 }
1342
[1d5424a]1343 void reserve(size_type count)
[e29ce3d]1344 {
[1d5424a]1345 rehash(count / max_load_factor_ + 1);
[e29ce3d]1346 }
1347
[947ad139]1348 bool is_eq_to(const hash_table& other) const
[e29ce3d]1349 {
1350 // TODO: implement
[871cfe0c]1351 return false;
[e29ce3d]1352 }
1353
1354 ~hash_table()
1355 {
1356 // Lists are deleted in ~hash_table_bucket.
[1d5424a]1357 if (table_)
1358 delete[] table_;
[e29ce3d]1359 }
1360
[947ad139]1361 place_type find_insertion_spot(const key_type& key) const
[871cfe0c]1362 {
1363 return Policy::find_insertion_spot(*this, key);
1364 }
1365
[947ad139]1366 place_type find_insertion_spot(key_type&& key) const
[871cfe0c]1367 {
1368 return Policy::find_insertion_spot(*this, key);
1369 }
1370
[947ad139]1371 const key_type& get_key(const value_type& val) const
[875788a8]1372 {
1373 return key_extractor_(val);
1374 }
1375
[86b3ae98]1376 bool keys_equal(const key_type& key, const value_type& val)
[8ec1cd2]1377 {
[86b3ae98]1378 return key_eq_(key, key_extractor_(val));
[8ec1cd2]1379 }
1380
[99bf4c4]1381 bool keys_equal(const key_type& key, const value_type& val) const
1382 {
1383 return key_eq_(key, key_extractor_(val));
1384 }
1385
[86b3ae98]1386 hash_table_bucket<value_type, size_type>* table()
[8ec1cd2]1387 {
[86b3ae98]1388 return table_;
[8ec1cd2]1389 }
1390
[86b3ae98]1391 hash_table_bucket<value_type, size_type>* head(size_type idx)
1392 {
1393 if (idx < bucket_count_)
[947ad139]1394 return table_[idx]->head;
[86b3ae98]1395 else
1396 return nullptr;
1397 }
1398
[3be3752]1399 void rehash_if_needed()
1400 {
1401 if (size_ > max_load_factor_ * bucket_count_)
1402 rehash(bucket_count_ * bucket_count_growth_factor_);
1403 }
1404
1405 void increment_size()
1406 {
1407 ++size_;
[e912cdf]1408
1409 rehash_if_needed();
1410 }
1411
1412 void decrement_size()
1413 {
1414 --size_;
1415 }
1416
[86b3ae98]1417 private:
[e29ce3d]1418 hash_table_bucket<value_type, size_type>* table_;
1419 size_type bucket_count_;
1420 size_type size_;
1421 hasher hasher_;
1422 key_equal key_eq_;
[871cfe0c]1423 key_extract key_extractor_;
[e29ce3d]1424 float max_load_factor_;
1425
[3be3752]1426 static constexpr float bucket_count_growth_factor_{1.25};
1427
[871cfe0c]1428 size_type get_bucket_idx_(const key_type& key) const
1429 {
1430 return hasher_(key) % bucket_count_;
1431 }
[e29ce3d]1432
[871cfe0c]1433 friend Policy;
[e29ce3d]1434 };
[871cfe0c]1435}
[e29ce3d]1436
1437#endif
Note: See TracBrowser for help on using the repository browser.