inlined_vector.h (39576B)
1 // Copyright 2019 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_INLINED_VECTOR_H_ 16 #define ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_ 17 18 #include <algorithm> 19 #include <cstddef> 20 #include <cstring> 21 #include <iterator> 22 #include <limits> 23 #include <memory> 24 #include <new> 25 #include <type_traits> 26 #include <utility> 27 28 #include "absl/base/attributes.h" 29 #include "absl/base/config.h" 30 #include "absl/base/internal/identity.h" 31 #include "absl/base/macros.h" 32 #include "absl/container/internal/compressed_tuple.h" 33 #include "absl/memory/memory.h" 34 #include "absl/meta/type_traits.h" 35 #include "absl/types/span.h" 36 37 namespace absl { 38 ABSL_NAMESPACE_BEGIN 39 namespace inlined_vector_internal { 40 41 // GCC does not deal very well with the below code 42 #if !defined(__clang__) && defined(__GNUC__) 43 #pragma GCC diagnostic push 44 #pragma GCC diagnostic ignored "-Warray-bounds" 45 #endif 46 47 template <typename A> 48 using AllocatorTraits = std::allocator_traits<A>; 49 template <typename A> 50 using ValueType = typename AllocatorTraits<A>::value_type; 51 template <typename A> 52 using SizeType = typename AllocatorTraits<A>::size_type; 53 template <typename A> 54 using Pointer = typename AllocatorTraits<A>::pointer; 55 template <typename A> 56 using ConstPointer = typename AllocatorTraits<A>::const_pointer; 57 template <typename A> 58 using SizeType = typename AllocatorTraits<A>::size_type; 59 template <typename A> 60 using DifferenceType = typename AllocatorTraits<A>::difference_type; 61 template <typename A> 62 using Reference = ValueType<A>&; 63 template <typename A> 64 using ConstReference = const ValueType<A>&; 65 template <typename A> 66 using Iterator = Pointer<A>; 67 template <typename A> 68 using ConstIterator = ConstPointer<A>; 69 template <typename A> 70 using ReverseIterator = typename std::reverse_iterator<Iterator<A>>; 71 template <typename A> 72 using ConstReverseIterator = typename std::reverse_iterator<ConstIterator<A>>; 73 template <typename A> 74 using MoveIterator = typename std::move_iterator<Iterator<A>>; 75 76 template <typename A> 77 using IsMoveAssignOk = std::is_move_assignable<ValueType<A>>; 78 template <typename A> 79 using IsSwapOk = absl::type_traits_internal::IsSwappable<ValueType<A>>; 80 81 template <typename A, 82 bool IsTriviallyDestructible = 83 absl::is_trivially_destructible<ValueType<A>>::value && 84 std::is_same<A, std::allocator<ValueType<A>>>::value> 85 struct DestroyAdapter; 86 87 template <typename A> 88 struct DestroyAdapter<A, /* IsTriviallyDestructible */ false> { 89 static void DestroyElements(A& allocator, Pointer<A> destroy_first, 90 SizeType<A> destroy_size) { 91 for (SizeType<A> i = destroy_size; i != 0;) { 92 --i; 93 AllocatorTraits<A>::destroy(allocator, destroy_first + i); 94 } 95 } 96 }; 97 98 template <typename A> 99 struct DestroyAdapter<A, /* IsTriviallyDestructible */ true> { 100 static void DestroyElements(A& allocator, Pointer<A> destroy_first, 101 SizeType<A> destroy_size) { 102 static_cast<void>(allocator); 103 static_cast<void>(destroy_first); 104 static_cast<void>(destroy_size); 105 } 106 }; 107 108 template <typename A> 109 struct Allocation { 110 Pointer<A> data = nullptr; 111 SizeType<A> capacity = 0; 112 }; 113 114 template <typename A, 115 bool IsOverAligned = 116 (alignof(ValueType<A>) > ABSL_INTERNAL_DEFAULT_NEW_ALIGNMENT)> 117 struct MallocAdapter { 118 static Allocation<A> Allocate(A& allocator, SizeType<A> requested_capacity) { 119 return {AllocatorTraits<A>::allocate(allocator, requested_capacity), 120 requested_capacity}; 121 } 122 123 static void Deallocate(A& allocator, Pointer<A> pointer, 124 SizeType<A> capacity) { 125 AllocatorTraits<A>::deallocate(allocator, pointer, capacity); 126 } 127 }; 128 129 template <typename A, typename ValueAdapter> 130 void ConstructElements(absl::internal::type_identity_t<A>& allocator, 131 Pointer<A> construct_first, ValueAdapter& values, 132 SizeType<A> construct_size) { 133 for (SizeType<A> i = 0; i < construct_size; ++i) { 134 ABSL_INTERNAL_TRY { values.ConstructNext(allocator, construct_first + i); } 135 ABSL_INTERNAL_CATCH_ANY { 136 DestroyAdapter<A>::DestroyElements(allocator, construct_first, i); 137 ABSL_INTERNAL_RETHROW; 138 } 139 } 140 } 141 142 template <typename A, typename ValueAdapter> 143 void AssignElements(Pointer<A> assign_first, ValueAdapter& values, 144 SizeType<A> assign_size) { 145 for (SizeType<A> i = 0; i < assign_size; ++i) { 146 values.AssignNext(assign_first + i); 147 } 148 } 149 150 template <typename A> 151 struct StorageView { 152 Pointer<A> data; 153 SizeType<A> size; 154 SizeType<A> capacity; 155 }; 156 157 template <typename A, typename Iterator> 158 class IteratorValueAdapter { 159 public: 160 explicit IteratorValueAdapter(const Iterator& it) : it_(it) {} 161 162 void ConstructNext(A& allocator, Pointer<A> construct_at) { 163 AllocatorTraits<A>::construct(allocator, construct_at, *it_); 164 ++it_; 165 } 166 167 void AssignNext(Pointer<A> assign_at) { 168 *assign_at = *it_; 169 ++it_; 170 } 171 172 private: 173 Iterator it_; 174 }; 175 176 template <typename A> 177 class CopyValueAdapter { 178 public: 179 explicit CopyValueAdapter(ConstPointer<A> p) : ptr_(p) {} 180 181 void ConstructNext(A& allocator, Pointer<A> construct_at) { 182 AllocatorTraits<A>::construct(allocator, construct_at, *ptr_); 183 } 184 185 void AssignNext(Pointer<A> assign_at) { *assign_at = *ptr_; } 186 187 private: 188 ConstPointer<A> ptr_; 189 }; 190 191 template <typename A> 192 class DefaultValueAdapter { 193 public: 194 explicit DefaultValueAdapter() {} 195 196 void ConstructNext(A& allocator, Pointer<A> construct_at) { 197 AllocatorTraits<A>::construct(allocator, construct_at); 198 } 199 200 void AssignNext(Pointer<A> assign_at) { *assign_at = ValueType<A>(); } 201 }; 202 203 template <typename A> 204 class AllocationTransaction { 205 public: 206 explicit AllocationTransaction(A& allocator) 207 : allocator_data_(allocator, nullptr), capacity_(0) {} 208 209 ~AllocationTransaction() { 210 if (DidAllocate()) { 211 MallocAdapter<A>::Deallocate(GetAllocator(), GetData(), GetCapacity()); 212 } 213 } 214 215 AllocationTransaction(const AllocationTransaction&) = delete; 216 void operator=(const AllocationTransaction&) = delete; 217 218 A& GetAllocator() { return allocator_data_.template get<0>(); } 219 Pointer<A>& GetData() { return allocator_data_.template get<1>(); } 220 SizeType<A>& GetCapacity() { return capacity_; } 221 222 bool DidAllocate() { return GetData() != nullptr; } 223 224 Pointer<A> Allocate(SizeType<A> requested_capacity) { 225 Allocation<A> result = 226 MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); 227 GetData() = result.data; 228 GetCapacity() = result.capacity; 229 return result.data; 230 } 231 232 [[nodiscard]] Allocation<A> Release() && { 233 Allocation<A> result = {GetData(), GetCapacity()}; 234 Reset(); 235 return result; 236 } 237 238 private: 239 void Reset() { 240 GetData() = nullptr; 241 GetCapacity() = 0; 242 } 243 244 container_internal::CompressedTuple<A, Pointer<A>> allocator_data_; 245 SizeType<A> capacity_; 246 }; 247 248 template <typename A> 249 class ConstructionTransaction { 250 public: 251 explicit ConstructionTransaction(A& allocator) 252 : allocator_data_(allocator, nullptr), size_(0) {} 253 254 ~ConstructionTransaction() { 255 if (DidConstruct()) { 256 DestroyAdapter<A>::DestroyElements(GetAllocator(), GetData(), GetSize()); 257 } 258 } 259 260 ConstructionTransaction(const ConstructionTransaction&) = delete; 261 void operator=(const ConstructionTransaction&) = delete; 262 263 A& GetAllocator() { return allocator_data_.template get<0>(); } 264 Pointer<A>& GetData() { return allocator_data_.template get<1>(); } 265 SizeType<A>& GetSize() { return size_; } 266 267 bool DidConstruct() { return GetData() != nullptr; } 268 template <typename ValueAdapter> 269 void Construct(Pointer<A> data, ValueAdapter& values, SizeType<A> size) { 270 ConstructElements<A>(GetAllocator(), data, values, size); 271 GetData() = data; 272 GetSize() = size; 273 } 274 void Commit() && { 275 GetData() = nullptr; 276 GetSize() = 0; 277 } 278 279 private: 280 container_internal::CompressedTuple<A, Pointer<A>> allocator_data_; 281 SizeType<A> size_; 282 }; 283 284 template <typename T, size_t N, typename A> 285 class Storage { 286 public: 287 struct MemcpyPolicy {}; 288 struct ElementwiseAssignPolicy {}; 289 struct ElementwiseSwapPolicy {}; 290 struct ElementwiseConstructPolicy {}; 291 292 using MoveAssignmentPolicy = absl::conditional_t< 293 // Fast path: if the value type can be trivially move assigned and 294 // destroyed, and we know the allocator doesn't do anything fancy, then 295 // it's safe for us to simply adopt the contents of the storage for 296 // `other` and remove its own reference to them. It's as if we had 297 // individually move-assigned each value and then destroyed the original. 298 absl::conjunction<absl::is_trivially_move_assignable<ValueType<A>>, 299 absl::is_trivially_destructible<ValueType<A>>, 300 std::is_same<A, std::allocator<ValueType<A>>>>::value, 301 MemcpyPolicy, 302 // Otherwise we use move assignment if possible. If not, we simulate 303 // move assignment using move construction. 304 // 305 // Note that this is in contrast to e.g. std::vector and std::optional, 306 // which are themselves not move-assignable when their contained type is 307 // not. 308 absl::conditional_t<IsMoveAssignOk<A>::value, ElementwiseAssignPolicy, 309 ElementwiseConstructPolicy>>; 310 311 // The policy to be used specifically when swapping inlined elements. 312 using SwapInlinedElementsPolicy = absl::conditional_t< 313 // Fast path: if the value type can be trivially relocated, and we 314 // know the allocator doesn't do anything fancy, then it's safe for us 315 // to simply swap the bytes in the inline storage. It's as if we had 316 // relocated the first vector's elements into temporary storage, 317 // relocated the second's elements into the (now-empty) first's, 318 // and then relocated from temporary storage into the second. 319 absl::conjunction<absl::is_trivially_relocatable<ValueType<A>>, 320 std::is_same<A, std::allocator<ValueType<A>>>>::value, 321 MemcpyPolicy, 322 absl::conditional_t<IsSwapOk<A>::value, ElementwiseSwapPolicy, 323 ElementwiseConstructPolicy>>; 324 325 static SizeType<A> NextCapacity(SizeType<A> current_capacity) { 326 return current_capacity * 2; 327 } 328 329 static SizeType<A> ComputeCapacity(SizeType<A> current_capacity, 330 SizeType<A> requested_capacity) { 331 return (std::max)(NextCapacity(current_capacity), requested_capacity); 332 } 333 334 // --------------------------------------------------------------------------- 335 // Storage Constructors and Destructor 336 // --------------------------------------------------------------------------- 337 338 Storage() : metadata_(A(), /* size and is_allocated */ 0u) {} 339 340 explicit Storage(const A& allocator) 341 : metadata_(allocator, /* size and is_allocated */ 0u) {} 342 343 ~Storage() { 344 // Fast path: if we are empty and not allocated, there's nothing to do. 345 if (GetSizeAndIsAllocated() == 0) { 346 return; 347 } 348 349 // Fast path: if no destructors need to be run and we know the allocator 350 // doesn't do anything fancy, then all we need to do is deallocate (and 351 // maybe not even that). 352 if (absl::is_trivially_destructible<ValueType<A>>::value && 353 std::is_same<A, std::allocator<ValueType<A>>>::value) { 354 DeallocateIfAllocated(); 355 return; 356 } 357 358 DestroyContents(); 359 } 360 361 // --------------------------------------------------------------------------- 362 // Storage Member Accessors 363 // --------------------------------------------------------------------------- 364 365 SizeType<A>& GetSizeAndIsAllocated() { return metadata_.template get<1>(); } 366 367 const SizeType<A>& GetSizeAndIsAllocated() const { 368 return metadata_.template get<1>(); 369 } 370 371 SizeType<A> GetSize() const { return GetSizeAndIsAllocated() >> 1; } 372 373 bool GetIsAllocated() const { return GetSizeAndIsAllocated() & 1; } 374 375 Pointer<A> GetAllocatedData() { 376 // GCC 12 has a false-positive -Wmaybe-uninitialized warning here. 377 #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) 378 #pragma GCC diagnostic push 379 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 380 #endif 381 return data_.allocated.allocated_data; 382 #if ABSL_INTERNAL_HAVE_MIN_GNUC_VERSION(12, 0) 383 #pragma GCC diagnostic pop 384 #endif 385 } 386 387 ConstPointer<A> GetAllocatedData() const { 388 return data_.allocated.allocated_data; 389 } 390 391 // ABSL_ATTRIBUTE_NO_SANITIZE_CFI is used because the memory pointed to may be 392 // uninitialized, a common pattern in allocate()+construct() APIs. 393 // https://clang.llvm.org/docs/ControlFlowIntegrity.html#bad-cast-checking 394 // NOTE: When this was written, LLVM documentation did not explicitly 395 // mention that casting `char*` and using `reinterpret_cast` qualifies 396 // as a bad cast. 397 ABSL_ATTRIBUTE_NO_SANITIZE_CFI Pointer<A> GetInlinedData() { 398 return reinterpret_cast<Pointer<A>>(data_.inlined.inlined_data); 399 } 400 401 ABSL_ATTRIBUTE_NO_SANITIZE_CFI ConstPointer<A> GetInlinedData() const { 402 return reinterpret_cast<ConstPointer<A>>(data_.inlined.inlined_data); 403 } 404 405 SizeType<A> GetAllocatedCapacity() const { 406 return data_.allocated.allocated_capacity; 407 } 408 409 SizeType<A> GetInlinedCapacity() const { 410 return static_cast<SizeType<A>>(kOptimalInlinedSize); 411 } 412 413 StorageView<A> MakeStorageView() { 414 return GetIsAllocated() ? StorageView<A>{GetAllocatedData(), GetSize(), 415 GetAllocatedCapacity()} 416 : StorageView<A>{GetInlinedData(), GetSize(), 417 GetInlinedCapacity()}; 418 } 419 420 A& GetAllocator() { return metadata_.template get<0>(); } 421 422 const A& GetAllocator() const { return metadata_.template get<0>(); } 423 424 // --------------------------------------------------------------------------- 425 // Storage Member Mutators 426 // --------------------------------------------------------------------------- 427 428 ABSL_ATTRIBUTE_NOINLINE void InitFrom(const Storage& other); 429 430 template <typename ValueAdapter> 431 void Initialize(ValueAdapter values, SizeType<A> new_size); 432 433 template <typename ValueAdapter> 434 void Assign(ValueAdapter values, SizeType<A> new_size); 435 436 template <typename ValueAdapter> 437 void Resize(ValueAdapter values, SizeType<A> new_size); 438 439 template <typename ValueAdapter> 440 Iterator<A> Insert(ConstIterator<A> pos, ValueAdapter values, 441 SizeType<A> insert_count); 442 443 template <typename... Args> 444 Reference<A> EmplaceBack(Args&&... args); 445 446 Iterator<A> Erase(ConstIterator<A> from, ConstIterator<A> to); 447 448 void Reserve(SizeType<A> requested_capacity); 449 450 void ShrinkToFit(); 451 452 void Swap(Storage* other_storage_ptr); 453 454 void SetIsAllocated() { 455 GetSizeAndIsAllocated() |= static_cast<SizeType<A>>(1); 456 } 457 458 void UnsetIsAllocated() { 459 GetSizeAndIsAllocated() &= ((std::numeric_limits<SizeType<A>>::max)() - 1); 460 } 461 462 void SetSize(SizeType<A> size) { 463 GetSizeAndIsAllocated() = 464 (size << 1) | static_cast<SizeType<A>>(GetIsAllocated()); 465 } 466 467 void SetAllocatedSize(SizeType<A> size) { 468 GetSizeAndIsAllocated() = (size << 1) | static_cast<SizeType<A>>(1); 469 } 470 471 void SetInlinedSize(SizeType<A> size) { 472 GetSizeAndIsAllocated() = size << static_cast<SizeType<A>>(1); 473 } 474 475 void AddSize(SizeType<A> count) { 476 GetSizeAndIsAllocated() += count << static_cast<SizeType<A>>(1); 477 } 478 479 void SubtractSize(SizeType<A> count) { 480 ABSL_HARDENING_ASSERT(count <= GetSize()); 481 482 GetSizeAndIsAllocated() -= count << static_cast<SizeType<A>>(1); 483 } 484 485 void SetAllocation(Allocation<A> allocation) { 486 data_.allocated.allocated_data = allocation.data; 487 data_.allocated.allocated_capacity = allocation.capacity; 488 } 489 490 void MemcpyFrom(const Storage& other_storage) { 491 // Assumption check: it doesn't make sense to memcpy inlined elements unless 492 // we know the allocator doesn't do anything fancy, and one of the following 493 // holds: 494 // 495 // * The elements are trivially relocatable. 496 // 497 // * It's possible to trivially assign the elements and then destroy the 498 // source. 499 // 500 // * It's possible to trivially copy construct/assign the elements. 501 // 502 { 503 using V = ValueType<A>; 504 ABSL_HARDENING_ASSERT( 505 other_storage.GetIsAllocated() || 506 (std::is_same<A, std::allocator<V>>::value && 507 ( 508 // First case above 509 absl::is_trivially_relocatable<V>::value || 510 // Second case above 511 (absl::is_trivially_move_assignable<V>::value && 512 absl::is_trivially_destructible<V>::value) || 513 // Third case above 514 (absl::is_trivially_copy_constructible<V>::value || 515 absl::is_trivially_copy_assignable<V>::value)))); 516 } 517 518 GetSizeAndIsAllocated() = other_storage.GetSizeAndIsAllocated(); 519 data_ = other_storage.data_; 520 } 521 522 void DeallocateIfAllocated() { 523 if (GetIsAllocated()) { 524 MallocAdapter<A>::Deallocate(GetAllocator(), GetAllocatedData(), 525 GetAllocatedCapacity()); 526 } 527 } 528 529 private: 530 ABSL_ATTRIBUTE_NOINLINE void DestroyContents(); 531 532 using Metadata = container_internal::CompressedTuple<A, SizeType<A>>; 533 534 struct Allocated { 535 Pointer<A> allocated_data; 536 SizeType<A> allocated_capacity; 537 }; 538 539 // `kOptimalInlinedSize` is an automatically adjusted inlined capacity of the 540 // `InlinedVector`. Sometimes, it is possible to increase the capacity (from 541 // the user requested `N`) without increasing the size of the `InlinedVector`. 542 static constexpr size_t kOptimalInlinedSize = 543 (std::max)(N, sizeof(Allocated) / sizeof(ValueType<A>)); 544 545 struct Inlined { 546 alignas(ValueType<A>) unsigned char inlined_data[sizeof( 547 ValueType<A>[kOptimalInlinedSize])]; 548 }; 549 550 union Data { 551 Allocated allocated; 552 Inlined inlined; 553 }; 554 555 void SwapN(ElementwiseSwapPolicy, Storage* other, SizeType<A> n); 556 void SwapN(ElementwiseConstructPolicy, Storage* other, SizeType<A> n); 557 558 void SwapInlinedElements(MemcpyPolicy, Storage* other); 559 template <typename NotMemcpyPolicy> 560 void SwapInlinedElements(NotMemcpyPolicy, Storage* other); 561 562 template <typename... Args> 563 ABSL_ATTRIBUTE_NOINLINE Reference<A> EmplaceBackSlow(Args&&... args); 564 565 Metadata metadata_; 566 Data data_; 567 }; 568 569 template <typename T, size_t N, typename A> 570 void Storage<T, N, A>::DestroyContents() { 571 Pointer<A> data = GetIsAllocated() ? GetAllocatedData() : GetInlinedData(); 572 DestroyAdapter<A>::DestroyElements(GetAllocator(), data, GetSize()); 573 DeallocateIfAllocated(); 574 } 575 576 template <typename T, size_t N, typename A> 577 void Storage<T, N, A>::InitFrom(const Storage& other) { 578 const SizeType<A> n = other.GetSize(); 579 ABSL_HARDENING_ASSERT(n > 0); // Empty sources handled handled in caller. 580 ConstPointer<A> src; 581 Pointer<A> dst; 582 if (!other.GetIsAllocated()) { 583 dst = GetInlinedData(); 584 src = other.GetInlinedData(); 585 } else { 586 // Because this is only called from the `InlinedVector` constructors, it's 587 // safe to take on the allocation with size `0`. If `ConstructElements(...)` 588 // throws, deallocation will be automatically handled by `~Storage()`. 589 SizeType<A> requested_capacity = ComputeCapacity(GetInlinedCapacity(), n); 590 Allocation<A> allocation = 591 MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); 592 SetAllocation(allocation); 593 dst = allocation.data; 594 src = other.GetAllocatedData(); 595 } 596 597 // Fast path: if the value type is trivially copy constructible and we know 598 // the allocator doesn't do anything fancy, then we know it is legal for us to 599 // simply memcpy the other vector's elements. 600 if (absl::is_trivially_copy_constructible<ValueType<A>>::value && 601 std::is_same<A, std::allocator<ValueType<A>>>::value) { 602 std::memcpy(reinterpret_cast<char*>(dst), 603 reinterpret_cast<const char*>(src), n * sizeof(ValueType<A>)); 604 } else { 605 auto values = IteratorValueAdapter<A, ConstPointer<A>>(src); 606 ConstructElements<A>(GetAllocator(), dst, values, n); 607 } 608 609 GetSizeAndIsAllocated() = other.GetSizeAndIsAllocated(); 610 } 611 612 template <typename T, size_t N, typename A> 613 template <typename ValueAdapter> 614 auto Storage<T, N, A>::Initialize(ValueAdapter values, 615 SizeType<A> new_size) -> void { 616 // Only callable from constructors! 617 ABSL_HARDENING_ASSERT(!GetIsAllocated()); 618 ABSL_HARDENING_ASSERT(GetSize() == 0); 619 620 Pointer<A> construct_data; 621 if (new_size > GetInlinedCapacity()) { 622 // Because this is only called from the `InlinedVector` constructors, it's 623 // safe to take on the allocation with size `0`. If `ConstructElements(...)` 624 // throws, deallocation will be automatically handled by `~Storage()`. 625 SizeType<A> requested_capacity = 626 ComputeCapacity(GetInlinedCapacity(), new_size); 627 Allocation<A> allocation = 628 MallocAdapter<A>::Allocate(GetAllocator(), requested_capacity); 629 construct_data = allocation.data; 630 SetAllocation(allocation); 631 SetIsAllocated(); 632 } else { 633 construct_data = GetInlinedData(); 634 } 635 636 ConstructElements<A>(GetAllocator(), construct_data, values, new_size); 637 638 // Since the initial size was guaranteed to be `0` and the allocated bit is 639 // already correct for either case, *adding* `new_size` gives us the correct 640 // result faster than setting it directly. 641 AddSize(new_size); 642 } 643 644 template <typename T, size_t N, typename A> 645 template <typename ValueAdapter> 646 auto Storage<T, N, A>::Assign(ValueAdapter values, 647 SizeType<A> new_size) -> void { 648 StorageView<A> storage_view = MakeStorageView(); 649 650 AllocationTransaction<A> allocation_tx(GetAllocator()); 651 652 absl::Span<ValueType<A>> assign_loop; 653 absl::Span<ValueType<A>> construct_loop; 654 absl::Span<ValueType<A>> destroy_loop; 655 656 if (new_size > storage_view.capacity) { 657 SizeType<A> requested_capacity = 658 ComputeCapacity(storage_view.capacity, new_size); 659 construct_loop = {allocation_tx.Allocate(requested_capacity), new_size}; 660 destroy_loop = {storage_view.data, storage_view.size}; 661 } else if (new_size > storage_view.size) { 662 assign_loop = {storage_view.data, storage_view.size}; 663 construct_loop = {storage_view.data + storage_view.size, 664 new_size - storage_view.size}; 665 } else { 666 assign_loop = {storage_view.data, new_size}; 667 destroy_loop = {storage_view.data + new_size, storage_view.size - new_size}; 668 } 669 670 AssignElements<A>(assign_loop.data(), values, assign_loop.size()); 671 672 ConstructElements<A>(GetAllocator(), construct_loop.data(), values, 673 construct_loop.size()); 674 675 DestroyAdapter<A>::DestroyElements(GetAllocator(), destroy_loop.data(), 676 destroy_loop.size()); 677 678 if (allocation_tx.DidAllocate()) { 679 DeallocateIfAllocated(); 680 SetAllocation(std::move(allocation_tx).Release()); 681 SetIsAllocated(); 682 } 683 684 SetSize(new_size); 685 } 686 687 template <typename T, size_t N, typename A> 688 template <typename ValueAdapter> 689 auto Storage<T, N, A>::Resize(ValueAdapter values, 690 SizeType<A> new_size) -> void { 691 StorageView<A> storage_view = MakeStorageView(); 692 Pointer<A> const base = storage_view.data; 693 const SizeType<A> size = storage_view.size; 694 A& alloc = GetAllocator(); 695 if (new_size <= size) { 696 // Destroy extra old elements. 697 DestroyAdapter<A>::DestroyElements(alloc, base + new_size, size - new_size); 698 } else if (new_size <= storage_view.capacity) { 699 // Construct new elements in place. 700 ConstructElements<A>(alloc, base + size, values, new_size - size); 701 } else { 702 // Steps: 703 // a. Allocate new backing store. 704 // b. Construct new elements in new backing store. 705 // c. Move existing elements from old backing store to new backing store. 706 // d. Destroy all elements in old backing store. 707 // Use transactional wrappers for the first two steps so we can roll 708 // back if necessary due to exceptions. 709 AllocationTransaction<A> allocation_tx(alloc); 710 SizeType<A> requested_capacity = 711 ComputeCapacity(storage_view.capacity, new_size); 712 Pointer<A> new_data = allocation_tx.Allocate(requested_capacity); 713 714 ConstructionTransaction<A> construction_tx(alloc); 715 construction_tx.Construct(new_data + size, values, new_size - size); 716 717 IteratorValueAdapter<A, MoveIterator<A>> move_values( 718 (MoveIterator<A>(base))); 719 ConstructElements<A>(alloc, new_data, move_values, size); 720 721 DestroyAdapter<A>::DestroyElements(alloc, base, size); 722 std::move(construction_tx).Commit(); 723 DeallocateIfAllocated(); 724 SetAllocation(std::move(allocation_tx).Release()); 725 SetIsAllocated(); 726 } 727 SetSize(new_size); 728 } 729 730 template <typename T, size_t N, typename A> 731 template <typename ValueAdapter> 732 auto Storage<T, N, A>::Insert(ConstIterator<A> pos, ValueAdapter values, 733 SizeType<A> insert_count) -> Iterator<A> { 734 StorageView<A> storage_view = MakeStorageView(); 735 736 auto insert_index = static_cast<SizeType<A>>( 737 std::distance(ConstIterator<A>(storage_view.data), pos)); 738 SizeType<A> insert_end_index = insert_index + insert_count; 739 SizeType<A> new_size = storage_view.size + insert_count; 740 741 if (new_size > storage_view.capacity) { 742 AllocationTransaction<A> allocation_tx(GetAllocator()); 743 ConstructionTransaction<A> construction_tx(GetAllocator()); 744 ConstructionTransaction<A> move_construction_tx(GetAllocator()); 745 746 IteratorValueAdapter<A, MoveIterator<A>> move_values( 747 MoveIterator<A>(storage_view.data)); 748 749 SizeType<A> requested_capacity = 750 ComputeCapacity(storage_view.capacity, new_size); 751 Pointer<A> new_data = allocation_tx.Allocate(requested_capacity); 752 753 construction_tx.Construct(new_data + insert_index, values, insert_count); 754 755 move_construction_tx.Construct(new_data, move_values, insert_index); 756 757 ConstructElements<A>(GetAllocator(), new_data + insert_end_index, 758 move_values, storage_view.size - insert_index); 759 760 DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, 761 storage_view.size); 762 763 std::move(construction_tx).Commit(); 764 std::move(move_construction_tx).Commit(); 765 DeallocateIfAllocated(); 766 SetAllocation(std::move(allocation_tx).Release()); 767 768 SetAllocatedSize(new_size); 769 return Iterator<A>(new_data + insert_index); 770 } else { 771 SizeType<A> move_construction_destination_index = 772 (std::max)(insert_end_index, storage_view.size); 773 774 ConstructionTransaction<A> move_construction_tx(GetAllocator()); 775 776 IteratorValueAdapter<A, MoveIterator<A>> move_construction_values( 777 MoveIterator<A>(storage_view.data + 778 (move_construction_destination_index - insert_count))); 779 absl::Span<ValueType<A>> move_construction = { 780 storage_view.data + move_construction_destination_index, 781 new_size - move_construction_destination_index}; 782 783 Pointer<A> move_assignment_values = storage_view.data + insert_index; 784 absl::Span<ValueType<A>> move_assignment = { 785 storage_view.data + insert_end_index, 786 move_construction_destination_index - insert_end_index}; 787 788 absl::Span<ValueType<A>> insert_assignment = {move_assignment_values, 789 move_construction.size()}; 790 791 absl::Span<ValueType<A>> insert_construction = { 792 insert_assignment.data() + insert_assignment.size(), 793 insert_count - insert_assignment.size()}; 794 795 move_construction_tx.Construct(move_construction.data(), 796 move_construction_values, 797 move_construction.size()); 798 799 for (Pointer<A> 800 destination = move_assignment.data() + move_assignment.size(), 801 last_destination = move_assignment.data(), 802 source = move_assignment_values + move_assignment.size(); 803 ;) { 804 --destination; 805 --source; 806 if (destination < last_destination) break; 807 *destination = std::move(*source); 808 } 809 810 AssignElements<A>(insert_assignment.data(), values, 811 insert_assignment.size()); 812 813 ConstructElements<A>(GetAllocator(), insert_construction.data(), values, 814 insert_construction.size()); 815 816 std::move(move_construction_tx).Commit(); 817 818 AddSize(insert_count); 819 return Iterator<A>(storage_view.data + insert_index); 820 } 821 } 822 823 template <typename T, size_t N, typename A> 824 template <typename... Args> 825 auto Storage<T, N, A>::EmplaceBack(Args&&... args) -> Reference<A> { 826 StorageView<A> storage_view = MakeStorageView(); 827 const SizeType<A> n = storage_view.size; 828 if (ABSL_PREDICT_TRUE(n != storage_view.capacity)) { 829 // Fast path; new element fits. 830 Pointer<A> last_ptr = storage_view.data + n; 831 AllocatorTraits<A>::construct(GetAllocator(), last_ptr, 832 std::forward<Args>(args)...); 833 AddSize(1); 834 return *last_ptr; 835 } 836 // TODO(b/173712035): Annotate with musttail attribute to prevent regression. 837 return EmplaceBackSlow(std::forward<Args>(args)...); 838 } 839 840 template <typename T, size_t N, typename A> 841 template <typename... Args> 842 auto Storage<T, N, A>::EmplaceBackSlow(Args&&... args) -> Reference<A> { 843 StorageView<A> storage_view = MakeStorageView(); 844 AllocationTransaction<A> allocation_tx(GetAllocator()); 845 IteratorValueAdapter<A, MoveIterator<A>> move_values( 846 MoveIterator<A>(storage_view.data)); 847 SizeType<A> requested_capacity = NextCapacity(storage_view.capacity); 848 Pointer<A> construct_data = allocation_tx.Allocate(requested_capacity); 849 Pointer<A> last_ptr = construct_data + storage_view.size; 850 851 // Construct new element. 852 AllocatorTraits<A>::construct(GetAllocator(), last_ptr, 853 std::forward<Args>(args)...); 854 // Move elements from old backing store to new backing store. 855 ABSL_INTERNAL_TRY { 856 ConstructElements<A>(GetAllocator(), allocation_tx.GetData(), move_values, 857 storage_view.size); 858 } 859 ABSL_INTERNAL_CATCH_ANY { 860 AllocatorTraits<A>::destroy(GetAllocator(), last_ptr); 861 ABSL_INTERNAL_RETHROW; 862 } 863 // Destroy elements in old backing store. 864 DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, 865 storage_view.size); 866 867 DeallocateIfAllocated(); 868 SetAllocation(std::move(allocation_tx).Release()); 869 SetIsAllocated(); 870 AddSize(1); 871 return *last_ptr; 872 } 873 874 template <typename T, size_t N, typename A> 875 auto Storage<T, N, A>::Erase(ConstIterator<A> from, 876 ConstIterator<A> to) -> Iterator<A> { 877 StorageView<A> storage_view = MakeStorageView(); 878 879 auto erase_size = static_cast<SizeType<A>>(std::distance(from, to)); 880 auto erase_index = static_cast<SizeType<A>>( 881 std::distance(ConstIterator<A>(storage_view.data), from)); 882 SizeType<A> erase_end_index = erase_index + erase_size; 883 884 // Fast path: if the value type is trivially relocatable and we know 885 // the allocator doesn't do anything fancy, then we know it is legal for us to 886 // simply destroy the elements in the "erasure window" (which cannot throw) 887 // and then memcpy downward to close the window. 888 if (absl::is_trivially_relocatable<ValueType<A>>::value && 889 std::is_nothrow_destructible<ValueType<A>>::value && 890 std::is_same<A, std::allocator<ValueType<A>>>::value) { 891 DestroyAdapter<A>::DestroyElements( 892 GetAllocator(), storage_view.data + erase_index, erase_size); 893 std::memmove( 894 reinterpret_cast<char*>(storage_view.data + erase_index), 895 reinterpret_cast<const char*>(storage_view.data + erase_end_index), 896 (storage_view.size - erase_end_index) * sizeof(ValueType<A>)); 897 } else { 898 IteratorValueAdapter<A, MoveIterator<A>> move_values( 899 MoveIterator<A>(storage_view.data + erase_end_index)); 900 901 AssignElements<A>(storage_view.data + erase_index, move_values, 902 storage_view.size - erase_end_index); 903 904 DestroyAdapter<A>::DestroyElements( 905 GetAllocator(), storage_view.data + (storage_view.size - erase_size), 906 erase_size); 907 } 908 SubtractSize(erase_size); 909 return Iterator<A>(storage_view.data + erase_index); 910 } 911 912 template <typename T, size_t N, typename A> 913 auto Storage<T, N, A>::Reserve(SizeType<A> requested_capacity) -> void { 914 StorageView<A> storage_view = MakeStorageView(); 915 916 if (ABSL_PREDICT_FALSE(requested_capacity <= storage_view.capacity)) return; 917 918 AllocationTransaction<A> allocation_tx(GetAllocator()); 919 920 IteratorValueAdapter<A, MoveIterator<A>> move_values( 921 MoveIterator<A>(storage_view.data)); 922 923 SizeType<A> new_requested_capacity = 924 ComputeCapacity(storage_view.capacity, requested_capacity); 925 Pointer<A> new_data = allocation_tx.Allocate(new_requested_capacity); 926 927 ConstructElements<A>(GetAllocator(), new_data, move_values, 928 storage_view.size); 929 930 DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, 931 storage_view.size); 932 933 DeallocateIfAllocated(); 934 SetAllocation(std::move(allocation_tx).Release()); 935 SetIsAllocated(); 936 } 937 938 template <typename T, size_t N, typename A> 939 auto Storage<T, N, A>::ShrinkToFit() -> void { 940 // May only be called on allocated instances! 941 ABSL_HARDENING_ASSERT(GetIsAllocated()); 942 943 StorageView<A> storage_view{GetAllocatedData(), GetSize(), 944 GetAllocatedCapacity()}; 945 946 if (ABSL_PREDICT_FALSE(storage_view.size == storage_view.capacity)) return; 947 948 AllocationTransaction<A> allocation_tx(GetAllocator()); 949 950 IteratorValueAdapter<A, MoveIterator<A>> move_values( 951 MoveIterator<A>(storage_view.data)); 952 953 Pointer<A> construct_data; 954 if (storage_view.size > GetInlinedCapacity()) { 955 SizeType<A> requested_capacity = storage_view.size; 956 construct_data = allocation_tx.Allocate(requested_capacity); 957 if (allocation_tx.GetCapacity() >= storage_view.capacity) { 958 // Already using the smallest available heap allocation. 959 return; 960 } 961 } else { 962 construct_data = GetInlinedData(); 963 } 964 965 ABSL_INTERNAL_TRY { 966 ConstructElements<A>(GetAllocator(), construct_data, move_values, 967 storage_view.size); 968 } 969 ABSL_INTERNAL_CATCH_ANY { 970 SetAllocation({storage_view.data, storage_view.capacity}); 971 ABSL_INTERNAL_RETHROW; 972 } 973 974 DestroyAdapter<A>::DestroyElements(GetAllocator(), storage_view.data, 975 storage_view.size); 976 977 MallocAdapter<A>::Deallocate(GetAllocator(), storage_view.data, 978 storage_view.capacity); 979 980 if (allocation_tx.DidAllocate()) { 981 SetAllocation(std::move(allocation_tx).Release()); 982 } else { 983 UnsetIsAllocated(); 984 } 985 } 986 987 template <typename T, size_t N, typename A> 988 auto Storage<T, N, A>::Swap(Storage* other_storage_ptr) -> void { 989 using std::swap; 990 ABSL_HARDENING_ASSERT(this != other_storage_ptr); 991 992 if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { 993 swap(data_.allocated, other_storage_ptr->data_.allocated); 994 } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { 995 SwapInlinedElements(SwapInlinedElementsPolicy{}, other_storage_ptr); 996 } else { 997 Storage* allocated_ptr = this; 998 Storage* inlined_ptr = other_storage_ptr; 999 if (!allocated_ptr->GetIsAllocated()) swap(allocated_ptr, inlined_ptr); 1000 1001 StorageView<A> allocated_storage_view{ 1002 allocated_ptr->GetAllocatedData(), allocated_ptr->GetSize(), 1003 allocated_ptr->GetAllocatedCapacity()}; 1004 1005 IteratorValueAdapter<A, MoveIterator<A>> move_values( 1006 MoveIterator<A>(inlined_ptr->GetInlinedData())); 1007 1008 ABSL_INTERNAL_TRY { 1009 ConstructElements<A>(inlined_ptr->GetAllocator(), 1010 allocated_ptr->GetInlinedData(), move_values, 1011 inlined_ptr->GetSize()); 1012 } 1013 ABSL_INTERNAL_CATCH_ANY { 1014 allocated_ptr->SetAllocation(Allocation<A>{ 1015 allocated_storage_view.data, allocated_storage_view.capacity}); 1016 ABSL_INTERNAL_RETHROW; 1017 } 1018 1019 DestroyAdapter<A>::DestroyElements(inlined_ptr->GetAllocator(), 1020 inlined_ptr->GetInlinedData(), 1021 inlined_ptr->GetSize()); 1022 1023 inlined_ptr->SetAllocation(Allocation<A>{allocated_storage_view.data, 1024 allocated_storage_view.capacity}); 1025 } 1026 1027 swap(GetSizeAndIsAllocated(), other_storage_ptr->GetSizeAndIsAllocated()); 1028 swap(GetAllocator(), other_storage_ptr->GetAllocator()); 1029 } 1030 1031 template <typename T, size_t N, typename A> 1032 void Storage<T, N, A>::SwapN(ElementwiseSwapPolicy, Storage* other, 1033 SizeType<A> n) { 1034 std::swap_ranges(GetInlinedData(), GetInlinedData() + n, 1035 other->GetInlinedData()); 1036 } 1037 1038 template <typename T, size_t N, typename A> 1039 void Storage<T, N, A>::SwapN(ElementwiseConstructPolicy, Storage* other, 1040 SizeType<A> n) { 1041 Pointer<A> a = GetInlinedData(); 1042 Pointer<A> b = other->GetInlinedData(); 1043 // see note on allocators in `SwapInlinedElements`. 1044 A& allocator_a = GetAllocator(); 1045 A& allocator_b = other->GetAllocator(); 1046 for (SizeType<A> i = 0; i < n; ++i, ++a, ++b) { 1047 ValueType<A> tmp(std::move(*a)); 1048 1049 AllocatorTraits<A>::destroy(allocator_a, a); 1050 AllocatorTraits<A>::construct(allocator_b, a, std::move(*b)); 1051 1052 AllocatorTraits<A>::destroy(allocator_b, b); 1053 AllocatorTraits<A>::construct(allocator_a, b, std::move(tmp)); 1054 } 1055 } 1056 1057 template <typename T, size_t N, typename A> 1058 void Storage<T, N, A>::SwapInlinedElements(MemcpyPolicy, Storage* other) { 1059 Data tmp = data_; 1060 data_ = other->data_; 1061 other->data_ = tmp; 1062 } 1063 1064 template <typename T, size_t N, typename A> 1065 template <typename NotMemcpyPolicy> 1066 void Storage<T, N, A>::SwapInlinedElements(NotMemcpyPolicy policy, 1067 Storage* other) { 1068 // Note: `destroy` needs to use pre-swap allocator while `construct` - 1069 // post-swap allocator. Allocators will be swapped later on outside of 1070 // `SwapInlinedElements`. 1071 Storage* small_ptr = this; 1072 Storage* large_ptr = other; 1073 if (small_ptr->GetSize() > large_ptr->GetSize()) { 1074 std::swap(small_ptr, large_ptr); 1075 } 1076 1077 auto small_size = small_ptr->GetSize(); 1078 auto diff = large_ptr->GetSize() - small_size; 1079 SwapN(policy, other, small_size); 1080 1081 IteratorValueAdapter<A, MoveIterator<A>> move_values( 1082 MoveIterator<A>(large_ptr->GetInlinedData() + small_size)); 1083 1084 ConstructElements<A>(large_ptr->GetAllocator(), 1085 small_ptr->GetInlinedData() + small_size, move_values, 1086 diff); 1087 1088 DestroyAdapter<A>::DestroyElements(large_ptr->GetAllocator(), 1089 large_ptr->GetInlinedData() + small_size, 1090 diff); 1091 } 1092 1093 // End ignore "array-bounds" 1094 #if !defined(__clang__) && defined(__GNUC__) 1095 #pragma GCC diagnostic pop 1096 #endif 1097 1098 } // namespace inlined_vector_internal 1099 ABSL_NAMESPACE_END 1100 } // namespace absl 1101 1102 #endif // ABSL_CONTAINER_INTERNAL_INLINED_VECTOR_H_