aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2026-05-01 11:34:54 +0200
committerFelix Morgner <felix.morgner@ost.ch>2026-05-01 11:34:54 +0200
commita958c515e4cfcd5572ac7e2c3a567e832630a12d (patch)
treea5636689d2338c71e9b67d7ed1e2446b4d765a49 /libs
parentbc7389dd19eee57fa2f34cf2e7ba7d1ebfad0878 (diff)
downloadkernel-a958c515e4cfcd5572ac7e2c3a567e832630a12d.tar.xz
kernel-a958c515e4cfcd5572ac7e2c3a567e832630a12d.zip
kstd/vector: implement append_range
Diffstat (limited to 'libs')
-rw-r--r--libs/kstd/include/kstd/bits/concepts.hpp15
-rw-r--r--libs/kstd/include/kstd/vector69
-rw-r--r--libs/kstd/tests/src/vector.cpp31
3 files changed, 113 insertions, 2 deletions
diff --git a/libs/kstd/include/kstd/bits/concepts.hpp b/libs/kstd/include/kstd/bits/concepts.hpp
new file mode 100644
index 0000000..74c25cb
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/concepts.hpp
@@ -0,0 +1,15 @@
+#ifndef KSTD_BITS_CONCEPTS_HPP
+#define KSTD_BITS_CONCEPTS_HPP
+
+#include <concepts>
+#include <ranges>
+namespace kstd::bits
+{
+
+ template<typename RangeType, typename ValueType>
+ concept container_compatible_range =
+ std::ranges::input_range<RangeType> && std::convertible_to<std::ranges::range_reference_t<RangeType>, ValueType>;
+
+}
+
+#endif
diff --git a/libs/kstd/include/kstd/vector b/libs/kstd/include/kstd/vector
index 97fdffe..2ecb6a8 100644
--- a/libs/kstd/include/kstd/vector
+++ b/libs/kstd/include/kstd/vector
@@ -2,6 +2,7 @@
#define KSTD_VECTOR_HPP
#include <kstd/allocator>
+#include <kstd/bits/concepts.hpp>
#include <kstd/os/error.hpp>
#include <kstd/ranges>
@@ -882,6 +883,68 @@ namespace kstd
return this->back();
}
+ //! Append the elements of a given range to this vector.
+ //!
+ //! @param range The range of elements to be appended.
+ //! @tparam SourceRange A container compatible range type.
+ template<kstd::bits::container_compatible_range<ValueType> SourceRange>
+ requires requires(Allocator allocator, pointer destination, SourceRange range) {
+ std::allocator_traits<Allocator>::construct(allocator, destination, *std::ranges::begin(range));
+ }
+ // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward, misc-no-recursion)
+ constexpr auto append_range(SourceRange && range) -> void
+ {
+ if constexpr (std::ranges::forward_range<SourceRange> || std::ranges::sized_range<SourceRange>)
+ {
+ auto number_of_elements = static_cast<size_type>(std::ranges::distance(range));
+
+ if (!capacity())
+ {
+ reserve(number_of_elements);
+ }
+
+ if (capacity() - size() >= number_of_elements)
+ {
+ uninitialized_copy_with_allocator(std::ranges::begin(range), end(), number_of_elements);
+ m_size += number_of_elements;
+ return;
+ }
+
+ auto new_capacity = m_capacity + std::max(size(), number_of_elements);
+ auto new_data = allocate_n(new_capacity);
+ auto old_size = size();
+
+ uninitialized_move_with_allocator(begin(), new_data, size());
+ uninitialized_copy_with_allocator(std::ranges::begin(range), new_data + size(), number_of_elements);
+ clear_and_deallocate();
+ m_data = new_data;
+ m_capacity = new_capacity;
+ m_size = old_size + number_of_elements;
+ return;
+ }
+
+ auto range_begin = std::ranges::begin(range);
+ auto range_end = std::ranges::end(range);
+
+ for (auto i = capacity() - size(); i > 0; --i, ++range_begin)
+ {
+ emplace_back(*range_begin);
+ }
+
+ if (range_begin == range_end)
+ {
+ return;
+ }
+
+ auto remainder = vector{get_allocator()};
+ for (; range_begin != range_end; ++range_begin)
+ {
+ remainder.emplace_back(*static_cast<std::ranges::iterator_t<SourceRange>>(range_begin));
+ }
+ reserve(size() + std::max(size(), remainder.size()));
+ append_range(remainder);
+ }
+
//! Remove the last element of this vector.
//!
//! If this vector is empty, the behavior is undefined.
@@ -950,7 +1013,8 @@ namespace kstd
//! @param from The start of the source range.
//! @param to The start of the target range inside this vector.
//! @param count The number of elements to copy
- constexpr auto uninitialized_copy_with_allocator(const_iterator from, iterator to, size_type count)
+ template<typename SourceIterator>
+ constexpr auto uninitialized_copy_with_allocator(SourceIterator from, iterator to, size_type count)
{
for (auto i = 0uz; i < count; ++i)
{
@@ -963,7 +1027,8 @@ namespace kstd
//! @param from The start of the source range.
//! @param to The start of the target range inside this vector.
//! @param count The number of elements to copy
- constexpr auto uninitialized_move_with_allocator(iterator from, iterator to, size_type count)
+ template<typename SourceIterator>
+ constexpr auto uninitialized_move_with_allocator(SourceIterator from, iterator to, size_type count)
{
for (auto i = 0uz; i < count; ++i)
{
diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp
index b838f42..bbbd3d1 100644
--- a/libs/kstd/tests/src/vector.cpp
+++ b/libs/kstd/tests/src/vector.cpp
@@ -516,6 +516,21 @@ SCENARIO("Vector modifiers", "[vector]")
REQUIRE(it == v.begin());
}
}
+
+ WHEN("appending a range")
+ {
+ auto const range = std::views::iota(0, 3);
+ v.append_range(range);
+
+ THEN("the size and capacity increase and the elements are appended")
+ {
+ REQUIRE(v.size() == 3);
+ REQUIRE(v.capacity() >= 3);
+ REQUIRE(v[0] == 0);
+ REQUIRE(v[1] == 1);
+ REQUIRE(v[2] == 2);
+ }
+ }
}
GIVEN("A populated vector")
@@ -832,6 +847,22 @@ SCENARIO("Vector modifiers", "[vector]")
REQUIRE(it == v.begin() + 1);
}
}
+
+ WHEN("appending a range")
+ {
+ auto initial_size = v.size();
+ v.append_range(std::views::iota(0, 3));
+
+ THEN("capacity and size are increased and the elements are appended")
+ {
+ REQUIRE(v.capacity() >= initial_capacity);
+ REQUIRE(v.size() == initial_size + 3);
+ REQUIRE(v[initial_size + 0] == 0);
+ REQUIRE(v[initial_size + 1] == 1);
+ REQUIRE(v[initial_size + 2] == 2);
+ }
+ }
+
}
}