diff --git a/include/Nazara/Core/StackVector.inl b/include/Nazara/Core/StackVector.inl index c8b71a5cb..cfe7b3d0c 100644 --- a/include/Nazara/Core/StackVector.inl +++ b/include/Nazara/Core/StackVector.inl @@ -123,14 +123,19 @@ namespace Nz assert(m_size < m_capacity); assert(pos >= begin() && pos <= end()); - std::size_t index = std::distance(begin(), pos); + std::size_t index = std::distance(cbegin(), pos); if (pos < end()) { iterator lastElement = end() - 1; PlacementNew(&m_ptr[m_size], std::move(*lastElement)); if (&m_ptr[index] < lastElement) - std::move(&m_ptr[index], lastElement, &m_ptr[index + 1]); + { + std::size_t count = m_size - index - 1; + std::move_backward(&m_ptr[index], &m_ptr[index + count], &m_ptr[m_size]); + } + + PlacementDestroy(&m_ptr[index]); } m_size++; @@ -167,8 +172,8 @@ namespace Nz typename StackVector::iterator StackVector::erase(const_iterator pos) { assert(pos < end()); - std::size_t index = std::distance(begin(), pos); - std::move(pos + 1, end(), pos); + std::size_t index = std::distance(cbegin(), pos); + std::move(begin() + index + 1, end(), begin() + index); pop_back(); return iterator(&m_ptr[index]); @@ -177,15 +182,18 @@ namespace Nz template typename StackVector::iterator StackVector::erase(const_iterator first, const_iterator last) { + std::size_t index = std::distance(cbegin(), first); + if (first == last) - return first; + return begin() + index; assert(first < last); assert(first >= begin() && last <= end()); - std::size_t index = std::distance(begin(), first); - std::move(last, end(), first); - resize(size() - (last - first)); + std::size_t count = std::distance(first, last); + + std::move(begin() + index + count, end(), begin() + index); + resize(size() - count); return iterator(&m_ptr[index]); } diff --git a/tests/Engine/Core/StackVector.cpp b/tests/Engine/Core/StackVector.cpp index ceb36da89..6a3140abc 100644 --- a/tests/Engine/Core/StackVector.cpp +++ b/tests/Engine/Core/StackVector.cpp @@ -1,14 +1,113 @@ +#include #include #include #include #include +// This is a quick way to check that checks are valid +#define USE_STD_VECTOR 0 + +class DestructionCounter +{ + public: + DestructionCounter() : + m_counter(nullptr), + m_value(0) + { + } + + DestructionCounter(std::size_t* counter, int value) : + m_counter(counter), + m_value(value) + { + if (m_counter) + (*m_counter)++; + } + + DestructionCounter(const DestructionCounter& counter) : + m_counter(counter.m_counter), + m_value(counter.m_value) + { + if (m_counter) + (*m_counter)++; + } + + DestructionCounter(DestructionCounter&& counter) : + m_counter(counter.m_counter), + m_value(counter.m_value) + { + if (m_counter) + (*m_counter)++; + } + + ~DestructionCounter() + { + if (m_counter) + { + assert(*m_counter > 0); + (*m_counter)--; + } + } + + operator int() const + { + return m_value; + } + + DestructionCounter& operator=(const DestructionCounter& counter) + { + if (m_counter) + { + assert(*m_counter > 0); + (*m_counter)--; + } + + m_counter = counter.m_counter; + m_value = counter.m_value; + + if (m_counter) + (*m_counter)++; + + return *this; + } + + DestructionCounter& operator=(DestructionCounter&& counter) + { + if (this == &counter) + return *this; + + if (m_counter) + { + assert(*m_counter > 0); + (*m_counter)--; + } + + m_counter = counter.m_counter; + m_value = counter.m_value; + + if (m_counter) + (*m_counter)++; + + return *this; + } + + private: + std::size_t* m_counter; + int m_value; +}; + SCENARIO("StackVector", "[CORE][STACKVECTOR]") { GIVEN("A StackVector to contain multiple int") { volatile std::size_t capacity = 50; - Nz::StackVector vector = NazaraStackVector(int, capacity); +#if USE_STD_VECTOR + std::vector vector; + vector.reserve(capacity); +#else + Nz::StackVector vector = NazaraStackVector(DestructionCounter, capacity); +#endif + std::size_t counter = 0; WHEN("At construction, the vector is empty but has capacity") { @@ -21,8 +120,11 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") { for (std::size_t i = 0; i < 5; ++i) { - int val = int(i); - CHECK(vector.emplace_back(val) == val); +#if USE_STD_VECTOR + vector.emplace_back(&counter, int(i)); +#else + CHECK(vector.emplace_back(&counter, int(i)) == int(i)); +#endif } CHECK(!vector.empty()); @@ -36,8 +138,12 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") { for (std::size_t i = 0; i < 3; ++i) { - int val = int(i); + DestructionCounter val(&counter, int(i)); +#if USE_STD_VECTOR + vector.push_back(val); +#else CHECK(vector.push_back(val) == val); +#endif } CHECK(!vector.empty()); @@ -76,19 +182,70 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") WHEN("We generate its content will iota") { vector.resize(10); - std::iota(vector.begin(), vector.end(), -5); + for (std::size_t i = 0; i < vector.size(); ++i) + vector[i] = DestructionCounter(&counter, -5 + int(i)); std::array expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + CHECK(vector.size() == expectedValues.size()); CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + + AND_WHEN("We pop back some elements") + { + for (std::size_t i = 0; i < 5; ++i) + vector.pop_back(); + + std::array expectedValues = { -5, -4, -3, -2, -1 }; + CHECK(vector.size() == expectedValues.size()); + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + AND_WHEN("We erase elements at the beginning") + { + vector.erase(vector.begin()); + vector.erase(vector.begin()); + + std::array expectedValues = { -3, -2, -1, 0, 1, 2, 3, 4 }; + CHECK(vector.size() == expectedValues.size()); + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + AND_WHEN("We erase elements in the middle") + { + vector.erase(vector.begin() + 2); + vector.erase(vector.begin() + 2); + vector.erase(vector.begin() + 6); + + std::array expectedValues = { -5, -4, -1, 0, 1, 2, 4 }; + CHECK(vector.size() == expectedValues.size()); + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + AND_WHEN("We erase elements at the end") + { + vector.erase(vector.end() - 1); + vector.erase(vector.end() - 1); + + std::array expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2 }; + CHECK(vector.size() == expectedValues.size()); + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + AND_WHEN("We erase a range") + { + vector.erase(vector.begin() + 2, vector.end() - 3); + + std::array expectedValues = { -5, -4, 2, 3, 4 }; + CHECK(vector.size() == expectedValues.size()); + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + AND_WHEN("We erase everything") + { + vector.erase(vector.begin(), vector.end()); + + CHECK(vector.empty()); + } } WHEN("We generate its content using emplace") { for (std::size_t i = 0; i < 5; ++i) - { - int val = int(i); - CHECK(*vector.emplace(vector.end(), val) == val); - } + CHECK(*vector.emplace(vector.end(), &counter, int(i)) == int(i)); CHECK(!vector.empty()); CHECK(vector.size() == 5); @@ -100,10 +257,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") WHEN("We generate its content using emplace, in reverse order") { for (std::size_t i = 0; i < 5; ++i) - { - int val = int(i); - CHECK(*vector.emplace(vector.begin(), val) == val); - } + CHECK(*vector.emplace(vector.begin(), &counter, int(i)) == int(i)); CHECK(!vector.empty()); CHECK(vector.size() == 5); @@ -115,10 +269,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") WHEN("We generate its content using emplace, at the middle") { for (std::size_t i = 0; i < 10; ++i) - { - int val = int(i); - CHECK(*vector.emplace(vector.begin() + i / 2, val) == val); - } + CHECK(*vector.emplace(vector.begin() + i / 2, &counter, int(i)) == int(i)); CHECK(!vector.empty()); CHECK(vector.size() == 10); @@ -130,10 +281,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") WHEN("We generate its content using insert") { for (std::size_t i = 0; i < 5; ++i) - { - int val = int(i); - CHECK(*vector.insert(vector.end(), val) == val); - } + CHECK(*vector.insert(vector.end(), DestructionCounter(&counter, int(i))) == int(i)); CHECK(!vector.empty()); CHECK(vector.size() == 5); @@ -145,10 +293,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") WHEN("We generate its content using insert, in reverse order") { for (std::size_t i = 0; i < 5; ++i) - { - int val = int(i); - CHECK(*vector.insert(vector.begin(), val) == val); - } + CHECK(*vector.insert(vector.begin(), DestructionCounter(&counter, int(i))) == int(i)); CHECK(!vector.empty()); CHECK(vector.size() == 5); @@ -160,10 +305,7 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") WHEN("We generate its content using insert, at the middle") { for (std::size_t i = 0; i < 10; ++i) - { - int val = int(i); - CHECK(*vector.insert(vector.begin() + i / 2, val) == val); - } + CHECK(*vector.insert(vector.begin() + i / 2, DestructionCounter(&counter, int(i))) == int(i)); CHECK(!vector.empty()); CHECK(vector.size() == 10); @@ -171,5 +313,8 @@ SCENARIO("StackVector", "[CORE][STACKVECTOR]") std::array expectedValues = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 }; CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); } + + vector.clear(); + CHECK(counter == 0); } }