171 lines
5.7 KiB
C++
Vendored
171 lines
5.7 KiB
C++
Vendored
// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
|
|
// SPDX-FileCopyrightText: 2025 Jorrit Rouwe
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#pragma once
|
|
|
|
#include <Jolt/Core/STLAllocator.h>
|
|
|
|
JPH_NAMESPACE_BEGIN
|
|
|
|
#ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
|
|
|
|
/// STL allocator that keeps N elements in a local buffer before falling back to regular allocations
|
|
template <typename T, size_t N>
|
|
class STLLocalAllocator : private STLAllocator<T>
|
|
{
|
|
using Base = STLAllocator<T>;
|
|
|
|
public:
|
|
/// General properties
|
|
using value_type = T;
|
|
using pointer = T *;
|
|
using const_pointer = const T *;
|
|
using reference = T &;
|
|
using const_reference = const T &;
|
|
using size_type = size_t;
|
|
using difference_type = ptrdiff_t;
|
|
|
|
/// The allocator is not stateless (has local buffer)
|
|
using is_always_equal = std::false_type;
|
|
|
|
/// We cannot copy, move or swap allocators
|
|
using propagate_on_container_copy_assignment = std::false_type;
|
|
using propagate_on_container_move_assignment = std::false_type;
|
|
using propagate_on_container_swap = std::false_type;
|
|
|
|
/// Constructor
|
|
STLLocalAllocator() = default;
|
|
STLLocalAllocator(const STLLocalAllocator &) = delete; // Can't copy an allocator as the buffer is local to the original
|
|
STLLocalAllocator(STLLocalAllocator &&) = delete; // Can't move an allocator as the buffer is local to the original
|
|
STLLocalAllocator & operator = (const STLLocalAllocator &) = delete; // Can't copy an allocator as the buffer is local to the original
|
|
|
|
/// Constructor used when rebinding to another type. This expects the allocator to use the original memory pool from the first allocator,
|
|
/// but in our case we cannot use the local buffer of the original allocator as it has different size and alignment rules.
|
|
/// To solve this we make this allocator fall back to the heap immediately.
|
|
template <class T2>
|
|
explicit STLLocalAllocator(const STLLocalAllocator<T2, N> &) : mNumElementsUsed(N) { }
|
|
|
|
/// Check if inPointer is in the local buffer
|
|
inline bool is_local(const_pointer inPointer) const
|
|
{
|
|
ptrdiff_t diff = inPointer - reinterpret_cast<const_pointer>(mElements);
|
|
return diff >= 0 && diff < ptrdiff_t(N);
|
|
}
|
|
|
|
/// Allocate memory
|
|
inline pointer allocate(size_type inN)
|
|
{
|
|
// If we allocate more than we have, fall back to the heap
|
|
if (mNumElementsUsed + inN > N)
|
|
return Base::allocate(inN);
|
|
|
|
// Allocate from our local buffer
|
|
pointer result = reinterpret_cast<pointer>(mElements) + mNumElementsUsed;
|
|
mNumElementsUsed += inN;
|
|
return result;
|
|
}
|
|
|
|
/// Always implements a reallocate function as we can often reallocate in place
|
|
static constexpr bool has_reallocate = true;
|
|
|
|
/// Reallocate memory
|
|
inline pointer reallocate(pointer inOldPointer, size_type inOldSize, size_type inNewSize)
|
|
{
|
|
JPH_ASSERT(inNewSize > 0); // Reallocating to zero size is implementation dependent, so we don't allow it
|
|
|
|
// If there was no previous allocation, we can go through the regular allocate function
|
|
if (inOldPointer == nullptr)
|
|
return allocate(inNewSize);
|
|
|
|
// If the pointer is outside our local buffer, fall back to the heap
|
|
if (!is_local(inOldPointer))
|
|
{
|
|
if constexpr (AllocatorHasReallocate<Base>::sValue)
|
|
return Base::reallocate(inOldPointer, inOldSize, inNewSize);
|
|
else
|
|
return ReallocateImpl(inOldPointer, inOldSize, inNewSize);
|
|
}
|
|
|
|
// If we happen to have space left, we only need to update our bookkeeping
|
|
pointer base_ptr = reinterpret_cast<pointer>(mElements) + mNumElementsUsed - inOldSize;
|
|
if (inOldPointer == base_ptr
|
|
&& mNumElementsUsed - inOldSize + inNewSize <= N)
|
|
{
|
|
mNumElementsUsed += inNewSize - inOldSize;
|
|
return base_ptr;
|
|
}
|
|
|
|
// We can't reallocate in place, fall back to the heap
|
|
return ReallocateImpl(inOldPointer, inOldSize, inNewSize);
|
|
}
|
|
|
|
/// Free memory
|
|
inline void deallocate(pointer inPointer, size_type inN)
|
|
{
|
|
// If the pointer is not in our local buffer, fall back to the heap
|
|
if (!is_local(inPointer))
|
|
return Base::deallocate(inPointer, inN);
|
|
|
|
// Else we can only reclaim memory if it was the last allocation
|
|
if (inPointer == reinterpret_cast<pointer>(mElements) + mNumElementsUsed - inN)
|
|
mNumElementsUsed -= inN;
|
|
}
|
|
|
|
/// Allocators are not-stateless, assume if allocator address matches that the allocators are the same
|
|
inline bool operator == (const STLLocalAllocator<T, N> &inRHS) const
|
|
{
|
|
return this == &inRHS;
|
|
}
|
|
|
|
inline bool operator != (const STLLocalAllocator<T, N> &inRHS) const
|
|
{
|
|
return this != &inRHS;
|
|
}
|
|
|
|
/// Converting to allocator for other type
|
|
template <typename T2>
|
|
struct rebind
|
|
{
|
|
using other = STLLocalAllocator<T2, N>;
|
|
};
|
|
|
|
private:
|
|
/// Implements reallocate when the base class doesn't or when we go from local buffer to heap
|
|
inline pointer ReallocateImpl(pointer inOldPointer, size_type inOldSize, size_type inNewSize)
|
|
{
|
|
pointer new_pointer = Base::allocate(inNewSize);
|
|
size_type n = min(inOldSize, inNewSize);
|
|
if constexpr (std::is_trivially_copyable<T>())
|
|
{
|
|
// Can use mem copy
|
|
memcpy(new_pointer, inOldPointer, n * sizeof(T));
|
|
}
|
|
else
|
|
{
|
|
// Need to actually move the elements
|
|
for (size_t i = 0; i < n; ++i)
|
|
{
|
|
new (new_pointer + i) T(std::move(inOldPointer[i]));
|
|
inOldPointer[i].~T();
|
|
}
|
|
}
|
|
deallocate(inOldPointer, inOldSize);
|
|
return new_pointer;
|
|
}
|
|
|
|
alignas(T) uint8 mElements[N * sizeof(T)];
|
|
size_type mNumElementsUsed = 0;
|
|
};
|
|
|
|
/// The STLLocalAllocator always implements a reallocate function as it can often reallocate in place
|
|
template <class T, size_t N> struct AllocatorHasReallocate<STLLocalAllocator<T, N>> { static constexpr bool sValue = STLLocalAllocator<T, N>::has_reallocate; };
|
|
|
|
#else
|
|
|
|
template <typename T, size_t N> using STLLocalAllocator = std::allocator<T>;
|
|
|
|
#endif // !JPH_DISABLE_CUSTOM_ALLOCATOR
|
|
|
|
JPH_NAMESPACE_END
|