aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-03-20 17:16:16 +0100
committerFelix Morgner <felix.morgner@ost.ch>2026-03-20 17:16:16 +0100
commit2f9e3917ef86ac0b00a517394df8a903c97770e1 (patch)
tree6779cc922909b2c4ba3516ff894746a2a8d1e71b /libs
parentfb366b3c59941ea4c8e0a8d6e29ba0263f89bb02 (diff)
downloadteachos-2f9e3917ef86ac0b00a517394df8a903c97770e1.tar.xz
teachos-2f9e3917ef86ac0b00a517394df8a903c97770e1.zip
kstd/vector: allow self-referential pushes
Previously, calling `v.push_back(v.front())` might have resulted in undefined behavior if reallocation needed to occur. This patch provides for this and allows self-referential pushes.
Diffstat (limited to 'libs')
-rw-r--r--libs/kstd/include/kstd/vector60
1 files changed, 45 insertions, 15 deletions
diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector
index 7042ff1..a49572b 100644
--- a/libs/kstd/include/kstd/vector
+++ b/libs/kstd/include/kstd/vector
@@ -590,16 +590,42 @@ namespace kstd
//! Append a given element to this vector via copy construction.
constexpr auto push_back(value_type const & value) -> void
{
- increase_capacity_if_full();
- std::allocator_traits<allocator_type>::construct(m_allocator, data() + size(), value);
+ if (m_capacity == m_size)
+ {
+ auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2;
+ auto new_data = allocate_n(new_capacity);
+ std::allocator_traits<allocator_type>::construct(m_allocator, new_data + m_size, value);
+ uninitialized_move_with_allocator(begin(), new_data, size());
+ destroy_n(begin(), size());
+ deallocate();
+ m_data = new_data;
+ m_capacity = new_capacity;
+ }
+ else
+ {
+ std::allocator_traits<allocator_type>::construct(m_allocator, data() + size(), value);
+ }
++m_size;
}
//! Append a given element to this vector via move construction.
constexpr auto push_back(value_type && value) -> void
{
- increase_capacity_if_full();
- std::allocator_traits<allocator_type>::construct(m_allocator, data() + size(), std::move(value));
+ if (m_capacity == m_size)
+ {
+ auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2;
+ auto new_data = allocate_n(new_capacity);
+ std::allocator_traits<allocator_type>::construct(m_allocator, new_data + m_size, std::move(value));
+ uninitialized_move_with_allocator(begin(), new_data, size());
+ destroy_n(begin(), size());
+ deallocate();
+ m_data = new_data;
+ m_capacity = new_capacity;
+ }
+ else
+ {
+ std::allocator_traits<allocator_type>::construct(m_allocator, data() + size(), std::move(value));
+ }
++m_size;
}
@@ -607,8 +633,21 @@ namespace kstd
template<class... Args>
constexpr auto emplace_back(Args &&... args) -> reference
{
- increase_capacity_if_full();
- std::allocator_traits<allocator_type>::construct(m_allocator, data() + size(), std::forward<Args>(args)...);
+ if (m_capacity == m_size)
+ {
+ auto new_capacity = m_capacity == 0 ? 1 : m_capacity * 2;
+ auto new_data = allocate_n(new_capacity);
+ std::allocator_traits<allocator_type>::construct(m_allocator, new_data + m_size, std::forward<Args>(args)...);
+ uninitialized_move_with_allocator(begin(), new_data, size());
+ destroy_n(begin(), size());
+ deallocate();
+ m_data = new_data;
+ m_capacity = new_capacity;
+ }
+ else
+ {
+ std::allocator_traits<allocator_type>::construct(m_allocator, data() + size(), std::forward<Args>(args)...);
+ }
++m_size;
return this->back();
}
@@ -675,15 +714,6 @@ namespace kstd
});
}
- //! Check if there is still room in this vector, and if not allocate more space.
- constexpr auto increase_capacity_if_full() -> void
- {
- if (m_size == m_capacity)
- {
- reserve(m_capacity == 0U ? 1U : m_capacity * 2U);
- }
- }
-
//! Move a number of elements from one storage location to another.
//!
//! @param from The start of the source range.