source: mainline/uspace/lib/cpp/include/impl/unordered_set.hpp@ 379ce989

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

cpp: implemented - well mostly copypasted, but that's the beauty of our uber table - unordered_set

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