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