From 3e81f3809806a921a7914f6b9c4b02c8532fbc9f Mon Sep 17 00:00:00 2001 From: paradust7 <102263465+paradust7@users.noreply.github.com> Date: Sat, 21 May 2022 14:56:36 -0700 Subject: [PATCH] Make irrArray backed by std::vector (#101) --- include/ISkinnedMesh.h | 2 +- include/heapsort.h | 70 ---- include/irrArray.h | 476 +++++++------------------ include/irrlicht.h | 1 - source/Irrlicht/COBJMeshFileLoader.cpp | 6 +- source/Irrlicht/COGLESDriver.cpp | 4 +- source/Irrlicht/COGLESDriver.h | 2 +- source/Irrlicht/CSceneManager.h | 6 + source/Irrlicht/CSkinnedMesh.cpp | 2 +- source/Irrlicht/CSkinnedMesh.h | 4 +- 10 files changed, 147 insertions(+), 426 deletions(-) delete mode 100644 include/heapsort.h diff --git a/include/ISkinnedMesh.h b/include/ISkinnedMesh.h index 44d05e58..33ae23c4 100644 --- a/include/ISkinnedMesh.h +++ b/include/ISkinnedMesh.h @@ -100,7 +100,7 @@ namespace scene private: //! Internal members used by CSkinnedMesh friend class CSkinnedMesh; - bool *Moved; + char *Moved; core::vector3df StaticPos; core::vector3df StaticNormal; }; diff --git a/include/heapsort.h b/include/heapsort.h deleted file mode 100644 index 36211c4d..00000000 --- a/include/heapsort.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#ifndef __IRR_HEAPSORT_H_INCLUDED__ -#define __IRR_HEAPSORT_H_INCLUDED__ - -#include "irrTypes.h" - -namespace irr -{ -namespace core -{ - -//! Sinks an element into the heap. -template -inline void heapsink(T*array, s32 element, s32 max) -{ - while ((element<<1) < max) // there is a left child - { - s32 j = (element<<1); - - if (j+1 < max && array[j] < array[j+1]) - j = j+1; // take right child - - if (array[element] < array[j]) - { - T t = array[j]; // swap elements - array[j] = array[element]; - array[element] = t; - element = j; - } - else - return; - } -} - - -//! Sorts an array with size 'size' using heapsort. -template -inline void heapsort(T* array_, s32 size) -{ - // for heapsink we pretend this is not c++, where - // arrays start with index 0. So we decrease the array pointer, - // the maximum always +2 and the element always +1 - - T* virtualArray = array_ - 1; - s32 virtualSize = size + 2; - s32 i; - - // build heap - - for (i=((size-1)/2); i>=0; --i) - heapsink(virtualArray, i+1, virtualSize-1); - - // sort array, leave out the last element (0) - for (i=size-1; i>0; --i) - { - T t = array_[0]; - array_[0] = array_[i]; - array_[i] = t; - heapsink(virtualArray, 1, i + 1); - } -} - -} // end namespace core -} // end namespace irr - -#endif - diff --git a/include/irrArray.h b/include/irrArray.h index 99ecb2de..dbad8c87 100644 --- a/include/irrArray.h +++ b/include/irrArray.h @@ -5,9 +5,11 @@ #ifndef __IRR_ARRAY_H_INCLUDED__ #define __IRR_ARRAY_H_INCLUDED__ +#include +#include +#include + #include "irrTypes.h" -#include "heapsort.h" -#include "irrAllocator.h" #include "irrMath.h" namespace irr @@ -18,44 +20,27 @@ namespace core //! Self reallocating template array (like stl vector) with additional features. /** Some features are: Heap sorting, binary search methods, easier debugging. */ -template > +template class array { - public: + static_assert(!std::is_same::value, + "irr::core::array with T = bool not supported. Use std::vector instead."); //! Default constructor for empty array. - array() : data(0), allocated(0), used(0), - strategy(ALLOC_STRATEGY_DOUBLE), free_when_destroyed(true), is_sorted(true) - { - } - + array() : m_data(), is_sorted(true) + { } //! Constructs an array and allocates an initial chunk of memory. /** \param start_count Amount of elements to pre-allocate. */ - explicit array(u32 start_count) : data(0), allocated(0), used(0), - strategy(ALLOC_STRATEGY_DOUBLE), - free_when_destroyed(true), is_sorted(true) + explicit array(u32 start_count) : m_data(), is_sorted(true) { - reallocate(start_count); + m_data.reserve(start_count); } - //! Copy constructor - array(const array& other) : data(0) - { - *this = other; - } - - - //! Destructor. - /** Frees allocated memory, if set_free_when_destroyed was not set to - false by the user before. */ - ~array() - { - clear(); - } - + array(const array& other) : m_data(other.m_data), is_sorted(other.is_sorted) + { } //! Reallocates the array, make it bigger or smaller. /** \param new_size New size of array. @@ -65,52 +50,28 @@ public: */ void reallocate(u32 new_size, bool canShrink=true) { - if (allocated==new_size) - return; - if (!canShrink && (new_size < allocated)) - return; - - T* old_data = data; - - data = allocator.allocate(new_size); //new T[new_size]; - allocated = new_size; - - // copy old data - const s32 end = used < new_size ? used : new_size; - - for (s32 i=0; iused) // access violation - - if (used + 1 > allocated) - { - // this doesn't work if the element is in the same - // array. So we'll copy the element first to be sure - // we'll get no data corruption - const T e(element); - - // increase data block - u32 newAlloc; - switch ( strategy ) - { - case ALLOC_STRATEGY_DOUBLE: - newAlloc = used + 5 + (allocated < 500 ? used : used >> 2); - break; - default: - case ALLOC_STRATEGY_SAFE: - newAlloc = used + 1; - break; - } - reallocate( newAlloc); - - // move array content and construct new element - // first move end one up - for (u32 i=used; i>index; --i) - { - if (i index) - allocator.destruct(&data[index]); - allocator.construct(&data[index], e); // data[index] = e; - } - else - { - // element inserted not at end - if ( used > index ) - { - // create one new element at the end - allocator.construct(&data[used], data[used-1]); - - // move the rest of the array content - for (u32 i=used-1; i>index; --i) - { - data[i] = data[i-1]; - } - // insert the new element - data[index] = element; - } - else - { - // insert the new element to the end - allocator.construct(&data[index], element); - } - } - // set to false as we don't know if we have the comparison operators + _IRR_DEBUG_BREAK_IF(index > m_data.size()) // access violation + auto pos = std::next(m_data.begin(), index); + m_data.insert(pos, element); is_sorted = false; - ++used; } - //! Clears the array and deletes all allocated memory. void clear() { - if (free_when_destroyed) - { - for (u32 i=0; i().swap(m_data); is_sorted = true; } - - //! Sets pointer to new array, using this as new workspace. - /** Make sure that set_free_when_destroyed is used properly. - \param newPointer: Pointer to new array of elements. - \param size: Size of the new array. - \param _is_sorted Flag which tells whether the new array is already - sorted. - \param _free_when_destroyed Sets whether the new memory area shall be - freed by the array upon destruction, or if this will be up to the user - application. */ - void set_pointer(T* newPointer, u32 size, bool _is_sorted=false, bool _free_when_destroyed=true) - { - clear(); - data = newPointer; - allocated = size; - used = size; - is_sorted = _is_sorted; - free_when_destroyed=_free_when_destroyed; - } - //! Set (copy) data from given memory block /** \param newData data to set, must have newSize elements \param newSize Amount of elements in newData @@ -240,12 +123,11 @@ public: */ void set_data(const T* newData, u32 newSize, bool newDataIsSorted=false, bool canShrink=false) { - reallocate(newSize, canShrink); - set_used(newSize); - for ( u32 i=0; i& operator=(const array& other) + const array& operator=(const array& other) { if (this == &other) return *this; - strategy = other.strategy; - - // (TODO: we could probably avoid re-allocations of data when (allocated < other.allocated) - - if (data) - clear(); - - used = other.used; - free_when_destroyed = true; + m_data = other.m_data; is_sorted = other.is_sorted; - allocated = other.allocated; - - if (other.allocated == 0) - { - data = 0; - } - else - { - data = allocator.allocate(other.allocated); // new T[other.allocated]; - - for (u32 i=0; i& operator=(const std::vector &other) + { + m_data = other; + is_sorted = false; + return *this; + } + + array& operator=(std::vector &&other) + { + m_data = std::move(other); + is_sorted = false; + return *this; + } //! Equality operator - bool operator == (const array& other) const + bool operator == (const array& other) const { return equals(other.const_pointer(), other.size()); } //! Inequality operator - bool operator != (const array& other) const + bool operator != (const array& other) const { return !(*this==other); } @@ -342,36 +190,36 @@ public: //! Direct access operator T& operator [](u32 index) { - _IRR_DEBUG_BREAK_IF(index>=used) // access violation + _IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation - return data[index]; + return m_data[index]; } //! Direct const access operator const T& operator [](u32 index) const { - _IRR_DEBUG_BREAK_IF(index>=used) // access violation + _IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation - return data[index]; + return m_data[index]; } //! Gets last element. T& getLast() { - _IRR_DEBUG_BREAK_IF(!used) // access violation + _IRR_DEBUG_BREAK_IF(m_data.empty()) // access violation - return data[used-1]; + return m_data.back(); } //! Gets last element const T& getLast() const { - _IRR_DEBUG_BREAK_IF(!used) // access violation + _IRR_DEBUG_BREAK_IF(m_data.empty()) // access violation - return data[used-1]; + return m_data.back(); } @@ -379,7 +227,7 @@ public: /** \return Pointer to the array. */ T* pointer() { - return data; + return &m_data[0]; } @@ -387,7 +235,7 @@ public: /** \return Pointer to the array. */ const T* const_pointer() const { - return data; + return &m_data[0]; } @@ -395,7 +243,7 @@ public: /** \return Size of elements in the array which are actually occupied. */ u32 size() const { - return used; + return m_data.size(); } @@ -404,7 +252,7 @@ public: allocated would be allocated_size() * sizeof(ElementTypeUsed); */ u32 allocated_size() const { - return allocated; + return m_data.capacity(); } @@ -412,7 +260,7 @@ public: /** \return True if the array is empty false if not. */ bool empty() const { - return used == 0; + return m_data.empty(); } @@ -421,9 +269,10 @@ public: O(n*log n) in worst case. */ void sort() { - if (!is_sorted && used>1) - heapsort(data, used); - is_sorted = true; + if (!is_sorted) { + std::sort(m_data.begin(), m_data.end()); + is_sorted = true; + } } @@ -437,10 +286,9 @@ public: s32 binary_search(const T& element) { sort(); - return binary_search(element, 0, used-1); + return binary_search(element, 0, (s32)m_data.size() - 1); } - //! Performs a binary search for an element if possible, returns -1 if not found. /** This method is for const arrays and so cannot call sort(), if the array is not sorted then linear_search will be used instead. Potentially very slow! @@ -450,12 +298,11 @@ public: s32 binary_search(const T& element) const { if (is_sorted) - return binary_search(element, 0, used-1); + return binary_search(element, 0, (s32)m_data.size() - 1); else return linear_search(element); } - //! Performs a binary search for an element, returns -1 if not found. /** \param element: Element to search for. \param left First left index @@ -464,31 +311,15 @@ public: is returned. */ s32 binary_search(const T& element, s32 left, s32 right) const { - if (!used) + if (left > right) return -1; - - s32 m; - - do - { - m = (left+right)>>1; - - if (element < data[m]) - right = m - 1; - else - left = m + 1; - - } while((element < data[m] || data[m] < element) && left<=right); - // this last line equals to: - // " while((element != array[m]) && left<=right);" - // but we only want to use the '<' operator. - // the same in next line, it is "(element == array[m])" - - - if (!(element < data[m]) && !(data[m] < element)) - return m; - - return -1; + auto lpos = std::next(m_data.begin(), left); + auto rpos = std::next(m_data.begin(), right); + auto it = std::lower_bound(lpos, rpos, element); + // *it = first element in [first, last) that is >= element, or last if not found. + if (*it < element || element < *it) + return -1; + return it - m_data.begin(); } @@ -503,25 +334,11 @@ public: s32 binary_search_multi(const T& element, s32 &last) { sort(); - s32 index = binary_search(element, 0, used-1); - if ( index < 0 ) - return index; - - // The search can be somewhere in the middle of the set - // look linear previous and past the index - last = index; - - while ( index > 0 && !(element < data[index - 1]) && !(data[index - 1] < element) ) - { - index -= 1; - } - // look linear up - while ( last < (s32) used - 1 && !(element < data[last + 1]) && !(data[last + 1] < element) ) - { - last += 1; - } - - return index; + auto iters = std::equal_range(m_data.begin(), m_data.end(), element); + if (iters.first == iters.second) + return -1; + last = (iters.second - m_data.begin()) - 1; + return iters.first - m_data.begin(); } @@ -533,11 +350,10 @@ public: is returned. */ s32 linear_search(const T& element) const { - for (u32 i=0; i=0; --i) - if (data[i] == element) - return i; - - return -1; + auto it = std::find(m_data.rbegin(), m_data.rend(), element); + if (it == m_data.rend()) + return -1; + size_t offset = it - m_data.rbegin(); + return m_data.size() - offset - 1; } @@ -563,17 +379,9 @@ public: \param index: Index of element to be erased. */ void erase(u32 index) { - _IRR_DEBUG_BREAK_IF(index>=used) // access violation - - for (u32 i=index+1; i= m_data.size()) // access violation + auto it = std::next(m_data.begin(), index); + m_data.erase(it); } @@ -584,30 +392,14 @@ public: \param count: Amount of elements to be erased. */ void erase(u32 index, s32 count) { - if (index>=used || count<1) + if (index >= m_data.size() || count < 1) return; - if (index+count>used) - count = used-index; - - u32 i; - for (i=index; i= index+count) // not already destructed before loop - allocator.destruct(&data[i-count]); - - allocator.construct(&data[i-count], data[i]); // data[i-count] = data[i]; - - if (i >= used-count) // those which are not overwritten - allocator.destruct(&data[i]); - } - - used-= count; + count = std::min(count, (s32)m_data.size() - (s32)index); + auto first = std::next(m_data.begin(), index); + auto last = std::next(first, count); + m_data.erase(first, last); } - //! Sets if the array is sorted void set_sorted(bool _is_sorted) { @@ -619,38 +411,30 @@ public: /** Afterward this object will contain the content of the other object and the other object will contain the content of this object. \param other Swap content with this object */ - void swap(array& other) + void swap(array& other) { - core::swap(data, other.data); - core::swap(allocated, other.allocated); - core::swap(used, other.used); - core::swap(allocator, other.allocator); // memory is still released by the same allocator used for allocation - eAllocStrategy helper_strategy(strategy); // can't use core::swap with bitfields - strategy = other.strategy; - other.strategy = helper_strategy; - bool helper_free_when_destroyed(free_when_destroyed); - free_when_destroyed = other.free_when_destroyed; - other.free_when_destroyed = helper_free_when_destroyed; - bool helper_is_sorted(is_sorted); - is_sorted = other.is_sorted; - other.is_sorted = helper_is_sorted; + m_data.swap(other.m_data); + std::swap(is_sorted, other.is_sorted); + } + + //! Pull the contents of this array as a vector. + // The array is left empty. + std::vector steal() + { + std::vector ret = std::move(m_data); + m_data.clear(); + is_sorted = true; + return ret; } - typedef TAlloc allocator_type; typedef T value_type; typedef u32 size_type; private: - T* data; - u32 allocated; - u32 used; - TAlloc allocator; - eAllocStrategy strategy:4; - bool free_when_destroyed:1; - bool is_sorted:1; + std::vector m_data; + bool is_sorted; }; - } // end namespace core } // end namespace irr diff --git a/include/irrlicht.h b/include/irrlicht.h index 706795a3..97864362 100644 --- a/include/irrlicht.h +++ b/include/irrlicht.h @@ -50,7 +50,6 @@ #include "EMessageBoxFlags.h" #include "ESceneNodeTypes.h" #include "fast_atof.h" -#include "heapsort.h" #include "IAnimatedMesh.h" #include "IAnimatedMeshSceneNode.h" #include "IAttributes.h" diff --git a/source/Irrlicht/COBJMeshFileLoader.cpp b/source/Irrlicht/COBJMeshFileLoader.cpp index cdd953b1..8746eb61 100644 --- a/source/Irrlicht/COBJMeshFileLoader.cpp +++ b/source/Irrlicht/COBJMeshFileLoader.cpp @@ -70,9 +70,9 @@ IAnimatedMesh* COBJMeshFileLoader::createMesh(io::IReadFile* file) const u32 WORD_BUFFER_LENGTH = 512; - core::array > vertexBuffer(1000); - core::array > normalsBuffer(1000); - core::array > textureCoordBuffer(1000); + core::array vertexBuffer(1000); + core::array normalsBuffer(1000); + core::array textureCoordBuffer(1000); SObjMtl * currMtl = new SObjMtl(); Materials.push_back(currMtl); diff --git a/source/Irrlicht/COGLESDriver.cpp b/source/Irrlicht/COGLESDriver.cpp index a48909e1..4ee95eab 100644 --- a/source/Irrlicht/COGLESDriver.cpp +++ b/source/Irrlicht/COGLESDriver.cpp @@ -112,12 +112,12 @@ bool COGLES1Driver::genericDriverInit(const core::dimension2d& screenSize, glPixelStorei(GL_PACK_ALIGNMENT, 1); UserClipPlane.reallocate(MaxUserClipPlanes); - UserClipPlaneEnabled.reallocate(MaxUserClipPlanes); + UserClipPlaneEnabled.resize(MaxUserClipPlanes); for (s32 i = 0; i < MaxUserClipPlanes; ++i) { UserClipPlane.push_back(core::plane3df()); - UserClipPlaneEnabled.push_back(false); + UserClipPlaneEnabled[i] = false; } for (s32 i = 0; i < ETS_COUNT; ++i) diff --git a/source/Irrlicht/COGLESDriver.h b/source/Irrlicht/COGLESDriver.h index 85e51566..2e04848e 100644 --- a/source/Irrlicht/COGLESDriver.h +++ b/source/Irrlicht/COGLESDriver.h @@ -363,7 +363,7 @@ namespace video SMaterial Material, LastMaterial; core::array UserClipPlane; - core::array UserClipPlaneEnabled; + std::vector UserClipPlaneEnabled; core::stringc VendorName; diff --git a/source/Irrlicht/CSceneManager.h b/source/Irrlicht/CSceneManager.h index dcad7279..6af151f8 100644 --- a/source/Irrlicht/CSceneManager.h +++ b/source/Irrlicht/CSceneManager.h @@ -231,6 +231,9 @@ namespace scene struct DefaultNodeEntry { + DefaultNodeEntry() + { } + DefaultNodeEntry(ISceneNode* n) : Node(n), TextureValue(0) { @@ -251,6 +254,9 @@ namespace scene //! sort on distance (center) to camera struct TransparentNodeEntry { + TransparentNodeEntry() + { } + TransparentNodeEntry(ISceneNode* n, const core::vector3df& camera) : Node(n) { diff --git a/source/Irrlicht/CSkinnedMesh.cpp b/source/Irrlicht/CSkinnedMesh.cpp index e41d6c3e..9f47f3b6 100644 --- a/source/Irrlicht/CSkinnedMesh.cpp +++ b/source/Irrlicht/CSkinnedMesh.cpp @@ -1042,7 +1042,7 @@ void CSkinnedMesh::finalize() for (i=0; i() ); + Vertices_Moved.push_back( core::array() ); Vertices_Moved[i].set_used(LocalBuffers[i]->getVertexCount()); } diff --git a/source/Irrlicht/CSkinnedMesh.h b/source/Irrlicht/CSkinnedMesh.h index 86d3a6bf..f70fc3f7 100644 --- a/source/Irrlicht/CSkinnedMesh.h +++ b/source/Irrlicht/CSkinnedMesh.h @@ -196,7 +196,9 @@ private: core::array AllJoints; core::array RootJoints; - core::array< core::array > Vertices_Moved; + // bool can't be used here because std::vector + // doesn't allow taking a reference to individual elements. + core::array< core::array > Vertices_Moved; core::aabbox3d BoundingBox;