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

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

cpp: fixed semantic errors, added support functions for higher level containers

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