// SPDX-FileCopyrightText: 2021 Jorrit Rouwe // SPDX-License-Identifier: MIT #pragma once JPH_NAMESPACE_BEGIN /// Simple variable length array backed by a fixed size buffer template class [[nodiscard]] StaticArray { public: using value_type = T; using size_type = uint; /// Default constructor StaticArray() = default; /// Constructor from initializer list explicit StaticArray(std::initializer_list inList) { JPH_ASSERT(inList.size() <= N); for (typename std::initializer_list::iterator i = inList.begin(); i != inList.end(); ++i) ::new (reinterpret_cast(&mElements[mSize++])) T(*i); } /// Copy constructor StaticArray(const StaticArray &inRHS) { while (mSize < inRHS.mSize) { ::new (&mElements[mSize]) T(inRHS[mSize]); ++mSize; } } /// Destruct all elements ~StaticArray() { if constexpr (!is_trivially_destructible()) for (T *e = reinterpret_cast(mElements), *end = e + mSize; e < end; ++e) e->~T(); } /// Destruct all elements and set length to zero void clear() { if constexpr (!is_trivially_destructible()) for (T *e = reinterpret_cast(mElements), *end = e + mSize; e < end; ++e) e->~T(); mSize = 0; } /// Add element to the back of the array void push_back(const T &inElement) { JPH_ASSERT(mSize < N); ::new (&mElements[mSize++]) T(inElement); } /// Construct element at the back of the array template void emplace_back(A &&... inElement) { JPH_ASSERT(mSize < N); ::new (&mElements[mSize++]) T(std::forward(inElement)...); } /// Remove element from the back of the array void pop_back() { JPH_ASSERT(mSize > 0); reinterpret_cast(mElements[--mSize]).~T(); } /// Returns true if there are no elements in the array bool empty() const { return mSize == 0; } /// Returns amount of elements in the array size_type size() const { return mSize; } /// Returns maximum amount of elements the array can hold size_type capacity() const { return N; } /// Resize array to new length void resize(size_type inNewSize) { JPH_ASSERT(inNewSize <= N); if constexpr (!is_trivially_constructible()) for (T *element = reinterpret_cast(mElements) + mSize, *element_end = reinterpret_cast(mElements) + inNewSize; element < element_end; ++element) ::new (element) T; if constexpr (!is_trivially_destructible()) for (T *element = reinterpret_cast(mElements) + inNewSize, *element_end = reinterpret_cast(mElements) + mSize; element < element_end; ++element) element->~T(); mSize = inNewSize; } using const_iterator = const T *; /// Iterators const_iterator begin() const { return reinterpret_cast(mElements); } const_iterator end() const { return reinterpret_cast(mElements + mSize); } using iterator = T *; iterator begin() { return reinterpret_cast(mElements); } iterator end() { return reinterpret_cast(mElements + mSize); } const T * data() const { return reinterpret_cast(mElements); } T * data() { return reinterpret_cast(mElements); } /// Access element T & operator [] (size_type inIdx) { JPH_ASSERT(inIdx < mSize); return reinterpret_cast(mElements[inIdx]); } const T & operator [] (size_type inIdx) const { JPH_ASSERT(inIdx < mSize); return reinterpret_cast(mElements[inIdx]); } /// First element in the array const T & front() const { JPH_ASSERT(mSize > 0); return reinterpret_cast(mElements[0]); } T & front() { JPH_ASSERT(mSize > 0); return reinterpret_cast(mElements[0]); } /// Last element in the array const T & back() const { JPH_ASSERT(mSize > 0); return reinterpret_cast(mElements[mSize - 1]); } T & back() { JPH_ASSERT(mSize > 0); return reinterpret_cast(mElements[mSize - 1]); } /// Remove one element from the array void erase(const_iterator inIter) { size_type p = size_type(inIter - begin()); JPH_ASSERT(p < mSize); reinterpret_cast(mElements[p]).~T(); if (p + 1 < mSize) memmove(mElements + p, mElements + p + 1, (mSize - p - 1) * sizeof(T)); --mSize; } /// Remove multiple element from the array void erase(const_iterator inBegin, const_iterator inEnd) { size_type p = size_type(inBegin - begin()); size_type n = size_type(inEnd - inBegin); JPH_ASSERT(inEnd <= end()); for (size_type i = 0; i < n; ++i) reinterpret_cast(mElements[p + i]).~T(); if (p + n < mSize) memmove(mElements + p, mElements + p + n, (mSize - p - n) * sizeof(T)); mSize -= n; } /// Assignment operator StaticArray & operator = (const StaticArray &inRHS) { size_type rhs_size = inRHS.size(); if ((void *)this != (void *)&inRHS) { clear(); while (mSize < rhs_size) { ::new (&mElements[mSize]) T(inRHS[mSize]); ++mSize; } } return *this; } /// Assignment operator with static array of different max length template StaticArray & operator = (const StaticArray &inRHS) { size_type rhs_size = inRHS.size(); JPH_ASSERT(rhs_size <= N); if ((void *)this != (void *)&inRHS) { clear(); while (mSize < rhs_size) { ::new (&mElements[mSize]) T(inRHS[mSize]); ++mSize; } } return *this; } /// Comparing arrays bool operator == (const StaticArray &inRHS) const { if (mSize != inRHS.mSize) return false; for (size_type i = 0; i < mSize; ++i) if (!(reinterpret_cast(mElements[i]) == reinterpret_cast(inRHS.mElements[i]))) return false; return true; } bool operator != (const StaticArray &inRHS) const { if (mSize != inRHS.mSize) return true; for (size_type i = 0; i < mSize; ++i) if (reinterpret_cast(mElements[i]) != reinterpret_cast(inRHS.mElements[i])) return true; return false; } protected: struct alignas(T) Storage { uint8 mData[sizeof(T)]; }; static_assert(sizeof(T) == sizeof(Storage), "Mismatch in size"); static_assert(alignof(T) == alignof(Storage), "Mismatch in alignment"); size_type mSize = 0; Storage mElements[N]; }; JPH_NAMESPACE_END JPH_SUPPRESS_WARNING_PUSH JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat") namespace std { /// Declare std::hash for StaticArray template struct hash> { size_t operator () (const JPH::StaticArray &inRHS) const { std::size_t ret = 0; // Hash length first JPH::HashCombine(ret, inRHS.size()); // Then hash elements for (const T &t : inRHS) JPH::HashCombine(ret, t); return ret; } }; } JPH_SUPPRESS_WARNING_POP