Skip to content

Commit

Permalink
fix: destruct elements no longer in use only when erasing
Browse files Browse the repository at this point in the history
  • Loading branch information
tearfur authored and alandefreitas committed Mar 11, 2024
1 parent 7450d91 commit af241b0
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
28 changes: 17 additions & 11 deletions include/small/vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <small/detail/traits/enable_allocator_from_this.hpp>
#include <small/detail/traits/is_range.hpp>
#include <small/detail/traits/is_relocatable.hpp>
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdlib>
Expand Down Expand Up @@ -1068,33 +1069,38 @@ namespace small {
if (first == last) {
return unconst(first);
}

// Directly destroy elements before mem moving
if constexpr (!std::is_trivially_destructible_v<T>) {
for (auto it = first; it != last; ++it) {
it->~value_type();
}
}


const auto n_erase = last - first;
if constexpr (is_relocatable_v<value_type> && using_std_allocator) {
// Move elements directly in memory
const auto n_erase = last - first;
const auto n_after_erase = cend() - last;
if (n_erase >= n_after_erase) {
std::memcpy(
(void *) first.base(),
(void *) last.base(),
(cend() - last) * sizeof(T));
(cend() - last) * sizeof(value_type));
} else {
std::memmove(
(void *) first.base(),
(void *) last.base(),
(cend() - last) * sizeof(T));
(cend() - last) * sizeof(value_type));
}
} else {
// Move elements in memory
std::move(unconst(last), end(), unconst(first));

// Destruct elements that were moved from and no longer in-use
// N.B. Only do this for non-relocatable types, otherwise you'd
// be running the destructor on exact byte copies of in-use
// elements, and you might free their internal buffers (oh no!).
if constexpr (!std::is_trivially_destructible_v<value_type>) {
std::for_each_n(
crbegin(),
n_erase,
[](value_type const &ele) { ele.~value_type(); });
}
}

// Directly set internal size. Elements are already destroyed.
set_internal_size(size() - std::distance(first, last));
return unconst(first);
Expand Down
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ add_small_test(ptr_wrapper small::small)
add_small_test(pod_small_vector small::small)
add_small_test(string_small_vector small::small)
add_small_test(custom_small_vector small::small)
add_small_test(shared_ptr_small_vector small::small)

add_small_test(unicode_functions small::small)
add_small_test(small_string_make small::small)
Expand Down
33 changes: 33 additions & 0 deletions test/unit/shared_ptr_small_vector.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// Copyright (c) 2022 Yat Ho ([email protected])
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
//

// C++
#include <memory>

// External
#include <catch2/catch.hpp>

// Small
#include <small/vector.hpp>

TEST_CASE("Shared Ptr Vector") {
using namespace small;

STATIC_REQUIRE(!is_relocatable_v<std::shared_ptr<int>>);

SECTION("Erase in middle") {
vector<std::shared_ptr<int>> a;

for (int i = 0; i < 2; ++i) {
a.emplace_back(std::make_shared<int>(i));
}

a.erase(a.begin());

REQUIRE(*a[0] == 1);
}
}

0 comments on commit af241b0

Please sign in to comment.