/src/poco/Foundation/include/Poco/ordered_set.h
Line | Count | Source |
1 | | /** |
2 | | * MIT License |
3 | | * |
4 | | * Copyright (c) 2017 Thibaut Goetghebuer-Planchon <tessil@gmx.com> |
5 | | * |
6 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | | * of this software and associated documentation files (the "Software"), to deal |
8 | | * in the Software without restriction, including without limitation the rights |
9 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | | * copies of the Software, and to permit persons to whom the Software is |
11 | | * furnished to do so, subject to the following conditions: |
12 | | * |
13 | | * The above copyright notice and this permission notice shall be included in |
14 | | * all copies or substantial portions of the Software. |
15 | | * |
16 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
19 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | | * SOFTWARE. |
23 | | */ |
24 | | #ifndef TSL_ORDERED_SET_H |
25 | | #define TSL_ORDERED_SET_H |
26 | | |
27 | | #include <cstddef> |
28 | | #include <cstdint> |
29 | | #include <deque> |
30 | | #include <functional> |
31 | | #include <initializer_list> |
32 | | #include <memory> |
33 | | #include <type_traits> |
34 | | #include <utility> |
35 | | #include <vector> |
36 | | |
37 | | #include "ordered_hash.h" |
38 | | |
39 | | namespace tsl { |
40 | | |
41 | | /** |
42 | | * Implementation of an hash set using open addressing with robin hood with |
43 | | * backshift delete to resolve collisions. |
44 | | * |
45 | | * The particularity of this hash set is that it remembers the order in which |
46 | | * the elements were added and provide a way to access the structure which |
47 | | * stores these values through the 'values_container()' method. The used |
48 | | * container is defined by ValueTypeContainer, by default a std::deque is used |
49 | | * (grows faster) but a std::vector may be used. In this case the set provides a |
50 | | * 'data()' method which give a direct access to the memory used to store the |
51 | | * values (which can be useful to communicate with C API's). |
52 | | * |
53 | | * The Key must be copy constructible and/or move constructible. To use |
54 | | * `unordered_erase` it also must be swappable. |
55 | | * |
56 | | * The behaviour of the hash set is undefined if the destructor of Key throws an |
57 | | * exception. |
58 | | * |
59 | | * By default the maximum size of a set is limited to 2^32 - 1 values, if needed |
60 | | * this can be changed through the IndexType template parameter. Using an |
61 | | * `uint64_t` will raise this limit to 2^64 - 1 values but each bucket will use |
62 | | * 16 bytes instead of 8 bytes in addition to the space needed to store the |
63 | | * values. |
64 | | * |
65 | | * Iterators invalidation: |
66 | | * - clear, operator=, reserve, rehash: always invalidate the iterators (also |
67 | | * invalidate end()). |
68 | | * - insert, emplace, emplace_hint, operator[]: when a std::vector is used as |
69 | | * ValueTypeContainer and if size() < capacity(), only end(). Otherwise all the |
70 | | * iterators are invalidated if an insert occurs. |
71 | | * - erase, unordered_erase: when a std::vector is used as ValueTypeContainer |
72 | | * invalidate the iterator of the erased element and all the ones after the |
73 | | * erased element (including end()). Otherwise all the iterators are invalidated |
74 | | * if an erase occurs. |
75 | | */ |
76 | | template <class Key, class Hash = std::hash<Key>, |
77 | | class KeyEqual = std::equal_to<Key>, |
78 | | class Allocator = std::allocator<Key>, |
79 | | class ValueTypeContainer = std::deque<Key, Allocator>, |
80 | | class IndexType = std::uint_least32_t> |
81 | | class ordered_set { |
82 | | private: |
83 | | template <typename U> |
84 | | using has_is_transparent = tsl::detail_ordered_hash::has_is_transparent<U>; |
85 | | |
86 | | class KeySelect { |
87 | | public: |
88 | | using key_type = Key; |
89 | | |
90 | 0 | const key_type& operator()(const Key& key) const noexcept { return key; }Unexecuted instantiation: tsl::ordered_set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::deque<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, unsigned int>::KeySelect::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const Unexecuted instantiation: tsl::ordered_set<int, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<int>, std::__1::deque<int, std::__1::allocator<int> >, unsigned int>::KeySelect::operator()(int const&) const |
91 | | |
92 | 0 | key_type& operator()(Key& key) noexcept { return key; }Unexecuted instantiation: tsl::ordered_set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::deque<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, unsigned int>::KeySelect::operator()(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) Unexecuted instantiation: tsl::ordered_set<int, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<int>, std::__1::deque<int, std::__1::allocator<int> >, unsigned int>::KeySelect::operator()(int&) |
93 | | }; |
94 | | |
95 | | using ht = detail_ordered_hash::ordered_hash<Key, KeySelect, void, Hash, |
96 | | KeyEqual, Allocator, |
97 | | ValueTypeContainer, IndexType>; |
98 | | |
99 | | public: |
100 | | using key_type = typename ht::key_type; |
101 | | using value_type = typename ht::value_type; |
102 | | using size_type = typename ht::size_type; |
103 | | using difference_type = typename ht::difference_type; |
104 | | using hasher = typename ht::hasher; |
105 | | using key_equal = typename ht::key_equal; |
106 | | using allocator_type = typename ht::allocator_type; |
107 | | using reference = typename ht::reference; |
108 | | using const_reference = typename ht::const_reference; |
109 | | using pointer = typename ht::pointer; |
110 | | using const_pointer = typename ht::const_pointer; |
111 | | using iterator = typename ht::iterator; |
112 | | using const_iterator = typename ht::const_iterator; |
113 | | using reverse_iterator = typename ht::reverse_iterator; |
114 | | using const_reverse_iterator = typename ht::const_reverse_iterator; |
115 | | |
116 | | using values_container_type = typename ht::values_container_type; |
117 | | |
118 | | /* |
119 | | * Constructors |
120 | | */ |
121 | 0 | ordered_set() : ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE) {}Unexecuted instantiation: tsl::ordered_set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::deque<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, unsigned int>::ordered_set() Unexecuted instantiation: tsl::ordered_set<int, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<int>, std::__1::deque<int, std::__1::allocator<int> >, unsigned int>::ordered_set() |
122 | | |
123 | | explicit ordered_set(size_type bucket_count, const Hash& hash = Hash(), |
124 | | const KeyEqual& equal = KeyEqual(), |
125 | | const Allocator& alloc = Allocator()) |
126 | 0 | : m_ht(bucket_count, hash, equal, alloc, ht::DEFAULT_MAX_LOAD_FACTOR) {}Unexecuted instantiation: tsl::ordered_set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::deque<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, unsigned int>::ordered_set(unsigned long, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const&, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const&, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > const&) Unexecuted instantiation: tsl::ordered_set<int, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<int>, std::__1::deque<int, std::__1::allocator<int> >, unsigned int>::ordered_set(unsigned long, std::__1::hash<int> const&, std::__1::equal_to<int> const&, std::__1::allocator<int> const&) |
127 | | |
128 | | ordered_set(size_type bucket_count, const Allocator& alloc) |
129 | | : ordered_set(bucket_count, Hash(), KeyEqual(), alloc) {} |
130 | | |
131 | | ordered_set(size_type bucket_count, const Hash& hash, const Allocator& alloc) |
132 | | : ordered_set(bucket_count, hash, KeyEqual(), alloc) {} |
133 | | |
134 | | explicit ordered_set(const Allocator& alloc) |
135 | | : ordered_set(ht::DEFAULT_INIT_BUCKETS_SIZE, alloc) {} |
136 | | |
137 | | template <class InputIt> |
138 | | ordered_set(InputIt first, InputIt last, |
139 | | size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, |
140 | | const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), |
141 | | const Allocator& alloc = Allocator()) |
142 | | : ordered_set(bucket_count, hash, equal, alloc) { |
143 | | insert(first, last); |
144 | | } |
145 | | |
146 | | template <class InputIt> |
147 | | ordered_set(InputIt first, InputIt last, size_type bucket_count, |
148 | | const Allocator& alloc) |
149 | | : ordered_set(first, last, bucket_count, Hash(), KeyEqual(), alloc) {} |
150 | | |
151 | | template <class InputIt> |
152 | | ordered_set(InputIt first, InputIt last, size_type bucket_count, |
153 | | const Hash& hash, const Allocator& alloc) |
154 | | : ordered_set(first, last, bucket_count, hash, KeyEqual(), alloc) {} |
155 | | |
156 | | ordered_set(std::initializer_list<value_type> init, |
157 | | size_type bucket_count = ht::DEFAULT_INIT_BUCKETS_SIZE, |
158 | | const Hash& hash = Hash(), const KeyEqual& equal = KeyEqual(), |
159 | | const Allocator& alloc = Allocator()) |
160 | | : ordered_set(init.begin(), init.end(), bucket_count, hash, equal, |
161 | | alloc) {} |
162 | | |
163 | | ordered_set(std::initializer_list<value_type> init, size_type bucket_count, |
164 | | const Allocator& alloc) |
165 | | : ordered_set(init.begin(), init.end(), bucket_count, Hash(), KeyEqual(), |
166 | | alloc) {} |
167 | | |
168 | | ordered_set(std::initializer_list<value_type> init, size_type bucket_count, |
169 | | const Hash& hash, const Allocator& alloc) |
170 | | : ordered_set(init.begin(), init.end(), bucket_count, hash, KeyEqual(), |
171 | | alloc) {} |
172 | | |
173 | | ordered_set& operator=(std::initializer_list<value_type> ilist) { |
174 | | m_ht.clear(); |
175 | | |
176 | | m_ht.reserve(ilist.size()); |
177 | | m_ht.insert(ilist.begin(), ilist.end()); |
178 | | |
179 | | return *this; |
180 | | } |
181 | | |
182 | | allocator_type get_allocator() const { return m_ht.get_allocator(); } |
183 | | |
184 | | /* |
185 | | * Iterators |
186 | | */ |
187 | | iterator begin() noexcept { return m_ht.begin(); } |
188 | | const_iterator begin() const noexcept { return m_ht.begin(); } |
189 | | const_iterator cbegin() const noexcept { return m_ht.cbegin(); } |
190 | | |
191 | | iterator end() noexcept { return m_ht.end(); } |
192 | | const_iterator end() const noexcept { return m_ht.end(); } |
193 | | const_iterator cend() const noexcept { return m_ht.cend(); } |
194 | | |
195 | | reverse_iterator rbegin() noexcept { return m_ht.rbegin(); } |
196 | | const_reverse_iterator rbegin() const noexcept { return m_ht.rbegin(); } |
197 | | const_reverse_iterator rcbegin() const noexcept { return m_ht.rcbegin(); } |
198 | | |
199 | | reverse_iterator rend() noexcept { return m_ht.rend(); } |
200 | | const_reverse_iterator rend() const noexcept { return m_ht.rend(); } |
201 | | const_reverse_iterator rcend() const noexcept { return m_ht.rcend(); } |
202 | | |
203 | | /* |
204 | | * Capacity |
205 | | */ |
206 | | bool empty() const noexcept { return m_ht.empty(); } |
207 | | size_type size() const noexcept { return m_ht.size(); } |
208 | | size_type max_size() const noexcept { return m_ht.max_size(); } |
209 | | |
210 | | /* |
211 | | * Modifiers |
212 | | */ |
213 | | void clear() noexcept { m_ht.clear(); } |
214 | | |
215 | 0 | std::pair<iterator, bool> insert(const value_type& value) { |
216 | 0 | return m_ht.insert(value); |
217 | 0 | } Unexecuted instantiation: tsl::ordered_set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::hash<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::equal_to<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::deque<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >, unsigned int>::insert(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) Unexecuted instantiation: tsl::ordered_set<int, std::__1::hash<int>, std::__1::equal_to<int>, std::__1::allocator<int>, std::__1::deque<int, std::__1::allocator<int> >, unsigned int>::insert(int const&) |
218 | | std::pair<iterator, bool> insert(value_type&& value) { |
219 | | return m_ht.insert(std::move(value)); |
220 | | } |
221 | | |
222 | | iterator insert(const_iterator hint, const value_type& value) { |
223 | | return m_ht.insert_hint(hint, value); |
224 | | } |
225 | | |
226 | | iterator insert(const_iterator hint, value_type&& value) { |
227 | | return m_ht.insert_hint(hint, std::move(value)); |
228 | | } |
229 | | |
230 | | template <class InputIt> |
231 | | void insert(InputIt first, InputIt last) { |
232 | | m_ht.insert(first, last); |
233 | | } |
234 | | void insert(std::initializer_list<value_type> ilist) { |
235 | | m_ht.insert(ilist.begin(), ilist.end()); |
236 | | } |
237 | | |
238 | | /** |
239 | | * Due to the way elements are stored, emplace will need to move or copy the |
240 | | * key-value once. The method is equivalent to |
241 | | * insert(value_type(std::forward<Args>(args)...)); |
242 | | * |
243 | | * Mainly here for compatibility with the std::unordered_map interface. |
244 | | */ |
245 | | template <class... Args> |
246 | | std::pair<iterator, bool> emplace(Args&&... args) { |
247 | | return m_ht.emplace(std::forward<Args>(args)...); |
248 | | } |
249 | | |
250 | | /** |
251 | | * Due to the way elements are stored, emplace_hint will need to move or copy |
252 | | * the key-value once. The method is equivalent to insert(hint, |
253 | | * value_type(std::forward<Args>(args)...)); |
254 | | * |
255 | | * Mainly here for compatibility with the std::unordered_map interface. |
256 | | */ |
257 | | template <class... Args> |
258 | | iterator emplace_hint(const_iterator hint, Args&&... args) { |
259 | | return m_ht.emplace_hint(hint, std::forward<Args>(args)...); |
260 | | } |
261 | | |
262 | | /** |
263 | | * When erasing an element, the insert order will be preserved and no holes |
264 | | * will be present in the container returned by 'values_container()'. |
265 | | * |
266 | | * The method is in O(bucket_count()), if the order is not important |
267 | | * 'unordered_erase(...)' method is faster with an O(1) average complexity. |
268 | | */ |
269 | | iterator erase(iterator pos) { return m_ht.erase(pos); } |
270 | | |
271 | | /** |
272 | | * @copydoc erase(iterator pos) |
273 | | */ |
274 | | iterator erase(const_iterator pos) { return m_ht.erase(pos); } |
275 | | |
276 | | /** |
277 | | * @copydoc erase(iterator pos) |
278 | | */ |
279 | | iterator erase(const_iterator first, const_iterator last) { |
280 | | return m_ht.erase(first, last); |
281 | | } |
282 | | |
283 | | /** |
284 | | * @copydoc erase(iterator pos) |
285 | | */ |
286 | | size_type erase(const key_type& key) { return m_ht.erase(key); } |
287 | | |
288 | | /** |
289 | | * @copydoc erase(iterator pos) |
290 | | * |
291 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
292 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
293 | | * the lookup to the value if you already have the hash. |
294 | | */ |
295 | | size_type erase(const key_type& key, std::size_t precalculated_hash) { |
296 | | return m_ht.erase(key, precalculated_hash); |
297 | | } |
298 | | |
299 | | /** |
300 | | * @copydoc erase(iterator pos) |
301 | | * |
302 | | * This overload only participates in the overload resolution if the typedef |
303 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
304 | | * to Key. |
305 | | */ |
306 | | template < |
307 | | class K, class KE = KeyEqual, |
308 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
309 | | size_type erase(const K& key) { |
310 | | return m_ht.erase(key); |
311 | | } |
312 | | |
313 | | /** |
314 | | * @copydoc erase(const key_type& key, std::size_t precalculated_hash) |
315 | | * |
316 | | * This overload only participates in the overload resolution if the typedef |
317 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
318 | | * to Key. |
319 | | */ |
320 | | template < |
321 | | class K, class KE = KeyEqual, |
322 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
323 | | size_type erase(const K& key, std::size_t precalculated_hash) { |
324 | | return m_ht.erase(key, precalculated_hash); |
325 | | } |
326 | | |
327 | | /** |
328 | | * @copydoc erase(iterator pos) |
329 | | * |
330 | | * Erases all elements that satisfy the predicate pred. The method is in |
331 | | * O(n). Note that the function only has the strong exception guarantee if |
332 | | * the Predicate, Hash, and Key predicates and moves of keys and values do |
333 | | * not throw. If an exception is raised, the object is in an invalid state. |
334 | | * It can still be cleared and destroyed without leaking memory. |
335 | | */ |
336 | | template <class Predicate> |
337 | | friend size_type erase_if(ordered_set &set, Predicate pred) { |
338 | | return set.m_ht.erase_if(pred); |
339 | | } |
340 | | |
341 | | void swap(ordered_set& other) { other.m_ht.swap(m_ht); } |
342 | | |
343 | | /* |
344 | | * Lookup |
345 | | */ |
346 | | size_type count(const Key& key) const { return m_ht.count(key); } |
347 | | |
348 | | /** |
349 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
350 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
351 | | * the lookup if you already have the hash. |
352 | | */ |
353 | | size_type count(const Key& key, std::size_t precalculated_hash) const { |
354 | | return m_ht.count(key, precalculated_hash); |
355 | | } |
356 | | |
357 | | /** |
358 | | * This overload only participates in the overload resolution if the typedef |
359 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
360 | | * to Key. |
361 | | */ |
362 | | template < |
363 | | class K, class KE = KeyEqual, |
364 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
365 | | size_type count(const K& key) const { |
366 | | return m_ht.count(key); |
367 | | } |
368 | | |
369 | | /** |
370 | | * @copydoc count(const K& key) const |
371 | | * |
372 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
373 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
374 | | * the lookup if you already have the hash. |
375 | | */ |
376 | | template < |
377 | | class K, class KE = KeyEqual, |
378 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
379 | | size_type count(const K& key, std::size_t precalculated_hash) const { |
380 | | return m_ht.count(key, precalculated_hash); |
381 | | } |
382 | | |
383 | | iterator find(const Key& key) { return m_ht.find(key); } |
384 | | |
385 | | /** |
386 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
387 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
388 | | * the lookup if you already have the hash. |
389 | | */ |
390 | | iterator find(const Key& key, std::size_t precalculated_hash) { |
391 | | return m_ht.find(key, precalculated_hash); |
392 | | } |
393 | | |
394 | | const_iterator find(const Key& key) const { return m_ht.find(key); } |
395 | | |
396 | | /** |
397 | | * @copydoc find(const Key& key, std::size_t precalculated_hash) |
398 | | */ |
399 | | const_iterator find(const Key& key, std::size_t precalculated_hash) const { |
400 | | return m_ht.find(key, precalculated_hash); |
401 | | } |
402 | | |
403 | | /** |
404 | | * This overload only participates in the overload resolution if the typedef |
405 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
406 | | * to Key. |
407 | | */ |
408 | | template < |
409 | | class K, class KE = KeyEqual, |
410 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
411 | | iterator find(const K& key) { |
412 | | return m_ht.find(key); |
413 | | } |
414 | | |
415 | | /** |
416 | | * @copydoc find(const K& key) |
417 | | * |
418 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
419 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
420 | | * the lookup if you already have the hash. |
421 | | */ |
422 | | template < |
423 | | class K, class KE = KeyEqual, |
424 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
425 | | iterator find(const K& key, std::size_t precalculated_hash) { |
426 | | return m_ht.find(key, precalculated_hash); |
427 | | } |
428 | | |
429 | | /** |
430 | | * @copydoc find(const K& key) |
431 | | */ |
432 | | template < |
433 | | class K, class KE = KeyEqual, |
434 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
435 | | const_iterator find(const K& key) const { |
436 | | return m_ht.find(key); |
437 | | } |
438 | | |
439 | | /** |
440 | | * @copydoc find(const K& key) |
441 | | * |
442 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
443 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
444 | | * the lookup if you already have the hash. |
445 | | */ |
446 | | template < |
447 | | class K, class KE = KeyEqual, |
448 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
449 | | const_iterator find(const K& key, std::size_t precalculated_hash) const { |
450 | | return m_ht.find(key, precalculated_hash); |
451 | | } |
452 | | |
453 | | bool contains(const Key& key) const { return m_ht.contains(key); } |
454 | | |
455 | | /** |
456 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
457 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
458 | | * the lookup if you already have the hash. |
459 | | */ |
460 | | bool contains(const Key& key, std::size_t precalculated_hash) const { |
461 | | return m_ht.contains(key, precalculated_hash); |
462 | | } |
463 | | |
464 | | /** |
465 | | * This overload only participates in the overload resolution if the typedef |
466 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
467 | | * to Key. |
468 | | */ |
469 | | template < |
470 | | class K, class KE = KeyEqual, |
471 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
472 | | bool contains(const K& key) const { |
473 | | return m_ht.contains(key); |
474 | | } |
475 | | |
476 | | /** |
477 | | * @copydoc contains(const K& key) const |
478 | | * |
479 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
480 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
481 | | * the lookup if you already have the hash. |
482 | | */ |
483 | | template < |
484 | | class K, class KE = KeyEqual, |
485 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
486 | | bool contains(const K& key, std::size_t precalculated_hash) const { |
487 | | return m_ht.contains(key, precalculated_hash); |
488 | | } |
489 | | |
490 | | std::pair<iterator, iterator> equal_range(const Key& key) { |
491 | | return m_ht.equal_range(key); |
492 | | } |
493 | | |
494 | | /** |
495 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
496 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
497 | | * the lookup if you already have the hash. |
498 | | */ |
499 | | std::pair<iterator, iterator> equal_range(const Key& key, |
500 | | std::size_t precalculated_hash) { |
501 | | return m_ht.equal_range(key, precalculated_hash); |
502 | | } |
503 | | |
504 | | std::pair<const_iterator, const_iterator> equal_range(const Key& key) const { |
505 | | return m_ht.equal_range(key); |
506 | | } |
507 | | |
508 | | /** |
509 | | * @copydoc equal_range(const Key& key, std::size_t precalculated_hash) |
510 | | */ |
511 | | std::pair<const_iterator, const_iterator> equal_range( |
512 | | const Key& key, std::size_t precalculated_hash) const { |
513 | | return m_ht.equal_range(key, precalculated_hash); |
514 | | } |
515 | | |
516 | | /** |
517 | | * This overload only participates in the overload resolution if the typedef |
518 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
519 | | * to Key. |
520 | | */ |
521 | | template < |
522 | | class K, class KE = KeyEqual, |
523 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
524 | | std::pair<iterator, iterator> equal_range(const K& key) { |
525 | | return m_ht.equal_range(key); |
526 | | } |
527 | | |
528 | | /** |
529 | | * @copydoc equal_range(const K& key) |
530 | | * |
531 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
532 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
533 | | * the lookup if you already have the hash. |
534 | | */ |
535 | | template < |
536 | | class K, class KE = KeyEqual, |
537 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
538 | | std::pair<iterator, iterator> equal_range(const K& key, |
539 | | std::size_t precalculated_hash) { |
540 | | return m_ht.equal_range(key, precalculated_hash); |
541 | | } |
542 | | |
543 | | /** |
544 | | * @copydoc equal_range(const K& key) |
545 | | */ |
546 | | template < |
547 | | class K, class KE = KeyEqual, |
548 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
549 | | std::pair<const_iterator, const_iterator> equal_range(const K& key) const { |
550 | | return m_ht.equal_range(key); |
551 | | } |
552 | | |
553 | | /** |
554 | | * @copydoc equal_range(const K& key, std::size_t precalculated_hash) |
555 | | */ |
556 | | template < |
557 | | class K, class KE = KeyEqual, |
558 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
559 | | std::pair<const_iterator, const_iterator> equal_range( |
560 | | const K& key, std::size_t precalculated_hash) const { |
561 | | return m_ht.equal_range(key, precalculated_hash); |
562 | | } |
563 | | |
564 | | /* |
565 | | * Bucket interface |
566 | | */ |
567 | | size_type bucket_count() const { return m_ht.bucket_count(); } |
568 | | size_type max_bucket_count() const { return m_ht.max_bucket_count(); } |
569 | | |
570 | | /* |
571 | | * Hash policy |
572 | | */ |
573 | | float load_factor() const { return m_ht.load_factor(); } |
574 | | float max_load_factor() const { return m_ht.max_load_factor(); } |
575 | | void max_load_factor(float ml) { m_ht.max_load_factor(ml); } |
576 | | |
577 | | void rehash(size_type count) { m_ht.rehash(count); } |
578 | | void reserve(size_type count) { m_ht.reserve(count); } |
579 | | |
580 | | /* |
581 | | * Observers |
582 | | */ |
583 | | hasher hash_function() const { return m_ht.hash_function(); } |
584 | | key_equal key_eq() const { return m_ht.key_eq(); } |
585 | | |
586 | | /* |
587 | | * Other |
588 | | */ |
589 | | |
590 | | /** |
591 | | * Convert a const_iterator to an iterator. |
592 | | */ |
593 | | iterator mutable_iterator(const_iterator pos) { |
594 | | return m_ht.mutable_iterator(pos); |
595 | | } |
596 | | |
597 | | /** |
598 | | * Requires index <= size(). |
599 | | * |
600 | | * Return an iterator to the element at index. Return end() if index == |
601 | | * size(). |
602 | | */ |
603 | | iterator nth(size_type index) { return m_ht.nth(index); } |
604 | | |
605 | | /** |
606 | | * @copydoc nth(size_type index) |
607 | | */ |
608 | | const_iterator nth(size_type index) const { return m_ht.nth(index); } |
609 | | |
610 | | /** |
611 | | * Return const_reference to the first element. Requires the container to not |
612 | | * be empty. |
613 | | */ |
614 | | const_reference front() const { return m_ht.front(); } |
615 | | |
616 | | /** |
617 | | * Return const_reference to the last element. Requires the container to not |
618 | | * be empty. |
619 | | */ |
620 | | const_reference back() const { return m_ht.back(); } |
621 | | |
622 | | /** |
623 | | * Only available if ValueTypeContainer is a std::vector. Same as calling |
624 | | * 'values_container().data()'. |
625 | | */ |
626 | | template <class U = values_container_type, |
627 | | typename std::enable_if< |
628 | | tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr> |
629 | | const typename values_container_type::value_type* data() const noexcept { |
630 | | return m_ht.data(); |
631 | | } |
632 | | |
633 | | /** |
634 | | * Return the container in which the values are stored. The values are in the |
635 | | * same order as the insertion order and are contiguous in the structure, no |
636 | | * holes (size() == values_container().size()). |
637 | | */ |
638 | | const values_container_type& values_container() const noexcept { |
639 | | return m_ht.values_container(); |
640 | | } |
641 | | |
642 | | /** |
643 | | * Release the container in which the values are stored. |
644 | | * |
645 | | * The set is empty after this operation. |
646 | | */ |
647 | | values_container_type release() { return m_ht.release(); } |
648 | | |
649 | | template <class U = values_container_type, |
650 | | typename std::enable_if< |
651 | | tsl::detail_ordered_hash::is_vector<U>::value>::type* = nullptr> |
652 | | size_type capacity() const noexcept { |
653 | | return m_ht.capacity(); |
654 | | } |
655 | | |
656 | | void shrink_to_fit() { m_ht.shrink_to_fit(); } |
657 | | |
658 | | /** |
659 | | * Insert the value before pos shifting all the elements on the right of pos |
660 | | * (including pos) one position to the right. |
661 | | * |
662 | | * O(bucket_count()) runtime complexity. |
663 | | */ |
664 | | std::pair<iterator, bool> insert_at_position(const_iterator pos, |
665 | | const value_type& value) { |
666 | | return m_ht.insert_at_position(pos, value); |
667 | | } |
668 | | |
669 | | /** |
670 | | * @copydoc insert_at_position(const_iterator pos, const value_type& value) |
671 | | */ |
672 | | std::pair<iterator, bool> insert_at_position(const_iterator pos, |
673 | | value_type&& value) { |
674 | | return m_ht.insert_at_position(pos, std::move(value)); |
675 | | } |
676 | | |
677 | | /** |
678 | | * @copydoc insert_at_position(const_iterator pos, const value_type& value) |
679 | | * |
680 | | * Same as insert_at_position(pos, value_type(std::forward<Args>(args)...), |
681 | | * mainly here for coherence. |
682 | | */ |
683 | | template <class... Args> |
684 | | std::pair<iterator, bool> emplace_at_position(const_iterator pos, |
685 | | Args&&... args) { |
686 | | return m_ht.emplace_at_position(pos, std::forward<Args>(args)...); |
687 | | } |
688 | | |
689 | | void pop_back() { m_ht.pop_back(); } |
690 | | |
691 | | /** |
692 | | * Faster erase operation with an O(1) average complexity but it doesn't |
693 | | * preserve the insertion order. |
694 | | * |
695 | | * If an erasure occurs, the last element of the map will take the place of |
696 | | * the erased element. |
697 | | */ |
698 | | iterator unordered_erase(iterator pos) { return m_ht.unordered_erase(pos); } |
699 | | |
700 | | /** |
701 | | * @copydoc unordered_erase(iterator pos) |
702 | | */ |
703 | | iterator unordered_erase(const_iterator pos) { |
704 | | return m_ht.unordered_erase(pos); |
705 | | } |
706 | | |
707 | | /** |
708 | | * @copydoc unordered_erase(iterator pos) |
709 | | */ |
710 | | size_type unordered_erase(const key_type& key) { |
711 | | return m_ht.unordered_erase(key); |
712 | | } |
713 | | |
714 | | /** |
715 | | * @copydoc unordered_erase(iterator pos) |
716 | | * |
717 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
718 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
719 | | * the lookup if you already have the hash. |
720 | | */ |
721 | | size_type unordered_erase(const key_type& key, |
722 | | std::size_t precalculated_hash) { |
723 | | return m_ht.unordered_erase(key, precalculated_hash); |
724 | | } |
725 | | |
726 | | /** |
727 | | * @copydoc unordered_erase(iterator pos) |
728 | | * |
729 | | * This overload only participates in the overload resolution if the typedef |
730 | | * KeyEqual::is_transparent exists. If so, K must be hashable and comparable |
731 | | * to Key. |
732 | | */ |
733 | | template < |
734 | | class K, class KE = KeyEqual, |
735 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
736 | | size_type unordered_erase(const K& key) { |
737 | | return m_ht.unordered_erase(key); |
738 | | } |
739 | | |
740 | | /** |
741 | | * @copydoc unordered_erase(const K& key) |
742 | | * |
743 | | * Use the hash value 'precalculated_hash' instead of hashing the key. The |
744 | | * hash value should be the same as hash_function()(key). Useful to speed-up |
745 | | * the lookup if you already have the hash. |
746 | | */ |
747 | | template < |
748 | | class K, class KE = KeyEqual, |
749 | | typename std::enable_if<has_is_transparent<KE>::value>::type* = nullptr> |
750 | | size_type unordered_erase(const K& key, std::size_t precalculated_hash) { |
751 | | return m_ht.unordered_erase(key, precalculated_hash); |
752 | | } |
753 | | |
754 | | /** |
755 | | * Serialize the set through the `serializer` parameter. |
756 | | * |
757 | | * The `serializer` parameter must be a function object that supports the |
758 | | * following call: |
759 | | * - `void operator()(const U& value);` where the types `std::uint64_t`, |
760 | | * `float` and `Key` must be supported for U. |
761 | | * |
762 | | * The implementation leaves binary compatibility (endianness, IEEE 754 for |
763 | | * floats, ...) of the types it serializes in the hands of the `Serializer` |
764 | | * function object if compatibility is required. |
765 | | */ |
766 | | template <class Serializer> |
767 | | void serialize(Serializer& serializer) const { |
768 | | m_ht.serialize(serializer); |
769 | | } |
770 | | |
771 | | /** |
772 | | * Deserialize a previously serialized set through the `deserializer` |
773 | | * parameter. |
774 | | * |
775 | | * The `deserializer` parameter must be a function object that supports the |
776 | | * following calls: |
777 | | * - `template<typename U> U operator()();` where the types `std::uint64_t`, |
778 | | * `float` and `Key` must be supported for U. |
779 | | * |
780 | | * If the deserialized hash set type is hash compatible with the serialized |
781 | | * set, the deserialization process can be sped up by setting |
782 | | * `hash_compatible` to true. To be hash compatible, the Hash and KeyEqual |
783 | | * must behave the same way than the ones used on the serialized map. The |
784 | | * `std::size_t` must also be of the same size as the one on the platform used |
785 | | * to serialize the map, the same apply for `IndexType`. If these criteria are |
786 | | * not met, the behaviour is undefined with `hash_compatible` sets to true. |
787 | | * |
788 | | * The behaviour is undefined if the type `Key` of the `ordered_set` is not |
789 | | * the same as the type used during serialization. |
790 | | * |
791 | | * The implementation leaves binary compatibility (endianness, IEEE 754 for |
792 | | * floats, size of int, ...) of the types it deserializes in the hands of the |
793 | | * `Deserializer` function object if compatibility is required. |
794 | | */ |
795 | | template <class Deserializer> |
796 | | static ordered_set deserialize(Deserializer& deserializer, |
797 | | bool hash_compatible = false) { |
798 | | ordered_set set(0); |
799 | | set.m_ht.deserialize(deserializer, hash_compatible); |
800 | | |
801 | | return set; |
802 | | } |
803 | | |
804 | | friend bool operator==(const ordered_set& lhs, const ordered_set& rhs) { |
805 | | return lhs.m_ht == rhs.m_ht; |
806 | | } |
807 | | friend bool operator!=(const ordered_set& lhs, const ordered_set& rhs) { |
808 | | return lhs.m_ht != rhs.m_ht; |
809 | | } |
810 | | friend bool operator<(const ordered_set& lhs, const ordered_set& rhs) { |
811 | | return lhs.m_ht < rhs.m_ht; |
812 | | } |
813 | | friend bool operator<=(const ordered_set& lhs, const ordered_set& rhs) { |
814 | | return lhs.m_ht <= rhs.m_ht; |
815 | | } |
816 | | friend bool operator>(const ordered_set& lhs, const ordered_set& rhs) { |
817 | | return lhs.m_ht > rhs.m_ht; |
818 | | } |
819 | | friend bool operator>=(const ordered_set& lhs, const ordered_set& rhs) { |
820 | | return lhs.m_ht >= rhs.m_ht; |
821 | | } |
822 | | |
823 | | friend void swap(ordered_set& lhs, ordered_set& rhs) { lhs.swap(rhs); } |
824 | | |
825 | | private: |
826 | | ht m_ht; |
827 | | }; |
828 | | |
829 | | } // end namespace tsl |
830 | | |
831 | | #endif |