123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- // Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
- // SPDX-FileCopyrightText: 2025 Jorrit Rouwe
- // SPDX-License-Identifier: MIT
- #include "UnitTestFramework.h"
- #include <Jolt/Core/STLLocalAllocator.h>
- TEST_SUITE("STLLocalAllocatorTest")
- {
- /// The number of elements in the local buffer
- static constexpr size_t N = 20;
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- template <class ArrayType>
- static bool sIsLocal(ArrayType &inArray)
- {
- #ifdef JPH_USE_STD_VECTOR
- // Check that the data pointer is within the array.
- // Note that when using std::vector we cannot use get_allocator as that makes a copy of the allocator internally
- // and we've disabled the copy constructor since our allocator cannot be copied.
- const uint8 *data = reinterpret_cast<const uint8 *>(inArray.data());
- const uint8 *array = reinterpret_cast<const uint8 *>(&inArray);
- return data >= array && data < array + sizeof(inArray);
- #else
- return inArray.get_allocator().is_local(inArray.data());
- #endif
- }
- #endif
- template <class ArrayType, bool NonTrivial>
- static void sTestArray()
- {
- // Allocate so that we will run out of local memory and reallocate from heap at least once
- ArrayType arr;
- for (int i = 0; i < 64; ++i)
- arr.push_back(i);
- CHECK(arr.size() == 64);
- for (int i = 0; i < 64; ++i)
- {
- CHECK(arr[i] == i);
- #if !defined(JPH_USE_STD_VECTOR) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR)
- // We only have to move elements once we run out of the local buffer, this happens as we resize
- // from 16 to 32 elements, we'll reallocate again at 32 and 64
- if constexpr (NonTrivial)
- CHECK(arr[i].GetNonTriv() == (i < 16? 3 : (i < 32? 2 : 1)));
- #endif
- }
- CHECK(IsAligned(arr.data(), alignof(typename ArrayType::value_type)));
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- CHECK(!sIsLocal(arr));
- #endif
- // Check that we can copy the array to another array
- ArrayType arr2;
- arr2 = arr;
- for (int i = 0; i < 64; ++i)
- {
- CHECK(arr2[i] == i);
- if constexpr (NonTrivial)
- CHECK(arr2[i].GetNonTriv() == -999);
- }
- CHECK(IsAligned(arr2.data(), alignof(typename ArrayType::value_type)));
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- CHECK(!sIsLocal(arr2));
- #endif
- // Clear the array
- arr.clear();
- arr.shrink_to_fit();
- CHECK(arr.size() == 0);
- #ifndef JPH_USE_STD_VECTOR // Some implementations of std::vector ignore shrink_to_fit
- CHECK(arr.capacity() == 0);
- CHECK(arr.data() == nullptr);
- #endif
- // Allocate so we stay within the local buffer
- for (int i = 0; i < 10; ++i)
- arr.push_back(i);
- CHECK(arr.size() == 10);
- for (int i = 0; i < 10; ++i)
- {
- CHECK(arr[i] == i);
- #if !defined(JPH_USE_STD_VECTOR) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR)
- // We never need to move elements as they stay within the local buffer
- if constexpr (NonTrivial)
- CHECK(arr[i].GetNonTriv() == 1);
- #endif
- }
- CHECK(IsAligned(arr.data(), alignof(typename ArrayType::value_type)));
- #if !defined(JPH_USE_STD_VECTOR) && !defined(JPH_DISABLE_CUSTOM_ALLOCATOR) // Doesn't work with std::vector since it doesn't use the reallocate function and runs out of space
- CHECK(sIsLocal(arr));
- #endif
- // Check that we can copy the array to the local buffer
- ArrayType arr3;
- arr3 = arr;
- CHECK(arr3.size() == 10);
- for (int i = 0; i < 10; ++i)
- {
- CHECK(arr3[i] == i);
- if constexpr (NonTrivial)
- CHECK(arr3[i].GetNonTriv() == -999);
- }
- CHECK(IsAligned(arr3.data(), alignof(typename ArrayType::value_type)));
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- CHECK(sIsLocal(arr3));
- #endif
- // Check that if we reserve the memory, that we can fully fill the array in local memory
- ArrayType arr4;
- arr4.reserve(N);
- for (int i = 0; i < int(N); ++i)
- arr4.push_back(i);
- CHECK(arr4.size() == N);
- CHECK(arr4.capacity() == N);
- for (int i = 0; i < int(N); ++i)
- {
- CHECK(arr4[i] == i);
- if constexpr (NonTrivial)
- CHECK(arr4[i].GetNonTriv() == 1);
- }
- CHECK(IsAligned(arr4.data(), alignof(typename ArrayType::value_type)));
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- CHECK(sIsLocal(arr4));
- #endif
- }
- TEST_CASE("TestAllocation")
- {
- using Allocator = STLLocalAllocator<int, N>;
- using ArrayType = Array<int, Allocator>;
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- static_assert(AllocatorHasReallocate<Allocator>::sValue);
- #endif
- sTestArray<ArrayType, false>();
- }
- TEST_CASE("TestAllocationAligned")
- {
- // Force the need for an aligned allocation
- struct alignas(64) Aligned
- {
- Aligned(int inValue) : mValue(inValue) { }
- operator int() const { return mValue; }
- private:
- int mValue;
- };
- static_assert(std::is_trivially_copyable<Aligned>());
- using Allocator = STLLocalAllocator<Aligned, N>;
- using ArrayType = Array<Aligned, Allocator>;
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- static_assert(AllocatorHasReallocate<Allocator>::sValue);
- #endif
- sTestArray<ArrayType, false>();
- }
- TEST_CASE("TestAllocationNonTrivial")
- {
- // Force non trivial copy constructor
- struct NonTriv
- {
- NonTriv(int inValue) : mValue(inValue) { }
- NonTriv(const NonTriv &inRHS) : mValue(inRHS.mValue), mMakeNonTriv(-999) { }
- NonTriv(NonTriv &&inRHS) : mValue(inRHS.mValue), mMakeNonTriv(inRHS.mMakeNonTriv + 1) { }
- NonTriv & operator = (const NonTriv &inRHS) { mValue = inRHS.mValue; mMakeNonTriv = -9999; return *this; }
- operator int() const { return mValue; }
- int GetNonTriv() const { return mMakeNonTriv; }
- private:
- int mValue;
- int mMakeNonTriv = 0;
- };
- static_assert(!std::is_trivially_copyable<NonTriv>());
- using Allocator = STLLocalAllocator<NonTriv, N>;
- using ArrayType = Array<NonTriv, Allocator>;
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- static_assert(AllocatorHasReallocate<Allocator>::sValue);
- #endif
- sTestArray<ArrayType, true>();
- }
- TEST_CASE("TestAllocationAlignedNonTrivial")
- {
- // Force non trivial copy constructor
- struct alignas(64) AlNonTriv
- {
- AlNonTriv(int inValue) : mValue(inValue) { }
- AlNonTriv(const AlNonTriv &inRHS) : mValue(inRHS.mValue), mMakeNonTriv(-999) { }
- AlNonTriv(AlNonTriv &&inRHS) : mValue(inRHS.mValue), mMakeNonTriv(inRHS.mMakeNonTriv + 1) { }
- AlNonTriv & operator = (const AlNonTriv &inRHS) { mValue = inRHS.mValue; mMakeNonTriv = -9999; return *this; }
- operator int() const { return mValue; }
- int GetNonTriv() const { return mMakeNonTriv; }
- private:
- int mValue;
- int mMakeNonTriv = 0;
- };
- static_assert(!std::is_trivially_copyable<AlNonTriv>());
- using Allocator = STLLocalAllocator<AlNonTriv, N>;
- using ArrayType = Array<AlNonTriv, Allocator>;
- #ifndef JPH_DISABLE_CUSTOM_ALLOCATOR
- static_assert(AllocatorHasReallocate<Allocator>::sValue);
- #endif
- sTestArray<ArrayType, true>();
- }
- }
|