Skip to content

File tree

2 files changed

+69
-8
lines changed

2 files changed

+69
-8
lines changed

absl/container/inlined_vector_test.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,53 @@ TEST(UniquePtr, MoveAssign) {
304304
}
305305
}
306306

307+
// Erasing from a container of unique pointers should work fine, with no
308+
// leaks, despite the fact that unique pointers are trivially relocatable but
309+
// not trivially destructible.
310+
TEST(UniquePtr, EraseSingle) {
311+
for (size_t size = 4; size < 16; ++size) {
312+
absl::InlinedVector<std::unique_ptr<size_t>, 8> a;
313+
for (size_t i = 0; i < size; ++i) {
314+
a.push_back(std::make_unique<size_t>(i));
315+
}
316+
a.erase(a.begin());
317+
ASSERT_THAT(a, SizeIs(size - 1));
318+
for (size_t i = 0; i < size - 1; ++i) {
319+
ASSERT_THAT(a[i], Pointee(i + 1));
320+
}
321+
a.erase(a.begin() + 2);
322+
ASSERT_THAT(a, SizeIs(size - 2));
323+
ASSERT_THAT(a[0], Pointee(1));
324+
ASSERT_THAT(a[1], Pointee(2));
325+
for (size_t i = 2; i < size - 2; ++i) {
326+
ASSERT_THAT(a[i], Pointee(i + 2));
327+
}
328+
}
329+
}
330+
331+
// Erasing from a container of unique pointers should work fine, with no
332+
// leaks, despite the fact that unique pointers are trivially relocatable but
333+
// not trivially destructible.
334+
TEST(UniquePtr, EraseMulti) {
335+
for (size_t size = 5; size < 16; ++size) {
336+
absl::InlinedVector<std::unique_ptr<size_t>, 8> a;
337+
for (size_t i = 0; i < size; ++i) {
338+
a.push_back(std::make_unique<size_t>(i));
339+
}
340+
a.erase(a.begin(), a.begin() + 2);
341+
ASSERT_THAT(a, SizeIs(size - 2));
342+
for (size_t i = 0; i < size - 2; ++i) {
343+
ASSERT_THAT(a[i], Pointee(i + 2));
344+
}
345+
a.erase(a.begin() + 1, a.begin() + 3);
346+
ASSERT_THAT(a, SizeIs(size - 4));
347+
ASSERT_THAT(a[0], Pointee(2));
348+
for (size_t i = 1; i < size - 4; ++i) {
349+
ASSERT_THAT(a[i], Pointee(i + 4));
350+
}
351+
}
352+
}
353+
307354
// At the end of this test loop, the elements between [erase_begin, erase_end)
308355
// should have reference counts == 0, and all others elements should have
309356
// reference counts == 1.

absl/container/internal/inlined_vector.h

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -894,16 +894,30 @@ auto Storage<T, N, A>::Erase(ConstIterator<A> from, ConstIterator<A> to)
894894
std::distance(ConstIterator<A>(storage_view.data), from));
895895
SizeType<A> erase_end_index = erase_index + erase_size;
896896

897-
IteratorValueAdapter<A, MoveIterator<A>> move_values(
898-
MoveIterator<A>(storage_view.data + erase_end_index));
899-
900-
AssignElements<A>(storage_view.data + erase_index, move_values,
901-
storage_view.size - erase_end_index);
897+
// Fast path: if the value type is trivially relocatable and we know
898+
// the allocator doesn't do anything fancy, then we know it is legal for us to
899+
// simply destroy the elements in the "erasure window" (which cannot throw)
900+
// and then memcpy downward to close the window.
901+
if (absl::is_trivially_relocatable<ValueType<A>>::value &&
902+
std::is_nothrow_destructible<ValueType<A>>::value &&
903+
std::is_same<A, std::allocator<ValueType<A>>>::value) {
904+
DestroyAdapter<A>::DestroyElements(
905+
GetAllocator(), storage_view.data + erase_index,
906+
erase_size);
907+
std::memmove(reinterpret_cast<char*>(storage_view.data + erase_index),
908+
reinterpret_cast<const char*>(storage_view.data + erase_end_index),
909+
(storage_view.size - erase_end_index) * sizeof(ValueType<A>));
910+
} else {
911+
IteratorValueAdapter<A, MoveIterator<A>> move_values(
912+
MoveIterator<A>(storage_view.data + erase_end_index));
902913

903-
DestroyAdapter<A>::DestroyElements(
904-
GetAllocator(), storage_view.data + (storage_view.size - erase_size),
905-
erase_size);
914+
AssignElements<A>(storage_view.data + erase_index, move_values,
915+
storage_view.size - erase_end_index);
906916

917+
DestroyAdapter<A>::DestroyElements(
918+
GetAllocator(), storage_view.data + (storage_view.size - erase_size),
919+
erase_size);
920+
}
907921
SubtractSize(erase_size);
908922
return Iterator<A>(storage_view.data + erase_index);
909923
}

0 commit comments

Comments
 (0)