Files
noahbackus 9d30169a8d
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
initial commit, 4.5 stable
2025-09-16 20:46:46 -04:00

714 lines
16 KiB
C++

// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2024 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
#include <Jolt/Core/STLAllocator.h>
#include <Jolt/Core/HashCombine.h>
#ifdef JPH_USE_STD_VECTOR
JPH_SUPPRESS_WARNINGS_STD_BEGIN
#include <vector>
JPH_SUPPRESS_WARNINGS_STD_END
JPH_NAMESPACE_BEGIN
template <class T, class Allocator = STLAllocator<T>> using Array = std::vector<T, Allocator>;
JPH_NAMESPACE_END
#else
JPH_NAMESPACE_BEGIN
/// Simple replacement for std::vector
///
/// Major differences:
/// - Memory is not initialized to zero (this was causing a lot of page faults when deserializing large MeshShapes / HeightFieldShapes)
/// - Iterators are simple pointers (for now)
/// - No exception safety
/// - No specialization like std::vector<bool> has
/// - Not all functions have been implemented
template <class T, class Allocator = STLAllocator<T>>
class [[nodiscard]] Array : private Allocator
{
public:
using value_type = T;
using allocator_type = Allocator;
using size_type = size_t;
using difference_type = typename Allocator::difference_type;
using pointer = T *;
using const_pointer = const T *;
using reference = T &;
using const_reference = const T &;
using const_iterator = const T *;
using iterator = T *;
/// An iterator that traverses the array in reverse order
class rev_it
{
public:
/// Constructor
rev_it() = default;
explicit rev_it(T *inValue) : mValue(inValue) { }
/// Copying
rev_it(const rev_it &) = default;
rev_it & operator = (const rev_it &) = default;
/// Comparison
bool operator == (const rev_it &inRHS) const { return mValue == inRHS.mValue; }
bool operator != (const rev_it &inRHS) const { return mValue != inRHS.mValue; }
/// Arithmetics
rev_it & operator ++ () { --mValue; return *this; }
rev_it operator ++ (int) { return rev_it(mValue--); }
rev_it & operator -- () { ++mValue; return *this; }
rev_it operator -- (int) { return rev_it(mValue++); }
rev_it operator + (int inValue) { return rev_it(mValue - inValue); }
rev_it operator - (int inValue) { return rev_it(mValue + inValue); }
rev_it & operator += (int inValue) { mValue -= inValue; return *this; }
rev_it & operator -= (int inValue) { mValue += inValue; return *this; }
/// Access
T & operator * () const { return *mValue; }
T & operator -> () const { return *mValue; }
private:
T * mValue;
};
/// A const iterator that traverses the array in reverse order
class crev_it
{
public:
/// Constructor
crev_it() = default;
explicit crev_it(const T *inValue) : mValue(inValue) { }
/// Copying
crev_it(const crev_it &) = default;
explicit crev_it(const rev_it &inValue) : mValue(inValue.mValue) { }
crev_it & operator = (const crev_it &) = default;
crev_it & operator = (const rev_it &inRHS) { mValue = inRHS.mValue; return *this; }
/// Comparison
bool operator == (const crev_it &inRHS) const { return mValue == inRHS.mValue; }
bool operator != (const crev_it &inRHS) const { return mValue != inRHS.mValue; }
/// Arithmetics
crev_it & operator ++ () { --mValue; return *this; }
crev_it operator ++ (int) { return crev_it(mValue--); }
crev_it & operator -- () { ++mValue; return *this; }
crev_it operator -- (int) { return crev_it(mValue++); }
crev_it operator + (int inValue) { return crev_it(mValue - inValue); }
crev_it operator - (int inValue) { return crev_it(mValue + inValue); }
crev_it & operator += (int inValue) { mValue -= inValue; return *this; }
crev_it & operator -= (int inValue) { mValue += inValue; return *this; }
/// Access
const T & operator * () const { return *mValue; }
const T & operator -> () const { return *mValue; }
private:
const T * mValue;
};
using reverse_iterator = rev_it;
using const_reverse_iterator = crev_it;
private:
/// Move elements from one location to another
inline void move(pointer inDestination, pointer inSource, size_type inCount)
{
if constexpr (std::is_trivially_copyable<T>())
memmove(inDestination, inSource, inCount * sizeof(T));
else
{
if (inDestination < inSource)
{
for (T *destination_end = inDestination + inCount; inDestination < destination_end; ++inDestination, ++inSource)
{
new (inDestination) T(std::move(*inSource));
inSource->~T();
}
}
else
{
for (T *destination = inDestination + inCount - 1, *source = inSource + inCount - 1; destination >= inDestination; --destination, --source)
{
new (destination) T(std::move(*source));
source->~T();
}
}
}
}
/// Reallocate the data block to inNewCapacity
inline void reallocate(size_type inNewCapacity)
{
JPH_ASSERT(inNewCapacity > 0 && inNewCapacity >= mSize);
pointer ptr;
if constexpr (AllocatorHasReallocate<Allocator>::sValue)
{
// Reallocate data block
ptr = get_allocator().reallocate(mElements, mCapacity, inNewCapacity);
}
else
{
// Copy data to a new location
ptr = get_allocator().allocate(inNewCapacity);
if (mElements != nullptr)
{
move(ptr, mElements, mSize);
get_allocator().deallocate(mElements, mCapacity);
}
}
mElements = ptr;
mCapacity = inNewCapacity;
}
/// Destruct elements [inStart, inEnd - 1]
inline void destruct(size_type inStart, size_type inEnd)
{
if constexpr (!std::is_trivially_destructible<T>())
if (inStart < inEnd)
for (T *element = mElements + inStart, *element_end = mElements + inEnd; element < element_end; ++element)
element->~T();
}
public:
/// Reserve array space
inline void reserve(size_type inNewSize)
{
if (mCapacity < inNewSize)
reallocate(inNewSize);
}
/// Resize array to new length
inline void resize(size_type inNewSize)
{
destruct(inNewSize, mSize);
reserve(inNewSize);
if constexpr (!std::is_trivially_constructible<T>())
for (T *element = mElements + mSize, *element_end = mElements + inNewSize; element < element_end; ++element)
new (element) T;
mSize = inNewSize;
}
/// Resize array to new length and initialize all elements with inValue
inline void resize(size_type inNewSize, const T &inValue)
{
JPH_ASSERT(&inValue < mElements || &inValue >= mElements + mSize, "Can't pass an element from the array to resize");
destruct(inNewSize, mSize);
reserve(inNewSize);
for (T *element = mElements + mSize, *element_end = mElements + inNewSize; element < element_end; ++element)
new (element) T(inValue);
mSize = inNewSize;
}
/// Destruct all elements and set length to zero
inline void clear()
{
destruct(0, mSize);
mSize = 0;
}
private:
/// Grow the array by at least inAmount elements
inline void grow(size_type inAmount = 1)
{
size_type min_size = mSize + inAmount;
if (min_size > mCapacity)
{
size_type new_capacity = max(min_size, mCapacity * 2);
reserve(new_capacity);
}
}
/// Free memory
inline void free()
{
get_allocator().deallocate(mElements, mCapacity);
mElements = nullptr;
mCapacity = 0;
}
/// Destroy all elements and free memory
inline void destroy()
{
if (mElements != nullptr)
{
clear();
free();
}
}
public:
/// Replace the contents of this array with inBegin .. inEnd
template <class Iterator>
inline void assign(Iterator inBegin, Iterator inEnd)
{
clear();
reserve(size_type(std::distance(inBegin, inEnd)));
for (Iterator element = inBegin; element != inEnd; ++element)
new (&mElements[mSize++]) T(*element);
}
/// Replace the contents of this array with inList
inline void assign(std::initializer_list<T> inList)
{
clear();
reserve(size_type(inList.size()));
for (const T &v : inList)
new (&mElements[mSize++]) T(v);
}
/// Default constructor
Array() = default;
/// Constructor with allocator
explicit inline Array(const Allocator &inAllocator) :
Allocator(inAllocator)
{
}
/// Constructor with length
explicit inline Array(size_type inLength, const Allocator &inAllocator = { }) :
Allocator(inAllocator)
{
resize(inLength);
}
/// Constructor with length and value
inline Array(size_type inLength, const T &inValue, const Allocator &inAllocator = { }) :
Allocator(inAllocator)
{
resize(inLength, inValue);
}
/// Constructor from initializer list
inline Array(std::initializer_list<T> inList, const Allocator &inAllocator = { }) :
Allocator(inAllocator)
{
assign(inList);
}
/// Constructor from iterator
inline Array(const_iterator inBegin, const_iterator inEnd, const Allocator &inAllocator = { }) :
Allocator(inAllocator)
{
assign(inBegin, inEnd);
}
/// Copy constructor
inline Array(const Array<T, Allocator> &inRHS) :
Allocator(inRHS.get_allocator())
{
assign(inRHS.begin(), inRHS.end());
}
/// Move constructor
inline Array(Array<T, Allocator> &&inRHS) noexcept :
Allocator(std::move(inRHS.get_allocator())),
mSize(inRHS.mSize),
mCapacity(inRHS.mCapacity),
mElements(inRHS.mElements)
{
inRHS.mSize = 0;
inRHS.mCapacity = 0;
inRHS.mElements = nullptr;
}
/// Destruct all elements
inline ~Array()
{
destroy();
}
/// Get the allocator
inline Allocator & get_allocator()
{
return *this;
}
inline const Allocator &get_allocator() const
{
return *this;
}
/// Add element to the back of the array
inline void push_back(const T &inValue)
{
JPH_ASSERT(&inValue < mElements || &inValue >= mElements + mSize, "Can't pass an element from the array to push_back");
grow();
T *element = mElements + mSize++;
new (element) T(inValue);
}
inline void push_back(T &&inValue)
{
grow();
T *element = mElements + mSize++;
new (element) T(std::move(inValue));
}
/// Construct element at the back of the array
template <class... A>
inline T & emplace_back(A &&... inValue)
{
grow();
T *element = mElements + mSize++;
new (element) T(std::forward<A>(inValue)...);
return *element;
}
/// Remove element from the back of the array
inline void pop_back()
{
JPH_ASSERT(mSize > 0);
mElements[--mSize].~T();
}
/// Returns true if there are no elements in the array
inline bool empty() const
{
return mSize == 0;
}
/// Returns amount of elements in the array
inline size_type size() const
{
return mSize;
}
/// Returns maximum amount of elements the array can hold
inline size_type capacity() const
{
return mCapacity;
}
/// Reduce the capacity of the array to match its size
void shrink_to_fit()
{
if (mElements != nullptr)
{
if (mSize == 0)
free();
else if (mCapacity > mSize)
reallocate(mSize);
}
}
/// Swap the contents of two arrays
void swap(Array<T, Allocator> &inRHS) noexcept
{
std::swap(get_allocator(), inRHS.get_allocator());
std::swap(mSize, inRHS.mSize);
std::swap(mCapacity, inRHS.mCapacity);
std::swap(mElements, inRHS.mElements);
}
template <class Iterator>
void insert(const_iterator inPos, Iterator inBegin, Iterator inEnd)
{
size_type num_elements = size_type(std::distance(inBegin, inEnd));
if (num_elements > 0)
{
// After grow() inPos may be invalid
size_type first_element = inPos - mElements;
grow(num_elements);
T *element_begin = mElements + first_element;
T *element_end = element_begin + num_elements;
move(element_end, element_begin, mSize - first_element);
for (T *element = element_begin; element < element_end; ++element, ++inBegin)
new (element) T(*inBegin);
mSize += num_elements;
}
}
void insert(const_iterator inPos, const T &inValue)
{
JPH_ASSERT(&inValue < mElements || &inValue >= mElements + mSize, "Can't pass an element from the array to insert");
// After grow() inPos may be invalid
size_type first_element = inPos - mElements;
grow();
T *element = mElements + first_element;
move(element + 1, element, mSize - first_element);
new (element) T(inValue);
mSize++;
}
/// Remove one element from the array
iterator erase(const_iterator inIter)
{
size_type p = size_type(inIter - begin());
JPH_ASSERT(p < mSize);
mElements[p].~T();
if (p + 1 < mSize)
move(mElements + p, mElements + p + 1, mSize - p - 1);
--mSize;
return const_cast<iterator>(inIter);
}
/// Remove multiple element from the array
iterator 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());
destruct(p, p + n);
if (p + n < mSize)
move(mElements + p, mElements + p + n, mSize - p - n);
mSize -= n;
return const_cast<iterator>(inBegin);
}
/// Iterators
inline const_iterator begin() const
{
return mElements;
}
inline const_iterator end() const
{
return mElements + mSize;
}
inline crev_it rbegin() const
{
return crev_it(mElements + mSize - 1);
}
inline crev_it rend() const
{
return crev_it(mElements - 1);
}
inline const_iterator cbegin() const
{
return begin();
}
inline const_iterator cend() const
{
return end();
}
inline crev_it crbegin() const
{
return rbegin();
}
inline crev_it crend() const
{
return rend();
}
inline iterator begin()
{
return mElements;
}
inline iterator end()
{
return mElements + mSize;
}
inline rev_it rbegin()
{
return rev_it(mElements + mSize - 1);
}
inline rev_it rend()
{
return rev_it(mElements - 1);
}
inline const T * data() const
{
return mElements;
}
inline T * data()
{
return mElements;
}
/// Access element
inline T & operator [] (size_type inIdx)
{
JPH_ASSERT(inIdx < mSize);
return mElements[inIdx];
}
inline const T & operator [] (size_type inIdx) const
{
JPH_ASSERT(inIdx < mSize);
return mElements[inIdx];
}
/// Access element
inline T & at(size_type inIdx)
{
JPH_ASSERT(inIdx < mSize);
return mElements[inIdx];
}
inline const T & at(size_type inIdx) const
{
JPH_ASSERT(inIdx < mSize);
return mElements[inIdx];
}
/// First element in the array
inline const T & front() const
{
JPH_ASSERT(mSize > 0);
return mElements[0];
}
inline T & front()
{
JPH_ASSERT(mSize > 0);
return mElements[0];
}
/// Last element in the array
inline const T & back() const
{
JPH_ASSERT(mSize > 0);
return mElements[mSize - 1];
}
inline T & back()
{
JPH_ASSERT(mSize > 0);
return mElements[mSize - 1];
}
/// Assignment operator
Array<T, Allocator> & operator = (const Array<T, Allocator> &inRHS)
{
if (static_cast<const void *>(this) != static_cast<const void *>(&inRHS))
assign(inRHS.begin(), inRHS.end());
return *this;
}
/// Assignment move operator
Array<T, Allocator> & operator = (Array<T, Allocator> &&inRHS) noexcept
{
if (static_cast<const void *>(this) != static_cast<const void *>(&inRHS))
{
destroy();
get_allocator() = std::move(inRHS.get_allocator());
mSize = inRHS.mSize;
mCapacity = inRHS.mCapacity;
mElements = inRHS.mElements;
inRHS.mSize = 0;
inRHS.mCapacity = 0;
inRHS.mElements = nullptr;
}
return *this;
}
/// Assignment operator
Array<T, Allocator> & operator = (std::initializer_list<T> inRHS)
{
assign(inRHS);
return *this;
}
/// Comparing arrays
bool operator == (const Array<T, Allocator> &inRHS) const
{
if (mSize != inRHS.mSize)
return false;
for (size_type i = 0; i < mSize; ++i)
if (!(mElements[i] == inRHS.mElements[i]))
return false;
return true;
}
bool operator != (const Array<T, Allocator> &inRHS) const
{
if (mSize != inRHS.mSize)
return true;
for (size_type i = 0; i < mSize; ++i)
if (mElements[i] != inRHS.mElements[i])
return true;
return false;
}
/// Get hash for this array
uint64 GetHash() const
{
// Hash length first
uint64 ret = Hash<uint32> { } (uint32(size()));
// Then hash elements
for (const T *element = mElements, *element_end = mElements + mSize; element < element_end; ++element)
HashCombine(ret, *element);
return ret;
}
private:
size_type mSize = 0;
size_type mCapacity = 0;
T * mElements = nullptr;
};
JPH_NAMESPACE_END
JPH_SUPPRESS_WARNING_PUSH
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat")
namespace std
{
/// Declare std::hash for Array
template <class T, class Allocator>
struct hash<JPH::Array<T, Allocator>>
{
size_t operator () (const JPH::Array<T, Allocator> &inRHS) const
{
return std::size_t(inRHS.GetHash());
}
};
}
JPH_SUPPRESS_WARNING_POP
#endif // JPH_USE_STD_VECTOR