diff options
Diffstat (limited to 'libs/kstd')
| -rw-r--r-- | libs/kstd/include/kstd/bits/concepts.hpp | 15 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/vector | 69 | ||||
| -rw-r--r-- | libs/kstd/tests/src/vector.cpp | 31 |
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); + } + } + } } |
