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

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

cpp: added copy/move constructor/assignment to the hash table

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