container_memory.h (18533B)
1 // Copyright 2018 The Abseil Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #ifndef ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ 16 #define ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_ 17 18 #include <cassert> 19 #include <cstddef> 20 #include <cstdint> 21 #include <cstring> 22 #include <memory> 23 #include <new> 24 #include <tuple> 25 #include <type_traits> 26 #include <utility> 27 28 #include "absl/base/config.h" 29 #include "absl/memory/memory.h" 30 #include "absl/meta/type_traits.h" 31 #include "absl/utility/utility.h" 32 33 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 34 #include <sanitizer/asan_interface.h> 35 #endif 36 37 #ifdef ABSL_HAVE_MEMORY_SANITIZER 38 #include <sanitizer/msan_interface.h> 39 #endif 40 41 namespace absl { 42 ABSL_NAMESPACE_BEGIN 43 namespace container_internal { 44 45 template <size_t Alignment> 46 struct alignas(Alignment) AlignedType {}; 47 48 // Allocates at least n bytes aligned to the specified alignment. 49 // Alignment must be a power of 2. It must be positive. 50 // 51 // Note that many allocators don't honor alignment requirements above certain 52 // threshold (usually either alignof(std::max_align_t) or alignof(void*)). 53 // Allocate() doesn't apply alignment corrections. If the underlying allocator 54 // returns insufficiently alignment pointer, that's what you are going to get. 55 template <size_t Alignment, class Alloc> 56 void* Allocate(Alloc* alloc, size_t n) { 57 static_assert(Alignment > 0, ""); 58 assert(n && "n must be positive"); 59 using M = AlignedType<Alignment>; 60 using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; 61 using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; 62 // On macOS, "mem_alloc" is a #define with one argument defined in 63 // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it 64 // with the "foo(bar)" syntax. 65 A my_mem_alloc(*alloc); 66 void* p = AT::allocate(my_mem_alloc, (n + sizeof(M) - 1) / sizeof(M)); 67 assert(reinterpret_cast<uintptr_t>(p) % Alignment == 0 && 68 "allocator does not respect alignment"); 69 return p; 70 } 71 72 // Returns true if the destruction of the value with given Allocator will be 73 // trivial. 74 template <class Allocator, class ValueType> 75 constexpr auto IsDestructionTrivial() { 76 constexpr bool result = 77 std::is_trivially_destructible<ValueType>::value && 78 std::is_same<typename absl::allocator_traits< 79 Allocator>::template rebind_alloc<char>, 80 std::allocator<char>>::value; 81 return std::integral_constant<bool, result>(); 82 } 83 84 // The pointer must have been previously obtained by calling 85 // Allocate<Alignment>(alloc, n). 86 template <size_t Alignment, class Alloc> 87 void Deallocate(Alloc* alloc, void* p, size_t n) { 88 static_assert(Alignment > 0, ""); 89 assert(n && "n must be positive"); 90 using M = AlignedType<Alignment>; 91 using A = typename absl::allocator_traits<Alloc>::template rebind_alloc<M>; 92 using AT = typename absl::allocator_traits<Alloc>::template rebind_traits<M>; 93 // On macOS, "mem_alloc" is a #define with one argument defined in 94 // rpc/types.h, so we can't name the variable "mem_alloc" and initialize it 95 // with the "foo(bar)" syntax. 96 A my_mem_alloc(*alloc); 97 AT::deallocate(my_mem_alloc, static_cast<M*>(p), 98 (n + sizeof(M) - 1) / sizeof(M)); 99 } 100 101 namespace memory_internal { 102 103 // Constructs T into uninitialized storage pointed by `ptr` using the args 104 // specified in the tuple. 105 template <class Alloc, class T, class Tuple, size_t... I> 106 void ConstructFromTupleImpl(Alloc* alloc, T* ptr, Tuple&& t, 107 absl::index_sequence<I...>) { 108 absl::allocator_traits<Alloc>::construct( 109 *alloc, ptr, std::get<I>(std::forward<Tuple>(t))...); 110 } 111 112 template <class T, class F> 113 struct WithConstructedImplF { 114 template <class... Args> 115 decltype(std::declval<F>()(std::declval<T>())) operator()( 116 Args&&... args) const { 117 return std::forward<F>(f)(T(std::forward<Args>(args)...)); 118 } 119 F&& f; 120 }; 121 122 template <class T, class Tuple, size_t... Is, class F> 123 decltype(std::declval<F>()(std::declval<T>())) WithConstructedImpl( 124 Tuple&& t, absl::index_sequence<Is...>, F&& f) { 125 return WithConstructedImplF<T, F>{std::forward<F>(f)}( 126 std::get<Is>(std::forward<Tuple>(t))...); 127 } 128 129 template <class T, size_t... Is> 130 auto TupleRefImpl(T&& t, absl::index_sequence<Is...>) 131 -> decltype(std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...)) { 132 return std::forward_as_tuple(std::get<Is>(std::forward<T>(t))...); 133 } 134 135 // Returns a tuple of references to the elements of the input tuple. T must be a 136 // tuple. 137 template <class T> 138 auto TupleRef(T&& t) -> decltype(TupleRefImpl( 139 std::forward<T>(t), 140 absl::make_index_sequence< 141 std::tuple_size<typename std::decay<T>::type>::value>())) { 142 return TupleRefImpl( 143 std::forward<T>(t), 144 absl::make_index_sequence< 145 std::tuple_size<typename std::decay<T>::type>::value>()); 146 } 147 148 template <class F, class K, class V> 149 decltype(std::declval<F>()(std::declval<const K&>(), std::piecewise_construct, 150 std::declval<std::tuple<K>>(), std::declval<V>())) 151 DecomposePairImpl(F&& f, std::pair<std::tuple<K>, V> p) { 152 const auto& key = std::get<0>(p.first); 153 return std::forward<F>(f)(key, std::piecewise_construct, std::move(p.first), 154 std::move(p.second)); 155 } 156 157 } // namespace memory_internal 158 159 // Constructs T into uninitialized storage pointed by `ptr` using the args 160 // specified in the tuple. 161 template <class Alloc, class T, class Tuple> 162 void ConstructFromTuple(Alloc* alloc, T* ptr, Tuple&& t) { 163 memory_internal::ConstructFromTupleImpl( 164 alloc, ptr, std::forward<Tuple>(t), 165 absl::make_index_sequence< 166 std::tuple_size<typename std::decay<Tuple>::type>::value>()); 167 } 168 169 // Constructs T using the args specified in the tuple and calls F with the 170 // constructed value. 171 template <class T, class Tuple, class F> 172 decltype(std::declval<F>()(std::declval<T>())) WithConstructed(Tuple&& t, 173 F&& f) { 174 return memory_internal::WithConstructedImpl<T>( 175 std::forward<Tuple>(t), 176 absl::make_index_sequence< 177 std::tuple_size<typename std::decay<Tuple>::type>::value>(), 178 std::forward<F>(f)); 179 } 180 181 // Given arguments of an std::pair's constructor, PairArgs() returns a pair of 182 // tuples with references to the passed arguments. The tuples contain 183 // constructor arguments for the first and the second elements of the pair. 184 // 185 // The following two snippets are equivalent. 186 // 187 // 1. std::pair<F, S> p(args...); 188 // 189 // 2. auto a = PairArgs(args...); 190 // std::pair<F, S> p(std::piecewise_construct, 191 // std::move(a.first), std::move(a.second)); 192 inline std::pair<std::tuple<>, std::tuple<>> PairArgs() { return {}; } 193 template <class F, class S> 194 std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(F&& f, S&& s) { 195 return {std::piecewise_construct, std::forward_as_tuple(std::forward<F>(f)), 196 std::forward_as_tuple(std::forward<S>(s))}; 197 } 198 template <class F, class S> 199 std::pair<std::tuple<const F&>, std::tuple<const S&>> PairArgs( 200 const std::pair<F, S>& p) { 201 return PairArgs(p.first, p.second); 202 } 203 template <class F, class S> 204 std::pair<std::tuple<F&&>, std::tuple<S&&>> PairArgs(std::pair<F, S>&& p) { 205 return PairArgs(std::forward<F>(p.first), std::forward<S>(p.second)); 206 } 207 template <class F, class S> 208 auto PairArgs(std::piecewise_construct_t, F&& f, S&& s) 209 -> decltype(std::make_pair(memory_internal::TupleRef(std::forward<F>(f)), 210 memory_internal::TupleRef(std::forward<S>(s)))) { 211 return std::make_pair(memory_internal::TupleRef(std::forward<F>(f)), 212 memory_internal::TupleRef(std::forward<S>(s))); 213 } 214 215 // A helper function for implementing apply() in map policies. 216 template <class F, class... Args> 217 auto DecomposePair(F&& f, Args&&... args) 218 -> decltype(memory_internal::DecomposePairImpl( 219 std::forward<F>(f), PairArgs(std::forward<Args>(args)...))) { 220 return memory_internal::DecomposePairImpl( 221 std::forward<F>(f), PairArgs(std::forward<Args>(args)...)); 222 } 223 224 // A helper function for implementing apply() in set policies. 225 template <class F, class Arg> 226 decltype(std::declval<F>()(std::declval<const Arg&>(), std::declval<Arg>())) 227 DecomposeValue(F&& f, Arg&& arg) { 228 const auto& key = arg; 229 return std::forward<F>(f)(key, std::forward<Arg>(arg)); 230 } 231 232 // Helper functions for asan and msan. 233 inline void SanitizerPoisonMemoryRegion(const void* m, size_t s) { 234 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 235 ASAN_POISON_MEMORY_REGION(m, s); 236 #endif 237 #ifdef ABSL_HAVE_MEMORY_SANITIZER 238 __msan_poison(m, s); 239 #endif 240 (void)m; 241 (void)s; 242 } 243 244 inline void SanitizerUnpoisonMemoryRegion(const void* m, size_t s) { 245 #ifdef ABSL_HAVE_ADDRESS_SANITIZER 246 ASAN_UNPOISON_MEMORY_REGION(m, s); 247 #endif 248 #ifdef ABSL_HAVE_MEMORY_SANITIZER 249 __msan_unpoison(m, s); 250 #endif 251 (void)m; 252 (void)s; 253 } 254 255 template <typename T> 256 inline void SanitizerPoisonObject(const T* object) { 257 SanitizerPoisonMemoryRegion(object, sizeof(T)); 258 } 259 260 template <typename T> 261 inline void SanitizerUnpoisonObject(const T* object) { 262 SanitizerUnpoisonMemoryRegion(object, sizeof(T)); 263 } 264 265 namespace memory_internal { 266 267 // If Pair is a standard-layout type, OffsetOf<Pair>::kFirst and 268 // OffsetOf<Pair>::kSecond are equivalent to offsetof(Pair, first) and 269 // offsetof(Pair, second) respectively. Otherwise they are -1. 270 // 271 // The purpose of OffsetOf is to avoid calling offsetof() on non-standard-layout 272 // type, which is non-portable. 273 template <class Pair, class = std::true_type> 274 struct OffsetOf { 275 static constexpr size_t kFirst = static_cast<size_t>(-1); 276 static constexpr size_t kSecond = static_cast<size_t>(-1); 277 }; 278 279 template <class Pair> 280 struct OffsetOf<Pair, typename std::is_standard_layout<Pair>::type> { 281 static constexpr size_t kFirst = offsetof(Pair, first); 282 static constexpr size_t kSecond = offsetof(Pair, second); 283 }; 284 285 template <class K, class V> 286 struct IsLayoutCompatible { 287 private: 288 struct Pair { 289 K first; 290 V second; 291 }; 292 293 // Is P layout-compatible with Pair? 294 template <class P> 295 static constexpr bool LayoutCompatible() { 296 return std::is_standard_layout<P>() && sizeof(P) == sizeof(Pair) && 297 alignof(P) == alignof(Pair) && 298 memory_internal::OffsetOf<P>::kFirst == 299 memory_internal::OffsetOf<Pair>::kFirst && 300 memory_internal::OffsetOf<P>::kSecond == 301 memory_internal::OffsetOf<Pair>::kSecond; 302 } 303 304 public: 305 // Whether pair<const K, V> and pair<K, V> are layout-compatible. If they are, 306 // then it is safe to store them in a union and read from either. 307 static constexpr bool value = std::is_standard_layout<K>() && 308 std::is_standard_layout<Pair>() && 309 memory_internal::OffsetOf<Pair>::kFirst == 0 && 310 LayoutCompatible<std::pair<K, V>>() && 311 LayoutCompatible<std::pair<const K, V>>(); 312 }; 313 314 } // namespace memory_internal 315 316 // The internal storage type for key-value containers like flat_hash_map. 317 // 318 // It is convenient for the value_type of a flat_hash_map<K, V> to be 319 // pair<const K, V>; the "const K" prevents accidental modification of the key 320 // when dealing with the reference returned from find() and similar methods. 321 // However, this creates other problems; we want to be able to emplace(K, V) 322 // efficiently with move operations, and similarly be able to move a 323 // pair<K, V> in insert(). 324 // 325 // The solution is this union, which aliases the const and non-const versions 326 // of the pair. This also allows flat_hash_map<const K, V> to work, even though 327 // that has the same efficiency issues with move in emplace() and insert() - 328 // but people do it anyway. 329 // 330 // If kMutableKeys is false, only the value member can be accessed. 331 // 332 // If kMutableKeys is true, key can be accessed through all slots while value 333 // and mutable_value must be accessed only via INITIALIZED slots. Slots are 334 // created and destroyed via mutable_value so that the key can be moved later. 335 // 336 // Accessing one of the union fields while the other is active is safe as 337 // long as they are layout-compatible, which is guaranteed by the definition of 338 // kMutableKeys. For C++11, the relevant section of the standard is 339 // https://timsong-cpp.github.io/cppwp/n3337/class.mem#19 (9.2.19) 340 template <class K, class V> 341 union map_slot_type { 342 map_slot_type() {} 343 ~map_slot_type() = delete; 344 using value_type = std::pair<const K, V>; 345 using mutable_value_type = 346 std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; 347 348 value_type value; 349 mutable_value_type mutable_value; 350 absl::remove_const_t<K> key; 351 }; 352 353 template <class K, class V> 354 struct map_slot_policy { 355 using slot_type = map_slot_type<K, V>; 356 using value_type = std::pair<const K, V>; 357 using mutable_value_type = 358 std::pair<absl::remove_const_t<K>, absl::remove_const_t<V>>; 359 360 private: 361 static void emplace(slot_type* slot) { 362 // The construction of union doesn't do anything at runtime but it allows us 363 // to access its members without violating aliasing rules. 364 new (slot) slot_type; 365 } 366 // If pair<const K, V> and pair<K, V> are layout-compatible, we can accept one 367 // or the other via slot_type. We are also free to access the key via 368 // slot_type::key in this case. 369 using kMutableKeys = memory_internal::IsLayoutCompatible<K, V>; 370 371 public: 372 static value_type& element(slot_type* slot) { return slot->value; } 373 static const value_type& element(const slot_type* slot) { 374 return slot->value; 375 } 376 377 static K& mutable_key(slot_type* slot) { 378 // Still check for kMutableKeys so that we can avoid calling std::launder 379 // unless necessary because it can interfere with optimizations. 380 return kMutableKeys::value ? slot->key 381 : *std::launder(const_cast<K*>( 382 std::addressof(slot->value.first))); 383 } 384 385 static const K& key(const slot_type* slot) { 386 return kMutableKeys::value ? slot->key : slot->value.first; 387 } 388 389 template <class Allocator, class... Args> 390 static void construct(Allocator* alloc, slot_type* slot, Args&&... args) { 391 emplace(slot); 392 if (kMutableKeys::value) { 393 absl::allocator_traits<Allocator>::construct(*alloc, &slot->mutable_value, 394 std::forward<Args>(args)...); 395 } else { 396 absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, 397 std::forward<Args>(args)...); 398 } 399 } 400 401 // Construct this slot by moving from another slot. 402 template <class Allocator> 403 static void construct(Allocator* alloc, slot_type* slot, slot_type* other) { 404 emplace(slot); 405 if (kMutableKeys::value) { 406 absl::allocator_traits<Allocator>::construct( 407 *alloc, &slot->mutable_value, std::move(other->mutable_value)); 408 } else { 409 absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, 410 std::move(other->value)); 411 } 412 } 413 414 // Construct this slot by copying from another slot. 415 template <class Allocator> 416 static void construct(Allocator* alloc, slot_type* slot, 417 const slot_type* other) { 418 emplace(slot); 419 absl::allocator_traits<Allocator>::construct(*alloc, &slot->value, 420 other->value); 421 } 422 423 template <class Allocator> 424 static auto destroy(Allocator* alloc, slot_type* slot) { 425 if (kMutableKeys::value) { 426 absl::allocator_traits<Allocator>::destroy(*alloc, &slot->mutable_value); 427 } else { 428 absl::allocator_traits<Allocator>::destroy(*alloc, &slot->value); 429 } 430 return IsDestructionTrivial<Allocator, value_type>(); 431 } 432 433 template <class Allocator> 434 static auto transfer(Allocator* alloc, slot_type* new_slot, 435 slot_type* old_slot) { 436 // This should really just be 437 // typename absl::is_trivially_relocatable<value_type>::type() 438 // but std::pair is not trivially copyable in C++23 in some standard 439 // library versions. 440 // See https://github.com/llvm/llvm-project/pull/95444 for instance. 441 auto is_relocatable = typename std::conjunction< 442 absl::is_trivially_relocatable<typename value_type::first_type>, 443 absl::is_trivially_relocatable<typename value_type::second_type>>:: 444 type(); 445 446 emplace(new_slot); 447 if (is_relocatable) { 448 // TODO(b/247130232,b/251814870): remove casts after fixing warnings. 449 std::memcpy(static_cast<void*>(std::launder(&new_slot->value)), 450 static_cast<const void*>(&old_slot->value), 451 sizeof(value_type)); 452 return is_relocatable; 453 } 454 455 if (kMutableKeys::value) { 456 absl::allocator_traits<Allocator>::construct( 457 *alloc, &new_slot->mutable_value, std::move(old_slot->mutable_value)); 458 } else { 459 absl::allocator_traits<Allocator>::construct(*alloc, &new_slot->value, 460 std::move(old_slot->value)); 461 } 462 destroy(alloc, old_slot); 463 return is_relocatable; 464 } 465 }; 466 467 // Type erased function for computing hash of the slot. 468 using HashSlotFn = size_t (*)(const void* hash_fn, void* slot); 469 470 // Type erased function to apply `Fn` to data inside of the `slot`. 471 // The data is expected to have type `T`. 472 template <class Fn, class T> 473 size_t TypeErasedApplyToSlotFn(const void* fn, void* slot) { 474 const auto* f = static_cast<const Fn*>(fn); 475 return (*f)(*static_cast<const T*>(slot)); 476 } 477 478 // Type erased function to apply `Fn` to data inside of the `*slot_ptr`. 479 // The data is expected to have type `T`. 480 template <class Fn, class T> 481 size_t TypeErasedDerefAndApplyToSlotFn(const void* fn, void* slot_ptr) { 482 const auto* f = static_cast<const Fn*>(fn); 483 const T* slot = *static_cast<const T**>(slot_ptr); 484 return (*f)(*slot); 485 } 486 487 } // namespace container_internal 488 ABSL_NAMESPACE_END 489 } // namespace absl 490 491 #endif // ABSL_CONTAINER_INTERNAL_CONTAINER_MEMORY_H_