You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Code/CryEngine/CryCommon/CryFixedArray.h

310 lines
9.7 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
// Original file Copyright Crytek GMBH or its affiliates, used under license.
/*
CryFixedArray.h
- no longer support being created on the stack (since the alignment code was changed to support adding CryFixedArrays into stl::vectors)
- performs construction or destruction only on elements as they become live/dead or are moved around during the RemoveAt() reshuffle
- just a range checked equivelant of a standard array
- for now only allows push_back() population of array
- if using as a class member variable ensure to put the CryFixedArrays after all other member variables at the bottom of your class
to ensure all members stay on the same cacheline
*/
#ifndef CRYINCLUDE_CRYCOMMON_CRYFIXEDARRAY_H
#define CRYINCLUDE_CRYCOMMON_CRYFIXEDARRAY_H
#pragma once
#define DEBUG_CRYFIXED_ARRAY _DEBUG
template<
unsigned int align >
struct CryFixedArrayDatum
{
};
template<>
struct CryFixedArrayDatum< 4 >
{
typedef uint32 TDatum;
};
template<>
struct CryFixedArrayDatum< 8 >
{
typedef uint64 TDatum;
};
template <class T, unsigned int N>
class CryFixedArray
{
protected:
enum
{
ALIGN = MAX(alignof(T), sizeof(unsigned int))
}; // ALIGN at least sizeof(unsigned int)
typedef typename CryFixedArrayDatum< ALIGN >::TDatum TDatum;
uint32 m_curSize[ sizeof (TDatum) / sizeof (uint32) ]; // Padded for alignment
TDatum m_data[(N * sizeof(T) + (sizeof(TDatum) - 1)) / sizeof(TDatum)]; // simple debugging - in VS: just add to a watch as "(T*)m_data, <N>" to see the array. ie. "(int*)m_data, 5" - the size of the array has to be a literal int
public:
typedef T* iterator;
typedef const T* const_iterator;
CryFixedArray()
{
#if DEBUG_CRYFIXED_ARRAY
if (((uintptr_t)m_data & (ALIGN - 1)) != 0)
{
CryLogAlways("CryFixedArray() error - data is not aligned. This may happen if you are creating a CryFixedArray on the stack, which isn't supported.");
}
#endif
CRY_ASSERT_MESSAGE(((uintptr_t)m_data & (ALIGN - 1)) == 0, "CryFixedArray() error - data is not aligned. This may happen if you are creating a CryFixedArray on the stack, which isn't supported.");
m_curSize[0] = 0;
}
CryFixedArray(const CryFixedArray& other)
{
// doesn't require clear() this is newly constructed
m_curSize[0] = other.m_curSize[0];
int size = m_curSize[0];
for (int i = 0; i < size; i++)
{
T& ele = operator[](i);
const T& otherEle = other.operator[](i);
new (&ele)T(otherEle); // placement new
}
}
CryFixedArray& operator=(const CryFixedArray& other)
{
if (this != &other)
{
clear(); // necessary to avoid potentially leaking within existing elements
m_curSize[0] = other.m_curSize[0];
int size = m_curSize[0];
for (int i = 0; i < size; i++)
{
T& ele = operator[](i);
const T& otherEle = other.operator[](i);
//ele = otherEle; // assignment instead of placement new to keep type of operation consistent - this cannot be done until this is rewritten to assign over existing elements and deconstruct any left overs, and placement new any new elements
new (&ele)T(otherEle); // placement new
}
}
return *this;
}
virtual ~CryFixedArray()
{
clear();
}
ILINE T& at(unsigned int i)
{
#if DEBUG_CRYFIXED_ARRAY
if (i < size())
{
return alias_cast<T*>(m_data)[i];
}
else
{
// Log is required now as its possible to turn off assert output logging, yet you really want to know if this is happening!!!!
CryLogAlways("CryFixedArray::at(i=%d) failed as i is out of range of curSize=%d (maxSize=%d) - forcing a crash", i, m_curSize[0], N);
CRY_ASSERT_MESSAGE(0, string().Format("CryFixedArray::at(i=%d) failed as i is out of range of curSize=%d (maxSize=%d)", i, m_curSize[0], N));
abort(); // better option than dereferncing a nullptr?
}
#else
return alias_cast<T*>(m_data)[i];
#endif
}
ILINE const T& at(unsigned int i) const
{
#if DEBUG_CRYFIXED_ARRAY
if (i < size())
{
return alias_cast<const T*>(m_data)[i];
}
else
{
// Log is required now as its possible to turn off assert output logging, yet you really want to know if this is happening!!!!
CryLogAlways("CryFixedArray::at(i=%d) failed as i is out of range of curSize=%d (maxSize=%d) - forcing a crash", i, m_curSize[0], N);
CRY_ASSERT_MESSAGE(0, string().Format("CryFixedArray::at(i=%d) failed as i is out of range of curSize=%d (maxSize=%d)", i, m_curSize[0], N));
abort(); // better option than dereferncing a nullptr?
}
#else
return alias_cast<const T*>(m_data)[i];
#endif
}
ILINE const T& operator[](unsigned int i) const
{
return at(i);
}
ILINE T& operator[](unsigned int i)
{
return at(i);
}
ILINE void clear()
{
for (uint32 i = 0; i < m_curSize[0]; i++)
{
T& ele = operator[](i);
ele.~T();
}
m_curSize[0] = 0;
#if DEBUG_CRYFIXED_ARRAY
memset(m_data, 0, N * sizeof(T));
#endif
}
ILINE iterator begin()
{
return alias_cast<T*>(m_data);
}
ILINE const_iterator begin() const
{
return alias_cast<T*>(m_data);
}
ILINE iterator end()
{
return &(alias_cast<T*>(m_data))[m_curSize[0]];
}
ILINE const_iterator end() const
{
return &(alias_cast<T*>(m_data))[m_curSize[0]];
}
ILINE unsigned int max_size() const { return N; }
ILINE unsigned int size() const { return m_curSize[0]; }
ILINE bool empty() const { return size() == 0; }
ILINE unsigned int isfull() const { return (size() == max_size()); }
// allows you to push back default constructed elements
ILINE void push_back ()
{
unsigned int curSize = size();
if (curSize < N)
{
T* newT = &(alias_cast<T*>(m_data))[curSize];
new (newT) T();
m_curSize[0]++;
}
else
{
CryLogAlways("CryFixedArray::push_back() failing as array of size %u is full - NOT adding element", N);
CRY_ASSERT_TRACE(0, ("CryFixedArray::push_back() failing as array of size %u is full - NOT adding element", N));
}
}
ILINE void push_back (const T& ele)
{
unsigned int curSize = size();
if (curSize < N)
{
T* newT = &(alias_cast<T*>(m_data))[curSize];
new (newT) T(ele); // placement new copy constructor - setup vtable etc
m_curSize[0]++;
}
else
{
CryLogAlways("CryFixedArray::push_back() failing as array of size %u is full - NOT adding element", N);
CRY_ASSERT_TRACE(0, ("CryFixedArray::push_back() failing as array of size %u is full - NOT adding element", N));
}
}
ILINE void pop_back()
{
if (size() > 0)
{
back().~T(); // destruct back
m_curSize[0]--;
}
else
{
CryLogAlways("CryFixedArray::pop_back() failed as array is empty");
CRY_ASSERT_MESSAGE(0, "CryFixedArray::pop_back() failed as array is empty");
}
}
protected:
ILINE const T& backEx() const
{
#if DEBUG_CRYFIXED_ARRAY
if (m_curSize[0] > 0)
{
return (alias_cast<T*>(m_data))[m_curSize[0] - 1];
}
else
{
CryLogAlways("CryFixedArray::back() failed as array is empty");
CRY_ASSERT_MESSAGE(0, "CryFixedArray::back() failed as array is empty");
abort(); // better option than dereferncing a nullptr?
}
#else
return (alias_cast<T*>(m_data))[m_curSize[0] - 1];
#endif
}
public:
ILINE const T& back() const
{
return backEx();
}
ILINE T& back()
{
return (T&)(backEx());
}
// if returns true then an element has been swapped into the new element[i] and as such may need updating to reflect its new location in memory
ILINE bool removeAt(uint32 i)
{
bool swappedElement = false;
if (i < m_curSize[0])
{
if (i != m_curSize[0] - 1)
{
operator[](i).~T(); // destruct element being removed
// copy back() into element i
T* newT = &(alias_cast<T*>(m_data))[i];
new (newT) T(back()); // placement new copy constructor - setup vtable etc
swappedElement = true;
}
pop_back(); // will destruct back()
}
else
{
CryLog("CryFixedArray::removeAt() failed as i=%d is out of range of curSize=%d", i, m_curSize[0]);
CRY_ASSERT_MESSAGE(0, string().Format("CryFixedArray::removeAt() failed as i=%d is out of range of curSize=%d", i, m_curSize[0]));
}
return swappedElement;
}
};
#endif // CRYINCLUDE_CRYCOMMON_CRYFIXEDARRAY_H