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: 48 bool operator()(const value_type& lhs, const value_type& rhs) const { 49 return comp(lhs.first, rhs.first); 50 } 51 52 protected: 53 value_compare(compare_type c) : comp(c) {} 54 55 friend struct map_traits; 56 57 compare_type comp; 58 }; 59 60 static value_compare value_comp(compare_type comp) { return value_compare(comp); } 61 62 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; 99 concurrent_map( const concurrent_map& other, const allocator_type& alloc ) : base_type(other, alloc) {} 100 concurrent_map( concurrent_map&& ) = default; 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 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 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, 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, 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> 160 void merge(concurrent_map<key_type, mapped_type, OtherCompare, Allocator>& source) { 161 this->internal_merge(source); 162 } 163 164 template<typename OtherCompare> 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> 170 void merge(concurrent_multimap<key_type, mapped_type, OtherCompare, Allocator>& source) { 171 this->internal_merge(source); 172 } 173 174 template<typename OtherCompare> 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> 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; 250 concurrent_multimap( const concurrent_multimap& other, const allocator_type& alloc ) : base_type(other, alloc) {} 251 concurrent_multimap( concurrent_multimap&& ) = default; 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, 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, 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> 277 void merge(concurrent_multimap<key_type, mapped_type, OtherCompare, Allocator>& source) { 278 this->internal_merge(source); 279 } 280 281 template<typename OtherCompare> 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> 287 void merge(concurrent_map<key_type, mapped_type, OtherCompare, Allocator>& source) { 288 this->internal_merge(source); 289 } 290 291 template<typename OtherCompare> 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> 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