1 /*
2 Copyright (c) 2019-2021 Intel Corporation
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16
17 #ifndef __TBB_concurrent_map_H
18 #define __TBB_concurrent_map_H
19
20 #include "detail/_namespace_injection.h"
21 #include "detail/_concurrent_skip_list.h"
22 #include "tbb_allocator.h"
23 #include <functional>
24 #include <tuple>
25 #include <utility>
26
27 namespace tbb {
28 namespace detail {
29 namespace d2 {
30
31 template<typename Key, typename Value, typename KeyCompare, typename RandomGenerator,
32 typename Allocator, bool AllowMultimapping>
33 struct map_traits {
34 static constexpr std::size_t max_level = RandomGenerator::max_level;
35 using random_level_generator_type = RandomGenerator;
36 using key_type = Key;
37 using mapped_type = Value;
38 using compare_type = KeyCompare;
39 using value_type = std::pair<const key_type, mapped_type>;
40 using reference = value_type&;
41 using const_reference = const value_type&;
42 using allocator_type = Allocator;
43
44 static constexpr bool allow_multimapping = AllowMultimapping;
45
46 class value_compare {
47 public:
operatormap_traits48 bool operator()(const value_type& lhs, const value_type& rhs) const {
49 return comp(lhs.first, rhs.first);
50 }
51
52 protected:
value_comparemap_traits53 value_compare(compare_type c) : comp(c) {}
54
55 friend struct map_traits;
56
57 compare_type comp;
58 };
59
value_compmap_traits60 static value_compare value_comp(compare_type comp) { return value_compare(comp); }
61
get_keymap_traits62 static const key_type& get_key(const_reference val) {
63 return val.first;
64 }
65 }; // struct map_traits
66
67 template <typename Key, typename Value, typename Compare, typename Allocator>
68 class concurrent_multimap;
69
70 template <typename Key, typename Value, typename Compare = std::less<Key>, typename Allocator = tbb::tbb_allocator<std::pair<const Key, Value>>>
71 class concurrent_map : public concurrent_skip_list<map_traits<Key, Value, Compare, concurrent_geometric_level_generator<32>, Allocator, false>> {
72 using base_type = concurrent_skip_list<map_traits<Key, Value, Compare, concurrent_geometric_level_generator<32>, Allocator, false>>;
73 public:
74 using key_type = Key;
75 using mapped_type = Value;
76 using value_type = typename base_type::value_type;
77 using size_type = typename base_type::size_type;
78 using difference_type = typename base_type::difference_type;
79 using key_compare = Compare;
80 using value_compare = typename base_type::value_compare;
81 using allocator_type = Allocator;
82
83 using reference = typename base_type::reference;
84 using const_reference = typename base_type::const_reference;
85 using pointer = typename base_type::pointer;
86 using const_pointer = typename base_type::const_pointer;
87
88 using iterator = typename base_type::iterator;
89 using const_iterator = typename base_type::const_iterator;
90
91 using node_type = typename base_type::node_type;
92
93 // Include constructors of base type
94 using base_type::base_type;
95
96 // Required for implicit deduction guides
97 concurrent_map() = default;
98 concurrent_map( const concurrent_map& ) = default;
concurrent_map(const concurrent_map & other,const allocator_type & alloc)99 concurrent_map( const concurrent_map& other, const allocator_type& alloc ) : base_type(other, alloc) {}
100 concurrent_map( concurrent_map&& ) = default;
concurrent_map(concurrent_map && other,const allocator_type & alloc)101 concurrent_map( concurrent_map&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {}
102 // Required to respect the rule of 5
103 concurrent_map& operator=( const concurrent_map& ) = default;
104 concurrent_map& operator=( concurrent_map&& ) = default;
105
106 concurrent_map& operator=( std::initializer_list<value_type> il ) {
107 base_type::operator= (il);
108 return *this;
109 }
110
111 // Observers
at(const key_type & key)112 mapped_type& at(const key_type& key) {
113 iterator it = this->find(key);
114
115 if (it == this->end()) {
116 throw_exception(exception_id::invalid_key);
117 }
118 return it->second;
119 }
120
at(const key_type & key)121 const mapped_type& at(const key_type& key) const {
122 return const_cast<concurrent_map*>(this)->at(key);
123 }
124
125 mapped_type& operator[](const key_type& key) {
126 iterator it = this->find(key);
127
128 if (it == this->end()) {
129 it = this->emplace(std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()).first;
130 }
131 return it->second;
132 }
133
134 mapped_type& operator[](key_type&& key) {
135 iterator it = this->find(key);
136
137 if (it == this->end()) {
138 it = this->emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()).first;
139 }
140 return it->second;
141 }
142
143 using base_type::insert;
144
145 template <typename P>
146 typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(P && value)147 std::pair<iterator, bool>>::type insert( P&& value )
148 {
149 return this->emplace(std::forward<P>(value));
150 }
151
152 template <typename P>
153 typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(const_iterator hint,P && value)154 iterator>::type insert( const_iterator hint, P&& value )
155 {
156 return this->emplace_hint(hint, std::forward<P>(value));
157 }
158
159 template<typename OtherCompare>
merge(concurrent_map<key_type,mapped_type,OtherCompare,Allocator> & source)160 void merge(concurrent_map<key_type, mapped_type, OtherCompare, Allocator>& source) {
161 this->internal_merge(source);
162 }
163
164 template<typename OtherCompare>
merge(concurrent_map<key_type,mapped_type,OtherCompare,Allocator> && source)165 void merge(concurrent_map<key_type, mapped_type, OtherCompare, Allocator>&& source) {
166 this->internal_merge(std::move(source));
167 }
168
169 template<typename OtherCompare>
merge(concurrent_multimap<key_type,mapped_type,OtherCompare,Allocator> & source)170 void merge(concurrent_multimap<key_type, mapped_type, OtherCompare, Allocator>& source) {
171 this->internal_merge(source);
172 }
173
174 template<typename OtherCompare>
merge(concurrent_multimap<key_type,mapped_type,OtherCompare,Allocator> && source)175 void merge(concurrent_multimap<key_type, mapped_type, OtherCompare, Allocator>&& source) {
176 this->internal_merge(std::move(source));
177 }
178 }; // class concurrent_map
179
180 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
181
182 template <typename It,
183 typename Comp = std::less<iterator_key_t<It>>,
184 typename Alloc = tbb::tbb_allocator<iterator_alloc_pair_t<It>>,
185 typename = std::enable_if_t<is_input_iterator_v<It>>,
186 typename = std::enable_if_t<is_allocator_v<Alloc>>,
187 typename = std::enable_if_t<!is_allocator_v<Comp>>>
188 concurrent_map( It, It, Comp = Comp(), Alloc = Alloc() )
189 -> concurrent_map<iterator_key_t<It>, iterator_mapped_t<It>, Comp, Alloc>;
190
191 template <typename Key, typename T,
192 typename Comp = std::less<std::remove_const_t<Key>>,
193 typename Alloc = tbb::tbb_allocator<std::pair<const Key, T>>,
194 typename = std::enable_if_t<is_allocator_v<Alloc>>,
195 typename = std::enable_if_t<!is_allocator_v<Comp>>>
196 concurrent_map( std::initializer_list<std::pair<Key, T>>, Comp = Comp(), Alloc = Alloc() )
197 -> concurrent_map<std::remove_const_t<Key>, T, Comp, Alloc>;
198
199 template <typename It, typename Alloc,
200 typename = std::enable_if_t<is_input_iterator_v<It>>,
201 typename = std::enable_if_t<is_allocator_v<Alloc>>>
202 concurrent_map( It, It, Alloc )
203 -> concurrent_map<iterator_key_t<It>, iterator_mapped_t<It>,
204 std::less<iterator_key_t<It>>, Alloc>;
205
206 template <typename Key, typename T, typename Alloc,
207 typename = std::enable_if_t<is_allocator_v<Alloc>>>
208 concurrent_map( std::initializer_list<std::pair<Key, T>>, Alloc )
209 -> concurrent_map<std::remove_const_t<Key>, T, std::less<std::remove_const_t<Key>>, Alloc>;
210
211 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
212
213 template <typename Key, typename Value, typename Compare, typename Allocator>
swap(concurrent_map<Key,Value,Compare,Allocator> & lhs,concurrent_map<Key,Value,Compare,Allocator> & rhs)214 void swap( concurrent_map<Key, Value, Compare, Allocator>& lhs,
215 concurrent_map<Key, Value, Compare, Allocator>& rhs )
216 {
217 lhs.swap(rhs);
218 }
219
220 template <typename Key, typename Value, typename Compare = std::less<Key>, typename Allocator = tbb::tbb_allocator<std::pair<const Key, Value>>>
221 class concurrent_multimap : public concurrent_skip_list<map_traits<Key, Value, Compare, concurrent_geometric_level_generator<32>, Allocator, true>> {
222 using base_type = concurrent_skip_list<map_traits<Key, Value, Compare, concurrent_geometric_level_generator<32>, Allocator, true>>;
223 public:
224 using key_type = Key;
225 using mapped_type = Value;
226 using value_type = typename base_type::value_type;
227 using size_type = typename base_type::size_type;
228 using difference_type = typename base_type::difference_type;
229 using key_compare = Compare;
230 using value_compare = typename base_type::value_compare;
231 using allocator_type = Allocator;
232
233 using reference = typename base_type::reference;
234 using const_reference = typename base_type::const_reference;
235 using pointer = typename base_type::pointer;
236 using const_pointer = typename base_type::const_pointer;
237
238 using iterator = typename base_type::iterator;
239 using const_iterator = typename base_type::const_iterator;
240
241 using node_type = typename base_type::node_type;
242
243 // Include constructors of base_type
244 using base_type::base_type;
245 using base_type::insert;
246
247 // Required for implicit deduction guides
248 concurrent_multimap() = default;
249 concurrent_multimap( const concurrent_multimap& ) = default;
concurrent_multimap(const concurrent_multimap & other,const allocator_type & alloc)250 concurrent_multimap( const concurrent_multimap& other, const allocator_type& alloc ) : base_type(other, alloc) {}
251 concurrent_multimap( concurrent_multimap&& ) = default;
concurrent_multimap(concurrent_multimap && other,const allocator_type & alloc)252 concurrent_multimap( concurrent_multimap&& other, const allocator_type& alloc ) : base_type(std::move(other), alloc) {}
253 // Required to respect the rule of 5
254 concurrent_multimap& operator=( const concurrent_multimap& ) = default;
255 concurrent_multimap& operator=( concurrent_multimap&& ) = default;
256
257 concurrent_multimap& operator=( std::initializer_list<value_type> il ) {
258 base_type::operator= (il);
259 return *this;
260 }
261
262 template <typename P>
263 typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(P && value)264 std::pair<iterator, bool>>::type insert( P&& value )
265 {
266 return this->emplace(std::forward<P>(value));
267 }
268
269 template <typename P>
270 typename std::enable_if<std::is_constructible<value_type, P&&>::value,
insert(const_iterator hint,P && value)271 iterator>::type insert( const_iterator hint, P&& value )
272 {
273 return this->emplace_hint(hint, std::forward<P>(value));
274 }
275
276 template<typename OtherCompare>
merge(concurrent_multimap<key_type,mapped_type,OtherCompare,Allocator> & source)277 void merge(concurrent_multimap<key_type, mapped_type, OtherCompare, Allocator>& source) {
278 this->internal_merge(source);
279 }
280
281 template<typename OtherCompare>
merge(concurrent_multimap<key_type,mapped_type,OtherCompare,Allocator> && source)282 void merge(concurrent_multimap<key_type, mapped_type, OtherCompare, Allocator>&& source) {
283 this->internal_merge(std::move(source));
284 }
285
286 template<typename OtherCompare>
merge(concurrent_map<key_type,mapped_type,OtherCompare,Allocator> & source)287 void merge(concurrent_map<key_type, mapped_type, OtherCompare, Allocator>& source) {
288 this->internal_merge(source);
289 }
290
291 template<typename OtherCompare>
merge(concurrent_map<key_type,mapped_type,OtherCompare,Allocator> && source)292 void merge(concurrent_map<key_type, mapped_type, OtherCompare, Allocator>&& source) {
293 this->internal_merge(std::move(source));
294 }
295 }; // class concurrent_multimap
296
297 #if __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
298
299 template <typename It,
300 typename Comp = std::less<iterator_key_t<It>>,
301 typename Alloc = tbb::tbb_allocator<iterator_alloc_pair_t<It>>,
302 typename = std::enable_if_t<is_input_iterator_v<It>>,
303 typename = std::enable_if_t<is_allocator_v<Alloc>>,
304 typename = std::enable_if_t<!is_allocator_v<Comp>>>
305 concurrent_multimap( It, It, Comp = Comp(), Alloc = Alloc() )
306 -> concurrent_multimap<iterator_key_t<It>, iterator_mapped_t<It>, Comp, Alloc>;
307
308 template <typename Key, typename T,
309 typename Comp = std::less<std::remove_const_t<Key>>,
310 typename Alloc = tbb::tbb_allocator<std::pair<const Key, T>>,
311 typename = std::enable_if_t<is_allocator_v<Alloc>>,
312 typename = std::enable_if_t<!is_allocator_v<Comp>>>
313 concurrent_multimap( std::initializer_list<std::pair<Key, T>>, Comp = Comp(), Alloc = Alloc() )
314 -> concurrent_multimap<std::remove_const_t<Key>, T, Comp, Alloc>;
315
316 template <typename It, typename Alloc,
317 typename = std::enable_if_t<is_input_iterator_v<It>>,
318 typename = std::enable_if_t<is_allocator_v<Alloc>>>
319 concurrent_multimap( It, It, Alloc )
320 -> concurrent_multimap<iterator_key_t<It>, iterator_mapped_t<It>,
321 std::less<iterator_key_t<It>>, Alloc>;
322
323 template <typename Key, typename T, typename Alloc,
324 typename = std::enable_if_t<is_allocator_v<Alloc>>>
325 concurrent_multimap( std::initializer_list<std::pair<Key, T>>, Alloc )
326 -> concurrent_multimap<std::remove_const_t<Key>, T, std::less<std::remove_const_t<Key>>, Alloc>;
327
328
329 #endif // __TBB_CPP17_DEDUCTION_GUIDES_PRESENT
330
331 template <typename Key, typename Value, typename Compare, typename Allocator>
swap(concurrent_multimap<Key,Value,Compare,Allocator> & lhs,concurrent_multimap<Key,Value,Compare,Allocator> & rhs)332 void swap( concurrent_multimap<Key, Value, Compare, Allocator>& lhs,
333 concurrent_multimap<Key, Value, Compare, Allocator>& rhs )
334 {
335 lhs.swap(rhs);
336 }
337
338 } // namespace d2
339 } // namespace detail
340
341 inline namespace v1 {
342
343 using detail::d2::concurrent_map;
344 using detail::d2::concurrent_multimap;
345 using detail::split;
346
347 } // inline namespace v1
348 } // namespace tbb
349
350 #endif // __TBB_concurrent_map_H
351