From 7da0fffe07e3ce4d4bd5cb0ea140d3092de4ca65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Leclercq?= Date: Mon, 2 Jul 2018 17:56:27 +0200 Subject: [PATCH] Core: Add StackVector class --- include/Nazara/Core/StackVector.hpp | 112 +++++++++ include/Nazara/Core/StackVector.inl | 334 +++++++++++++++++++++++++++ src/Nazara/Physics3D/PhysWorld3D.cpp | 10 +- tests/Engine/Core/StackVector.cpp | 175 ++++++++++++++ 4 files changed, 624 insertions(+), 7 deletions(-) create mode 100644 include/Nazara/Core/StackVector.hpp create mode 100644 include/Nazara/Core/StackVector.inl create mode 100644 tests/Engine/Core/StackVector.cpp diff --git a/include/Nazara/Core/StackVector.hpp b/include/Nazara/Core/StackVector.hpp new file mode 100644 index 000000000..18d7e7437 --- /dev/null +++ b/include/Nazara/Core/StackVector.hpp @@ -0,0 +1,112 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_STACKVECTOR_HPP +#define NAZARA_STACKVECTOR_HPP + +#include + +#ifdef NAZARA_ALLOCA_SUPPORT + #define NazaraStackVector(T, capacity) Nz::StackVector(static_cast(NAZARA_ALLOCA((capacity) * sizeof(T))), capacity) +#else + #define NazaraStackVector(T, capacity) Nz::StackVector(static_cast(Nz::OperatorNew((capacity) * sizeof(T))), capacity) +#endif + +#include +#include +#include + +namespace Nz +{ + template + class StackVector + { + public: + using value_type = T; + using const_iterator = const value_type*; + using const_pointer = const value_type*; + using const_reference = const value_type&; + using const_reverse_iterator = std::reverse_iterator; + using difference_type = std::ptrdiff_t; + using iterator = value_type*; + using pointer = value_type*; + using reference = value_type&; + using reverse_iterator = std::reverse_iterator; + using size_type = std::size_t; + + StackVector(T* stackMemory, std::size_t capacity); + ~StackVector(); + + reference back(); + const_reference back() const; + + iterator begin() noexcept; + const_iterator begin() const noexcept; + + size_type capacity() const noexcept; + + void clear() noexcept; + + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + const_reverse_iterator crbegin() const noexcept; + const_reverse_iterator crend() const noexcept; + + T* data() noexcept; + const T* data() const noexcept; + + template + iterator emplace(const_iterator pos, Args&&... args); + + template + reference emplace_back(Args&&... args); + + bool empty() const noexcept; + + iterator end() noexcept; + const_iterator end() const noexcept; + + iterator erase(const_iterator pos); + iterator erase(const_iterator first, const_iterator last); + + reference front() noexcept; + const_reference front() const noexcept; + + iterator insert(const_iterator pos, const T& value); + iterator insert(const_iterator pos, T&& value); + + size_type max_size() const noexcept; + + reference push_back(const T& value) noexcept(std::is_nothrow_copy_constructible::value); + reference push_back(T&& value) noexcept(std::is_nothrow_move_constructible::value); + + void pop_back(); + + void resize(size_type count); + void resize(size_type count, const value_type& value); + + reverse_iterator rbegin() noexcept; + const_reverse_iterator rbegin() const noexcept; + + reverse_iterator rend() noexcept; + const_reverse_iterator rend() const noexcept; + + size_type size() const noexcept; + + reference operator[](size_type pos); + const_reference operator[](size_type pos) const; + + private: + std::size_t m_capacity; + std::size_t m_size; + T* m_ptr; + }; + +} + +#include + +#endif // NAZARA_STACKVECTOR_HPP diff --git a/include/Nazara/Core/StackVector.inl b/include/Nazara/Core/StackVector.inl new file mode 100644 index 000000000..e2012fe32 --- /dev/null +++ b/include/Nazara/Core/StackVector.inl @@ -0,0 +1,334 @@ +// Copyright (C) 2017 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +// I'm not proud of those five following lines but ti's hard to do with another way now +#ifdef NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION + #define NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION_DEFINED +#else + #define NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace Nz +{ + /*! + * \ingroup core + * \class Nz::StackVector + * \brief Core class that represents a stack-allocated (if alloca is present) vector, that is with a capacity different from its size + */ + template + StackVector::StackVector(T* stackMemory, std::size_t capacity) : + m_capacity(capacity), + m_size(0), + m_ptr(stackMemory) + { + } + + template + StackVector::~StackVector() + { + clear(); + + #ifndef NAZARA_ALLOCA_SUPPORT + OperatorDelete(m_ptr); + #endif + } + + template + typename StackVector::reference StackVector::back() + { + assert(m_size != 0); + return m_ptr[m_size - 1]; + } + + template + typename StackVector::const_reference StackVector::back() const + { + assert(m_size != 0); + return m_ptr[m_size - 1]; + } + + template + typename StackVector::iterator StackVector::begin() noexcept + { + return iterator(&m_ptr[0]); + } + + template + typename StackVector::const_iterator StackVector::begin() const noexcept + { + return const_iterator(&m_ptr[0]); + } + + template + typename StackVector::size_type StackVector::capacity() const noexcept + { + return m_capacity; + } + + template + void StackVector::clear() noexcept + { + resize(0); + } + + template + typename StackVector::const_iterator StackVector::cbegin() const noexcept + { + return const_iterator(&m_ptr[0]); + } + + template + typename StackVector::const_iterator StackVector::cend() const noexcept + { + return const_iterator(&m_ptr[m_size]); + } + + template + typename StackVector::const_reverse_iterator StackVector::crbegin() const noexcept + { + return const_reverse_iterator(&m_ptr[m_size]); + } + + template + typename StackVector::const_reverse_iterator StackVector::crend() const noexcept + { + return const_reverse_iterator(&m_ptr[0]); + } + + template + T* StackVector::data() noexcept + { + return m_ptr; + } + + template + const T* StackVector::data() const noexcept + { + return m_ptr; + } + + template + template + typename StackVector::iterator StackVector::emplace(const_iterator pos, Args&& ...args) + { + assert(m_size < m_capacity); + assert(pos >= begin() && pos <= end()); + + std::size_t index = std::distance(begin(), 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]); + } + m_size++; + + return PlacementNew(&m_ptr[index], std::forward(args)...); + } + + template + template + typename StackVector::reference Nz::StackVector::emplace_back(Args&&... args) + { + assert(m_size < m_capacity); + return *PlacementNew(&m_ptr[m_size++], std::forward(args)...); + } + + template + bool StackVector::empty() const noexcept + { + return m_size == 0; + } + + template + typename StackVector::iterator StackVector::end() noexcept + { + return iterator(&m_ptr[m_size]); + } + + template + typename StackVector::const_iterator StackVector::end() const noexcept + { + return const_iterator(&m_ptr[m_size]); + } + + template + 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); + pop_back(); + + return iterator(&m_ptr[index]); + } + + template + typename StackVector::iterator StackVector::erase(const_iterator first, const_iterator last) + { + if (first == last) + return first; + + 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)); + + return iterator(&m_ptr[index]); + } + + template + typename StackVector::reference StackVector::front() noexcept + { + return m_ptr[0]; + } + + template + typename StackVector::const_reference StackVector::front() const noexcept + { + return m_ptr[0]; + } + + template + typename StackVector::iterator StackVector::insert(const_iterator pos, const T& value) + { + return emplace(pos, value); + } + + template + typename StackVector::iterator StackVector::insert(const_iterator pos, T&& value) + { + return emplace(pos, std::move(value)); + } + + template + typename StackVector::size_type StackVector::max_size() const noexcept + { + return capacity(); + } + + template + typename StackVector::reference StackVector::push_back(const T& value) noexcept(std::is_nothrow_copy_constructible::value) + { + assert(m_size < m_capacity); + return *PlacementNew(&m_ptr[m_size++], value); + } + + template + typename StackVector::reference StackVector::push_back(T&& value) noexcept(std::is_nothrow_move_constructible::value) + { + assert(m_size < m_capacity); + return *PlacementNew(&m_ptr[m_size++], std::move(value)); + } + + template + void StackVector::pop_back() + { + assert(!empty()); + PlacementDestroy(&m_ptr[--m_size]); + } + + template + void StackVector::resize(size_type count) + { + assert(count < m_capacity); + if (count > m_size) + { + for (std::size_t i = m_size; i < count; ++i) + PlacementNew(&m_ptr[i]); + + m_size = count; + } + else if (count < m_size) + { + for (std::size_t i = count; i < m_size; ++i) + PlacementNew(&m_ptr[i]); + + m_size = count; + } + } + + template + void StackVector::resize(size_type count, const value_type& value) + { + assert(count < m_capacity); + if (count > m_size) + { + for (std::size_t i = m_size; i < count; ++i) + PlacementNew(&m_ptr[i], value); + + m_size = count; + } + else if (count < m_size) + { + for (std::size_t i = count; i < m_size; ++i) + PlacementDestroy(&m_ptr[i]); + + m_size = count; + } + } + + template + typename StackVector::reverse_iterator StackVector::rbegin() noexcept + { + return reverse_iterator(&m_ptr[m_size]); + } + + template + typename StackVector::const_reverse_iterator StackVector::rbegin() const noexcept + { + return reverse_iterator(&m_ptr[m_size]); + } + + template + typename StackVector::reverse_iterator StackVector::rend() noexcept + { + return reverse_iterator(&m_ptr[0]); + } + + template + typename StackVector::const_reverse_iterator StackVector::rend() const noexcept + { + return reverse_iterator(&m_ptr[0]); + } + + template + typename StackVector::size_type StackVector::size() const noexcept + { + return m_size; + } + + template + typename StackVector::reference StackVector::operator[](size_type pos) + { + assert(pos < m_size); + return m_ptr[pos]; + } + + template + typename StackVector::const_reference StackVector::operator[](size_type pos) const + { + assert(pos < m_size); + return m_ptr[pos]; + } +} + +#include + +// If we have defined the constant, then we have to undefine it (to avoid bloating in the engine) +#ifndef NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION_DEFINED + #undef NAZARA_DEBUG_NEWREDEFINITION_DISABLE_REDEFINITION +#endif diff --git a/src/Nazara/Physics3D/PhysWorld3D.cpp b/src/Nazara/Physics3D/PhysWorld3D.cpp index 225c28b9e..01ed2226b 100644 --- a/src/Nazara/Physics3D/PhysWorld3D.cpp +++ b/src/Nazara/Physics3D/PhysWorld3D.cpp @@ -3,7 +3,7 @@ // For conditions of distribution and use, see copyright notice in Config.hpp #include -#include +#include #include #include #include @@ -173,13 +173,9 @@ namespace Nz using ContactJoint = void*; // Query all joints first, to prevent removing a joint from the list while iterating on it - StackArray contacts = NazaraStackArray(ContactJoint, NewtonContactJointGetContactCount(contactJoint)); - std::size_t contactIndex = 0; + StackVector contacts = NazaraStackVector(ContactJoint, NewtonContactJointGetContactCount(contactJoint)); for (ContactJoint contact = NewtonContactJointGetFirstContact(contactJoint); contact; contact = NewtonContactJointGetNextContact(contactJoint, contact)) - { - assert(contactIndex < contacts.size()); - contacts[contactIndex++] = contact; - } + contacts.push_back(contact); for (ContactJoint contact : contacts) { diff --git a/tests/Engine/Core/StackVector.cpp b/tests/Engine/Core/StackVector.cpp new file mode 100644 index 000000000..ceb36da89 --- /dev/null +++ b/tests/Engine/Core/StackVector.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include + +SCENARIO("StackVector", "[CORE][STACKVECTOR]") +{ + GIVEN("A StackVector to contain multiple int") + { + volatile std::size_t capacity = 50; + Nz::StackVector vector = NazaraStackVector(int, capacity); + + WHEN("At construction, the vector is empty but has capacity") + { + CHECK(vector.capacity() == capacity); + CHECK(vector.empty()); + CHECK(vector.size() == 0); + } + + WHEN("Emplacing five elements, vector size increase accordingly") + { + for (std::size_t i = 0; i < 5; ++i) + { + int val = int(i); + CHECK(vector.emplace_back(val) == val); + } + + CHECK(!vector.empty()); + CHECK(vector.size() == 5); + + std::array expectedValues = { 0, 1, 2, 3, 4 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + WHEN("Pushing three elements, vector size increase accordingly") + { + for (std::size_t i = 0; i < 3; ++i) + { + int val = int(i); + CHECK(vector.push_back(val) == val); + } + + CHECK(!vector.empty()); + CHECK(vector.size() == 3); + + std::array expectedValues = { 0, 1, 2 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + + THEN("We resize to five") + { + vector.resize(5); + + CHECK(!vector.empty()); + CHECK(vector.size() == 5); + + std::array expectedValues = { 0, 1, 2, 0, 0 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + + AND_THEN("We resize it back to zero") + { + vector.resize(0); + + CHECK(vector.empty()); + CHECK(vector.size() == 0); + } + AND_THEN("We clear it") + { + vector.clear(); + + CHECK(vector.empty()); + CHECK(vector.size() == 0); + } + } + } + + WHEN("We generate its content will iota") + { + vector.resize(10); + std::iota(vector.begin(), vector.end(), -5); + + std::array expectedValues = { -5, -4, -3, -2, -1, 0, 1, 2, 3, 4 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + 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.empty()); + CHECK(vector.size() == 5); + + std::array expectedValues = { 0, 1, 2, 3, 4 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + 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.empty()); + CHECK(vector.size() == 5); + + std::array expectedValues = { 4, 3, 2, 1, 0 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + 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.empty()); + CHECK(vector.size() == 10); + + std::array expectedValues = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + 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.empty()); + CHECK(vector.size() == 5); + + std::array expectedValues = { 0, 1, 2, 3, 4 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + 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.empty()); + CHECK(vector.size() == 5); + + std::array expectedValues = { 4, 3, 2, 1, 0 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + + 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.empty()); + CHECK(vector.size() == 10); + + std::array expectedValues = { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 }; + CHECK(std::equal(vector.begin(), vector.end(), expectedValues.begin(), expectedValues.end())); + } + } +}