Undo buffer class. Use plain array for easier debugging.

This commit is contained in:
castano 2011-04-07 07:41:58 +00:00
parent 144a8d6d4b
commit 2643a62d4a

View File

@ -38,24 +38,28 @@ namespace nv
} }
} }
// @@ Move these templates to Utils.h
// @@ Specialize these methods for numeric, pointer, and pod types.
template <typename T> template <typename T>
void construct(T * restrict ptr, uint new_size, uint old_size) { void construct_range(T * restrict ptr, uint new_size, uint old_size) {
for (uint i = old_size; i < new_size; i++) { for (uint i = old_size; i < new_size; i++) {
new(ptr+i) T; // placement new new(ptr+i) T; // placement new
} }
} }
template <typename T> template <typename T>
void construct(T * restrict ptr, uint new_size, uint old_size, const T & elem) { void construct_range(T * restrict ptr, uint new_size, uint old_size, const T & elem) {
for (uint i = old_size; i < new_size; i++) { for (uint i = old_size; i < new_size; i++) {
new(ptr+i) T(elem); // placement new new(ptr+i) T(elem); // placement new
} }
} }
template <typename T> template <typename T>
void destroy(T * restrict ptr, uint new_size, uint old_size) { void destroy_range(T * restrict ptr, uint new_size, uint old_size) {
for (uint i = new_size; i < old_size; i++) { for (uint i = new_size; i < old_size; i++) {
(ptr+i)->~T(); // Explicit call to the destructor (ptr+i)->~T(); // Explicit call to the destructor
} }
} }
@ -86,119 +90,30 @@ namespace nv
class Buffer
{
NV_FORBID_COPY(Buffer)
public:
// Ctor.
Buffer() : m_buffer(NULL), m_buffer_size(0), m_size(0)
{
}
// Dtor.
~Buffer() {
allocate(0, 0);
}
// Get vector size.
NV_FORCEINLINE uint size() const { return m_size; }
// Get vector size.
NV_FORCEINLINE uint count() const { return m_size; }
// Is a null vector.
NV_FORCEINLINE bool isNull() const { return m_buffer == NULL; }
// Swap the members of this vector and the given vector.
friend void swap(Buffer & a, Buffer & b)
{
swap(a.m_buffer, b.m_buffer);
swap(a.m_buffer_size, b.m_buffer_size);
swap(a.m_size, b.m_size);
}
protected:
// Release ownership of allocated memory and returns pointer to it.
NV_NOINLINE void * release() {
void * tmp = m_buffer;
m_buffer = NULL;
m_buffer_size = 0;
m_size = 0;
return tmp;
}
NV_NOINLINE void resize(uint new_size, uint element_size)
{
m_size = new_size;
if (new_size > m_buffer_size) {
uint new_buffer_size;
if (m_buffer_size == 0) {
// first allocation
new_buffer_size = new_size;
}
else {
// growing
new_buffer_size = new_size + (new_size >> 2);
}
allocate( new_buffer_size, element_size );
}
}
/// Change buffer size.
NV_NOINLINE void allocate(uint count, uint element_size)
{
if (count == 0) {
// free the buffer.
if (m_buffer != NULL) {
::free(m_buffer);
m_buffer = NULL;
}
}
else {
// realloc the buffer
m_buffer = ::realloc(m_buffer, count * element_size);
}
m_buffer_size = count;
}
protected:
void * m_buffer;
uint m_buffer_size;
uint m_size;
};
/** /**
* Replacement for std::vector that is easier to debug and provides * Replacement for std::vector that is easier to debug and provides
* some nice foreach enumerators. * some nice foreach enumerators.
*/ */
template<typename T> template<typename T>
class NVCORE_CLASS Array : public Buffer { class NVCORE_CLASS Array {
public: public:
// Default constructor. // Default constructor.
NV_FORCEINLINE Array() : Buffer() {} NV_FORCEINLINE Array() : m_buffer(NULL), m_capacity(0), m_size(0) {}
// Copy constructor. // Copy constructor.
NV_FORCEINLINE Array(const Array & a) : Buffer() { NV_FORCEINLINE Array(const Array & a) : m_buffer(NULL), m_capacity(0), m_size(0) {
copy(a.buffer(), a.m_size); copy(a.m_buffer, a.m_size);
} }
// Constructor that initializes the vector with the given elements. // Constructor that initializes the vector with the given elements.
NV_FORCEINLINE Array(const T * ptr, int num) : Buffer() { NV_FORCEINLINE Array(const T * ptr, int num) : m_buffer(NULL), m_capacity(0), m_size(0) {
copy(ptr, num); copy(ptr, num);
} }
// Allocate array. // Allocate array.
NV_FORCEINLINE explicit Array(uint capacity) : Buffer() { NV_FORCEINLINE explicit Array(uint capacity) : m_buffer(NULL), m_capacity(0), m_size(0) {
allocate(capacity); setArrayCapacity(capacity);
} }
// Destructor. // Destructor.
@ -211,24 +126,24 @@ namespace nv
NV_FORCEINLINE const T & operator[]( uint index ) const NV_FORCEINLINE const T & operator[]( uint index ) const
{ {
nvDebugCheck(index < m_size); nvDebugCheck(index < m_size);
return buffer()[index]; return m_buffer[index];
} }
NV_FORCEINLINE const T & at( uint index ) const NV_FORCEINLINE const T & at( uint index ) const
{ {
nvDebugCheck(index < m_size); nvDebugCheck(index < m_size);
return buffer()[index]; return m_buffer[index];
} }
/// Element access. /// Element access.
NV_FORCEINLINE T & operator[] ( uint index ) NV_FORCEINLINE T & operator[] ( uint index )
{ {
nvDebugCheck(index < m_size); nvDebugCheck(index < m_size);
return buffer()[index]; return m_buffer[index];
} }
NV_FORCEINLINE T & at( uint index ) NV_FORCEINLINE T & at( uint index )
{ {
nvDebugCheck(index < m_size); nvDebugCheck(index < m_size);
return buffer()[index]; return m_buffer[index];
} }
/// Get vector size. /// Get vector size.
@ -238,10 +153,10 @@ namespace nv
NV_FORCEINLINE uint count() const { return m_size; } NV_FORCEINLINE uint count() const { return m_size; }
/// Get const vector pointer. /// Get const vector pointer.
NV_FORCEINLINE const T * buffer() const { return (const T *)m_buffer; } NV_FORCEINLINE const T * buffer() const { return m_buffer; }
/// Get vector pointer. /// Get vector pointer.
NV_FORCEINLINE T * buffer() { return (T *)m_buffer; } NV_FORCEINLINE T * buffer() { return m_buffer; }
/// Is vector empty. /// Is vector empty.
NV_FORCEINLINE bool isEmpty() const { return m_size == 0; } NV_FORCEINLINE bool isEmpty() const { return m_size == 0; }
@ -251,23 +166,33 @@ namespace nv
/// Push an element at the end of the vector. /// Push an element at the end of the vector.
void push_back( const T & val ) NV_FORCEINLINE void push_back( const T & val )
{ {
#if 1
nvDebugCheck(&val < m_buffer || &val > m_buffer+m_size);
setArraySize(m_size+1);
new(m_buffer+m_size-1) T(val);
#else
uint new_size = m_size + 1; uint new_size = m_size + 1;
if (new_size > m_buffer_size) if (new_size > m_capacity)
{ {
const T copy(val); // create a copy in case value is inside of this array. // @@ Create a copy without side effects. Do not call constructor/destructor here. // @@ Is there any way to avoid this copy?
// @@ Can we create a copy without side effects? Ie. without calls to constructor/destructor. Use alloca + memcpy?
// @@ Assert instead of copy?
const T copy(val); // create a copy in case value is inside of this array.
Buffer::resize(new_size, sizeof(T)); setArraySize(new_size);
new (buffer()+new_size-1) T(copy); new (m_buffer+new_size-1) T(copy);
} }
else else
{ {
m_size = new_size; m_size = new_size;
new(buffer()+new_size-1) T(val); new(m_buffer+new_size-1) T(val);
} }
#endif // 0/1
} }
NV_FORCEINLINE void pushBack( const T & val ) NV_FORCEINLINE void pushBack( const T & val )
{ {
@ -300,28 +225,28 @@ namespace nv
NV_FORCEINLINE const T & back() const NV_FORCEINLINE const T & back() const
{ {
nvDebugCheck( m_size > 0 ); nvDebugCheck( m_size > 0 );
return buffer()[m_size-1]; return m_buffer[m_size-1];
} }
/// Get back element. /// Get back element.
NV_FORCEINLINE T & back() NV_FORCEINLINE T & back()
{ {
nvDebugCheck( m_size > 0 ); nvDebugCheck( m_size > 0 );
return buffer()[m_size-1]; return m_buffer[m_size-1];
} }
/// Get front element. /// Get front element.
NV_FORCEINLINE const T & front() const NV_FORCEINLINE const T & front() const
{ {
nvDebugCheck( m_size > 0 ); nvDebugCheck( m_size > 0 );
return buffer()[0]; return m_buffer[0];
} }
/// Get front element. /// Get front element.
NV_FORCEINLINE T & front() NV_FORCEINLINE T & front()
{ {
nvDebugCheck( m_size > 0 ); nvDebugCheck( m_size > 0 );
return buffer()[0]; return m_buffer[0];
} }
/// Check if the given element is contained in the array. /// Check if the given element is contained in the array.
@ -339,21 +264,21 @@ namespace nv
/// Return true if element found within the given range. /// Return true if element found within the given range.
NV_FORCEINLINE bool find(const T & element, uint first, uint count, uint * index) const NV_FORCEINLINE bool find(const T & element, uint first, uint count, uint * index) const
{ {
return ::nv::find(element, buffer() + first, count, index); return ::nv::find(element, m_buffer + first, count, index);
} }
/// Remove the element at the given index. This is an expensive operation! /// Remove the element at the given index. This is an expensive operation!
void removeAt(uint index) void removeAt(uint index)
{ {
nvCheck(index >= 0 && index < m_size); nvDebugCheck(index >= 0 && index < m_size);
if (m_size == 1) { if (m_size == 1) {
clear(); clear();
} }
else { else {
buffer()[index].~T(); m_buffer[index].~T();
memmove(buffer()+index, buffer()+index+1, sizeof(T) * (m_size - 1 - index)); memmove(m_buffer+index, m_buffer+index+1, sizeof(T) * (m_size - 1 - index));
m_size--; m_size--;
} }
} }
@ -372,22 +297,22 @@ namespace nv
/// Insert the given element at the given index shifting all the elements up. /// Insert the given element at the given index shifting all the elements up.
void insertAt(uint index, const T & val = T()) void insertAt(uint index, const T & val = T())
{ {
nvCheck( index >= 0 && index <= m_size ); nvDebugCheck( index >= 0 && index <= m_size );
resize( m_size + 1 ); setArraySize(m_size + 1);
if (index < m_size - 1) { if (index < m_size - 1) {
memmove(buffer()+index+1, buffer()+index, sizeof(T) * (m_size - 1 - index)); memmove(m_buffer+index+1, m_buffer+index, sizeof(T) * (m_size - 1 - index));
} }
// Copy-construct into the newly opened slot. // Copy-construct into the newly opened slot.
new(buffer()+index) T(val); new(m_buffer+index) T(val);
} }
/// Append the given data to our vector. /// Append the given data to our vector.
NV_FORCEINLINE void append(const Array<T> & other) NV_FORCEINLINE void append(const Array<T> & other)
{ {
append(other.buffer(), other.m_size); append(other.m_buffer, other.m_size);
} }
/// Append the given data to our vector. /// Append the given data to our vector.
@ -395,10 +320,11 @@ namespace nv
{ {
if (count > 0) { if (count > 0) {
const uint old_size = m_size; const uint old_size = m_size;
resize(m_size + count);
// Must use operator=() to copy elements, in case of side effects (e.g. ref-counting). setArraySize(m_size + count);
for (uint i = 0; i < count; i++ ) { for (uint i = 0; i < count; i++ ) {
buffer()[old_size + i] = other[i]; new(m_buffer + old_size + i) T(other[i]);
} }
} }
} }
@ -408,8 +334,8 @@ namespace nv
void replaceWithLast(uint index) void replaceWithLast(uint index)
{ {
nvDebugCheck( index < m_size ); nvDebugCheck( index < m_size );
nv::swap(buffer()[index], back()); nv::swap(m_buffer[index], back());
(buffer()+m_size-1)->~T(); (m_buffer+m_size-1)->~T();
m_size--; m_size--;
} }
@ -420,69 +346,76 @@ namespace nv
uint old_size = m_size; uint old_size = m_size;
// Destruct old elements (if we're shrinking). // Destruct old elements (if we're shrinking).
destroy(buffer(), new_size, old_size); destroy_range(m_buffer, new_size, old_size);
Buffer::resize(new_size, sizeof(T)); setArraySize(new_size);
// Call default constructors // Call default constructors
construct(buffer(), new_size, old_size); construct_range(m_buffer, new_size, old_size);
} }
/// Resize the vector preserving existing elements and initializing the /// Resize the vector preserving existing elements and initializing the
/// new ones with the given value. /// new ones with the given value.
void resize( uint new_size, const T &elem ) void resize(uint new_size, const T & elem)
{ {
uint old_size = m_size; uint old_size = m_size;
// Destruct old elements (if we're shrinking). // Destruct old elements (if we're shrinking).
destroy(buffer(), new_size, old_size); destroy_range(m_buffer, new_size, old_size);
Buffer::resize(new_size, sizeof(T)); setArraySize(new_size);
// Call copy constructors // Call copy constructors
construct(buffer(), new_size, old_size, elem); construct_range(m_buffer, new_size, old_size, elem);
} }
/// Clear the buffer. /// Clear the buffer.
NV_FORCEINLINE void clear() NV_FORCEINLINE void clear()
{ {
resize(0); // Destruct old elements
destroy_range(m_buffer, 0, m_size);
m_size = 0;
} }
/// Shrink the allocated vector. /// Shrink the allocated vector.
NV_FORCEINLINE void shrink() NV_FORCEINLINE void shrink()
{ {
if (m_size < m_buffer_size) { if (m_size < m_capacity) {
allocate(m_size); setArrayCapacity(m_size);
} }
} }
/// Preallocate space. /// Preallocate space.
NV_FORCEINLINE void reserve(uint desired_size) NV_FORCEINLINE void reserve(uint desired_size)
{ {
if (desired_size > m_buffer_size) { if (desired_size > m_capacity) {
allocate(desired_size); setArrayCapacity(desired_size);
} }
} }
/// Copy elements to this array. Resizes it if needed. /// Copy elements to this array. Resizes it if needed.
NV_FORCEINLINE void copy(const T * ptr, uint num) NV_FORCEINLINE void copy(const T * ptr, uint num)
{ {
resize( num ); resize( num ); // @@ call copy operator from 0 to min(num,m_size) and copy constructor from min(num,m_size) to num
::nv::copy(buffer(), ptr, num); ::nv::copy(m_buffer, ptr, num);
} }
/// Assignment operator. /// Assignment operator.
NV_FORCEINLINE Array<T> & operator=( const Array<T> & a ) NV_FORCEINLINE Array<T> & operator=( const Array<T> & a )
{ {
copy(a.buffer(), a.m_size); copy(a.m_buffer, a.m_size);
return *this; return *this;
} }
// Release ownership of allocated memory and returns pointer to it. // Release ownership of allocated memory and returns pointer to it.
T * release() { T * release() {
return (T *)Buffer::release(); T * tmp = m_buffer;
m_buffer = NULL;
m_capacity = 0;
m_size = 0;
return tmp;
} }
/// Array serialization. /// Array serialization.
@ -498,7 +431,7 @@ namespace nv
} }
for (uint i = 0; i < p.m_size; i++) { for (uint i = 0; i < p.m_size; i++) {
s << p.buffer()[i]; s << p.m_buffer[i];
} }
return s; return s;
@ -514,19 +447,66 @@ namespace nv
#if NV_CC_MSVC #if NV_CC_MSVC
NV_FORCEINLINE T & operator[]( const PseudoIndexWrapper & i ) { NV_FORCEINLINE T & operator[]( const PseudoIndexWrapper & i ) {
return at(i(this)); return m_buffer[i(this)];
} }
NV_FORCEINLINE const T & operator[]( const PseudoIndexWrapper & i ) const { NV_FORCEINLINE const T & operator[]( const PseudoIndexWrapper & i ) const {
return at(i(this)); return m_buffer[i(this)];
} }
#endif #endif
// Swap the members of this vector and the given vector.
friend void swap(Array & a, Array & b)
{
swap(a.m_buffer, b.m_buffer);
swap(a.m_capacity, b.m_capacity);
swap(a.m_size, b.m_size);
}
protected: protected:
NV_FORCEINLINE void allocate(uint count) { // Change array size.
Buffer::allocate(count, sizeof(T)); void setArraySize(uint new_size) {
m_size = new_size;
if (new_size > m_capacity) {
uint new_buffer_size;
if (m_capacity == 0) {
// first allocation is exact
new_buffer_size = new_size;
}
else {
// following allocations grow array by 25%
new_buffer_size = new_size + (new_size >> 2);
}
setArrayCapacity( new_buffer_size );
}
} }
// Change array capacity.
void setArrayCapacity(uint new_capacity) {
nvDebugCheck(new_capacity >= m_size);
if (new_capacity == 0) {
// free the buffer.
if (m_buffer != NULL) {
free<T>(m_buffer);
m_buffer = NULL;
}
}
else {
// realloc the buffer
m_buffer = realloc<T>(m_buffer, new_capacity);
}
m_capacity = new_capacity;
}
T * m_buffer;
uint m_capacity;
uint m_size;
}; };
} // nv namespace } // nv namespace