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

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

cpp: fixed constness error and added max_bucket_count implementation

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