Line | Count | Source |
1 | | // |
2 | | // immer: immutable data structures for C++ |
3 | | // Copyright (C) 2016, 2017, 2018 Juan Pedro Bolivar Puente |
4 | | // |
5 | | // This software is distributed under the Boost Software License, Version 1.0. |
6 | | // See accompanying file LICENSE or copy at http://boost.org/LICENSE_1_0.txt |
7 | | // |
8 | | |
9 | | #pragma once |
10 | | |
11 | | #include <immer/detail/hamts/champ.hpp> |
12 | | #include <immer/detail/hamts/champ_iterator.hpp> |
13 | | #include <immer/memory_policy.hpp> |
14 | | |
15 | | #include <functional> |
16 | | |
17 | | namespace immer { |
18 | | |
19 | | template <typename T, |
20 | | typename Hash, |
21 | | typename Equal, |
22 | | typename MemoryPolicy, |
23 | | detail::hamts::bits_t B> |
24 | | class set_transient; |
25 | | |
26 | | /*! |
27 | | * Immutable set representing an unordered bag of values. |
28 | | * |
29 | | * @tparam T The type of the values to be stored in the container. |
30 | | * @tparam Hash The type of a function object capable of hashing |
31 | | * values of type `T`. |
32 | | * @tparam Equal The type of a function object capable of comparing |
33 | | * values of type `T`. |
34 | | * @tparam MemoryPolicy Memory management policy. See @ref |
35 | | * memory_policy. |
36 | | * |
37 | | * @rst |
38 | | * |
39 | | * This container provides a good trade-off between cache locality, |
40 | | * membership checks, update performance and structural sharing. It |
41 | | * does so by storing the data in contiguous chunks of :math:`2^{B}` |
42 | | * elements. When storing big objects, the size of these contiguous |
43 | | * chunks can become too big, damaging performance. If this is |
44 | | * measured to be problematic for a specific use-case, it can be |
45 | | * solved by using a `immer::box` to wrap the type `T`. |
46 | | * |
47 | | * **Example** |
48 | | * .. literalinclude:: ../example/set/intro.cpp |
49 | | * :language: c++ |
50 | | * :start-after: intro/start |
51 | | * :end-before: intro/end |
52 | | * |
53 | | * @endrst |
54 | | * |
55 | | */ |
56 | | template <typename T, |
57 | | typename Hash = std::hash<T>, |
58 | | typename Equal = std::equal_to<T>, |
59 | | typename MemoryPolicy = default_memory_policy, |
60 | | detail::hamts::bits_t B = default_bits> |
61 | | class set |
62 | | { |
63 | | using impl_t = detail::hamts::champ<T, Hash, Equal, MemoryPolicy, B>; |
64 | | |
65 | | using move_t = |
66 | | std::integral_constant<bool, MemoryPolicy::use_transient_rvalues>; |
67 | | |
68 | | struct project_value_ptr |
69 | | { |
70 | | const T* operator()(const T& v) const noexcept { return &v; } |
71 | | }; |
72 | | |
73 | | public: |
74 | | using value_type = T; |
75 | | using size_type = detail::hamts::size_t; |
76 | | using difference_type = std::ptrdiff_t; |
77 | | using hasher = Hash; |
78 | | using key_equal = Equal; |
79 | | using reference = const T&; |
80 | | using const_reference = const T&; |
81 | | |
82 | | using iterator = |
83 | | detail::hamts::champ_iterator<T, Hash, Equal, MemoryPolicy, B>; |
84 | | using const_iterator = iterator; |
85 | | |
86 | | using transient_type = set_transient<T, Hash, Equal, MemoryPolicy, B>; |
87 | | |
88 | | using memory_policy_type = MemoryPolicy; |
89 | | |
90 | | /*! |
91 | | * Default constructor. It creates a set of `size() == 0`. It |
92 | | * does not allocate memory and its complexity is @f$ O(1) @f$. |
93 | | */ |
94 | 16.6k | set() = default; |
95 | | |
96 | | /*! |
97 | | * Constructs a set containing the elements in `values`. |
98 | | */ |
99 | | set(std::initializer_list<value_type> values) |
100 | | : impl_{impl_t::from_initializer_list(values)} |
101 | | { |
102 | | } |
103 | | |
104 | | /*! |
105 | | * Constructs a set containing the elements in the range |
106 | | * defined by the input iterator `first` and range sentinel `last`. |
107 | | */ |
108 | | template <typename Iter, |
109 | | typename Sent, |
110 | | std::enable_if_t<detail::compatible_sentinel_v<Iter, Sent>, |
111 | | bool> = true> |
112 | | set(Iter first, Sent last) |
113 | | : impl_{impl_t::from_range(first, last)} |
114 | | { |
115 | | } |
116 | | |
117 | | /*! |
118 | | * Returns an iterator pointing at the first element of the |
119 | | * collection. It does not allocate memory and its complexity is |
120 | | * @f$ O(1) @f$. |
121 | | */ |
122 | 7.36k | IMMER_NODISCARD iterator begin() const { return {impl_}; } |
123 | | |
124 | | /*! |
125 | | * Returns an iterator pointing just after the last element of the |
126 | | * collection. It does not allocate and its complexity is @f$ O(1) @f$. |
127 | | */ |
128 | | IMMER_NODISCARD iterator end() const |
129 | 7.36k | { |
130 | 7.36k | return {impl_, typename iterator::end_t{}}; |
131 | 7.36k | } |
132 | | |
133 | | /*! |
134 | | * Returns the number of elements in the container. It does |
135 | | * not allocate memory and its complexity is @f$ O(1) @f$. |
136 | | */ |
137 | | IMMER_NODISCARD size_type size() const { return impl_.size; } |
138 | | |
139 | | /*! |
140 | | * Returns `true` if there are no elements in the container. It |
141 | | * does not allocate memory and its complexity is @f$ O(1) @f$. |
142 | | */ |
143 | | IMMER_NODISCARD bool empty() const { return impl_.size == 0; } |
144 | | |
145 | | /*! |
146 | | * Returns `1` when `value` is contained in the set or `0` |
147 | | * otherwise. It won't allocate memory and its complexity is |
148 | | * *effectively* @f$ O(1) @f$. |
149 | | * |
150 | | * This overload participates in overload resolution only if |
151 | | * `Hash::is_transparent` is valid and denotes a type. |
152 | | */ |
153 | | template <typename K, |
154 | | typename U = Hash, |
155 | | typename = typename U::is_transparent> |
156 | | IMMER_NODISCARD size_type count(const K& value) const |
157 | | { |
158 | | return impl_.template get<detail::constantly<size_type, 1>, |
159 | | detail::constantly<size_type, 0>>(value); |
160 | | } |
161 | | |
162 | | /*! |
163 | | * Returns `1` when `value` is contained in the set or `0` |
164 | | * otherwise. It won't allocate memory and its complexity is |
165 | | * *effectively* @f$ O(1) @f$. |
166 | | */ |
167 | | IMMER_NODISCARD size_type count(const T& value) const |
168 | 178k | { |
169 | 178k | return impl_.template get<detail::constantly<size_type, 1>, |
170 | 178k | detail::constantly<size_type, 0>>(value); |
171 | 178k | } |
172 | | |
173 | | /*! |
174 | | * Returns a pointer to the value if `value` is contained in the |
175 | | * set, or nullptr otherwise. |
176 | | * It does not allocate memory and its complexity is *effectively* |
177 | | * @f$ O(1) @f$. |
178 | | */ |
179 | | IMMER_NODISCARD const T* find(const T& value) const |
180 | | { |
181 | | return impl_.template get<project_value_ptr, |
182 | | detail::constantly<const T*, nullptr>>(value); |
183 | | } |
184 | | |
185 | | /*! |
186 | | * Returns a pointer to the value if `value` is contained in the |
187 | | * set, or nullptr otherwise. |
188 | | * It does not allocate memory and its complexity is *effectively* |
189 | | * @f$ O(1) @f$. |
190 | | * |
191 | | * This overload participates in overload resolution only if |
192 | | * `Hash::is_transparent` is valid and denotes a type. |
193 | | */ |
194 | | template <typename K, |
195 | | typename U = Hash, |
196 | | typename = typename U::is_transparent> |
197 | | IMMER_NODISCARD const T* find(const K& value) const |
198 | | { |
199 | | return impl_.template get<project_value_ptr, |
200 | | detail::constantly<const T*, nullptr>>(value); |
201 | | } |
202 | | |
203 | | /*! |
204 | | * Returns whether the sets are equal. |
205 | | */ |
206 | | IMMER_NODISCARD bool operator==(const set& other) const |
207 | | { |
208 | | return impl_.equals(other.impl_); |
209 | | } |
210 | | IMMER_NODISCARD bool operator!=(const set& other) const |
211 | | { |
212 | | return !(*this == other); |
213 | | } |
214 | | |
215 | | /*! |
216 | | * Returns a set containing `value`. If the `value` is already in |
217 | | * the set, it returns the same set. It may allocate memory and |
218 | | * its complexity is *effectively* @f$ O(1) @f$. |
219 | | */ |
220 | | IMMER_NODISCARD set insert(T value) const& |
221 | 150k | { |
222 | 150k | return impl_.add(std::move(value)); |
223 | 150k | } |
224 | | IMMER_NODISCARD decltype(auto) insert(T value) && |
225 | 23.6k | { |
226 | 23.6k | return insert_move(move_t{}, std::move(value)); |
227 | 23.6k | } |
228 | | |
229 | | /*! |
230 | | * Returns a set without `value`. If the `value` is not in the |
231 | | * set it returns the same set. It may allocate memory and its |
232 | | * complexity is *effectively* @f$ O(1) @f$. |
233 | | */ |
234 | | IMMER_NODISCARD set erase(const T& value) const& |
235 | 9.84k | { |
236 | 9.84k | return impl_.sub(value); |
237 | 9.84k | } |
238 | | IMMER_NODISCARD decltype(auto) erase(const T& value) && |
239 | 9.74k | { |
240 | 9.74k | return erase_move(move_t{}, value); |
241 | 9.74k | } |
242 | | |
243 | | /*! |
244 | | * Returns an @a transient form of this container, a |
245 | | * `immer::set_transient`. |
246 | | */ |
247 | | IMMER_NODISCARD transient_type transient() const& |
248 | | { |
249 | | return transient_type{impl_}; |
250 | | } |
251 | | IMMER_NODISCARD transient_type transient() && |
252 | | { |
253 | | return transient_type{std::move(impl_)}; |
254 | | } |
255 | | |
256 | | /*! |
257 | | * Returns a value that can be used as identity for the container. If two |
258 | | * values have the same identity, they are guaranteed to be equal and to |
259 | | * contain the same objects. However, two equal containers are not |
260 | | * guaranteed to have the same identity. |
261 | | */ |
262 | | void* identity() const { return impl_.root; } |
263 | | |
264 | | // Semi-private |
265 | 24.2k | const impl_t& impl() const { return impl_; } |
266 | | |
267 | | private: |
268 | | friend transient_type; |
269 | | |
270 | | set&& insert_move(std::true_type, value_type value) |
271 | 23.6k | { |
272 | 23.6k | impl_.add_mut({}, std::move(value)); |
273 | 23.6k | return std::move(*this); |
274 | 23.6k | } |
275 | | set insert_move(std::false_type, value_type value) |
276 | | { |
277 | | return impl_.add(std::move(value)); |
278 | | } |
279 | | |
280 | | set&& erase_move(std::true_type, const value_type& value) |
281 | 9.74k | { |
282 | 9.74k | impl_.sub_mut({}, value); |
283 | 9.74k | return std::move(*this); |
284 | 9.74k | } |
285 | | set erase_move(std::false_type, const value_type& value) |
286 | | { |
287 | | return impl_.sub(value); |
288 | | } |
289 | | |
290 | | // for immer::persist |
291 | | public: |
292 | | set(impl_t impl) |
293 | 160k | : impl_(std::move(impl)) |
294 | 160k | { |
295 | 160k | } |
296 | | |
297 | | private: |
298 | | impl_t impl_ = impl_t::empty(); |
299 | | }; |
300 | | |
301 | | static_assert(std::is_nothrow_move_constructible<set<int>>::value, |
302 | | "set is not nothrow move constructible"); |
303 | | static_assert(std::is_nothrow_move_assignable<set<int>>::value, |
304 | | "set is not nothrow move assignable"); |
305 | | |
306 | | } // namespace immer |