mirror of
https://github.com/minetest/minetest.git
synced 2024-11-24 00:23:46 +01:00
423 lines
12 KiB
C
423 lines
12 KiB
C
|
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
||
|
// This file is part of the "Irrlicht Engine" and the "irrXML" project.
|
||
|
// For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
|
||
|
|
||
|
#pragma once
|
||
|
#include <algorithm>
|
||
|
#include <iterator>
|
||
|
#include <vector>
|
||
|
|
||
|
#include "irrTypes.h"
|
||
|
#include "irrMath.h"
|
||
|
|
||
|
namespace irr
|
||
|
{
|
||
|
namespace core
|
||
|
{
|
||
|
|
||
|
//! Self reallocating template array (like stl vector) with additional features.
|
||
|
/** Some features are: Heap sorting, binary search methods, easier debugging.
|
||
|
*/
|
||
|
template <class T>
|
||
|
class array
|
||
|
{
|
||
|
public:
|
||
|
static_assert(!std::is_same<T, bool>::value,
|
||
|
"irr::core::array<T> with T = bool not supported. Use std::vector instead.");
|
||
|
|
||
|
//! Default constructor for empty array.
|
||
|
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) :
|
||
|
m_data(), is_sorted(true)
|
||
|
{
|
||
|
m_data.reserve(start_count);
|
||
|
}
|
||
|
|
||
|
//! Copy constructor
|
||
|
array(const array<T> &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.
|
||
|
\param canShrink Specifies whether the array is reallocated even if
|
||
|
enough space is available. Setting this flag to false can speed up
|
||
|
array usage, but may use more memory than required by the data.
|
||
|
*/
|
||
|
void reallocate(u32 new_size, bool canShrink = true)
|
||
|
{
|
||
|
size_t allocated = m_data.capacity();
|
||
|
if (new_size < allocated) {
|
||
|
if (canShrink)
|
||
|
m_data.resize(new_size);
|
||
|
} else {
|
||
|
m_data.reserve(new_size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Adds an element at back of array.
|
||
|
/** If the array is too small to add this new element it is made bigger.
|
||
|
\param element: Element to add at the back of the array. */
|
||
|
void push_back(const T &element)
|
||
|
{
|
||
|
m_data.push_back(element);
|
||
|
is_sorted = false;
|
||
|
}
|
||
|
|
||
|
void push_back(T &&element)
|
||
|
{
|
||
|
m_data.push_back(std::move(element));
|
||
|
is_sorted = false;
|
||
|
}
|
||
|
|
||
|
//! Adds an element at the front of the array.
|
||
|
/** If the array is to small to add this new element, the array is
|
||
|
made bigger. Please note that this is slow, because the whole array
|
||
|
needs to be copied for this.
|
||
|
\param element Element to add at the back of the array. */
|
||
|
void push_front(const T &element)
|
||
|
{
|
||
|
m_data.insert(m_data.begin(), element);
|
||
|
is_sorted = false;
|
||
|
}
|
||
|
|
||
|
void push_front(T &&element)
|
||
|
{
|
||
|
m_data.insert(m_data.begin(), std::move(element));
|
||
|
is_sorted = false;
|
||
|
}
|
||
|
|
||
|
//! Insert item into array at specified position.
|
||
|
/**
|
||
|
\param element: Element to be inserted
|
||
|
\param index: Where position to insert the new element. */
|
||
|
void insert(const T &element, u32 index = 0)
|
||
|
{
|
||
|
_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;
|
||
|
}
|
||
|
|
||
|
//! Clears the array and deletes all allocated memory.
|
||
|
void clear()
|
||
|
{
|
||
|
// vector::clear() reduces the size to 0, but doesn't free memory.
|
||
|
// This swap is guaranteed to delete the allocated memory.
|
||
|
std::vector<T>().swap(m_data);
|
||
|
is_sorted = true;
|
||
|
}
|
||
|
|
||
|
//! Set (copy) data from given memory block
|
||
|
/** \param newData data to set, must have newSize elements
|
||
|
\param newSize Amount of elements in newData
|
||
|
\param canShrink When true we reallocate the array even it can shrink.
|
||
|
May reduce memory usage, but call is more whenever size changes.
|
||
|
\param newDataIsSorted Info if you pass sorted/unsorted data
|
||
|
*/
|
||
|
void set_data(const T *newData, u32 newSize, bool newDataIsSorted = false, bool canShrink = false)
|
||
|
{
|
||
|
m_data.resize(newSize);
|
||
|
if (canShrink) {
|
||
|
m_data.shrink_to_fit();
|
||
|
}
|
||
|
std::copy(newData, newData + newSize, m_data.begin());
|
||
|
is_sorted = newDataIsSorted;
|
||
|
}
|
||
|
|
||
|
//! Compare if given data block is identical to the data in our array
|
||
|
/** Like operator ==, but without the need to create the array
|
||
|
\param otherData Address to data against which we compare, must contain size elements
|
||
|
\param size Amount of elements in otherData */
|
||
|
bool equals(const T *otherData, u32 size) const
|
||
|
{
|
||
|
if (m_data.size() != size)
|
||
|
return false;
|
||
|
return std::equal(m_data.begin(), m_data.end(), otherData);
|
||
|
}
|
||
|
|
||
|
//! Sets the size of the array and allocates new elements if necessary.
|
||
|
/** \param usedNow Amount of elements now used. */
|
||
|
void set_used(u32 usedNow)
|
||
|
{
|
||
|
m_data.resize(usedNow);
|
||
|
}
|
||
|
|
||
|
//! Assignment operator
|
||
|
const array<T> &operator=(const array<T> &other)
|
||
|
{
|
||
|
if (this == &other)
|
||
|
return *this;
|
||
|
m_data = other.m_data;
|
||
|
is_sorted = other.is_sorted;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
array<T> &operator=(const std::vector<T> &other)
|
||
|
{
|
||
|
m_data = other;
|
||
|
is_sorted = false;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
array<T> &operator=(std::vector<T> &&other)
|
||
|
{
|
||
|
m_data = std::move(other);
|
||
|
is_sorted = false;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
//! Equality operator
|
||
|
bool operator==(const array<T> &other) const
|
||
|
{
|
||
|
return equals(other.const_pointer(), other.size());
|
||
|
}
|
||
|
|
||
|
//! Inequality operator
|
||
|
bool operator!=(const array<T> &other) const
|
||
|
{
|
||
|
return !(*this == other);
|
||
|
}
|
||
|
|
||
|
//! Direct access operator
|
||
|
T &operator[](u32 index)
|
||
|
{
|
||
|
_IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation
|
||
|
|
||
|
return m_data[index];
|
||
|
}
|
||
|
|
||
|
//! Direct const access operator
|
||
|
const T &operator[](u32 index) const
|
||
|
{
|
||
|
_IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation
|
||
|
|
||
|
return m_data[index];
|
||
|
}
|
||
|
|
||
|
//! Gets last element.
|
||
|
T &getLast()
|
||
|
{
|
||
|
_IRR_DEBUG_BREAK_IF(m_data.empty()) // access violation
|
||
|
|
||
|
return m_data.back();
|
||
|
}
|
||
|
|
||
|
//! Gets last element
|
||
|
const T &getLast() const
|
||
|
{
|
||
|
_IRR_DEBUG_BREAK_IF(m_data.empty()) // access violation
|
||
|
|
||
|
return m_data.back();
|
||
|
}
|
||
|
|
||
|
//! Gets a pointer to the array.
|
||
|
/** \return Pointer to the array. */
|
||
|
T *pointer()
|
||
|
{
|
||
|
return m_data.empty() ? nullptr : &m_data[0];
|
||
|
}
|
||
|
|
||
|
//! Gets a const pointer to the array.
|
||
|
/** \return Pointer to the array. */
|
||
|
const T *const_pointer() const
|
||
|
{
|
||
|
return m_data.empty() ? nullptr : &m_data[0];
|
||
|
}
|
||
|
|
||
|
//! Get number of occupied elements of the array.
|
||
|
/** \return Size of elements in the array which are actually occupied. */
|
||
|
u32 size() const
|
||
|
{
|
||
|
return static_cast<u32>(m_data.size());
|
||
|
}
|
||
|
|
||
|
//! Get amount of memory allocated.
|
||
|
/** \return Amount of memory allocated. The amount of bytes
|
||
|
allocated would be allocated_size() * sizeof(ElementTypeUsed); */
|
||
|
u32 allocated_size() const
|
||
|
{
|
||
|
return m_data.capacity();
|
||
|
}
|
||
|
|
||
|
//! Check if array is empty.
|
||
|
/** \return True if the array is empty false if not. */
|
||
|
bool empty() const
|
||
|
{
|
||
|
return m_data.empty();
|
||
|
}
|
||
|
|
||
|
//! Sorts the array using heapsort.
|
||
|
/** There is no additional memory waste and the algorithm performs
|
||
|
O(n*log n) in worst case. */
|
||
|
void sort()
|
||
|
{
|
||
|
if (!is_sorted) {
|
||
|
std::sort(m_data.begin(), m_data.end());
|
||
|
is_sorted = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Performs a binary search for an element, returns -1 if not found.
|
||
|
/** The array will be sorted before the binary search if it is not
|
||
|
already sorted. Caution is advised! Be careful not to call this on
|
||
|
unsorted const arrays, or the slower method will be used.
|
||
|
\param element Element to search for.
|
||
|
\return Position of the searched element if it was found,
|
||
|
otherwise -1 is returned. */
|
||
|
s32 binary_search(const T &element)
|
||
|
{
|
||
|
sort();
|
||
|
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!
|
||
|
\param element Element to search for.
|
||
|
\return Position of the searched element if it was found,
|
||
|
otherwise -1 is returned. */
|
||
|
s32 binary_search(const T &element) const
|
||
|
{
|
||
|
if (is_sorted)
|
||
|
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
|
||
|
\param right Last right index.
|
||
|
\return Position of the searched element if it was found, otherwise -1
|
||
|
is returned. */
|
||
|
s32 binary_search(const T &element, s32 left, s32 right) const
|
||
|
{
|
||
|
if (left > right)
|
||
|
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 static_cast<u32>(it - m_data.begin());
|
||
|
}
|
||
|
|
||
|
//! Performs a binary search for an element, returns -1 if not found.
|
||
|
//! it is used for searching a multiset
|
||
|
/** The array will be sorted before the binary search if it is not
|
||
|
already sorted.
|
||
|
\param element Element to search for.
|
||
|
\param &last return lastIndex of equal elements
|
||
|
\return Position of the first searched element if it was found,
|
||
|
otherwise -1 is returned. */
|
||
|
s32 binary_search_multi(const T &element, s32 &last)
|
||
|
{
|
||
|
sort();
|
||
|
auto iters = std::equal_range(m_data.begin(), m_data.end(), element);
|
||
|
if (iters.first == iters.second)
|
||
|
return -1;
|
||
|
last = static_cast<s32>((iters.second - m_data.begin()) - 1);
|
||
|
return static_cast<s32>(iters.first - m_data.begin());
|
||
|
}
|
||
|
|
||
|
//! Finds an element in linear time, which is very slow.
|
||
|
/** Use binary_search for faster finding. Only works if ==operator is
|
||
|
implemented.
|
||
|
\param element Element to search for.
|
||
|
\return Position of the searched element if it was found, otherwise -1
|
||
|
is returned. */
|
||
|
s32 linear_search(const T &element) const
|
||
|
{
|
||
|
auto it = std::find(m_data.begin(), m_data.end(), element);
|
||
|
if (it == m_data.end())
|
||
|
return -1;
|
||
|
return static_cast<u32>(it - m_data.begin());
|
||
|
}
|
||
|
|
||
|
//! Finds an element in linear time, which is very slow.
|
||
|
/** Use binary_search for faster finding. Only works if ==operator is
|
||
|
implemented.
|
||
|
\param element: Element to search for.
|
||
|
\return Position of the searched element if it was found, otherwise -1
|
||
|
is returned. */
|
||
|
s32 linear_reverse_search(const T &element) const
|
||
|
{
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
//! Erases an element from the array.
|
||
|
/** May be slow, because all elements following after the erased
|
||
|
element have to be copied.
|
||
|
\param index: Index of element to be erased. */
|
||
|
void erase(u32 index)
|
||
|
{
|
||
|
_IRR_DEBUG_BREAK_IF(index >= m_data.size()) // access violation
|
||
|
auto it = std::next(m_data.begin(), index);
|
||
|
m_data.erase(it);
|
||
|
}
|
||
|
|
||
|
//! Erases some elements from the array.
|
||
|
/** May be slow, because all elements following after the erased
|
||
|
element have to be copied.
|
||
|
\param index: Index of the first element to be erased.
|
||
|
\param count: Amount of elements to be erased. */
|
||
|
void erase(u32 index, s32 count)
|
||
|
{
|
||
|
if (index >= m_data.size() || count < 1)
|
||
|
return;
|
||
|
count = core::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)
|
||
|
{
|
||
|
is_sorted = _is_sorted;
|
||
|
}
|
||
|
|
||
|
//! Swap the content of this array container with the content of another array
|
||
|
/** 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<T> &other)
|
||
|
{
|
||
|
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<T> steal()
|
||
|
{
|
||
|
std::vector<T> ret = std::move(m_data);
|
||
|
m_data.clear();
|
||
|
is_sorted = true;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
typedef T value_type;
|
||
|
typedef u32 size_type;
|
||
|
|
||
|
private:
|
||
|
std::vector<T> m_data;
|
||
|
bool is_sorted;
|
||
|
};
|
||
|
|
||
|
} // end namespace core
|
||
|
} // end namespace irr
|