source: mainline/uspace/lib/cpp/include/impl/unordered_map.hpp@ 5ae8168

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

cpp: refactored unordered_map's insertion functions and moved some logic to the underlying hash table, added rehashing to insertion when it's needed

  • Property mode set to 100644
File size: 24.8 KB
Line 
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_UNORDERED_MAP
30#define LIBCPP_UNORDERED_MAP
31
32#include <initializer_list>
33#include <internal/hash_table.hpp>
34#include <functional>
35#include <memory>
36#include <type_traits>
37#include <utility>
38
39namespace std
40{
41 /**
42 * 23.5.4, class template unordered_map:
43 */
44
45 template<
46 class Key, class Value,
47 class Hash = hash<Key>,
48 class Pred = equal_to<Key>,
49 class Alloc = allocator<pair<const Key, Value>>
50 >
51 class unordered_map
52 {
53 public:
54 using key_type = Key;
55 using mapped_type = Value;
56 using value_type = pair<const key_type, mapped_type>;
57 using hasher = Hash;
58 using key_equal = Pred;
59 using allocator_type = Alloc;
60 using pointer = typename allocator_traits<allocator_type>::pointer;
61 using const_pointer = typename allocator_traits<allocator_type>::const_pointer;
62 using reference = value_type&;
63 using const_reference = const value_type&;
64 using size_type = size_t;
65 using difference_type = ptrdiff_t;
66
67 using iterator = aux::hash_table_iterator<
68 value_type, reference, pointer, size_type
69 >;
70 using const_iterator = aux::hash_table_const_iterator<
71 value_type, const_reference, const_pointer, size_type
72 >;
73 using local_iterator = aux::hash_table_local_iterator<
74 value_type, reference, pointer
75 >;
76 using const_local_iterator = aux::hash_table_const_local_iterator<
77 value_type, const_reference, const_pointer
78 >;
79
80 unordered_map()
81 : unordered_map{default_bucket_count_}
82 { /* DUMMY BODY */ }
83
84 explicit unordered_map(size_type bucket_count,
85 const hasher& hf = hasher{},
86 const key_equal& eql = key_equal{},
87 const allocator_type& alloc = allocator_type{})
88 : table_{bucket_count, hf, eql}, allocator_{alloc}
89 { /* DUMMY BODY */ }
90
91 template<class InputIterator>
92 unordered_map(InputIterator first, InputIterator last,
93 size_type bucket_count = default_bucket_count_,
94 const hasher& hf = hasher{},
95 const key_equal& eql = key_equal{},
96 const allocator_type& alloc = allocator_type{})
97 : unordered_map{bucket_count, hf, eql, alloc}
98 {
99 insert(first, last);
100 }
101
102 unordered_map(const unordered_map& other)
103 : unordered_map{other, other.allocator_}
104 { /* DUMMY BODY */ }
105
106 unordered_map(unordered_map&& other)
107 : table_{move(other.table_)}, allocator_{move(other.allocator_)}
108 { /* DUMMY BODY */ }
109
110 explicit unordered_map(const allocator_type& alloc)
111 : table_{default_bucket_count_}, allocator_{alloc}
112 { /* DUMMY BODY */ }
113
114 unordered_map(const unordered_map& other, const allocator_type& alloc)
115 : table_{other.table_}, allocator_{alloc}
116 { /* DUMMY BODY */ }
117
118 unordered_map(unordered_map&& other, const allocator_type& alloc)
119 : table_{move(other.table_)}, allocator_{alloc}
120 { /* DUMMY BODY */ }
121
122 unordered_map(initializer_list<value_type> init,
123 size_type bucket_count = default_bucket_count_,
124 const hasher& hf = hasher{},
125 const key_equal& eql = key_equal{},
126 const allocator_type& alloc = allocator_type{})
127 : unordered_map{bucket_count, hf, eql, alloc}
128 {
129 insert(init.begin(), init.end());
130 }
131
132 unordered_map(size_type bucket_count, const allocator_type& alloc)
133 : unordered_map{bucket_count, hasher{}, key_equal{}, alloc}
134 { /* DUMMY BODY */ }
135
136 unordered_map(size_type bucket_count, const hasher& hf, const allocator_type& alloc)
137 : unordered_map{bucket_count, hf, key_equal{}, alloc}
138 { /* DUMMY BODY */ }
139
140 template<class InputIterator>
141 unordered_map(InputIterator first, InputIterator last,
142 size_type bucket_count, const allocator_type& alloc)
143 : unordered_map{first, last, bucket_count, hasher{}, key_equal{}, alloc}
144 { /* DUMMY BODY */ }
145
146 template<class InputIterator>
147 unordered_map(InputIterator first, InputIterator last,
148 size_type bucket_count, const hasher& hf, const allocator_type& alloc)
149 : unordered_map{first, last, bucket_count, hf, key_equal{}, alloc}
150 { /* DUMMY BODY */ }
151
152 unordered_map(initializer_list<value_type> init, size_type bucket_count,
153 const allocator_type& alloc)
154 : unordered_map{init, bucket_count, hasher{}, key_equal{}, alloc}
155 { /* DUMMY BODY */ }
156
157 unordered_map(initializer_list<value_type> init, size_type bucket_count,
158 const hasher& hf, const allocator_type& alloc)
159 : unordered_map{init, bucket_count, hf, key_equal{}, alloc}
160 { /* DUMMY BODY */ }
161
162 ~unordered_map()
163 { /* DUMMY BODY */ }
164
165 unordered_map& operator=(const unordered_map& other)
166 {
167 // TODO: implement
168 return *this;
169 }
170
171 unordered_map& operator=(unordered_map&& other)
172 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
173 is_nothrow_move_assignable<hasher>::value &&
174 is_nothrow_move_assignable<key_equal>::value)
175 {
176 // TODO: implement
177 return *this;
178 }
179
180 unordered_map& operator=(initializer_list<value_type>& init)
181 {
182 table_.clear();
183 table_.reserve(init.size());
184
185 insert(init.begin(), init.end());
186
187 return *this;
188 }
189
190 allocator_type get_allocator() const noexcept
191 {
192 return allocator_;
193 }
194
195 bool empty() const noexcept
196 {
197 return table_.empty();
198 }
199
200 size_type size() const noexcept
201 {
202 return table_.size();
203 }
204
205 size_type max_size() const noexcept
206 {
207 return table_.max_size();
208 }
209
210 iterator begin() noexcept
211 {
212 return table_.begin();
213 }
214
215 const_iterator begin() const noexcept
216 {
217 return table_.begin();
218 }
219
220 iterator end() noexcept
221 {
222 return table_.end();
223 }
224
225 const_iterator end() const noexcept
226 {
227 return table_.end();
228 }
229
230 const_iterator cbegin() const noexcept
231 {
232 return table_.cbegin();
233 }
234
235 const_iterator cend() const noexcept
236 {
237 return table_.cend();
238 }
239
240 template<class... Args>
241 pair<iterator, bool> emplace(Args&&... args)
242 {
243 /**
244 * Note: Some of these modifier functions start off
245 * by incrementing the table's element count and
246 * decrement it when insertion does not take place.
247 * This is because we need to cause rehashing if
248 * there are too many elements, but rehashing itself
249 * would invalidate all pointers we get from
250 * find_insertion_spot, which would require us to
251 * do another find. But this way we avoid two searches
252 * with the possibility of a rehash that is not necessary
253 * (but hardly a bad thing as the table needs just one
254 * element more to rehash).
255 *
256 * Also, there are 3 functions with similar bodies,
257 * but the duplicit code cannot be moved to a common
258 * sub-function because all three require different
259 * handling of the value (move, forward and copy).
260 */
261 table_.increment_size();
262
263 auto val = value_type{forward<Args>(args)...};
264 auto key = table_.get_key(val);
265 auto spot = table_.find_insertion_spot(key);
266 auto bucket = get<0>(spot);
267 auto idx = get<2>(spot);
268
269 if (!bucket)
270 return make_pair(end(), false);
271
272 auto target = table_.find_node_or_return_head(key, *bucket);
273 if (target && table_.keys_equal(key, target->value))
274 {
275 table_.decrement_size();
276 return make_pair(
277 iterator{
278 table_.table(), idx, table_.bucket_count(),
279 target
280 },
281 false
282 );
283 }
284 else
285 {
286 auto node = new node_type{move(val)};
287 bucket->append(node);
288
289 return make_pair(iterator{
290 table_.table(), idx,
291 table_.bucket_count(),
292 node
293 }, true);
294 }
295 }
296
297 template<class... Args>
298 iterator emplace_hint(const_iterator, Args&&... args)
299 {
300 return emplace(forward<Args>(args)...).first;
301 }
302
303 pair<iterator, bool> insert(const value_type& val)
304 {
305 table_.increment_size();
306
307 auto key = table_.get_key(val);
308 auto spot = table_.find_insertion_spot(key);
309 auto bucket = get<0>(spot);
310 auto idx = get<2>(spot);
311
312 if (!bucket)
313 return make_pair(end(), false);
314
315 auto target = table_.find_node_or_return_head(key, *bucket);
316 if (target && table_.keys_equal(key, target->value))
317 {
318 table_.decrement_size();
319 return make_pair(
320 iterator{
321 table_.table(), idx, table_.bucket_count(),
322 target
323 },
324 false
325 );
326 }
327 else
328 {
329 auto node = new node_type{val};
330 bucket->append(node);
331
332 return make_pair(iterator{
333 table_.table(), idx,
334 table_.bucket_count(),
335 node
336 }, true);
337 }
338 }
339
340 pair<iterator, bool> insert(value_type&& val)
341 {
342 table_.increment_size();
343
344 auto key = table_.get_key(val);
345 auto spot = table_.find_insertion_spot(key);
346 auto bucket = get<0>(spot);
347 auto idx = get<2>(spot);
348
349 if (!bucket)
350 return make_pair(end(), false);
351
352 auto target = table_.find_node_or_return_head(key, *bucket);
353 if (target && table_.keys_equal(key, target->value))
354 {
355 table_.decrement_size();
356 return make_pair(
357 iterator{
358 table_.table(), idx, table_.bucket_count(),
359 target
360 },
361 false
362 );
363 }
364 else
365 {
366 auto node = new node_type{forward<value_type>(val)};
367 bucket->append(node);
368
369 return make_pair(iterator{
370 table_.table(), idx,
371 table_.bucket_count(),
372 node
373 }, true);
374 }
375 }
376
377 template<
378 class T,
379 class = enable_if_t<is_constructible_v<value_type, T&&>, void>
380 >
381 pair<iterator, bool> insert(T&& val)
382 {
383 return emplace(forward<T>(val));
384 }
385
386 iterator insert(const_iterator, const value_type& val)
387 {
388 return insert(val).first;
389 }
390
391 iterator insert(const_iterator, value_type&& val)
392 {
393 return insert(forward<value_type>(val)).first;
394 }
395
396 template<
397 class T,
398 class = enable_if_t<is_constructible_v<value_type, T&&>, void>
399 >
400 iterator insert(const_iterator hint, T&& val)
401 {
402 return emplace_hint(hint, forward<T>(val));
403 }
404
405 template<class InputIterator>
406 void insert(InputIterator first, InputIterator last)
407 {
408 while (first != last)
409 insert(*first++);
410 }
411
412 void insert(initializer_list<value_type> init)
413 {
414 insert(init.begin(), init.end());
415 }
416
417 template<class... Args>
418 pair<iterator, bool> try_emplace(const key_type& key, Args&&... args)
419 {
420 // TODO: implement
421 }
422
423 template<class... Args>
424 pair<iterator, bool> try_emplace(key_type&& key, Args&&... args)
425 {
426 // TODO: implement
427 }
428
429 template<class... Args>
430 iterator try_emplace(const_iterator hint, const key_type& key, Args&&... args)
431 {
432 // TODO: implement
433 }
434
435 template<class... Args>
436 iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args)
437 {
438 // TODO: implement
439 }
440
441 template<class T>
442 pair<iterator, bool> insert_or_assign(const key_type& key, T&& val)
443 {
444 // TODO: implement
445 }
446
447 template<class T>
448 pair<iterator, bool> insert_or_assign(key_type&& key, T&& val)
449 {
450 // TODO: implement
451 }
452
453 template<class T>
454 iterator insert_or_assign(const_iterator hint, const key_type& key, T&& val)
455 {
456 // TODO: implement
457 }
458
459 template<class T>
460 iterator insert_or_assign(const_iterator hint, key_type&& key, T&& val)
461 {
462 // TODO: implement
463 }
464
465 iterator erase(const_iterator position)
466 {
467 return table_.erase(position);
468 }
469
470 size_type erase(const key_type& key)
471 {
472 return table_.erase(key);
473 }
474
475 iterator erase(const_iterator first, const_iterator last)
476 {
477 while (first != last)
478 first = erase(first);
479
480 return iterator{
481 table_.table(), first.idx(),
482 table_.bucket_count(), table_.head(first.idx())
483 };
484 }
485
486 void clear() noexcept
487 {
488 table_.clear();
489 }
490
491 void swap(unordered_map& other)
492 noexcept(allocator_traits<allocator_type>::is_always_equal::value &&
493 noexcept(std::swap(declval<hasher>(), declval<hasher>())) &&
494 noexcept(std::swap(declval<key_equal>(), declval<key_equal>())))
495 {
496 table_.swap(other.table_);
497 std::swap(allocator_, other.allocator_);
498 }
499
500 hasher hash_function() const
501 {
502 return table_.hash_function();
503 }
504
505 key_equal key_eq() const
506 {
507 return table_.key_eq();
508 }
509
510 iterator find(const key_type& key)
511 {
512 return table_.find(key);
513 }
514
515 const_iterator find(const key_type& key) const
516 {
517 return table_.find(key);
518 }
519
520 size_type count(const key_type& key) const
521 {
522 return table_.count(key);
523 }
524
525 pair<iterator, iterator> equal_range(const key_type& key)
526 {
527 return table_.equal_range(key);
528 }
529
530 pair<const_iterator, const_iterator> equal_range(const key_type& key) const
531 {
532 return table_.equal_range(key);
533 }
534
535 mapped_type& operator[](const key_type& key)
536 {
537 auto spot = table_.find_insertion_spot(key);
538 auto bucket = get<0>(spot);
539
540 if (bucket->head)
541 {
542 auto head = bucket->head;
543 auto current = bucket->head;
544
545 do
546 {
547 if (table_.keys_equal(key, current->value))
548 return current->value.second;
549 else
550 current = current->next;
551 }
552 while (current != head);
553 }
554
555 auto node = new node_type{key, mapped_type{}};
556 bucket->append(node);
557
558 table_.increment_size();
559 table_.rehash_if_needed();
560 return node->value.second;
561 }
562
563 mapped_type& operator[](key_type&& key)
564 {
565 auto spot = table_.find_insertion_spot(key);
566 auto bucket = get<0>(spot);
567
568 if (bucket->head)
569 {
570 auto head = bucket->head;
571 auto current = bucket->head;
572
573 do
574 {
575 if (table_.keys_equal(key, current->value))
576 return current->value.second;
577 else
578 current = current->next;
579 }
580 while (current != head);
581 }
582
583 auto node = new node_type{move(key), mapped_type{}};
584 bucket->append(node);
585
586 table_.increment_size();
587 table_.rehash_if_needed();
588 return node->value.second;
589 }
590
591 mapped_type& at(const key_type& key)
592 {
593 // TODO: implement
594 }
595
596 const mapped_type& at(const key_type& key) const
597 {
598 // TODO: implement
599 }
600
601 size_type bucket_count() const noexcept
602 {
603 return table_.bucket_count();
604 }
605
606 size_type max_bucket_count() const noexcept
607 {
608 return table_.max_bucket_count();
609 }
610
611 size_type bucket_size(size_type idx) const
612 {
613 return table_.bucket_size(idx);
614 }
615
616 size_type bucket(const key_type& key) const
617 {
618 return table_.bucket(key);
619 }
620
621 local_iterator begin(size_type idx)
622 {
623 return table_.begin(idx);
624 }
625
626 const_local_iterator begin(size_type idx) const
627 {
628 return table_.begin(idx);
629 }
630
631 local_iterator end(size_type idx)
632 {
633 return table_.end(idx);
634 }
635
636 const_local_iterator end(size_type idx) const
637 {
638 return table_.end(idx);
639 }
640
641 const_local_iterator cbegin(size_type idx) const
642 {
643 return table_.cbegin(idx);
644 }
645
646 const_local_iterator cend(size_type idx) const
647 {
648 return table_.cend(idx);
649 }
650
651 float load_factor() const noexcept
652 {
653 return table_.load_factor();
654 }
655
656 float max_load_factor() const noexcept
657 {
658 return table_.max_load_factor();
659 }
660
661 void max_load_factor(float factor)
662 {
663 table_.max_load_factor(factor);
664 }
665
666 void rehash(size_type bucket_count)
667 {
668 table_.rehash(bucket_count);
669 }
670
671 void reserve(size_type count)
672 {
673 table_.reserve(count);
674 }
675
676 private:
677 using table_type = aux::hash_table<
678 value_type, key_type, aux::key_value_key_extractor<key_type, mapped_type>,
679 hasher, key_equal, allocator_type, size_type,
680 iterator, const_iterator, local_iterator, const_local_iterator,
681 aux::hash_single_policy
682 >;
683 using node_type = typename table_type::node_type;
684
685 table_type table_;
686 allocator_type allocator_;
687
688 static constexpr size_type default_bucket_count_{16};
689 };
690
691 /**
692 * 23.5.5, class template unordered_multimap:
693 */
694
695 template<
696 class Key, class Value,
697 class Hash = hash<Key>,
698 class Pred = equal_to<Key>,
699 class Alloc = allocator<pair<const Key, Value>>
700 >
701 class unordered_multimap;
702
703 template<class Key, class Value, class Hash, class Pred, class Alloc>
704 void swap(unordered_map<Key, Value, Hash, Pred, Alloc>& lhs,
705 unordered_map<Key, Value, Hash, Pred, Alloc>& rhs)
706 noexcept(noexcept(lhs.swap(rhs)))
707 {
708 lhs.swap(rhs);
709 }
710
711 template<class Key, class Value, class Hash, class Pred, class Alloc>
712 void swap(unordered_multimap<Key, Value, Hash, Pred, Alloc>& lhs,
713 unordered_multimap<Key, Value, Hash, Pred, Alloc>& rhs)
714 noexcept(noexcept(lhs.swap(rhs)))
715 {
716 lhs.swap(rhs);
717 }
718
719 template<class Key, class Value, class Hash, class Pred, class Alloc>
720 bool operator==(unordered_map<Key, Value, Hash, Pred, Alloc>& lhs,
721 unordered_map<Key, Value, Hash, Pred, Alloc>& rhs)
722 {
723 // TODO: implement
724 return false;
725 }
726
727 template<class Key, class Value, class Hash, class Pred, class Alloc>
728 bool operator!=(unordered_map<Key, Value, Hash, Pred, Alloc>& lhs,
729 unordered_map<Key, Value, Hash, Pred, Alloc>& rhs)
730 {
731 return !(lhs == rhs);
732 }
733
734 template<class Key, class Value, class Hash, class Pred, class Alloc>
735 bool operator==(unordered_multimap<Key, Value, Hash, Pred, Alloc>& lhs,
736 unordered_multimap<Key, Value, Hash, Pred, Alloc>& rhs)
737 {
738 // TODO: implement
739 return false;
740 }
741
742 template<class Key, class Value, class Hash, class Pred, class Alloc>
743 bool operator!=(unordered_multimap<Key, Value, Hash, Pred, Alloc>& lhs,
744 unordered_multimap<Key, Value, Hash, Pred, Alloc>& rhs)
745 {
746 return !(lhs == rhs);
747 }
748}
749
750#endif
Note: See TracBrowser for help on using the repository browser.