mCRL2
Loading...
Searching...
No Matches
unordered_set.h
Go to the documentation of this file.
1// Author(s): Maurice Laveaux
2// Copyright: see the accompanying file COPYING or copy at
3// https://github.com/mCRL2org/mCRL2/blob/master/COPYING
4//
5// Distributed under the Boost Software License, Version 1.0.
6// (See accompanying file LICENSE_1_0.txt or copy at
7// http://www.boost.org/LICENSE_1_0.txt)
8
9#ifndef MCRL2_UTILITIES_UNORDERED_SET_H
10#define MCRL2_UTILITIES_UNORDERED_SET_H
11
15
16#include <cmath>
17#include <mutex>
18#include <vector>
19
20namespace mcrl2::utilities
21{
22
24constexpr static bool EnableLockfreeInsertion = true;
25
27constexpr static long BucketsPerMutex = 256;
28
30template<typename T>
32
33// Forward declaration of an unordered_map.
34template<typename Key,
35 typename T,
36 typename Hash = std::hash<Key>,
37 typename KeyEqual = std::equal_to<Key>,
38 typename Allocator = std::allocator<Key>,
39 bool ThreadSafe = false,
40 bool Resize = true>
41class unordered_map;
42
43namespace detail
44{
45 // Check for the existence of the is_transparent type.
46 template <typename...> using void_t = void;
47
48 template <typename X, typename = void> struct is_transparent : std::false_type
49 {};
50
51 template <typename X>
53 {};
54} // namespace detail
55
56
68template<typename Key,
69 typename Hash = std::hash<Key>,
70 typename Equals = std::equal_to<Key>,
71 typename Allocator = std::allocator<Key>,
72 bool ThreadSafe = false,
73 bool Resize = true>
75{
76 static_assert (!(ThreadSafe && Resize), "ThreadSafe cannot be enabled together with automatic resizing.");
77
78private:
81 using bucket_iterator = typename std::vector<bucket_type>::iterator;
82 using const_bucket_iterator = typename std::vector<bucket_type>::const_iterator;
83 using mutex_type = std::mutex;
84
85 template<typename Key_, typename T, typename Hash_, typename KeyEqual, typename Allocator_, bool ThreadSafe_, bool Resize_>
86 friend class unordered_map;
87
88public:
90 template<typename Bucket, bool Constant>
91 class unordered_set_iterator : std::iterator_traits<Key>
92 {
93 private:
94 friend class unordered_set;
95
96 template<typename Key_, typename T, typename Hash_, typename KeyEqual, typename Allocator_, bool ThreadSafe_, bool Resize_>
97 friend class unordered_map;
98
99 using bucket_it = typename std::vector<Bucket>::const_iterator;
100 using key_it_type = typename Bucket::const_iterator;
101
102 public:
103 using value_type = Key;
104 using reference = typename std::conditional<Constant, const Key&, Key&>::type;
105 using pointer = typename std::conditional<Constant, const Key*, Key*>::type;
106 using difference_type = std::ptrdiff_t;
107 using iterator_category = std::forward_iterator_tag;
108
110
112 {
114 }
115
117 {
119 ++m_key_it;
121 return *this;
122 }
123
125 {
126 unordered_set_iterator copy(*this);
127 ++(*this);
128 return *copy;
129 }
130
132 {
133 return const_cast<reference>(*m_key_it);
134 }
135
137 {
138 return const_cast<pointer>(&(*m_key_it));
139 }
140
141 bool operator!=(const unordered_set_iterator& other) const
142 {
143 return m_key_it != other.m_key_it || m_bucket_it != other.m_bucket_it;
144 }
145
146 bool operator==(const unordered_set_iterator& other) const
147 {
148 return !(*this != other);
149 }
150
151 private:
154 m_bucket_it(it), m_bucket_end(end), m_key_before_it(before_it), m_key_it(key)
155 {}
156
159 m_bucket_it(it), m_bucket_end(end), m_key_before_it((*it).before_begin()), m_key_it((*it).begin())
160 {
162 }
163
166 m_bucket_it(it)
167 {}
168
170 {
172 }
173
176
179
182
185 {
186 // Find the first bucket that is not empty.
187 while(!(m_key_it != detail::EndIterator))
188 {
189 // Take the next bucket and reset the key iterator.
190 ++m_bucket_it;
191
193 {
194 m_key_it = (*m_bucket_it).begin();
195 m_key_before_it = (*m_bucket_it).before_begin();
196 }
197 else
198 {
199 // Reached the end of the buckets.
200 break;
201 }
202 }
203
204 // The current bucket contains elements or we are at the end.
206 }
207
211 key_it_type m_key_it; // Invariant: m_key_it != EndIterator.
212 };
213
214 using key_type = Key;
215 using value_type = Key;
216 using hasher = Hash;
217 using key_equal = Equals;
219
222 using pointer = typename std::allocator_traits<Allocator>::pointer;
223 using const_pointer = typename std::allocator_traits<Allocator>::const_pointer;
224
229
230 using size_type = std::size_t;
231 using difference_type = std::ptrdiff_t;
232
234
237 const hasher& hash = hasher(),
238 const key_equal& equals = key_equal())
239 : m_hash(hash),
240 m_equals(equals)
241 {
243 }
244
245 // Copy operators.
248
249 // Default move operators.
250 unordered_set(unordered_set&& other) = default;
252
254
256 const allocator_type& get_allocator() const noexcept { return m_allocator; }
258
260 iterator begin() { return iterator(m_buckets.begin(), m_buckets.end()); }
261 iterator end() { return iterator(m_buckets.end()); }
262
264 const_iterator begin() const { return const_iterator(m_buckets.begin(), m_buckets.end()); }
265 const_iterator end() const { return const_iterator(m_buckets.end()); }
266
268 const_iterator cbegin() const { return const_iterator(m_buckets.begin(), m_buckets.end()); }
269 const_iterator cend() const { return const_iterator(m_buckets.end()); }
270
272 bool empty() const noexcept { return size() == 0; }
273
275 size_type size() const noexcept { return m_number_of_elements; }
276 size_type max_size() const noexcept { return m_buckets.max_size(); }
277
280 void clear();
281
286 template<typename ...Args>
287 std::pair<iterator, bool> emplace(Args&&... args);
288
290 template<typename...Args>
291 void erase(const Args&... args);
292
296
298 template<typename ...Args>
299 size_type count(const Args&... args) const;
300
303 template<typename...Args>
304 const_iterator find(const Args&... args) const;
305
306 template<typename...Args>
307 iterator find(const Args&... args);
308
310 local_iterator begin(size_type n) { return m_buckets[n].begin(); }
311 local_iterator end(size_type n) { return m_buckets[n].end(); }
312
313 const_local_iterator begin(size_type n) const { return m_buckets[n].begin(); }
314 const_local_iterator end(size_type n) const { return m_buckets[n].end(); }
315
316 const_local_iterator cbegin(size_type n) const { return m_buckets[n].begin(); }
317 const_local_iterator cend(size_type n) const { return m_buckets[n].end(); }
318
320 size_type bucket_count() const noexcept { return m_buckets_mask + 1; }
321 size_type max_bucket_count() const noexcept { return m_buckets.max_size(); }
322
323 size_type bucket_size(size_type n) const noexcept { return std::distance(m_buckets[n].begin(), m_buckets[n].end()); }
324 size_type bucket(const key_type& key) const noexcept { return std::distance(m_buckets.begin(), find_bucket_index(key)); }
325
326 float load_factor() const { return static_cast<float>(size()) / bucket_count(); }
327 float max_load_factor() const { return m_max_load_factor; }
328 void max_load_factor(float factor) { m_max_load_factor = factor; }
329
331 void rehash(size_type number_of_buckets);
332
334 void reserve(size_type count) { rehash(static_cast<std::size_t>(std::ceil(static_cast<float>(count) / max_load_factor()))); }
335
336 hasher hash_function() const { return m_hash; }
337 key_equal key_eq() const { return m_equals; }
338
341 size_type capacity() const noexcept { return m_buckets.size(); }
342
346
347private:
348 template<typename Key_, typename T, typename Hash_, typename KeyEqual, typename Allocator_, bool ThreadSafe_, bool Resize_>
349 friend class unordered_map;
350
353 template<typename ...Args>
354 std::pair<iterator, bool> emplace_impl(size_type bucket_index, Args&&... args);
355
357 template<typename ...Args>
358 void erase_impl(const Args&... args);
359
361 template<typename ...Args>
362 size_type find_bucket_index(const Args&... args) const;
363
365 template<typename ...Args>
366 const_iterator find_impl(size_type bucket_index, const Args&... args) const;
367
370
372 std::conditional_t<ThreadSafe, std::atomic<size_type>, size_type> m_number_of_elements = 0;
373
375 std::conditional_t<ThreadSafe, std::atomic<size_type>, size_type> m_buckets_mask = 0;
376
377 std::vector<bucket_type> m_buckets;
378 std::vector<std::mutex> m_bucket_mutexes;
379
380 float m_max_load_factor = 1.0f;
381
385};
386
388template<typename Key,
389 typename Hash = std::hash<Key>,
390 typename Equals = std::equal_to<Key>,
391 typename Allocator = mcrl2::utilities::block_allocator<Key>,
392 bool ThreadSafe = false>
394
395} // namespace mcrl2::utilities
396
398
399#endif // MCRL2_UTILITIES_UNORDERED_SET_H
The block allocator provides the allocator interface for the memory pool class. As such is can be use...
This essentially implements the std::forward_list, with the difference that it does not own the nodes...
Definition bucket_list.h:48
typename std::allocator_traits< Allocator >::template rebind_alloc< Bucket::node > NodeAllocator
Rebind the passed to allocator to a bucket list node allocator.
A class for a map of keys to values in T based using the simple hash table set implementation.
An iterator over all elements in the unordered set.
unordered_set_iterator(bucket_it it, bucket_it end, key_it_type before_it, key_it_type key)
Construct an iterator over all keys passed in this bucket and all remaining buckets.
void goto_next_bucket()
Iterate to the next non-empty bucket.
typename std::vector< Bucket >::const_iterator bucket_it
typename std::conditional< Constant, const Key *, Key * >::type pointer
unordered_set_iterator(bucket_it it)
Construct the end iterator.
unordered_set_iterator(bucket_it it, bucket_it end)
Construct the begin iterator (over all elements).
bool operator==(const unordered_set_iterator &other) const
bool operator!=(const unordered_set_iterator &other) const
typename std::conditional< Constant, const Key &, Key & >::type reference
A unordered_set with a subset of the interface of std::unordered_set that only stores a single pointe...
std::vector< std::mutex > m_bucket_mutexes
const_local_iterator cbegin(size_type n) const
const_local_iterator end(size_type n) const
void erase(const Args &... args)
Erases the given key_type(args...) from the unordered set.
typename bucket_type::const_iterator local_iterator
const_iterator find_impl(size_type bucket_index, const Args &... args) const
Searches for the element in the given bucket.
const value_type & const_reference
const_local_iterator begin(size_type n) const
size_type count(const Args &... args) const
Counts the number of occurrences of the given key (1 when it exists and 0 otherwise).
typename std::allocator_traits< Allocator >::pointer pointer
size_type find_bucket_index(const Args &... args) const
std::vector< bucket_type > m_buckets
std::pair< iterator, bool > emplace_impl(size_type bucket_index, Args &&... args)
Inserts T(args...) into the given bucket, assumes that it did not exists before. \threadsafe.
const_iterator cend() const
size_type bucket_count() const noexcept
bool empty() const noexcept
void erase_impl(const Args &... args)
Removes T(args...) from the set.
const_iterator begin() const
static constexpr bool allow_transparent
True iff the hash and equals functions allow transparent lookup,.
const_iterator end() const
typename std::vector< bucket_type >::iterator bucket_iterator
std::conditional_t< ThreadSafe, std::atomic< size_type >, size_type > m_buckets_mask
Always equal to m_buckets.size() - 1.
size_type max_bucket_count() const noexcept
typename bucket_type::const_iterator const_local_iterator
typename std::allocator_traits< Allocator >::const_pointer const_pointer
allocator_type & get_allocator() noexcept
std::pair< iterator, bool > emplace(Args &&... args)
Inserts an element Key(args...) into the set if it did not already exist.
void rehash_if_needed()
Resizes the hash table if necessary.
iterator find(const Args &... args)
const_iterator cbegin() const
std::conditional_t< ThreadSafe, std::atomic< size_type >, size_type > m_number_of_elements
The number of elements stored in this set.
unordered_set_iterator< bucket_type, true > const_iterator
size_type size() const noexcept
typename std::vector< bucket_type >::const_iterator const_bucket_iterator
void clear()
Removes all elements from the set.
unordered_set(const unordered_set &set)
iterator erase(const_iterator it)
Erases the element pointed to by the iterator.
typename bucket_type::NodeAllocator allocator_type
size_type max_size() const noexcept
void max_load_factor(float factor)
size_type bucket(const key_type &key) const noexcept
size_type capacity() const noexcept
local_iterator end(size_type n)
unordered_set(unordered_set &&other)=default
const_local_iterator cend(size_type n) const
const_iterator find(const Args &... args) const
Searches whether an object key_type(args...) occurs in the set.
void rehash(size_type number_of_buckets)
Resize the number buckets to at least number_of_buckets.
unordered_set & operator=(const unordered_set &set)
local_iterator begin(size_type n)
size_type bucket_size(size_type n) const noexcept
unordered_set(size_type bucket_count, const hasher &hash=hasher(), const key_equal &equals=key_equal())
Constructs an unordered_set that contains bucket_count number of buckets.
unordered_set & operator=(unordered_set &&other)=default
const allocator_type & get_allocator() const noexcept
void reserve(size_type count)
Resizes the set to the given number of elements.
static constexpr Sentinel EndIterator
A end of the iterator sentinel.
Definition bucket_list.h:28
static constexpr long BucketsPerMutex
Number of buckets per mutex.
static constexpr bool EnableLockfreeInsertion
Enables lockfree implementation of emplace.
void print_performance_statistics(const T &unordered_set)
Prints various information for unordered_set like data structures.
#define hash(l, r, m)
Definition tree_set.cpp:23