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

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

cpp: removed no longer needed todos

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