diff --git a/pw_containers/public/pw_containers/vector.h b/pw_containers/public/pw_containers/vector.h index 853e1ebec1..62ebc8aa12 100644 --- a/pw_containers/public/pw_containers/vector.h +++ b/pw_containers/public/pw_containers/vector.h @@ -327,26 +327,28 @@ class Vector void clear() noexcept; - // TODO(hepler): insert, emplace, and erase are not yet implemented. - // Currently, items can only be added to or removed from the end. - iterator insert(const_iterator index, const T& value); + iterator insert(const_iterator index, size_type count, const T& value); - iterator insert(const_iterator index, T&& value); + iterator insert(const_iterator index, const T& value) { + return insert(index, 1, value); + } - iterator insert(const_iterator index, size_type count, const T& value); + iterator insert(const_iterator index, T&& value); template iterator insert(const_iterator index, Iterator first, Iterator last); - iterator insert(const_iterator index, std::initializer_list list); + iterator insert(const_iterator index, std::initializer_list list) { + return insert(index, list.begin(), list.end()); + } template iterator emplace(const_iterator index, Args&&... args); - iterator erase(const_iterator index); - iterator erase(const_iterator first, const_iterator last); + iterator erase(const_iterator index) { return erase(index, index + 1); } + void push_back(const T& value) { emplace_back(value); } void push_back(T&& value) { emplace_back(std::move(value)); } @@ -446,8 +448,10 @@ bool operator>=(const Vector& lhs, template void Vector::clear() noexcept { - for (auto& item : *this) { - item.~T(); + if constexpr (!std::is_trivially_destructible_v) { + for (auto& item : *this) { + item.~T(); + } } size_ = 0; } @@ -464,7 +468,9 @@ void Vector::emplace_back(Args&&... args) { template void Vector::pop_back() { if (!empty()) { - back().~T(); + if constexpr (!std::is_trivially_destructible_v) { + back().~T(); + } size_ -= 1; } } @@ -481,6 +487,110 @@ void Vector::resize(size_type new_size, } } +template +typename Vector::iterator Vector::insert(Vector::const_iterator index, + T&& value) { + PW_DASSERT(index >= cbegin()); + PW_DASSERT(index <= cend()); + PW_DASSERT(!full()); + + iterator insertion_point = begin() + std::distance(cbegin(), index); + if (insertion_point == end()) { + emplace_back(std::move(value)); + return insertion_point; + } + + std::move_backward(insertion_point, end(), end() + 1); + *insertion_point = std::move(value); + ++size_; + + // Return an iterator pointing to the inserted value. + return insertion_point; +} + +template +template +typename Vector::iterator Vector::insert(Vector::const_iterator index, + Iterator first, + Iterator last) { + PW_DASSERT(index >= cbegin()); + PW_DASSERT(index <= cend()); + PW_DASSERT(!full()); + + iterator insertion_point = begin() + std::distance(cbegin(), index); + + const size_t insertion_count = std::distance(first, last); + if (insertion_count == 0) { + return insertion_point; + } + PW_DASSERT(size() + insertion_count <= max_size()); + + iterator return_value = insertion_point; + + if (insertion_point != end()) { + std::move_backward(insertion_point, end(), end() + insertion_count); + } + + while (first != last) { + *insertion_point = *first; + ++first; + ++insertion_point; + } + size_ += insertion_count; + + // Return an iterator pointing to the first element inserted. + return return_value; +} + +template +typename Vector::iterator Vector::insert(Vector::const_iterator index, + size_type count, + const T& value) { + PW_DASSERT(index >= cbegin()); + PW_DASSERT(index <= cend()); + PW_DASSERT(size() + count <= max_size()); + + iterator insertion_point = begin() + std::distance(cbegin(), index); + if (count == size_type{}) { + return insertion_point; + } + + if (insertion_point != end()) { + std::move_backward(insertion_point, end(), end() + count); + } + + iterator return_value = insertion_point; + + for (size_type final_count = size_ + count; size_ != final_count; ++size_) { + *insertion_point = value; + ++insertion_point; + } + + // Return an iterator pointing to the first element inserted. + return return_value; +} + +template +typename Vector::iterator Vector::erase(Vector::const_iterator first, + Vector::const_iterator last) { + iterator source = begin() + std::distance(cbegin(), last); + if (first == last) { + return source; + } + + if constexpr (!std::is_trivially_destructible_v) { + std::destroy(first, last); + } + + iterator destination = begin() + std::distance(cbegin(), first); + iterator new_end = std::move(source, end(), destination); + + size_ = std::distance(begin(), new_end); + + // Return an iterator following the last removed element. + return new_end; +} + template template void Vector::CopyFrom(Iterator first, Iterator last) { diff --git a/pw_containers/vector_test.cc b/pw_containers/vector_test.cc index e90da6f2ed..4bde24758d 100644 --- a/pw_containers/vector_test.cc +++ b/pw_containers/vector_test.cc @@ -76,6 +76,19 @@ struct Counter { moved += 1; } + Counter& operator=(const Counter& other) { + value = other.value; + created += 1; + return *this; + } + + Counter& operator=(Counter&& other) { + value = other.value; + other.value = 0; + moved += 1; + return *this; + } + ~Counter() { destroyed += 1; } int value; @@ -467,6 +480,212 @@ TEST(Vector, Modify_Resize_Zero) { EXPECT_EQ(vector.size(), 0u); } +TEST(Vector, Modify_Erase_TrivialRangeBegin) { + Vector vector; + + for (size_t i = 0; i < vector.max_size(); ++i) { + vector.emplace_back(i); + } + + vector.erase(vector.begin() + 2, vector.end()); + EXPECT_EQ(vector.size(), 2u); + + for (size_t i = 0; i < vector.size(); ++i) { + EXPECT_EQ(vector[i], i); + } +} + +TEST(Vector, Modify_Erase_TrivialRangeEnd) { + Vector vector; + + for (size_t i = 0; i < vector.max_size(); ++i) { + vector.emplace_back(i); + } + + vector.erase(vector.begin(), vector.end() - 2); + EXPECT_EQ(vector.size(), 2u); + + for (size_t i = 0; i < vector.size(); ++i) { + EXPECT_EQ(vector[i], 8u + i); + } +} + +TEST(Vector, Modify_Erase_TrivialSingleItem) { + Vector vector; + + for (size_t i = 0; i < vector.max_size(); ++i) { + vector.emplace_back(i); + } + + vector.erase(vector.begin() + 9); + + EXPECT_EQ(vector.size(), 9u); + EXPECT_EQ(vector.at(8), 8u); + EXPECT_EQ(vector.at(0), 0u); + + vector.erase(vector.begin()); + EXPECT_EQ(vector.size(), 8u); + EXPECT_EQ(vector.at(0), 1u); +} + +TEST(Vector, Modify_Erase_NonTrivialRangeBegin) { + Counter::Reset(); + Vector vector; + + for (size_t i = 0; i < vector.max_size(); ++i) { + vector.emplace_back(static_cast(i)); + } + + for (size_t i = 0; i < vector.size(); ++i) { + EXPECT_EQ(vector[i].value, static_cast(i)); + } + + vector.erase(vector.begin() + 2, vector.end()); + EXPECT_EQ(vector.size(), 2u); + + for (size_t i = 0; i < vector.size(); ++i) { + EXPECT_EQ(vector[i].value, static_cast(i)); + } + + EXPECT_EQ(Counter::destroyed, 8); + EXPECT_EQ(Counter::moved, 0); +} + +TEST(Vector, Modify_Erase_NonTrivialRangeEnd) { + Counter::Reset(); + Vector vector; + + for (size_t i = 0; i < vector.max_size(); ++i) { + vector.emplace_back(static_cast(i)); + } + + vector.erase(vector.begin(), vector.end() - 2); + EXPECT_EQ(vector.size(), 2u); + + for (size_t i = 0; i < vector.size(); ++i) { + EXPECT_EQ(vector[i].value, static_cast(8u + i)); + } + + EXPECT_EQ(Counter::destroyed, 8); + EXPECT_EQ(Counter::moved, 2); +} + +TEST(Vector, Modify_Erase_None) { + Counter::Reset(); + Vector vector; + + for (size_t i = 0; i < vector.max_size(); ++i) { + vector.emplace_back(static_cast(i)); + } + + vector.erase(vector.begin() + 2, vector.begin() + 2); + EXPECT_EQ(vector.size(), 10u); + + EXPECT_EQ(Counter::destroyed, 0); + EXPECT_EQ(Counter::moved, 0); + EXPECT_EQ(Counter::created, 10); +} + +TEST(Vector, Modify_Insert_End) { + Counter::Reset(); + Vector vector; + + for (size_t i = 0; i < 8u; ++i) { + vector.emplace_back(static_cast(i)); + } + + EXPECT_EQ(vector.size(), 8u); + EXPECT_EQ(Counter::created, 8); + + decltype(vector)::iterator it = vector.insert(vector.cend(), 8); + EXPECT_EQ(it->value, 8); + EXPECT_EQ(vector.at(8).value, 8); + + EXPECT_EQ(Counter::destroyed, 1); + EXPECT_EQ(Counter::moved, 1); + EXPECT_EQ(Counter::created, 9); +} + +TEST(Vector, Modify_Insert_Begin) { + Counter::Reset(); + Vector vector; + + for (size_t i = 0; i < 8u; ++i) { + vector.emplace_back(static_cast(i)); + } + + EXPECT_EQ(vector.size(), 8u); + + decltype(vector)::iterator it = vector.insert(vector.cbegin(), 123); + EXPECT_EQ(it->value, 123); + EXPECT_EQ(vector.at(0).value, 123); + EXPECT_EQ(vector.at(8).value, 7); + + EXPECT_EQ(Counter::moved, 9); +} + +TEST(Vector, Modify_Insert_CountCopies) { + Counter::Reset(); + Vector vector; + + vector.emplace_back(123); + vector.insert(vector.cbegin() + 1, 8, Counter{421}); + EXPECT_EQ(vector.at(0).value, 123); + EXPECT_EQ(vector.size(), 9u); + + for (size_t i = 1; i < vector.size(); ++i) { + EXPECT_EQ(vector[i].value, 421); + } + + EXPECT_EQ(Counter::moved, 0); + EXPECT_EQ(Counter::created, 10); +} + +TEST(Vector, Modify_Insert_IteratorRange) { + std::array array_to_insert_first{0, 1, 2, 8, 9}; + std::array array_to_insert_middle{3, 4, 5, 6, 7}; + + Vector vector(array_to_insert_first.begin(), + array_to_insert_first.end()); + + decltype(vector)::iterator it = vector.insert(vector.cbegin() + 3, + array_to_insert_middle.begin(), + array_to_insert_middle.end()); + EXPECT_EQ(*it, array_to_insert_middle.front()); + + for (size_t i = 0; i < vector.max_size(); ++i) { + EXPECT_EQ(vector[i], static_cast(i)); + } +} + +TEST(Vector, Modify_Insert_InitializerListRange) { + std::array array_to_insert_first{0, 1, 2, 8, 9}; + Vector vector(array_to_insert_first.begin(), + array_to_insert_first.end()); + + decltype(vector)::iterator it = + vector.insert(vector.cbegin() + 3, {3, 4, 5, 6, 7}); + EXPECT_EQ(*it, 3); + + for (size_t i = 0; i < vector.max_size(); ++i) { + EXPECT_EQ(vector[i], static_cast(i)); + } +} + +TEST(Vector, Modify_Insert_NonTrivial_InitializerListRange) { + std::array array_to_insert_first{0, 1, 2, 8, 9}; + Vector vector(array_to_insert_first.begin(), + array_to_insert_first.end()); + + decltype(vector)::iterator it = vector.insert( + vector.cbegin() + 3, std::initializer_list{3, 4, 5, 6, 7}); + EXPECT_EQ(it->value, 3); + + for (size_t i = 0; i < vector.max_size(); ++i) { + EXPECT_EQ(vector[i].value, static_cast(i)); + } +} + TEST(Vector, Generic) { Vector vector{1, 2, 3, 4, 5};