source: mainline/uspace/lib/cpp/include/impl/unordered_map.hpp@ 6562af2

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

cpp: added move/copy assignment

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