1467 lines
44 KiB
C++
1467 lines
44 KiB
C++
/**
|
|
\file Array.h
|
|
|
|
\maintainer Morgan McGuire, graphics3d.com
|
|
\cite Portions written by Aaron Orenstein, a@orenstein.name
|
|
|
|
\created 2001-03-11
|
|
\edited 2013-01-28
|
|
|
|
Copyright 2000-2012, Morgan McGuire, http://graphics.cs.williams.edu
|
|
All rights reserved.
|
|
*/
|
|
|
|
#ifndef G3D_Array_h
|
|
#define G3D_Array_h
|
|
|
|
#include "G3D/platform.h"
|
|
#include "G3D/debug.h"
|
|
#include "G3D/MemoryManager.h"
|
|
#include "G3D/System.h"
|
|
#ifdef G3D_DEBUG
|
|
// For formatting error messages
|
|
# include "G3D/format.h"
|
|
#endif
|
|
#include <vector>
|
|
#include <algorithm>
|
|
|
|
#ifdef _MSC_VER
|
|
# include <new>
|
|
|
|
# pragma warning (push)
|
|
// debug information too long
|
|
# pragma warning( disable : 4312)
|
|
# pragma warning( disable : 4786)
|
|
#endif
|
|
|
|
|
|
namespace G3D {
|
|
|
|
/**
|
|
Constant for passing to Array::resize
|
|
*/
|
|
const bool DONT_SHRINK_UNDERLYING_ARRAY = false;
|
|
|
|
/** Constant for Array::sort */
|
|
const int SORT_INCREASING = 1;
|
|
/** Constant for Array::sort */
|
|
const int SORT_DECREASING = -1;
|
|
|
|
|
|
|
|
/**
|
|
\brief Dynamic 1D array tuned for performance.
|
|
|
|
Objects must have a default constructor (constructor that
|
|
takes no arguments) in order to be used with this template.
|
|
You will get the error "no appropriate default constructor found"
|
|
if they do not.
|
|
|
|
Do not use with objects that overload placement <code>operator new</code>,
|
|
since the speed of Array is partly due to pooled allocation.
|
|
|
|
Array is highly optimized compared to std::vector.
|
|
Array operations are less expensive than on std::vector and for large
|
|
amounts of data, Array consumes only 1.5x the total size of the
|
|
data, while std::vector consumes 2.0x. The default
|
|
array takes up zero heap space. The first resize (or append)
|
|
operation grows it to a reasonable internal size so it is efficient
|
|
to append to small arrays.
|
|
|
|
Then Array needs to copy
|
|
data internally on a resize operation it correctly invokes copy
|
|
constructors of the elements (the MSVC6 implementation of
|
|
std::vector uses realloc, which can create memory leaks for classes
|
|
containing references and pointers). Array provides a guaranteed
|
|
safe way to access the underlying data as a flat C array --
|
|
Array::getCArray. Although (T*)std::vector::begin() can be used for
|
|
this purpose, it is not guaranteed to succeed on all platforms.
|
|
|
|
To serialize an array, see G3D::serialize.
|
|
|
|
The template parameter MIN_ELEMENTS indicates the smallest number of
|
|
elements that will be allocated. The default of 10 is designed to avoid
|
|
the overhead of repeatedly allocating the array as it grows from 1, to 2, and so on.
|
|
If you are creating a lot of small Arrays, however, you may want to set this smaller
|
|
to reduce the memory cost. Once the array has been allocated, it will never
|
|
deallocate the underlying array unless MIN_ELEMENTS is set to 0, MIN_BYTES is 0, and the array
|
|
is empty.
|
|
|
|
Do not subclass an Array.
|
|
|
|
\sa G3D::SmallArray
|
|
*/
|
|
template <class T, size_t MIN_ELEMENTS = 10>
|
|
class Array {
|
|
|
|
private:
|
|
/** Once the array has been allocated, it will never deallocate the underlying
|
|
array unless MIN_ELEMENTS is set to 0, MIN_BYTES is 0, and the array is empty. */
|
|
static const size_t MIN_BYTES = 32;
|
|
|
|
/** 0...num-1 are initialized elements, num...numAllocated-1 are not */
|
|
T* data;
|
|
|
|
size_t num;
|
|
size_t numAllocated;
|
|
|
|
MemoryManager::Ref m_memoryManager;
|
|
|
|
/** \param n Number of elements
|
|
*/
|
|
void init(size_t n, const MemoryManager::Ref& m) {
|
|
m_memoryManager = m;
|
|
this->num = 0;
|
|
this->numAllocated = 0;
|
|
data = NULL;
|
|
if (n > 0) {
|
|
resize(n);
|
|
} else {
|
|
data = NULL;
|
|
}
|
|
}
|
|
|
|
void _copy(const Array &other) {
|
|
init(other.num, MemoryManager::create());
|
|
for (size_t i = 0; i < num; ++i) {
|
|
data[i] = other.data[i];
|
|
}
|
|
}
|
|
|
|
/**
|
|
Returns true iff address points to an element of this array.
|
|
Used by append.
|
|
*/
|
|
inline bool inArray(const T* address) {
|
|
return (address >= data) && (address < data + num);
|
|
}
|
|
|
|
|
|
/** Only compiled if you use the sort procedure. */
|
|
static bool __cdecl compareGT(const T& a, const T& b) {
|
|
return a > b;
|
|
}
|
|
|
|
|
|
/**
|
|
Allocates a new array of size numAllocated (not a parameter to the method)
|
|
and then copies at most oldNum elements from the old array to it. Destructors are
|
|
called for oldNum elements of the old array.
|
|
*/
|
|
void realloc(size_t oldNum) {
|
|
T* oldData = data;
|
|
|
|
// The allocation is separate from the constructor invocation because we don't want
|
|
// to pay for the cost of constructors until the newly allocated
|
|
// elements are actually revealed to the application. They
|
|
// will be constructed in the resize() method.
|
|
|
|
data = (T*)m_memoryManager->alloc(sizeof(T) * numAllocated);
|
|
alwaysAssertM(data, "Memory manager returned NULL: out of memory?");
|
|
|
|
// Call the copy constructors
|
|
{const size_t N = G3D::min(oldNum, numAllocated);
|
|
const T* end = data + N;
|
|
T* oldPtr = oldData;
|
|
for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) {
|
|
|
|
// Use placement new to invoke the constructor at the location
|
|
// that we determined. Use the copy constructor to make the assignment.
|
|
const T* constructed = new (ptr) T(*oldPtr);
|
|
|
|
(void)constructed;
|
|
debugAssertM(constructed == ptr,
|
|
"new returned a different address than the one provided by Array.");
|
|
}}
|
|
|
|
// Call destructors on the old array (if there is no destructor, this will compile away)
|
|
{const T* end = oldData + oldNum;
|
|
for (T* ptr = oldData; ptr < end; ++ptr) {
|
|
ptr->~T();
|
|
}}
|
|
|
|
m_memoryManager->free(oldData);
|
|
}
|
|
|
|
public:
|
|
/**
|
|
Assignment operator. Will be private in a future release because this is slow and can be invoked by accident by novice C++ programmers.
|
|
If you really want to copy an Array, use the explicit copy constructor.
|
|
*/
|
|
Array& operator=(const Array& other) {
|
|
resize(other.num);
|
|
for (int i = 0; i < (int)num; ++i) {
|
|
data[i] = other[i];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Array& operator=(const std::vector<T>& other) {
|
|
resize(other.size());
|
|
for (size_t i = 0; i < num; ++i) {
|
|
data[i] = other[i];
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
public:
|
|
|
|
/**
|
|
G3D C++ STL style iterator variable. Call begin() to get
|
|
the first iterator, pre-increment (++i) the iterator to get to
|
|
the next value. Use dereference (*i) to access the element.
|
|
*/
|
|
typedef T* Iterator;
|
|
/** G3D C++ STL style const iterator in same style as Iterator. */
|
|
typedef const T* ConstIterator;
|
|
|
|
/** stl porting compatibility helper */
|
|
typedef Iterator iterator;
|
|
/** stl porting compatibility helper */
|
|
typedef ConstIterator const_iterator;
|
|
/** stl porting compatibility helper */
|
|
typedef T value_type;
|
|
/** stl porting compatibility helper */
|
|
typedef int size_type;
|
|
/** stl porting compatibility helper */
|
|
typedef int difference_type;
|
|
|
|
/**
|
|
C++ STL style iterator method. Returns the first iterator element.
|
|
Do not change the size of the array while iterating.
|
|
*/
|
|
Iterator begin() {
|
|
return data;
|
|
}
|
|
|
|
ConstIterator begin() const {
|
|
return data;
|
|
}
|
|
/**
|
|
C++ STL style iterator method. Returns one after the last valid iterator
|
|
element.
|
|
*/
|
|
ConstIterator end() const {
|
|
return data + num;
|
|
}
|
|
|
|
Iterator end() {
|
|
return data + num;
|
|
}
|
|
|
|
/**
|
|
The array returned is only valid until the next append() or resize call, or
|
|
the Array is deallocated.
|
|
*/
|
|
T* getCArray() {
|
|
return data;
|
|
}
|
|
|
|
/** Exchanges all data between the two arrays, which are required to have a common MemoryManager.
|
|
This is a convenient
|
|
way to avoid large array copies when handing off data without involving reference counting
|
|
or manual memory management. Beware that pointers or references into the arrays will
|
|
access memory in the <i>other</i> array after the swap. */
|
|
static void swap(Array<T, MIN_ELEMENTS>& a, Array<T, MIN_ELEMENTS>& b) {
|
|
alwaysAssertM(a.memoryManager() == b.memoryManager(), "The arrays are required to have the same memory manager");
|
|
std::swap(a.data, b.data);
|
|
std::swap(a.num, b.num);
|
|
std::swap(a.numAllocated, b.numAllocated);
|
|
}
|
|
|
|
/**
|
|
The array returned is only valid until the next append() or resize call, or
|
|
the Array is deallocated.
|
|
*/
|
|
const T* getCArray() const {
|
|
return data;
|
|
}
|
|
|
|
/** Creates a zero length array (no heap allocation occurs until resize). */
|
|
Array() : num(0) {
|
|
init(0, MemoryManager::create());
|
|
}
|
|
|
|
|
|
/** Creates an array containing v0. */
|
|
explicit Array(const T& v0) {
|
|
init(1, MemoryManager::create());
|
|
(*this)[0] = v0;
|
|
}
|
|
|
|
/** Creates an array containing v0 and v1. */
|
|
Array(const T& v0, const T& v1) {
|
|
init(2, MemoryManager::create());
|
|
(*this)[0] = v0;
|
|
(*this)[1] = v1;
|
|
}
|
|
|
|
/** Creates an array containing v0...v2. */
|
|
Array(const T& v0, const T& v1, const T& v2) {
|
|
init(3, MemoryManager::create());
|
|
(*this)[0] = v0;
|
|
(*this)[1] = v1;
|
|
(*this)[2] = v2;
|
|
}
|
|
|
|
/** Creates an array containing v0...v3. */
|
|
Array(const T& v0, const T& v1, const T& v2, const T& v3) {
|
|
init(4, MemoryManager::create());
|
|
(*this)[0] = v0;
|
|
(*this)[1] = v1;
|
|
(*this)[2] = v2;
|
|
(*this)[3] = v3;
|
|
}
|
|
|
|
/** Creates an array containing v0...v4. */
|
|
Array(const T& v0, const T& v1, const T& v2, const T& v3, const T& v4) {
|
|
init(5, MemoryManager::create());
|
|
(*this)[0] = v0;
|
|
(*this)[1] = v1;
|
|
(*this)[2] = v2;
|
|
(*this)[3] = v3;
|
|
(*this)[4] = v4;
|
|
}
|
|
|
|
|
|
/**
|
|
Copy constructor. Copying arrays is slow...perhaps you want to pass a reference or a pointer instead?
|
|
*/
|
|
//TODO: patch rest of the API to prevent returning Arrays by value, then make explicit
|
|
Array(const Array& other) : num(0) {
|
|
_copy(other);
|
|
}
|
|
|
|
explicit Array(const std::vector<T>& other) : num(0), data(NULL) {
|
|
*this = other;
|
|
}
|
|
|
|
|
|
/* Sets this to hold the same contents as other, with num = numAllocated (no unused allocated space) */
|
|
void copyFrom(const Array<T>& other) {
|
|
resize(0);
|
|
append(other);
|
|
}
|
|
|
|
|
|
/** Resizes this to match the size of \a other and then copies the data from other using memcpy. This is only safe for POD types */
|
|
void copyPOD(const Array<T>& other) {
|
|
if (numAllocated < other.num) {
|
|
m_memoryManager->free(data);
|
|
data = NULL;
|
|
if (other.data) {
|
|
data = (T*)m_memoryManager->alloc(sizeof(T) * other.num);
|
|
}
|
|
numAllocated = other.num;
|
|
}
|
|
|
|
num = other.num;
|
|
if (other.data && (num > 0)) {
|
|
System::memcpy(data, other.data, sizeof(T) * num);
|
|
}
|
|
}
|
|
|
|
/** Resizes this to just barely match the size of \a other + itself and then copies the data to the end of the array from other using memcpy.
|
|
This is only safe for POD types */
|
|
void appendPOD(const Array<T>& other) {
|
|
const size_t oldSize = num;
|
|
num += other.num;
|
|
if (numAllocated < num) {
|
|
alwaysAssertM(other.data, "non-zero array with no allocated space");
|
|
T* old = data;
|
|
data = (T*)m_memoryManager->alloc(sizeof(T) * num);
|
|
System::memcpy(data, old, sizeof(T) * oldSize);
|
|
m_memoryManager->free(old);
|
|
numAllocated = num;
|
|
}
|
|
if (other.data) {
|
|
System::memcpy((data + oldSize), other.data, sizeof(T) * other.num);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Destructor does not delete() the objects if T is a pointer type
|
|
(e.g. T = int*) instead, it deletes the <B>pointers themselves</B> and
|
|
leaves the objects. Call deleteAll if you want to dealocate
|
|
the objects referenced. Do not call deleteAll if <CODE>T</CODE> is not a pointer
|
|
type (e.g. do call Array<Foo*>::deleteAll, do <B>not</B> call Array<Foo>::deleteAll).
|
|
*/
|
|
~Array() {
|
|
// Invoke the destructors on the elements
|
|
for (size_t i = 0; i < num; ++i) {
|
|
(data + i)->~T();
|
|
}
|
|
|
|
m_memoryManager->free(data);
|
|
// Set to 0 in case this Array is global and gets referenced during app exit
|
|
data = NULL;
|
|
num = 0;
|
|
numAllocated = 0;
|
|
}
|
|
|
|
/**
|
|
Removes all elements. Use resize(0, false) or fastClear if you want to
|
|
remove all elements without deallocating the underlying array
|
|
so that future append() calls will be faster.
|
|
*/
|
|
void clear(bool shrink = true) {
|
|
resize(0, shrink);
|
|
}
|
|
|
|
void clearAndSetMemoryManager(const MemoryManager::Ref& m) {
|
|
clear();
|
|
debugAssert(data == NULL);
|
|
m_memoryManager = m;
|
|
}
|
|
|
|
/** resize(0, false)
|
|
@deprecated*/
|
|
void fastClear() {
|
|
clear(false);
|
|
}
|
|
|
|
inline MemoryManager::Ref memoryManager() const {
|
|
return m_memoryManager;
|
|
}
|
|
|
|
/**
|
|
Number of elements in the array.
|
|
*/
|
|
inline int size() const {
|
|
return (int)num;
|
|
}
|
|
|
|
/**
|
|
Number of elements in the array. (Same as size; this is just
|
|
here for convenience).
|
|
*/
|
|
inline int length() const {
|
|
return size();
|
|
}
|
|
|
|
/**
|
|
Swaps element index with the last element in the array then
|
|
shrinks the array by one.
|
|
*/
|
|
void fastRemove(int index, bool shrinkIfNecessary = false) {
|
|
debugAssert(index < (int)num);
|
|
data[index] = data[num - 1];
|
|
resize(size() - 1, shrinkIfNecessary);
|
|
}
|
|
|
|
|
|
/**
|
|
Inserts at the specified index and shifts all other elements up by one.
|
|
*/
|
|
void insert(int n, const T& value) {
|
|
// Add space for the extra element
|
|
resize(num + 1, false);
|
|
|
|
for (size_t i = (size_t)(num - 1); i > (size_t)n; --i) {
|
|
data[i] = data[i - 1];
|
|
}
|
|
data[n] = value;
|
|
}
|
|
|
|
/** Sets all elements currently in the array to \param value */
|
|
void setAll(const T& value) {
|
|
for (size_t i = 0; i < num; ++i) {
|
|
data[i] = value;
|
|
}
|
|
}
|
|
|
|
/** Resize this array to consume exactly the capacity required by its size.
|
|
\sa clear, resize, capacity, size
|
|
*/
|
|
void trimToSize() {
|
|
if (size() != capacity()) {
|
|
size_t oldNum = numAllocated;
|
|
numAllocated = size();
|
|
realloc(oldNum);
|
|
}
|
|
}
|
|
|
|
/** @param shrinkIfNecessary if false, memory will never be
|
|
reallocated when the array shrinks. This makes resizing much
|
|
faster but can waste memory. Default = true.
|
|
|
|
\sa clear, trimToSize
|
|
*/
|
|
void resize(size_t n, bool shrinkIfNecessary = true) {
|
|
alwaysAssertM(n < 0xFFFFFFFF, "This implementation does not support arrays with more than 2^32 elements, although the size in memory may be larger.");
|
|
if (num == n) {
|
|
return;
|
|
}
|
|
|
|
size_t oldNum = num;
|
|
num = n;
|
|
|
|
// Call the destructors on newly hidden elements if there are any
|
|
for (size_t i = num; i < oldNum; ++i) {
|
|
(data + i)->~T();
|
|
}
|
|
|
|
// Once allocated, always maintain MIN_ELEMENTS elements or 32 bytes, whichever is higher.
|
|
const size_t minSize = G3D::max(MIN_ELEMENTS, (size_t)(MIN_BYTES / sizeof(T)));
|
|
|
|
if ((MIN_ELEMENTS == 0) && (MIN_BYTES == 0) && (n == 0) && shrinkIfNecessary) {
|
|
// Deallocate the array completely
|
|
numAllocated = 0;
|
|
m_memoryManager->free(data);
|
|
data = NULL;
|
|
return;
|
|
}
|
|
|
|
if (num > numAllocated) {
|
|
// Grow the underlying array
|
|
|
|
if (numAllocated == 0) {
|
|
// First allocation; grow to exactly the size requested to avoid wasting space.
|
|
numAllocated = n;
|
|
debugAssert(oldNum == 0);
|
|
realloc(oldNum);
|
|
} else {
|
|
|
|
if (num < minSize) {
|
|
// Grow to at least the minimum size
|
|
numAllocated = minSize;
|
|
|
|
} else {
|
|
|
|
// Increase the underlying size of the array. Grow aggressively
|
|
// up to 64k, less aggressively up to 400k, and then grow relatively
|
|
// slowly (1.5x per resize) to avoid excessive space consumption.
|
|
//
|
|
// These numbers are tweaked according to performance tests.
|
|
|
|
double growFactor = 3.0f;
|
|
|
|
size_t oldSizeBytes = numAllocated * sizeof(T);
|
|
if (oldSizeBytes > 10000000) {
|
|
// Conserve memory more tightly above 10 MB
|
|
growFactor = 1.2f;
|
|
} else if (oldSizeBytes > 400000) {
|
|
// Avoid bloat above 400k
|
|
growFactor = 1.5f;
|
|
} else if (oldSizeBytes > 64000) {
|
|
// This is what std:: uses at all times
|
|
growFactor = 2.0f;
|
|
}
|
|
|
|
numAllocated = (num - numAllocated) + (size_t)(numAllocated * growFactor);
|
|
|
|
if (numAllocated < minSize) {
|
|
numAllocated = minSize;
|
|
}
|
|
}
|
|
|
|
realloc(oldNum);
|
|
}
|
|
|
|
} else if ((num <= numAllocated / 3) && shrinkIfNecessary && (num > minSize)) {
|
|
// Shrink the underlying array
|
|
|
|
// Only copy over old elements that still remain after resizing
|
|
// (destructors were called for others if we're shrinking)
|
|
realloc(min(num, oldNum));
|
|
|
|
}
|
|
|
|
// Call the constructors on newly revealed elements.
|
|
// Do not use parens because we don't want the intializer
|
|
// invoked for POD types.
|
|
for (size_t i = oldNum; i < num; ++i) {
|
|
new (data + i) T;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Add an element to the end of the array. Will not shrink the underlying array
|
|
under any circumstances. It is safe to append an element that is already
|
|
in the array.
|
|
*/
|
|
inline void append(const T& value) {
|
|
|
|
if (num < numAllocated) {
|
|
// This is a simple situation; just stick it in the next free slot using
|
|
// the copy constructor.
|
|
new (data + num) T(value);
|
|
++num;
|
|
} else if (inArray(&value)) {
|
|
// The value was in the original array; resizing
|
|
// is dangerous because it may move the value
|
|
// we have a reference to.
|
|
T tmp = value;
|
|
append(tmp);
|
|
} else {
|
|
// Here we run the empty initializer where we don't have to, but
|
|
// this simplifies the computation.
|
|
resize(num + 1, DONT_SHRINK_UNDERLYING_ARRAY);
|
|
data[num - 1] = value;
|
|
}
|
|
}
|
|
|
|
|
|
inline void append(const T& v1, const T& v2) {
|
|
if (inArray(&v1) || inArray(&v2)) {
|
|
// Copy into temporaries so that the references won't break when
|
|
// the array resizes.
|
|
T t1 = v1;
|
|
T t2 = v2;
|
|
append(t1, t2);
|
|
} else if (num + 1 < numAllocated) {
|
|
// This is a simple situation; just stick it in the next free slot using
|
|
// the copy constructor.
|
|
new (data + num) T(v1);
|
|
new (data + num + 1) T(v2);
|
|
num += 2;
|
|
} else {
|
|
// Resize the array. Note that neither value is already in the array.
|
|
resize(num + 2, DONT_SHRINK_UNDERLYING_ARRAY);
|
|
data[num - 2] = v1;
|
|
data[num - 1] = v2;
|
|
}
|
|
}
|
|
|
|
|
|
inline void append(const T& v1, const T& v2, const T& v3) {
|
|
if (inArray(&v1) || inArray(&v2) || inArray(&v3)) {
|
|
T t1 = v1;
|
|
T t2 = v2;
|
|
T t3 = v3;
|
|
append(t1, t2, t3);
|
|
} else if (num + 2 < numAllocated) {
|
|
// This is a simple situation; just stick it in the next free slot using
|
|
// the copy constructor.
|
|
new (data + num) T(v1);
|
|
new (data + num + 1) T(v2);
|
|
new (data + num + 2) T(v3);
|
|
num += 3;
|
|
} else {
|
|
resize(num + 3, DONT_SHRINK_UNDERLYING_ARRAY);
|
|
data[num - 3] = v1;
|
|
data[num - 2] = v2;
|
|
data[num - 1] = v3;
|
|
}
|
|
}
|
|
|
|
|
|
inline void append(const T& v1, const T& v2, const T& v3, const T& v4) {
|
|
if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4)) {
|
|
T t1 = v1;
|
|
T t2 = v2;
|
|
T t3 = v3;
|
|
T t4 = v4;
|
|
append(t1, t2, t3, t4);
|
|
} else if (num + 3 < numAllocated) {
|
|
// This is a simple situation; just stick it in the next free slot using
|
|
// the copy constructor.
|
|
new (data + num) T(v1);
|
|
new (data + num + 1) T(v2);
|
|
new (data + num + 2) T(v3);
|
|
new (data + num + 3) T(v4);
|
|
num += 4;
|
|
} else {
|
|
resize(num + 4, DONT_SHRINK_UNDERLYING_ARRAY);
|
|
data[num - 4] = v1;
|
|
data[num - 3] = v2;
|
|
data[num - 2] = v3;
|
|
data[num - 1] = v4;
|
|
}
|
|
}
|
|
|
|
void append(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5) {
|
|
if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4) || inArray(&v5)) {
|
|
T t1 = v1;
|
|
T t2 = v2;
|
|
T t3 = v3;
|
|
T t4 = v4;
|
|
T t5 = v5;
|
|
append(t1, t2, t3, t4, t5);
|
|
} else if (num + 4 < numAllocated) {
|
|
// This is a simple situation; just stick it in the next free slot using
|
|
// the copy constructor.
|
|
new (data + num) T(v1);
|
|
new (data + num + 1) T(v2);
|
|
new (data + num + 2) T(v3);
|
|
new (data + num + 3) T(v4);
|
|
new (data + num + 4) T(v5);
|
|
num += 5;
|
|
} else {
|
|
resize(num + 5, DONT_SHRINK_UNDERLYING_ARRAY);
|
|
data[num - 5] = v1;
|
|
data[num - 4] = v2;
|
|
data[num - 3] = v3;
|
|
data[num - 2] = v4;
|
|
data[num - 1] = v5;
|
|
}
|
|
}
|
|
|
|
void append(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6) {
|
|
if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4) || inArray(&v5) || inArray(&v6)) {
|
|
T t1 = v1;
|
|
T t2 = v2;
|
|
T t3 = v3;
|
|
T t4 = v4;
|
|
T t5 = v5;
|
|
T t6 = v6;
|
|
append(t1, t2, t3, t4, t5, t6);
|
|
} else if (num + 5 < numAllocated) {
|
|
// This is a simple situation; just stick it in the next free slot using
|
|
// the copy constructor.
|
|
new (data + num) T(v1);
|
|
new (data + num + 1) T(v2);
|
|
new (data + num + 2) T(v3);
|
|
new (data + num + 3) T(v4);
|
|
new (data + num + 4) T(v5);
|
|
new (data + num + 5) T(v6);
|
|
num += 6;
|
|
} else {
|
|
resize(num + 6, DONT_SHRINK_UNDERLYING_ARRAY);
|
|
data[num - 6] = v1;
|
|
data[num - 5] = v2;
|
|
data[num - 4] = v3;
|
|
data[num - 3] = v4;
|
|
data[num - 2] = v5;
|
|
data[num - 1] = v6;
|
|
}
|
|
}
|
|
/**
|
|
Returns true if the given element is in the array.
|
|
*/
|
|
bool contains(const T& e) const {
|
|
for (int i = 0; i < size(); ++i) {
|
|
if ((*this)[i] == e) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
Append the elements of array. Cannot be called with this array
|
|
as an argument.
|
|
*/
|
|
void append(const Array<T>& array) {
|
|
debugAssert(this != &array);
|
|
size_t oldNum = num;
|
|
size_t arrayLength = array.length();
|
|
|
|
resize(num + arrayLength, false);
|
|
|
|
for (size_t i = 0; i < arrayLength; ++i) {
|
|
data[oldNum + i] = array.data[i];
|
|
}
|
|
}
|
|
|
|
/**
|
|
Pushes a new element onto the end and returns its address.
|
|
This is the same as A.resize(A.size() + 1, false); A.last()
|
|
*/
|
|
inline T& next() {
|
|
resize(num + 1, false);
|
|
return last();
|
|
}
|
|
|
|
/**
|
|
Pushes an element onto the end (appends)
|
|
*/
|
|
inline void push(const T& value) {
|
|
append(value);
|
|
}
|
|
|
|
inline void push(const Array<T>& array) {
|
|
append(array);
|
|
}
|
|
|
|
/** Alias to provide std::vector compatibility */
|
|
inline void push_back(const T& v) {
|
|
push(v);
|
|
}
|
|
|
|
/** "The member function removes the last element of the controlled sequence, which must be non-empty."
|
|
For compatibility with std::vector. */
|
|
inline void pop_back() {
|
|
pop();
|
|
}
|
|
|
|
/**
|
|
"The member function returns the storage currently allocated to hold the controlled
|
|
sequence, a value at least as large as size()"
|
|
For compatibility with std::vector.
|
|
*/
|
|
int capacity() const {
|
|
return (int)numAllocated;
|
|
}
|
|
|
|
/**
|
|
"The member function returns a reference to the first element of the controlled sequence,
|
|
which must be non-empty."
|
|
For compatibility with std::vector.
|
|
*/
|
|
T& front() {
|
|
return (*this)[0];
|
|
}
|
|
|
|
/**
|
|
"The member function returns a reference to the first element of the controlled sequence,
|
|
which must be non-empty."
|
|
For compatibility with std::vector.
|
|
*/
|
|
const T& front() const {
|
|
return (*this)[0];
|
|
}
|
|
|
|
/**
|
|
"The member function returns a reference to the last element of the controlled sequence,
|
|
which must be non-empty."
|
|
For compatibility with std::vector.
|
|
*/
|
|
T& back() {
|
|
return (*this)[size()-1];
|
|
}
|
|
|
|
/**
|
|
"The member function returns a reference to the last element of the controlled sequence,
|
|
which must be non-empty."
|
|
For compatibility with std::vector.
|
|
*/
|
|
const T& back() const {
|
|
return (*this)[size()-1];
|
|
}
|
|
|
|
/**
|
|
Removes the last element and returns it. By default, shrinks the underlying array.
|
|
*/
|
|
inline T pop(bool shrinkUnderlyingArrayIfNecessary = true) {
|
|
debugAssert(num > 0);
|
|
T temp = data[num - 1];
|
|
resize(num - 1, shrinkUnderlyingArrayIfNecessary);
|
|
return temp;
|
|
}
|
|
|
|
/** Pops the last element and discards it without returning anything. Faster than pop.
|
|
By default, does not shrink the underlying array.*/
|
|
inline void popDiscard(bool shrinkUnderlyingArrayIfNecessary = false) {
|
|
debugAssert(num > 0);
|
|
resize(num - 1, shrinkUnderlyingArrayIfNecessary);
|
|
}
|
|
|
|
|
|
/**
|
|
"The member function swaps the controlled sequences between *this and str."
|
|
Note that this is slower than the optimal std implementation.
|
|
|
|
For compatibility with std::vector.
|
|
*/
|
|
void swap(Array<T>& str) {
|
|
Array<T> temp = str;
|
|
str = *this;
|
|
*this = temp;
|
|
}
|
|
|
|
|
|
/**
|
|
Performs bounds checks in debug mode
|
|
*/
|
|
inline T& operator[](int n) {
|
|
debugAssertM((n >= 0) && (n < (int)num),
|
|
format("Array index out of bounds. n = %d, size() = %d", (int)n, (int)num));
|
|
debugAssert(data!=NULL);
|
|
return data[n];
|
|
}
|
|
|
|
inline T& operator[](uint32 n) {
|
|
debugAssertM(n < (uint32)num, format("Array index out of bounds. n = %d, size() = %d",
|
|
(int)n, (int)num));
|
|
return data[n];
|
|
}
|
|
|
|
inline T& operator[](uint64 n) {
|
|
debugAssertM(n < (uint64)num, format("Array index out of bounds. n = %d, size() = %d", (int)n, (int)num));
|
|
return data[n];
|
|
}
|
|
|
|
/**
|
|
Performs bounds checks in debug mode
|
|
*/
|
|
inline const T& operator[](int n) const {
|
|
debugAssert((n >= 0) && (n < (int)num));
|
|
debugAssert(data != NULL);
|
|
return data[n];
|
|
}
|
|
|
|
inline const T& operator[](uint32 n) const {
|
|
debugAssert((n < (uint32)num));
|
|
debugAssert(data!=NULL);
|
|
return data[n];
|
|
}
|
|
|
|
inline const T& operator[](uint64 n) const {
|
|
debugAssert((n < (uint64)num));
|
|
debugAssert(data!=NULL);
|
|
return data[n];
|
|
}
|
|
|
|
inline T& randomElement() {
|
|
debugAssert(num > 0);
|
|
debugAssert(data!=NULL);
|
|
return data[iRandom(0, (int)num - 1)];
|
|
}
|
|
|
|
inline const T& randomElement() const {
|
|
debugAssert(num > 0);
|
|
debugAssert(data!=NULL);
|
|
return data[iRandom(0, num - 1)];
|
|
}
|
|
|
|
/**
|
|
Returns the last element, performing a check in
|
|
debug mode that there is at least one element.
|
|
*/
|
|
inline const T& last() const {
|
|
debugAssert(num > 0);
|
|
debugAssert(data!=NULL);
|
|
return data[num - 1];
|
|
}
|
|
|
|
/** Returns element lastIndex() */
|
|
inline T& last() {
|
|
debugAssert(num > 0);
|
|
debugAssert(data!=NULL);
|
|
return data[num - 1];
|
|
}
|
|
|
|
/** Returns <i>size() - 1</i> */
|
|
inline int lastIndex() const {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return num - 1;
|
|
}
|
|
|
|
inline int firstIndex() const {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return 0;
|
|
}
|
|
|
|
/** Returns element firstIndex(), performing a check in debug mode to ensure that there is at least one */
|
|
inline T& first() {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return data[0];
|
|
}
|
|
|
|
inline const T& first() const {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return data[0];
|
|
}
|
|
|
|
/** Returns iFloor(size() / 2), throws an assertion in debug mode if the array is empty */
|
|
inline int middleIndex() const {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return num >> 1;
|
|
}
|
|
|
|
/** Returns element middleIndex() */
|
|
inline const T& middle() const {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return data[num >> 1];
|
|
}
|
|
|
|
/** Returns element middleIndex() */
|
|
inline T& middle() {
|
|
debugAssertM(num > 0, "Array is empty");
|
|
return data[num >> 1];
|
|
}
|
|
|
|
/**
|
|
Calls delete on all objects[0...size-1]
|
|
and sets the size to zero.
|
|
*/
|
|
void invokeDeleteOnAllElements() {
|
|
for (size_t i = 0; i < num; ++i) {
|
|
delete data[i];
|
|
}
|
|
resize(0);
|
|
}
|
|
|
|
/** \deprecated */
|
|
void G3D_DEPRECATED deleteAll() {
|
|
invokeDeleteOnAllElements();
|
|
}
|
|
|
|
/**
|
|
Returns the index of (the first occurance of) an index or -1 if
|
|
not found. Searches from the right.
|
|
*/
|
|
int rfindIndex(const T& value) const {
|
|
for (int i = num -1 ; i >= 0; --i) {
|
|
if (data[i] == value) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
Returns the index of (the first occurance of) an index or -1 if
|
|
not found.
|
|
*/
|
|
int findIndex(const T& value) const {
|
|
for (size_t i = 0; i < num; ++i) {
|
|
if (data[i] == value) {
|
|
return (int)i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
Finds an element and returns the iterator to it. If the element
|
|
isn't found then returns end().
|
|
*/
|
|
Iterator find(const T& value) {
|
|
for (int i = 0; i < num; ++i) {
|
|
if (data[i] == value) {
|
|
return data + i;
|
|
}
|
|
}
|
|
return end();
|
|
}
|
|
|
|
ConstIterator find(const T& value) const {
|
|
for (int i = 0; i < num; ++i) {
|
|
if (data[i] == value) {
|
|
return data + i;
|
|
}
|
|
}
|
|
return end();
|
|
}
|
|
|
|
/**
|
|
Removes count elements from the array
|
|
referenced either by index or Iterator.
|
|
*/
|
|
void remove(Iterator element, int count = 1) {
|
|
debugAssert((element >= begin()) && (element < end()));
|
|
debugAssert((count > 0) && (element + count) <= end());
|
|
Iterator last = end() - count;
|
|
|
|
while(element < last) {
|
|
element[0] = element[count];
|
|
++element;
|
|
}
|
|
|
|
resize(num - count);
|
|
}
|
|
|
|
void remove(int index, int count = 1) {
|
|
debugAssert((index >= 0) && (index < (int)num));
|
|
debugAssert((count > 0) && (index + count <= (int)num));
|
|
|
|
remove(begin() + index, count);
|
|
}
|
|
|
|
/**
|
|
Reverse the elements of the array in place.
|
|
*/
|
|
void reverse() {
|
|
T temp;
|
|
|
|
size_t n2 = num / 2;
|
|
for (size_t i = 0; i < n2; ++i) {
|
|
temp = data[num - 1 - i];
|
|
data[num - 1 - i] = data[i];
|
|
data[i] = temp;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Sort using a specific less-than function, e.g.:
|
|
|
|
\code
|
|
bool __cdecl myLT(const MyClass& elem1, const MyClass& elem2) {
|
|
return elem1.x < elem2.x;
|
|
}
|
|
\endcode
|
|
|
|
Note that for pointer arrays, the <CODE>const</CODE> must come
|
|
<I>after</I> the class name, e.g., <CODE>Array<MyClass*></CODE> uses:
|
|
|
|
\code
|
|
bool __cdecl myLT(MyClass*const& elem1, MyClass*const& elem2) {
|
|
return elem1->x < elem2->x;
|
|
}
|
|
\endcode
|
|
|
|
or a functor, e.g.,
|
|
\code
|
|
bool
|
|
less_than_functor::operator()( const double& lhs, const double& rhs ) const
|
|
{
|
|
return( lhs < rhs? true : false );
|
|
}
|
|
\endcode
|
|
*/
|
|
// void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
|
|
// std::sort(data, data + num, lessThan);
|
|
//}
|
|
template<class LessThan>
|
|
void sort(const LessThan& lessThan) {
|
|
// Using std::sort, which according to http://www.open-std.org/JTC1/SC22/WG21/docs/D_4.cpp
|
|
// was 2x faster than qsort for arrays around size 2000 on intel core2 with gcc
|
|
std::sort(data, data + num, lessThan);
|
|
}
|
|
|
|
/**
|
|
Sorts the array in increasing order using the > or < operator. To
|
|
invoke this method on Array<T>, T must override those operator.
|
|
You can overide these operators as follows:
|
|
\code
|
|
bool T::operator>(const T& other) const {
|
|
return ...;
|
|
}
|
|
bool T::operator<(const T& other) const {
|
|
return ...;
|
|
}
|
|
\endcode
|
|
*/
|
|
void sort(int direction = SORT_INCREASING) {
|
|
if (direction == SORT_INCREASING) {
|
|
std::sort(data, data + num);
|
|
} else {
|
|
std::sort(data, data + num, compareGT);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Sorts elements beginIndex through and including endIndex.
|
|
*/
|
|
void sortSubArray(int beginIndex, int endIndex, int direction = SORT_INCREASING) {
|
|
if (direction == SORT_INCREASING) {
|
|
std::sort(data + beginIndex, data + endIndex + 1);
|
|
} else {
|
|
std::sort(data + beginIndex, data + endIndex + 1, compareGT);
|
|
}
|
|
}
|
|
|
|
void sortSubArray(int beginIndex, int endIndex, bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
|
|
std::sort(data + beginIndex, data + endIndex + 1, lessThan);
|
|
}
|
|
|
|
/**
|
|
The StrictWeakOrdering can be either a class that overloads the function call operator() or
|
|
a function pointer of the form <code>bool (__cdecl *lessThan)(const T& elem1, const T& elem2)</code>
|
|
*/
|
|
template<typename StrictWeakOrdering>
|
|
void sortSubArray(int beginIndex, int endIndex, StrictWeakOrdering& lessThan) {
|
|
std::sort(data + beginIndex, data + endIndex + 1, lessThan);
|
|
}
|
|
|
|
/** Uses < and == to evaluate operator(); this is the default comparator for Array::partition. */
|
|
class DefaultComparator {
|
|
public:
|
|
inline int operator()(const T& A, const T& B) const {
|
|
if (A < B) {
|
|
return 1;
|
|
} else if (A == B) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
};
|
|
|
|
/** The output arrays are resized with fastClear() so that if they are already of the same size
|
|
as this array no memory is allocated during partitioning.
|
|
|
|
@param comparator A function, or class instance with an overloaded operator() that compares
|
|
two elements of type <code>T</code> and returns 0 if they are equal, -1 if the second is smaller,
|
|
and 1 if the first is smaller (i.e., following the conventions of std::string::compare). For example:
|
|
|
|
<pre>
|
|
int compare(int A, int B) {
|
|
if (A < B) {
|
|
return 1;
|
|
} else if (A == B) {
|
|
return 0;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
</pre>
|
|
*/
|
|
template<typename Comparator>
|
|
void partition(
|
|
const T& partitionElement,
|
|
Array<T>& ltArray,
|
|
Array<T>& eqArray,
|
|
Array<T>& gtArray,
|
|
const Comparator& comparator) const {
|
|
|
|
// Make sure all arrays are independent
|
|
debugAssert(<Array != this);
|
|
debugAssert(&eqArray != this);
|
|
debugAssert(>Array != this);
|
|
debugAssert(<Array != &eqArray);
|
|
debugAssert(<Array != >Array);
|
|
debugAssert(&eqArray != >Array);
|
|
|
|
// Clear the arrays
|
|
ltArray.fastClear();
|
|
eqArray.fastClear();
|
|
gtArray.fastClear();
|
|
|
|
// Form a table of buckets for lt, eq, and gt
|
|
Array<T>* bucket[3] = {<Array, &eqArray, >Array};
|
|
|
|
for (size_t i = 0; i < num; ++i) {
|
|
int c = comparator(partitionElement, data[i]);
|
|
debugAssertM(c >= -1 && c <= 1, "Comparator returned an illegal value.");
|
|
|
|
// Insert into the correct bucket, 0, 1, or 2
|
|
bucket[c + 1]->append(data[i]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Uses < and == on elements to perform a partition. See partition().
|
|
*/
|
|
void partition(
|
|
const T& partitionElement,
|
|
Array<T>& ltArray,
|
|
Array<T>& eqArray,
|
|
Array<T>& gtArray) const {
|
|
|
|
partition(partitionElement, ltArray, eqArray, gtArray, typename Array<T>::DefaultComparator());
|
|
}
|
|
|
|
/**
|
|
Paritions the array into those below the median, those above the median, and those elements
|
|
equal to the median in expected O(n) time using quickselect. If the array has an even
|
|
number of different elements, the median for partition purposes is the largest value
|
|
less than the median.
|
|
|
|
@param tempArray used for working scratch space
|
|
@param comparator see parition() for a discussion.*/
|
|
template<typename Comparator>
|
|
void medianPartition(
|
|
Array<T>& ltMedian,
|
|
Array<T>& eqMedian,
|
|
Array<T>& gtMedian,
|
|
Array<T>& tempArray,
|
|
const Comparator& comparator) const {
|
|
|
|
ltMedian.fastClear();
|
|
eqMedian.fastClear();
|
|
gtMedian.fastClear();
|
|
|
|
// Handle trivial cases first
|
|
switch (size()) {
|
|
case 0:
|
|
// Array is empty; no parition is possible
|
|
return;
|
|
|
|
case 1:
|
|
// One element
|
|
eqMedian.append(first());
|
|
return;
|
|
|
|
case 2:
|
|
{
|
|
// Two element array; median is the smaller
|
|
int c = comparator(first(), last());
|
|
|
|
switch (c) {
|
|
case -1:
|
|
// first was bigger
|
|
eqMedian.append(last());
|
|
gtMedian.append(first());
|
|
break;
|
|
|
|
case 0:
|
|
// Both equal to the median
|
|
eqMedian.append(first(), last());
|
|
break;
|
|
|
|
case 1:
|
|
// Last was bigger
|
|
eqMedian.append(first());
|
|
gtMedian.append(last());
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
// All other cases use a recursive randomized median
|
|
|
|
// Number of values less than all in the current arrays
|
|
int ltBoost = 0;
|
|
|
|
// Number of values greater than all in the current arrays
|
|
int gtBoost = 0;
|
|
|
|
// For even length arrays, force the gt array to be one larger than the
|
|
// lt array:
|
|
// [1 2 3] size = 3, choose half = (s + 1) /2
|
|
//
|
|
int lowerHalfSize, upperHalfSize;
|
|
if (isEven(size())) {
|
|
lowerHalfSize = size() / 2;
|
|
upperHalfSize = lowerHalfSize + 1;
|
|
} else {
|
|
lowerHalfSize = upperHalfSize = (size() + 1) / 2;
|
|
}
|
|
const T* xPtr = NULL;
|
|
|
|
// Maintain pointers to the arrays; we'll switch these around during sorting
|
|
// to avoid copies.
|
|
const Array<T>* source = this;
|
|
Array<T>* lt = <Median;
|
|
Array<T>* eq = &eqMedian;
|
|
Array<T>* gt = >Median;
|
|
Array<T>* extra = &tempArray;
|
|
|
|
while (true) {
|
|
// Choose a random element -- choose the middle element; this is theoretically
|
|
// suboptimal, but for loosly sorted array is actually the best strategy
|
|
|
|
xPtr = &(source->middle());
|
|
if (source->size() == 1) {
|
|
// Done; there's only one element left
|
|
break;
|
|
}
|
|
const T& x = *xPtr;
|
|
|
|
// Note: partition (fast) clears the arrays for us
|
|
source->partition(x, *lt, *eq, *gt, comparator);
|
|
|
|
int L = lt->size() + ltBoost + eq->size();
|
|
int U = gt->size() + gtBoost + eq->size();
|
|
if ((L >= lowerHalfSize) &&
|
|
(U >= upperHalfSize)) {
|
|
|
|
// x must be the partition median
|
|
break;
|
|
|
|
} else if (L < lowerHalfSize) {
|
|
|
|
// x must be smaller than the median. Recurse into the 'gt' array.
|
|
ltBoost += lt->size() + eq->size();
|
|
|
|
// The new gt array will be the old source array, unless
|
|
// that was the this pointer (i.e., unless we are on the
|
|
// first iteration)
|
|
Array<T>* newGt = (source == this) ? extra : const_cast<Array<T>*>(source);
|
|
|
|
// Now set up the gt array as the new source
|
|
source = gt;
|
|
gt = newGt;
|
|
|
|
} else {
|
|
|
|
// x must be bigger than the median. Recurse into the 'lt' array.
|
|
gtBoost += gt->size() + eq->size();
|
|
|
|
// The new lt array will be the old source array, unless
|
|
// that was the this pointer (i.e., unless we are on the
|
|
// first iteration)
|
|
Array<T>* newLt = (source == this) ? extra : const_cast<Array<T>*>(source);
|
|
|
|
// Now set up the lt array as the new source
|
|
source = lt;
|
|
lt = newLt;
|
|
}
|
|
}
|
|
|
|
// Now that we know the median, make a copy of it (since we're about to destroy the array that it
|
|
// points into).
|
|
T median = *xPtr;
|
|
xPtr = NULL;
|
|
|
|
// Partition the original array (note that this fast clears for us)
|
|
partition(median, ltMedian, eqMedian, gtMedian, comparator);
|
|
}
|
|
|
|
/**
|
|
Computes a median partition using the default comparator and a dynamically allocated temporary
|
|
working array. If the median is not in the array, it is chosen to be the largest value smaller
|
|
than the true median.
|
|
*/
|
|
void medianPartition(
|
|
Array<T>& ltMedian,
|
|
Array<T>& eqMedian,
|
|
Array<T>& gtMedian) const {
|
|
|
|
Array<T> temp;
|
|
medianPartition(ltMedian, eqMedian, gtMedian, temp, DefaultComparator());
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Redistributes the elements so that the new order is statistically independent
|
|
of the original order. O(n) time.*/
|
|
void randomize() {
|
|
T temp;
|
|
|
|
for (int i = size() - 1; i >= 0; --i) {
|
|
int x = iRandom(0, i);
|
|
|
|
temp = data[i];
|
|
data[i] = data[x];
|
|
data[x] = temp;
|
|
}
|
|
}
|
|
|
|
|
|
/** Ensures that future append() calls can grow up to size \a n without allocating memory.*/
|
|
void reserve(int n) {
|
|
debugAssert(n >= size());
|
|
const int oldSize = size();
|
|
resize(n);
|
|
resize(oldSize, false);
|
|
}
|
|
|
|
/** Number of bytes used by the array object and the memory allocated for it's data pointer. Does *not*
|
|
* include the memory of objects pointed to by objects in the data array */
|
|
size_t sizeInMemory() const {
|
|
return sizeof(Array<T>) + (sizeof(T) * numAllocated);
|
|
}
|
|
|
|
/** Remove all NULL elements in linear time without affecting order of the other elements. */
|
|
void removeNulls() {
|
|
int nextNull = 0;
|
|
for (int i = 0; i < size(); ++i) {
|
|
if (notNull(data[i])) {
|
|
if (i > nextNull) {
|
|
// Move value i down to squeeze out NULLs
|
|
data[nextNull] = data[i];
|
|
}
|
|
++nextNull;
|
|
}
|
|
}
|
|
|
|
resize(nextNull, false);
|
|
}
|
|
};
|
|
|
|
|
|
/** Array::contains for C-arrays */
|
|
template<class T> bool contains(const T* array, int len, const T& e) {
|
|
for (int i = len - 1; i >= 0; --i) {
|
|
if (array[i] == e) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#ifdef _MSC_VER
|
|
# pragma warning (pop)
|
|
#endif
|
|
|
|
#endif
|