Skip to content

Commit

Permalink
Added FixedVector
Browse files Browse the repository at this point in the history
Allocator isn't a template template anymore, that doesn't work with multiple template parameters.
  • Loading branch information
jlaumon committed Dec 20, 2024
1 parent 44d734e commit 1f861c6
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 77 deletions.
72 changes: 50 additions & 22 deletions Bedrock/Allocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,62 +34,80 @@ struct TempAllocator


// Allocates from an externally provided MemArena.
template <typename taType, int taMaxPendingFrees>
struct ArenaAllocatorBase
template <typename taType, int taMaxPendingFrees = cDefaultMaxPendingFrees>
struct ArenaAllocator
{
using MemArenaType = MemArena<taMaxPendingFrees>;

ArenaAllocatorBase() = default;
ArenaAllocatorBase(MemArenaType& inArena) : mArena(&inArena) {}
ArenaAllocator() = default;
ArenaAllocator(MemArenaType& inArena) : mArena(&inArena) {}

// Allocate memory.
taType* Allocate(int inSize) { return (taType*)mArena->Alloc(inSize * sizeof(taType)).mPtr; }
void Free(taType* inPtr, int inSize) { mArena->Free({ (uint8*)inPtr, inSize * (int64)sizeof(taType) }); }
taType* Allocate(int inSize) { return (taType*)mArena->Alloc(inSize * sizeof(taType)).mPtr; }
void Free(taType* inPtr, int inSize) { mArena->Free({ (uint8*)inPtr, inSize * (int64)sizeof(taType) }); }

// Try changing the size of an existing allocation, return false if unsuccessful.
bool TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize);
bool TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize);

int MaxCapacity() const { return (int)mArena->GetMemBlock().mSize / sizeof(taType); }
MemArenaType* GetArena() { return mArena; }
const MemArenaType* GetArena() const { return mArena; }

private:
MemArenaType* mArena = nullptr;
};

// Shorter version with default number of allowed out of order frees.
// This alias is needed because containers only accept alloctor with a single template parameter.
template <typename taType>
using ArenaAllocator = ArenaAllocatorBase<taType, cDefaultMaxPendingFrees>;


// Allocates from an internal VMemArena which uses virtual memory.
// The VMemArena can grow as necessary by committing more virtual memory.
template <typename taType>
struct VMemAllocator
{
using VMemArenaType = VMemArena<0>; // Don't need to support any out of order free since the arena isn't shared.
using MemArenaType = VMemArena<0>; // Don't need to support any out of order free since the arena isn't shared.

static constexpr int64 cDefaultReservedSize = VMemArenaType::cDefaultReservedSize; // By default the arena will reserve that much virtual memory.
static constexpr int64 cDefaultCommitSize = VMemArenaType::cDefaultCommitSize; // By default the arena will commit that much virtual memory every time it grows.
static constexpr int64 cDefaultReservedSize = MemArenaType::cDefaultReservedSize; // By default the arena will reserve that much virtual memory.
static constexpr int64 cDefaultCommitSize = MemArenaType::cDefaultCommitSize; // By default the arena will commit that much virtual memory every time it grows.

VMemAllocator() = default;
VMemAllocator(int inReservedSizeInBytes, int inCommitIncreaseSizeInBytes = cDefaultCommitSize)
: mArena(inReservedSizeInBytes, inCommitIncreaseSizeInBytes) {}

// Allocate memory.
taType* Allocate(int inSize);
void Free(taType* inPtr, int inSize) { mArena.Free({ (uint8*)inPtr, inSize * (int64)sizeof(taType) }); }
taType* Allocate(int inSize);
void Free(taType* inPtr, int inSize) { mArena.Free({ (uint8*)inPtr, inSize * (int64)sizeof(taType) }); }

// Try changing the size of an existing allocation, return false if unsuccessful.
bool TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize);
bool TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize);

const VMemArenaType* GetArena() const { return &mArena; }
int MaxCapacity() const { return mArena.GetReservedSize() / sizeof(taType); }
const MemArenaType* GetArena() const { return &mArena; }

private:
VMemArenaType mArena;
MemArenaType mArena;
};


// Allocates from an internal FixedMemArena.
template <typename taType, int taSize>
struct FixedAllocator
{
using MemArenaType = FixedMemArena<taSize * sizeof(taType), 0>; // Don't need to support any out of order free since the arena isn't shared.

// Allocate memory.
// Allocate memory.
taType* Allocate(int inSize) { return (taType*)mArena.Alloc(inSize * sizeof(taType)).mPtr; }
void Free(taType* inPtr, int inSize) { mArena.Free({ (uint8*)inPtr, inSize * (int64)sizeof(taType) }); }

// Try changing the size of an existing allocation, return false if unsuccessful.
bool TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize);

constexpr int MaxCapacity() const { return taSize; }
const MemArenaType* GetArena() const { return &mArena; }

private:
MemArenaType mArena;
};



template <typename taType>
Expand Down Expand Up @@ -130,7 +148,7 @@ bool TempAllocator<taType>::TryRealloc(taType* inPtr, int inCurrentSize, int inN


template <typename taType, int taMaxPendingFrees>
bool ArenaAllocatorBase<taType, taMaxPendingFrees>::TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize)
bool ArenaAllocator<taType, taMaxPendingFrees>::TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize)
{
gAssert(inPtr != nullptr); // Call Allocate instead.

Expand All @@ -145,7 +163,7 @@ taType* VMemAllocator<taType>::Allocate(int inSize)
// If the arena wasn't initialized yet, do it now (with default values).
// It's better to do it lazily than reserving virtual memory in every container default constructor.
if (mArena.GetMemBlock() == nullptr) [[unlikely]]
mArena = VMemArenaType(cDefaultReservedSize, cDefaultCommitSize);
mArena = MemArenaType(cDefaultReservedSize, cDefaultCommitSize);

return (taType*)mArena.Alloc(inSize * sizeof(taType)).mPtr;
}
Expand All @@ -159,3 +177,13 @@ bool VMemAllocator<taType>::TryRealloc(taType* inPtr, int inCurrentSize, int inN
MemBlock mem = { (uint8*)inPtr, inCurrentSize * (int64)sizeof(taType) };
return mArena.TryRealloc(mem, inNewSize * sizeof(taType));
}


template <typename taType, int taSize>
bool FixedAllocator<taType, taSize>::TryRealloc(taType* inPtr, int inCurrentSize, int inNewSize)
{
gAssert(inPtr != nullptr); // Call Allocate instead.

MemBlock mem = { (uint8*)inPtr, inCurrentSize * (int64)sizeof(taType) };
return mArena.TryRealloc(mem, inNewSize * sizeof(taType));
}
4 changes: 2 additions & 2 deletions Bedrock/HashMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -541,8 +541,8 @@ struct HashMap : taHash
mBuckets[bucket_index] = {};
}

Vector<KeyValue, taAllocator> mKeyValues; // Key-value pairs stored in a dense array.
Vector<Bucket, taAllocator> mBuckets; // Bucket metadata.
Vector<KeyValue, taAllocator<KeyValue>> mKeyValues; // Key-value pairs stored in a dense array.
Vector<Bucket, taAllocator<Bucket>> mBuckets; // Bucket metadata.
};


Expand Down
43 changes: 42 additions & 1 deletion Bedrock/Vector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,6 @@ REGISTER_TEST("ArenaVector")
};



REGISTER_TEST("VMemVector")
{
VMemVector<int> test;
Expand Down Expand Up @@ -326,4 +325,46 @@ REGISTER_TEST("VMemVector")
test2 = gMove(test);
TEST_TRUE(Span(test2) == heap_vec);
TEST_TRUE(test.Empty());
};


REGISTER_TEST("FixedVector")
{
FixedVector<int, 4> test;
test = { 1, 2, 3, 4 };

TEST_TRUE(test.Size() == 4);
TEST_TRUE(test.Capacity() == 4);
TEST_TRUE(test.MaxCapacity() == 4);

int* test_begin = test.Begin();

Vector<int> heap_vec = test;
TEST_TRUE(test.Begin() != heap_vec.Begin());
TEST_TRUE(Span(test) == Span(heap_vec));

heap_vec = { 7, 8, 9 };
test = heap_vec;
TEST_TRUE(test.Begin() == test_begin);
TEST_TRUE(test.Begin() != heap_vec.Begin());
TEST_TRUE(Span(test) == heap_vec);

test.PushBack(1);
test.PopBack();
TEST_TRUE(test.Capacity() > test.Size());
test.ShrinkToFit();
TEST_TRUE(test.Capacity() == test.Size());

FixedVector<int, 8> test2;

// Copy to a different vector
test2 = test;
TEST_TRUE(test2 == Span(test));
TEST_TRUE(test2.Data() != test.Data());

// Move also copies since there's no move operator
test2 = gMove(test);
TEST_TRUE(Span(test2) == heap_vec);
TEST_TRUE(!test.Empty());
TEST_TRUE(test2.Data() != test.Data());
};
Loading

0 comments on commit 1f861c6

Please sign in to comment.