use simple deleted flag instead of tracking inactive indices

removing Property_container::reserve as it basically adds inactive indices and thus means linear insertion time
This commit is contained in:
Sven Oesau 2025-07-03 12:33:47 +02:00
parent 50fe8040ac
commit bbf0a0e7ac
2 changed files with 32 additions and 22 deletions

View File

@ -248,6 +248,7 @@ class Property_container {
std::multimap<std::string, std::shared_ptr<Property_array_base<Index>>> m_properties;
std::vector<bool> m_active_indices{};
bool m_has_deleted_elements = false;
public:
@ -258,6 +259,7 @@ public:
Property_container(const Property_container<Index>& other) {
m_active_indices = other.m_active_indices;
m_has_deleted_elements = other.m_has_deleted_elements;
for (auto [name, array] : other.m_properties) {
// todo: this could probably be made faster using emplace_hint
@ -273,6 +275,7 @@ public:
// This is not exactly an assignment as existing unique properties are kept.
Property_container<Index>& operator=(const Property_container<Index>& other) {
m_active_indices = other.m_active_indices;
m_has_deleted_elements = other.m_has_deleted_elements;
for (auto [name, array] : other.m_properties) {
// search if property already exists
@ -295,6 +298,7 @@ public:
// This is not exactly an assignment as existing unique properties are kept.
Property_container<Index>& operator=(Property_container<Index>&& other) {
m_active_indices = std::move(other.m_active_indices);
m_has_deleted_elements = other.m_has_deleted_elements;
for (auto [name, array] : other.m_properties) {
// search if property already exists
@ -312,7 +316,7 @@ public:
}
// The moved-from property map should retain all of its properties, but contain 0 elements
other.reserve(0);
other.resize(0);
return *this;
}
@ -441,16 +445,15 @@ public:
}*/
public:
void reserve(std::size_t n) {
m_active_indices.resize(n);
for (auto [name, array]: m_properties)
array->reserve(n);
}
void resize(std::size_t n) {
reserve(n);
m_active_indices.resize(n);
for (auto [name, array] : m_properties)
array->reserve(n);
std::fill(m_active_indices.begin(), m_active_indices.end(), true);
m_has_deleted_elements = false;
}
[[nodiscard]] std::size_t size() const { return std::count(m_active_indices.begin(), m_active_indices.end(), true); }
@ -460,8 +463,11 @@ public:
Index emplace_back() {
// Expand the storage and return the last element
reserve(capacity() + 1);
m_active_indices.back() = true;
m_active_indices.push_back(true);
for (auto [name, array] : m_properties)
array->reserve(capacity());
auto first_new_index = Index(capacity() - 1);
reset(first_new_index);
return first_new_index;
@ -469,6 +475,9 @@ public:
Index emplace() {
if (!m_has_deleted_elements)
return emplace_back();
// If there are empty slots, return the index of one of them and mark it as full
auto first_unused = std::find_if(m_active_indices.begin(), m_active_indices.end(), [](bool used) { return !used; });
if (first_unused != m_active_indices.end()) {
@ -478,20 +487,27 @@ public:
return index;
}
m_has_deleted_elements = false;
return emplace_back();
}
Index emplace_group_back(std::size_t n) {
// Expand the storage and return the start of the new region
reserve(capacity() + n);
for (auto it = m_active_indices.end() - n; it < m_active_indices.end(); ++it)
*it = true;
m_active_indices.resize(capacity() + n, true);
for (auto [name, array] : m_properties)
array->reserve(capacity());
return Index(capacity() - n);
}
Index emplace_group(std::size_t n) {
if (!m_has_deleted_elements)
return emplace_group_back(n);
auto search_start = m_active_indices.begin();
while (search_start != m_active_indices.end()) {
@ -544,6 +560,7 @@ public:
void erase(Index i) {
m_active_indices[i] = false;
m_has_deleted_elements = true;
for (auto [name, array]: m_properties)
array->reset(i);
}

View File

@ -41,11 +41,6 @@ void test_element_access() {
auto& integers = properties.add_property("integers", 5);
// Reserve space for 100 elements
properties.reserve(100);
assert(properties.capacity() == 100);
assert(properties.size() == 0);
// Newly emplaced elements should go at the front
assert(properties.emplace() == 0);
assert(properties.emplace() == 1);
@ -61,7 +56,6 @@ void test_element_access() {
auto& floats = properties.add_property("floats", 6.0f);
// The new property array should already be of the right size
assert(floats.capacity() == 100);
assert(properties.size() == 3);
// Pre-existing elements should contain the default value
@ -87,9 +81,8 @@ void test_element_access() {
// Erase an element, and the size should be reduced
properties.erase(1);
assert(properties.size() == 2);
assert(properties.capacity() == 100);
assert(properties.active_list().size() == 2);
assert(properties.inactive_list().size() == 98);
assert(properties.inactive_list().size() == 1);
// A newly emplaced element should take the empty slot
assert(properties.emplace() == 1);