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

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

cpp: added aux functions and rehashing to insertion when needed

  • Property mode set to 100644
File size: 35.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>
[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
722 // TODO: copy/move constructor/assignment operator
723
[e29ce3d]724 bool empty() const noexcept
725 {
726 return size_ == 0;
727 }
728
729 size_type size() const noexcept
730 {
731 return size_;
732 }
733
734 template<class Allocator>
735 size_type max_size(Allocator& alloc)
736 {
737 return allocator_traits<Allocator>::max_size(alloc);
738 }
739
740 iterator begin() noexcept
741 {
[871cfe0c]742 return iterator{
743 table_, size_type{}, bucket_count_,
744 table_[0].head
745 };
[e29ce3d]746 }
747
748 const_iterator begin() const noexcept
749 {
[871cfe0c]750 return cbegin();
[e29ce3d]751 }
752
753 iterator end() noexcept
754 {
[871cfe0c]755 return iterator{};
[e29ce3d]756 }
757
758 const_iterator end() const noexcept
759 {
[871cfe0c]760 return cend();
[e29ce3d]761 }
762
763 const_iterator cbegin() const noexcept
764 {
[871cfe0c]765 return const_iterator{
766 table_, size_type{}, bucket_count_,
767 table_[0].head
768 };
[e29ce3d]769 }
770
771 const_iterator cend() const noexcept
772 {
[871cfe0c]773 return const_iterator{};
[e29ce3d]774 }
775
776 template<class Allocator, class... Args>
[1d5424a]777 void emplace(const hint_type& where, Allocator& alloc, Args&&... args)
[e29ce3d]778 {
[1d5424a]779 if (!hint_ok_(where))
780 return;
781
782 auto node = new list_node<value_type>{forward<Args&&>(args)...};
783 if (get<1>(where) == nullptr) // Append here will create a new head.
784 get<0>(where)->append(node);
785 else // Prepending before an exact position is common in the standard.
786 get<1>(where)->prepend(node);
787
788 ++size_;
[3be3752]789
790 rehash_if_needed();
[e29ce3d]791 }
792
[871cfe0c]793 void insert(const hint_type& where, const value_type& val)
[e29ce3d]794 {
[871cfe0c]795 if (!hint_ok_(where))
796 return;
797
798 auto node = new list_node<value_type>{val};
[1d5424a]799 if (get<1>(where) == nullptr)
[871cfe0c]800 get<0>(where)->append(node);
[1d5424a]801 else
[871cfe0c]802 get<1>(where)->prepend(node);
803
804 ++size_;
[3be3752]805
806 rehash_if_needed();
[871cfe0c]807 }
808
809 void insert(const hint_type& where, value_type&& val)
810 {
811 if (!hint_ok_(where))
812 return;
813
814 auto node = new list_node<value_type>{forward<value_type>(val)};
815 if (get<1>(where) == nullptr)
816 get<0>(where)->append(node);
817 else
818 get<1>(where)->prepend(node);
819
820 ++size_;
[3be3752]821
822 rehash_if_needed();
[e29ce3d]823 }
824
825 size_type erase(const key_type& key)
826 {
[e9027b5]827 return Policy::erase(*this, key);
[e29ce3d]828 }
829
830 iterator erase(const_iterator it)
831 {
[e9027b5]832 auto node = it.node();
833 auto idx = it.idx();
834
835 /**
836 * Note: This way we will continue on the next bucket
837 * if this is the last element in its bucket.
838 */
839 iterator res{table_, idx, size_, node};
840 ++res;
841
842 if (table_[idx].head == node)
843 {
844 if (node->next != node)
845 table_[idx].head = node->next;
846 else
847 table_[idx].head = nullptr;
848 }
849
850 node->unlink();
851 delete node;
852
853 return res;
[e29ce3d]854 }
855
856 void clear() noexcept
857 {
[7320ca6]858 for (size_type i = 0; i < bucket_count_; ++i)
859 table_[i].clear();
[871cfe0c]860 size_ = size_type{};
[e29ce3d]861 }
862
863 void swap(hash_table& other)
[1d5424a]864 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
[e29ce3d]865 noexcept(swap(declval<Hasher&>(), declval<Hasher&>())) &&
866 noexcept(swap(declval<KeyEq&>(), declval<KeyEq&>())))
867 {
[871cfe0c]868 std::swap(table_, other.table_);
869 std::swap(bucket_count_, other.bucket_count_);
870 std::swap(size_, other.size_);
871 std::swap(hasher_, other.hasher_);
872 std::swap(key_eq_, other.key_eq_);
873 std::swap(max_load_factor_, other.max_load_factor_);
[e29ce3d]874 }
875
876 hasher hash_function() const
877 {
878 return hasher_;
879 }
880
881 key_equal key_eq() const
882 {
883 return key_eq_;
884 }
885
886 iterator find(const key_type& key)
887 {
[1d5424a]888 auto idx = get_bucket_idx_(key);
889 auto head = table_[idx].head;
890
891 if (!head)
892 return end();
893
894 auto current = head;
895 do
896 {
897 if (key_eq_(key, key_extractor_(current->value)))
898 return iterator{table_, idx, size_, current};
899 current = current->next;
900 }
901 while (current != head);
902
903 return end();
[e29ce3d]904 }
905
906 const_iterator find(const key_type& key) const
907 {
[1d5424a]908 auto idx = get_bucket_idx_(key);
909 auto head = table_[idx].head;
910
911 if (!head)
912 return end();
913
914 auto current = head;
915 do
916 {
917 if (key_eq_(key, key_extractor_(current->value)))
918 return iterator{table_, idx, size_, current};
919 current = current->next;
920 }
921 while (current != head);
922
923 return end();
[e29ce3d]924 }
925
926 size_type count(const key_type& key) const
927 {
[871cfe0c]928 return Policy::count(*this, key);
[e29ce3d]929 }
930
931 pair<iterator, iterator> equal_range(const key_type& key)
932 {
[1d5424a]933 return Policy::equal_range(*this, key);
[e29ce3d]934 }
935
936 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
937 {
[1d5424a]938 return Policy::equal_range_const(*this, key);
[e29ce3d]939 }
940
941 size_type bucket_count() const noexcept
942 {
943 return bucket_count_;
944 }
945
946 size_type max_bucket_count() const noexcept
947 {
948 // TODO: implement
949 return 0;
950 }
951
952 size_type bucket_size(size_type n) const
953 {
[871cfe0c]954 return table_[n].size();
[e29ce3d]955 }
956
957 size_type bucket(const key_type& key) const
958 {
[871cfe0c]959 return get_bucket_idx_(key);
[e29ce3d]960 }
961
962 local_iterator begin(size_type n)
963 {
[871cfe0c]964 return local_iterator{table_[n].head, table_[n].head};
[e29ce3d]965 }
966
967 const_local_iterator begin(size_type n) const
968 {
[871cfe0c]969 return cbegin(n);
[e29ce3d]970 }
971
[871cfe0c]972 local_iterator end(size_type n)
[e29ce3d]973 {
[871cfe0c]974 return local_iterator{};
975 }
976
977 const_local_iterator end(size_type n) const
978 {
979 return cend(n);
[e29ce3d]980 }
981
982 const_local_iterator cbegin(size_type n) const
983 {
[871cfe0c]984 return const_local_iterator{table_[n].head, table_[n].head};
[e29ce3d]985 }
986
987 const_local_iterator cend(size_type n) const
988 {
[871cfe0c]989 return const_local_iterator{};
[e29ce3d]990 }
991
992 float load_factor() const noexcept
993 {
994 return size_ / static_cast<float>(bucket_count_);
995 }
996
997 float max_load_factor() const noexcept
998 {
999 return max_load_factor_;
1000 }
1001
1002 void max_load_factor(float factor)
1003 {
1004 /**
1005 * Note: According to the standard, this function
1006 * can have no effect.
1007 */
[1d5424a]1008 // TODO: change max factor and rehash if needed
[e29ce3d]1009 }
1010
[1d5424a]1011 void rehash(size_type count)
[e29ce3d]1012 {
[1d5424a]1013 if (count < size_ / max_load_factor_)
1014 count = size_ / max_load_factor_;
1015
1016 // Note: This is the only place where an exception can be
1017 // thrown (no mem) in this function, so wrap it
1018 // in try-catch-rethrow.
1019 hash_table new_table{count, max_load_factor_};
1020
1021 for (std::size_t i = 0; i < bucket_count_; ++i)
1022 {
1023 auto head = table_[i].head;
1024 if (!head)
1025 continue;
1026
1027 auto current = head;
1028
1029 do
1030 {
1031 auto next = current->next;
1032
1033 current->next = current;
1034 current->prev = current;
1035
1036 auto where = Policy::find_insertion_spot(
1037 new_table, key_extractor_(current->value)
1038 );
1039
1040 /**
1041 * Note: We're rehashing, so we know each
1042 * key can be inserted.
1043 */
1044 auto new_bucket = get<0>(where);
1045 auto new_successor = get<1>(where);
1046
1047 if (new_successor)
1048 new_successor->append(current);
1049 else
1050 new_bucket->append(current);
1051
1052 current = next;
1053 } while (current != head);
1054
1055 table_[i].head = nullptr;
1056 }
1057
1058 new_table.size_ = size_;
1059 swap(new_table);
1060
1061 delete[] new_table.table_;
1062 new_table.table_ = nullptr;
[e29ce3d]1063 }
1064
[1d5424a]1065 void reserve(size_type count)
[e29ce3d]1066 {
[1d5424a]1067 rehash(count / max_load_factor_ + 1);
[e29ce3d]1068 }
1069
1070 bool is_eq_to(hash_table& other)
1071 {
1072 // TODO: implement
[871cfe0c]1073 return false;
[e29ce3d]1074 }
1075
1076 ~hash_table()
1077 {
1078 // Lists are deleted in ~hash_table_bucket.
[1d5424a]1079 if (table_)
1080 delete[] table_;
[e29ce3d]1081 }
1082
1083 void set_hint(const_iterator hint)
1084 {
1085 // TODO: hint_ should be a ptr and we extract it here,
1086 // then set it to nullptr after each operation
1087 }
1088
[871cfe0c]1089 hint_type find_insertion_spot(const key_type& key)
1090 {
1091 return Policy::find_insertion_spot(*this, key);
1092 }
1093
1094 hint_type find_insertion_spot(key_type&& key)
1095 {
1096 return Policy::find_insertion_spot(*this, key);
1097 }
1098
[875788a8]1099 const key_type& get_key(const value_type& val)
1100 {
1101 return key_extractor_(val);
1102 }
1103
[86b3ae98]1104 bool keys_equal(const key_type& key, const value_type& val)
[8ec1cd2]1105 {
[86b3ae98]1106 return key_eq_(key, key_extractor_(val));
[8ec1cd2]1107 }
1108
[86b3ae98]1109 hash_table_bucket<value_type, size_type>* table()
[8ec1cd2]1110 {
[86b3ae98]1111 return table_;
[8ec1cd2]1112 }
1113
[86b3ae98]1114 hash_table_bucket<value_type, size_type>* head(size_type idx)
1115 {
1116 if (idx < bucket_count_)
1117 return &table_[idx];
1118 else
1119 return nullptr;
1120 }
1121
[3be3752]1122 void rehash_if_needed()
1123 {
1124 if (size_ > max_load_factor_ * bucket_count_)
1125 rehash(bucket_count_ * bucket_count_growth_factor_);
1126 }
1127
1128 void increment_size()
1129 {
1130 ++size_;
1131 }
1132
[86b3ae98]1133 private:
[e29ce3d]1134 hash_table_bucket<value_type, size_type>* table_;
1135 size_type bucket_count_;
1136 size_type size_;
1137 hasher hasher_;
1138 key_equal key_eq_;
[871cfe0c]1139 key_extract key_extractor_;
[e29ce3d]1140 float max_load_factor_;
1141
[3be3752]1142 static constexpr float bucket_count_growth_factor_{1.25};
1143
[871cfe0c]1144 size_type get_bucket_idx_(const key_type& key) const
1145 {
1146 return hasher_(key) % bucket_count_;
1147 }
[e29ce3d]1148
[871cfe0c]1149 bool hint_ok_(const hint_type& hint)
1150 {
1151 // TODO: pass this to the policy, because the multi policy
1152 // will need to check if a similar key is close,
1153 // that is something like:
1154 // return get<1>(hint)->prev->key == key || !bucket.contains(key)
[1d5424a]1155 // TODO: also, make it public and make hint usage one level above?
1156 // (since we already have insert with decisive hint)
[871cfe0c]1157 return get<0>(hint) != nullptr && get<2>(hint) < bucket_count_;
1158 }
1159
1160 // Praise C++11 for this.
1161 friend Policy;
[e29ce3d]1162 };
[871cfe0c]1163}
[e29ce3d]1164
1165#endif
Note: See TracBrowser for help on using the repository browser.