From 2fc358dab079c220feb81f75b5a366f8d1464cd4 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Tue, 25 Oct 2022 12:55:46 -0700 Subject: `absl::InlinedVector::swap` supports non-assignable types. PiperOrigin-RevId: 483752526 Change-Id: Ie6b63a4a3cc7593e5b8bf255ba571a77d609ce04 --- absl/container/internal/inlined_vector.h | 106 +++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 20 deletions(-) (limited to 'absl/container/internal/inlined_vector.h') diff --git a/absl/container/internal/inlined_vector.h b/absl/container/internal/inlined_vector.h index f623494c..e2dd843f 100644 --- a/absl/container/internal/inlined_vector.h +++ b/absl/container/internal/inlined_vector.h @@ -85,6 +85,8 @@ using IsMemcpyOk = template using IsMoveAssignOk = std::is_move_assignable>; +template +using IsSwapOk = absl::type_traits_internal::IsSwappable>; template struct TypeIdentity { @@ -300,6 +302,20 @@ class ConstructionTransaction { template class Storage { public: + struct MemcpyPolicy {}; + struct ElementwiseAssignPolicy {}; + struct ElementwiseSwapPolicy {}; + struct ElementwiseConstructPolicy {}; + + using MoveAssignmentPolicy = absl::conditional_t< + IsMemcpyOk::value, MemcpyPolicy, + absl::conditional_t::value, ElementwiseAssignPolicy, + ElementwiseConstructPolicy>>; + using SwapPolicy = absl::conditional_t< + IsMemcpyOk::value, MemcpyPolicy, + absl::conditional_t::value, ElementwiseSwapPolicy, + ElementwiseConstructPolicy>>; + static SizeType NextCapacity(SizeType current_capacity) { return current_capacity * 2; } @@ -476,6 +492,13 @@ class Storage { Inlined inlined; }; + void SwapN(ElementwiseSwapPolicy, Storage* other, SizeType n); + void SwapN(ElementwiseConstructPolicy, Storage* other, SizeType n); + + void SwapInlinedElements(MemcpyPolicy, Storage* other); + template + void SwapInlinedElements(NotMemcpyPolicy, Storage* other); + template ABSL_ATTRIBUTE_NOINLINE Reference EmplaceBackSlow(Args&&... args); @@ -889,26 +912,7 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { if (GetIsAllocated() && other_storage_ptr->GetIsAllocated()) { swap(data_.allocated, other_storage_ptr->data_.allocated); } else if (!GetIsAllocated() && !other_storage_ptr->GetIsAllocated()) { - Storage* small_ptr = this; - Storage* large_ptr = other_storage_ptr; - if (small_ptr->GetSize() > large_ptr->GetSize()) swap(small_ptr, large_ptr); - - for (SizeType i = 0; i < small_ptr->GetSize(); ++i) { - swap(small_ptr->GetInlinedData()[i], large_ptr->GetInlinedData()[i]); - } - - IteratorValueAdapter> move_values( - MoveIterator(large_ptr->GetInlinedData() + small_ptr->GetSize())); - - ConstructElements(large_ptr->GetAllocator(), - small_ptr->GetInlinedData() + small_ptr->GetSize(), - move_values, - large_ptr->GetSize() - small_ptr->GetSize()); - - DestroyAdapter::DestroyElements( - large_ptr->GetAllocator(), - large_ptr->GetInlinedData() + small_ptr->GetSize(), - large_ptr->GetSize() - small_ptr->GetSize()); + SwapInlinedElements(SwapPolicy{}, other_storage_ptr); } else { Storage* allocated_ptr = this; Storage* inlined_ptr = other_storage_ptr; @@ -944,6 +948,68 @@ auto Storage::Swap(Storage* other_storage_ptr) -> void { swap(GetAllocator(), other_storage_ptr->GetAllocator()); } +template +void Storage::SwapN(ElementwiseSwapPolicy, Storage* other, + SizeType n) { + std::swap_ranges(GetInlinedData(), GetInlinedData() + n, + other->GetInlinedData()); +} + +template +void Storage::SwapN(ElementwiseConstructPolicy, Storage* other, + SizeType n) { + Pointer a = GetInlinedData(); + Pointer b = other->GetInlinedData(); + // see note on allocators in `SwapInlinedElements`. + A& allocator_a = GetAllocator(); + A& allocator_b = other->GetAllocator(); + for (SizeType i = 0; i < n; ++i, ++a, ++b) { + ValueType tmp(std::move(*a)); + + AllocatorTraits::destroy(allocator_a, a); + AllocatorTraits::construct(allocator_b, a, std::move(*b)); + + AllocatorTraits::destroy(allocator_b, b); + AllocatorTraits::construct(allocator_a, b, std::move(tmp)); + } +} + +template +void Storage::SwapInlinedElements(MemcpyPolicy, Storage* other) { + Data tmp = data_; + data_ = other->data_; + other->data_ = tmp; +} + +template +template +void Storage::SwapInlinedElements(NotMemcpyPolicy policy, + Storage* other) { + // Note: `destroy` needs to use pre-swap allocator while `construct` - + // post-swap allocator. Allocators will be swaped later on outside of + // `SwapInlinedElements`. + Storage* small_ptr = this; + Storage* large_ptr = other; + if (small_ptr->GetSize() > large_ptr->GetSize()) { + std::swap(small_ptr, large_ptr); + } + + auto small_size = small_ptr->GetSize(); + auto diff = large_ptr->GetSize() - small_size; + SwapN(policy, other, small_size); + + IteratorValueAdapter> move_values( + MoveIterator(large_ptr->GetInlinedData() + small_size)); + + ConstructElements(large_ptr->GetAllocator(), + small_ptr->GetInlinedData() + small_size, move_values, + diff); + + DestroyAdapter::DestroyElements(large_ptr->GetAllocator(), + large_ptr->GetInlinedData() + small_size, + diff); +} + // End ignore "array-bounds" #if !defined(__clang__) && defined(__GNUC__) #pragma GCC diagnostic pop -- cgit v1.2.3