#ifndef __IRR_ARRAY_H_INCLUDED__\r
#define __IRR_ARRAY_H_INCLUDED__\r
\r
+#include <algorithm>\r
+#include <iterator>\r
+#include <vector>\r
+\r
#include "irrTypes.h"\r
-#include "heapsort.h"\r
-#include "irrAllocator.h"\r
#include "irrMath.h"\r
\r
namespace irr\r
//! Self reallocating template array (like stl vector) with additional features.\r
/** Some features are: Heap sorting, binary search methods, easier debugging.\r
*/\r
-template <class T, typename TAlloc = irrAllocator<T> >\r
+template <class T>\r
class array\r
{\r
-\r
public:\r
+ static_assert(!std::is_same<T, bool>::value,\r
+ "irr::core::array<T> with T = bool not supported. Use std::vector instead.");\r
\r
//! Default constructor for empty array.\r
- array() : data(0), allocated(0), used(0),\r
- strategy(ALLOC_STRATEGY_DOUBLE), free_when_destroyed(true), is_sorted(true)\r
- {\r
- }\r
-\r
+ array() : m_data(), is_sorted(true)\r
+ { }\r
\r
//! Constructs an array and allocates an initial chunk of memory.\r
/** \param start_count Amount of elements to pre-allocate. */\r
- explicit array(u32 start_count) : data(0), allocated(0), used(0),\r
- strategy(ALLOC_STRATEGY_DOUBLE),\r
- free_when_destroyed(true), is_sorted(true)\r
+ explicit array(u32 start_count) : m_data(), is_sorted(true)\r
{\r
- reallocate(start_count);\r
+ m_data.reserve(start_count);\r
}\r
\r
-\r
//! Copy constructor\r
- array(const array<T, TAlloc>& other) : data(0)\r
- {\r
- *this = other;\r
- }\r
-\r
-\r
- //! Destructor.\r
- /** Frees allocated memory, if set_free_when_destroyed was not set to\r
- false by the user before. */\r
- ~array()\r
- {\r
- clear();\r
- }\r
-\r
+ array(const array<T>& other) : m_data(other.m_data), is_sorted(other.is_sorted)\r
+ { }\r
\r
//! Reallocates the array, make it bigger or smaller.\r
/** \param new_size New size of array.\r
*/\r
void reallocate(u32 new_size, bool canShrink=true)\r
{\r
- if (allocated==new_size)\r
- return;\r
- if (!canShrink && (new_size < allocated))\r
- return;\r
-\r
- T* old_data = data;\r
-\r
- data = allocator.allocate(new_size); //new T[new_size];\r
- allocated = new_size;\r
-\r
- // copy old data\r
- const s32 end = used < new_size ? used : new_size;\r
-\r
- for (s32 i=0; i<end; ++i)\r
- {\r
- // data[i] = old_data[i];\r
- allocator.construct(&data[i], old_data[i]);\r
+ size_t allocated = m_data.capacity();\r
+ if (new_size < allocated) {\r
+ if (canShrink)\r
+ m_data.resize(new_size);\r
+ } else {\r
+ m_data.reserve(new_size);\r
}\r
-\r
- // destruct old data\r
- for (u32 j=0; j<used; ++j)\r
- allocator.destruct(&old_data[j]);\r
-\r
- if (allocated < used)\r
- used = allocated;\r
-\r
- allocator.deallocate(old_data); //delete [] old_data;\r
}\r
\r
-\r
- //! set a new allocation strategy\r
- /** if the maximum size of the array is unknown, you can define how big the\r
- allocation should happen.\r
- \param newStrategy New strategy to apply to this array. */\r
- void setAllocStrategy ( eAllocStrategy newStrategy = ALLOC_STRATEGY_DOUBLE )\r
- {\r
- strategy = newStrategy;\r
- }\r
-\r
-\r
//! Adds an element at back of array.\r
/** If the array is too small to add this new element it is made bigger.\r
\param element: Element to add at the back of the array. */\r
void push_back(const T& element)\r
{\r
- insert(element, used);\r
+ m_data.push_back(element);\r
+ is_sorted = false;\r
+ }\r
+\r
+ void push_back(T&& element)\r
+ {\r
+ m_data.push_back(std::move(element));\r
+ is_sorted = false;\r
}\r
\r
\r
\param element Element to add at the back of the array. */\r
void push_front(const T& element)\r
{\r
- insert(element);\r
+ m_data.insert(m_data.begin(), element);\r
+ is_sorted = false;\r
+ }\r
+\r
+ void push_front(T&& element)\r
+ {\r
+ m_data.insert(m_data.begin(), std::move(element));\r
+ is_sorted = false;\r
}\r
\r
\r
\param index: Where position to insert the new element. */\r
void insert(const T& element, u32 index=0)\r
{\r
- _IRR_DEBUG_BREAK_IF(index>used) // access violation\r
-\r
- if (used + 1 > allocated)\r
- {\r
- // this doesn't work if the element is in the same\r
- // array. So we'll copy the element first to be sure\r
- // we'll get no data corruption\r
- const T e(element);\r
-\r
- // increase data block\r
- u32 newAlloc;\r
- switch ( strategy )\r
- {\r
- case ALLOC_STRATEGY_DOUBLE:\r
- newAlloc = used + 5 + (allocated < 500 ? used : used >> 2);\r
- break;\r
- default:\r
- case ALLOC_STRATEGY_SAFE:\r
- newAlloc = used + 1;\r
- break;\r
- }\r
- reallocate( newAlloc);\r
-\r
- // move array content and construct new element\r
- // first move end one up\r
- for (u32 i=used; i>index; --i)\r
- {\r
- if (i<used)\r
- allocator.destruct(&data[i]);\r
- allocator.construct(&data[i], data[i-1]); // data[i] = data[i-1];\r
- }\r
- // then add new element\r
- if (used > index)\r
- allocator.destruct(&data[index]);\r
- allocator.construct(&data[index], e); // data[index] = e;\r
- }\r
- else\r
- {\r
- // element inserted not at end\r
- if ( used > index )\r
- {\r
- // create one new element at the end\r
- allocator.construct(&data[used], data[used-1]);\r
-\r
- // move the rest of the array content\r
- for (u32 i=used-1; i>index; --i)\r
- {\r
- data[i] = data[i-1];\r
- }\r
- // insert the new element\r
- data[index] = element;\r
- }\r
- else\r
- {\r
- // insert the new element to the end\r
- allocator.construct(&data[index], element);\r
- }\r
- }\r
- // set to false as we don't know if we have the comparison operators\r
+ _IRR_DEBUG_BREAK_IF(index > m_data.size()) // access violation\r
+ auto pos = std::next(m_data.begin(), index);\r
+ m_data.insert(pos, element);\r
is_sorted = false;\r
- ++used;\r
}\r
\r
-\r
//! Clears the array and deletes all allocated memory.\r
void clear()\r
{\r
- if (free_when_destroyed)\r
- {\r
- for (u32 i=0; i<used; ++i)\r
- allocator.destruct(&data[i]);\r
-\r
- allocator.deallocate(data); // delete [] data;\r
- }\r
- data = 0;\r
- used = 0;\r
- allocated = 0;\r
+ // vector::clear() reduces the size to 0, but doesn't free memory.\r
+ // This swap is guaranteed to delete the allocated memory.\r
+ std::vector<T>().swap(m_data);\r
is_sorted = true;\r
}\r
\r
-\r
- //! Sets pointer to new array, using this as new workspace.\r
- /** Make sure that set_free_when_destroyed is used properly.\r
- \param newPointer: Pointer to new array of elements.\r
- \param size: Size of the new array.\r
- \param _is_sorted Flag which tells whether the new array is already\r
- sorted.\r
- \param _free_when_destroyed Sets whether the new memory area shall be\r
- freed by the array upon destruction, or if this will be up to the user\r
- application. */\r
- void set_pointer(T* newPointer, u32 size, bool _is_sorted=false, bool _free_when_destroyed=true)\r
- {\r
- clear();\r
- data = newPointer;\r
- allocated = size;\r
- used = size;\r
- is_sorted = _is_sorted;\r
- free_when_destroyed=_free_when_destroyed;\r
- }\r
-\r
//! Set (copy) data from given memory block\r
/** \param newData data to set, must have newSize elements\r
\param newSize Amount of elements in newData\r
*/\r
void set_data(const T* newData, u32 newSize, bool newDataIsSorted=false, bool canShrink=false)\r
{\r
- reallocate(newSize, canShrink);\r
- set_used(newSize);\r
- for ( u32 i=0; i<newSize; ++i)\r
- {\r
- data[i] = newData[i];\r
+ m_data.resize(newSize);\r
+ if (canShrink) {\r
+ m_data.shrink_to_fit();\r
}\r
+ std::copy(newData, newData + newSize, m_data.begin());\r
is_sorted = newDataIsSorted;\r
}\r
\r
\param size Amount of elements in otherData */\r
bool equals(const T* otherData, u32 size) const\r
{\r
- if (used != size)\r
+ if (m_data.size() != size)\r
return false;\r
-\r
- for (u32 i=0; i<size; ++i)\r
- if (data[i] != otherData[i])\r
- return false;\r
- return true;\r
+ return std::equal(m_data.begin(), m_data.end(), otherData);\r
}\r
\r
-\r
-\r
- //! Sets if the array should delete the memory it uses upon destruction.\r
- /** Also clear and set_pointer will only delete the (original) memory\r
- area if this flag is set to true, which is also the default. The\r
- methods reallocate, set_used, push_back, push_front, insert, and erase\r
- will still try to deallocate the original memory, which might cause\r
- troubles depending on the intended use of the memory area.\r
- \param f If true, the array frees the allocated memory in its\r
- destructor, otherwise not. The default is true. */\r
- void set_free_when_destroyed(bool f)\r
- {\r
- free_when_destroyed = f;\r
- }\r
-\r
-\r
//! Sets the size of the array and allocates new elements if necessary.\r
- /** Please note: This is only secure when using it with simple types,\r
- because no default constructor will be called for the added elements.\r
- \param usedNow Amount of elements now used. */\r
+ /** \param usedNow Amount of elements now used. */\r
void set_used(u32 usedNow)\r
{\r
- if (allocated < usedNow)\r
- reallocate(usedNow);\r
-\r
- used = usedNow;\r
+ m_data.resize(usedNow);\r
}\r
\r
//! Assignment operator\r
- const array<T, TAlloc>& operator=(const array<T, TAlloc>& other)\r
+ const array<T>& operator=(const array<T>& other)\r
{\r
if (this == &other)\r
return *this;\r
- strategy = other.strategy;\r
-\r
- // (TODO: we could probably avoid re-allocations of data when (allocated < other.allocated)\r
-\r
- if (data)\r
- clear();\r
-\r
- used = other.used;\r
- free_when_destroyed = true;\r
+ m_data = other.m_data;\r
is_sorted = other.is_sorted;\r
- allocated = other.allocated;\r
-\r
- if (other.allocated == 0)\r
- {\r
- data = 0;\r
- }\r
- else\r
- {\r
- data = allocator.allocate(other.allocated); // new T[other.allocated];\r
-\r
- for (u32 i=0; i<other.used; ++i)\r
- allocator.construct(&data[i], other.data[i]); // data[i] = other.data[i];\r
- }\r
+ return *this;\r
+ }\r
\r
+ array<T>& operator=(const std::vector<T> &other)\r
+ {\r
+ m_data = other;\r
+ is_sorted = false;\r
return *this;\r
}\r
\r
+ array<T>& operator=(std::vector<T> &&other)\r
+ {\r
+ m_data = std::move(other);\r
+ is_sorted = false;\r
+ return *this;\r
+ }\r
\r
//! Equality operator\r
- bool operator == (const array<T, TAlloc>& other) const\r
+ bool operator == (const array<T>& other) const\r
{\r
return equals(other.const_pointer(), other.size());\r
}\r
\r
\r
//! Inequality operator\r
- bool operator != (const array<T, TAlloc>& other) const\r
+ bool operator != (const array<T>& other) const\r
{\r
return !(*this==other);\r
}\r
//! Direct access operator\r
T& operator [](u32 index)\r
{\r
- _IRR_DEBUG_BREAK_IF(index>=used) // access violation\r
+ _IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation\r
\r
- return data[index];\r
+ return m_data[index];\r
}\r
\r
\r
//! Direct const access operator\r
const T& operator [](u32 index) const\r
{\r
- _IRR_DEBUG_BREAK_IF(index>=used) // access violation\r
+ _IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation\r
\r
- return data[index];\r
+ return m_data[index];\r
}\r
\r
\r
//! Gets last element.\r
T& getLast()\r
{\r
- _IRR_DEBUG_BREAK_IF(!used) // access violation\r
+ _IRR_DEBUG_BREAK_IF(m_data.empty()) // access violation\r
\r
- return data[used-1];\r
+ return m_data.back();\r
}\r
\r
\r
//! Gets last element\r
const T& getLast() const\r
{\r
- _IRR_DEBUG_BREAK_IF(!used) // access violation\r
+ _IRR_DEBUG_BREAK_IF(m_data.empty()) // access violation\r
\r
- return data[used-1];\r
+ return m_data.back();\r
}\r
\r
\r
/** \return Pointer to the array. */\r
T* pointer()\r
{\r
- return data;\r
+ return &m_data[0];\r
}\r
\r
\r
/** \return Pointer to the array. */\r
const T* const_pointer() const\r
{\r
- return data;\r
+ return &m_data[0];\r
}\r
\r
\r
/** \return Size of elements in the array which are actually occupied. */\r
u32 size() const\r
{\r
- return used;\r
+ return m_data.size();\r
}\r
\r
\r
allocated would be allocated_size() * sizeof(ElementTypeUsed); */\r
u32 allocated_size() const\r
{\r
- return allocated;\r
+ return m_data.capacity();\r
}\r
\r
\r
/** \return True if the array is empty false if not. */\r
bool empty() const\r
{\r
- return used == 0;\r
+ return m_data.empty();\r
}\r
\r
\r
O(n*log n) in worst case. */\r
void sort()\r
{\r
- if (!is_sorted && used>1)\r
- heapsort(data, used);\r
- is_sorted = true;\r
+ if (!is_sorted) {\r
+ std::sort(m_data.begin(), m_data.end());\r
+ is_sorted = true;\r
+ }\r
}\r
\r
\r
s32 binary_search(const T& element)\r
{\r
sort();\r
- return binary_search(element, 0, used-1);\r
+ return binary_search(element, 0, (s32)m_data.size() - 1);\r
}\r
\r
-\r
//! Performs a binary search for an element if possible, returns -1 if not found.\r
/** This method is for const arrays and so cannot call sort(), if the array is\r
not sorted then linear_search will be used instead. Potentially very slow!\r
s32 binary_search(const T& element) const\r
{\r
if (is_sorted)\r
- return binary_search(element, 0, used-1);\r
+ return binary_search(element, 0, (s32)m_data.size() - 1);\r
else\r
return linear_search(element);\r
}\r
\r
-\r
//! Performs a binary search for an element, returns -1 if not found.\r
/** \param element: Element to search for.\r
\param left First left index\r
is returned. */\r
s32 binary_search(const T& element, s32 left, s32 right) const\r
{\r
- if (!used)\r
+ if (left > right)\r
return -1;\r
-\r
- s32 m;\r
-\r
- do\r
- {\r
- m = (left+right)>>1;\r
-\r
- if (element < data[m])\r
- right = m - 1;\r
- else\r
- left = m + 1;\r
-\r
- } while((element < data[m] || data[m] < element) && left<=right);\r
- // this last line equals to:\r
- // " while((element != array[m]) && left<=right);"\r
- // but we only want to use the '<' operator.\r
- // the same in next line, it is "(element == array[m])"\r
-\r
-\r
- if (!(element < data[m]) && !(data[m] < element))\r
- return m;\r
-\r
- return -1;\r
+ auto lpos = std::next(m_data.begin(), left);\r
+ auto rpos = std::next(m_data.begin(), right);\r
+ auto it = std::lower_bound(lpos, rpos, element);\r
+ // *it = first element in [first, last) that is >= element, or last if not found.\r
+ if (*it < element || element < *it)\r
+ return -1;\r
+ return it - m_data.begin();\r
}\r
\r
\r
s32 binary_search_multi(const T& element, s32 &last)\r
{\r
sort();\r
- s32 index = binary_search(element, 0, used-1);\r
- if ( index < 0 )\r
- return index;\r
-\r
- // The search can be somewhere in the middle of the set\r
- // look linear previous and past the index\r
- last = index;\r
-\r
- while ( index > 0 && !(element < data[index - 1]) && !(data[index - 1] < element) )\r
- {\r
- index -= 1;\r
- }\r
- // look linear up\r
- while ( last < (s32) used - 1 && !(element < data[last + 1]) && !(data[last + 1] < element) )\r
- {\r
- last += 1;\r
- }\r
-\r
- return index;\r
+ auto iters = std::equal_range(m_data.begin(), m_data.end(), element);\r
+ if (iters.first == iters.second)\r
+ return -1;\r
+ last = (iters.second - m_data.begin()) - 1;\r
+ return iters.first - m_data.begin();\r
}\r
\r
\r
is returned. */\r
s32 linear_search(const T& element) const\r
{\r
- for (u32 i=0; i<used; ++i)\r
- if (element == data[i])\r
- return (s32)i;\r
-\r
- return -1;\r
+ auto it = std::find(m_data.begin(), m_data.end(), element);\r
+ if (it == m_data.end())\r
+ return -1;\r
+ return it - m_data.begin();\r
}\r
\r
\r
is returned. */\r
s32 linear_reverse_search(const T& element) const\r
{\r
- for (s32 i=used-1; i>=0; --i)\r
- if (data[i] == element)\r
- return i;\r
-\r
- return -1;\r
+ auto it = std::find(m_data.rbegin(), m_data.rend(), element);\r
+ if (it == m_data.rend())\r
+ return -1;\r
+ size_t offset = it - m_data.rbegin();\r
+ return m_data.size() - offset - 1;\r
}\r
\r
\r
\param index: Index of element to be erased. */\r
void erase(u32 index)\r
{\r
- _IRR_DEBUG_BREAK_IF(index>=used) // access violation\r
-\r
- for (u32 i=index+1; i<used; ++i)\r
- {\r
- allocator.destruct(&data[i-1]);\r
- allocator.construct(&data[i-1], data[i]); // data[i-1] = data[i];\r
- }\r
-\r
- allocator.destruct(&data[used-1]);\r
-\r
- --used;\r
+ _IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation\r
+ auto it = std::next(m_data.begin(), index);\r
+ m_data.erase(it);\r
}\r
\r
\r
\param count: Amount of elements to be erased. */\r
void erase(u32 index, s32 count)\r
{\r
- if (index>=used || count<1)\r
+ if (index >= m_data.size() || count < 1)\r
return;\r
- if (index+count>used)\r
- count = used-index;\r
-\r
- u32 i;\r
- for (i=index; i<index+count; ++i)\r
- allocator.destruct(&data[i]);\r
-\r
- for (i=index+count; i<used; ++i)\r
- {\r
- if (i-count >= index+count) // not already destructed before loop\r
- allocator.destruct(&data[i-count]);\r
-\r
- allocator.construct(&data[i-count], data[i]); // data[i-count] = data[i];\r
-\r
- if (i >= used-count) // those which are not overwritten\r
- allocator.destruct(&data[i]);\r
- }\r
-\r
- used-= count;\r
+ count = std::min(count, (s32)m_data.size() - (s32)index);\r
+ auto first = std::next(m_data.begin(), index);\r
+ auto last = std::next(first, count);\r
+ m_data.erase(first, last);\r
}\r
\r
-\r
//! Sets if the array is sorted\r
void set_sorted(bool _is_sorted)\r
{\r
/** Afterward this object will contain the content of the other object and the other\r
object will contain the content of this object.\r
\param other Swap content with this object */\r
- void swap(array<T, TAlloc>& other)\r
- {\r
- core::swap(data, other.data);\r
- core::swap(allocated, other.allocated);\r
- core::swap(used, other.used);\r
- core::swap(allocator, other.allocator); // memory is still released by the same allocator used for allocation\r
- eAllocStrategy helper_strategy(strategy); // can't use core::swap with bitfields\r
- strategy = other.strategy;\r
- other.strategy = helper_strategy;\r
- bool helper_free_when_destroyed(free_when_destroyed);\r
- free_when_destroyed = other.free_when_destroyed;\r
- other.free_when_destroyed = helper_free_when_destroyed;\r
- bool helper_is_sorted(is_sorted);\r
- is_sorted = other.is_sorted;\r
- other.is_sorted = helper_is_sorted;\r
+ void swap(array<T>& other)\r
+ {\r
+ m_data.swap(other.m_data);\r
+ std::swap(is_sorted, other.is_sorted);\r
+ }\r
+\r
+ //! Pull the contents of this array as a vector.\r
+ // The array is left empty.\r
+ std::vector<T> steal()\r
+ {\r
+ std::vector<T> ret = std::move(m_data);\r
+ m_data.clear();\r
+ is_sorted = true;\r
+ return ret;\r
}\r
\r
- typedef TAlloc allocator_type;\r
typedef T value_type;\r
typedef u32 size_type;\r
\r
private:\r
- T* data;\r
- u32 allocated;\r
- u32 used;\r
- TAlloc allocator;\r
- eAllocStrategy strategy:4;\r
- bool free_when_destroyed:1;\r
- bool is_sorted:1;\r
+ std::vector<T> m_data;\r
+ bool is_sorted;\r
};\r
\r
-\r
} // end namespace core\r
} // end namespace irr\r
\r