-
Notifications
You must be signed in to change notification settings - Fork 961
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement FixedAllocator and use it for allocating memory in VmtCopy
- Loading branch information
1 parent
43eb73a
commit 4f281c8
Showing
11 changed files
with
269 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
#pragma once | ||
|
||
#include <cstddef> | ||
|
||
#include "FreeMemoryRegionList.h" | ||
|
||
template <std::size_t Capacity> | ||
class FixedAllocator { | ||
public: | ||
FixedAllocator() = default; | ||
FixedAllocator(const FixedAllocator&) = delete; | ||
FixedAllocator(FixedAllocator&&) = delete; | ||
FixedAllocator& operator=(const FixedAllocator&) = delete; | ||
FixedAllocator& operator=(FixedAllocator&&) = delete; | ||
|
||
static_assert(Capacity >= FreeMemoryRegionList::minimumAllocationSize()); | ||
|
||
[[nodiscard]] std::byte* allocate(std::size_t size) noexcept | ||
{ | ||
return freeRegionList.allocate(size); | ||
} | ||
|
||
void deallocate(std::byte* pointer, std::size_t size) noexcept | ||
{ | ||
freeRegionList.deallocate(pointer, size); | ||
} | ||
|
||
private: | ||
alignas(FreeMemoryRegionList::minimumAlignment()) std::byte storage[Capacity]; | ||
FreeMemoryRegionList freeRegionList{ storage }; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#pragma once | ||
|
||
#include <cassert> | ||
#include <cstddef> | ||
#include <cstdint> | ||
|
||
struct FreeMemoryRegion { | ||
std::size_t size; | ||
FreeMemoryRegion* nextFreeRegion; | ||
|
||
[[nodiscard]] bool isLargeEnoughForAllocation(std::size_t allocationSize) const noexcept | ||
{ | ||
if (size < allocationSize) | ||
return false; | ||
|
||
const auto sizeAfterAllocation = size - allocationSize; | ||
return sizeAfterAllocation == 0 || sizeAfterAllocation >= sizeof(FreeMemoryRegion); | ||
} | ||
|
||
void tryMergeWithNextRegion() noexcept | ||
{ | ||
assert(nextFreeRegion == nullptr || reinterpret_cast<std::uintptr_t>(nextFreeRegion) > reinterpret_cast<std::uintptr_t>(this)); | ||
if (nextFreeRegion && reinterpret_cast<std::uintptr_t>(nextFreeRegion) - reinterpret_cast<std::uintptr_t>(this) == size) { | ||
size += nextFreeRegion->size; | ||
nextFreeRegion = nextFreeRegion->nextFreeRegion; | ||
} | ||
} | ||
|
||
void appendRegion(std::byte* newRegionPointer, std::size_t newRegionSize) noexcept | ||
{ | ||
assert(newRegionPointer != nullptr); | ||
assert(reinterpret_cast<std::uintptr_t>(newRegionPointer) > reinterpret_cast<std::uintptr_t>(this)); | ||
assert(newRegionSize >= sizeof(FreeMemoryRegion)); | ||
|
||
nextFreeRegion = new (newRegionPointer) FreeMemoryRegion{ newRegionSize, nextFreeRegion }; | ||
nextFreeRegion->tryMergeWithNextRegion(); | ||
tryMergeWithNextRegion(); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <cstdint> | ||
|
||
#include "FreeMemoryRegion.h" | ||
|
||
struct FreeMemoryRegionFinder { | ||
explicit FreeMemoryRegionFinder(FreeMemoryRegion** firstFreeRegion) noexcept | ||
: firstFreeRegion{ firstFreeRegion } | ||
{ | ||
} | ||
|
||
[[nodiscard]] FreeMemoryRegion** findFreeRegion(std::size_t size) const noexcept | ||
{ | ||
for (FreeMemoryRegion** region = firstFreeRegion; *region != nullptr; region = &(*region)->nextFreeRegion) { | ||
if ((*region)->isLargeEnoughForAllocation(size)) | ||
return region; | ||
} | ||
return nullptr; | ||
} | ||
|
||
[[nodiscard]] FreeMemoryRegion** findNearestFreeRegion(std::uintptr_t address) const noexcept | ||
{ | ||
FreeMemoryRegion** region = firstFreeRegion; | ||
while (*region != nullptr && (*region)->nextFreeRegion != nullptr && reinterpret_cast<std::uintptr_t>((*region)->nextFreeRegion) < address) | ||
region = &(*region)->nextFreeRegion; | ||
return region; | ||
} | ||
|
||
private: | ||
FreeMemoryRegion** firstFreeRegion; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
#pragma once | ||
|
||
#include <cassert> | ||
#include <cstddef> | ||
#include <cstdint> | ||
#include <span> | ||
|
||
#include "FreeMemoryRegion.h" | ||
#include "FreeMemoryRegionFinder.h" | ||
|
||
#ifndef NDEBUG | ||
#include <Utils/MemorySection.h> | ||
#endif | ||
|
||
class FreeMemoryRegionList { | ||
public: | ||
explicit FreeMemoryRegionList(std::span<std::byte> memory) noexcept | ||
: firstFreeRegion{ createInitialFreeRegion(memory) } | ||
#ifndef NDEBUG | ||
, memory{ memory } | ||
#endif | ||
{ | ||
} | ||
|
||
FreeMemoryRegionList(const FreeMemoryRegionList&) = delete; | ||
FreeMemoryRegionList(FreeMemoryRegionList&&) = delete; | ||
FreeMemoryRegionList& operator=(const FreeMemoryRegionList&) = delete; | ||
FreeMemoryRegionList& operator=(FreeMemoryRegionList&&) = delete; | ||
|
||
[[nodiscard]] static constexpr std::size_t minimumAlignment() noexcept | ||
{ | ||
return alignof(FreeMemoryRegion); | ||
} | ||
|
||
[[nodiscard]] static constexpr std::size_t minimumAllocationSize() noexcept | ||
{ | ||
return sizeof(FreeMemoryRegion); | ||
} | ||
|
||
#ifndef NDEBUG | ||
~FreeMemoryRegionList() noexcept | ||
{ | ||
assert(firstFreeRegion != nullptr && firstFreeRegion->size == memory.size() && firstFreeRegion->nextFreeRegion == nullptr && "Memory leak detected"); | ||
} | ||
#endif | ||
|
||
[[nodiscard]] std::byte* allocate(std::size_t size) noexcept | ||
{ | ||
assert(size >= minimumAllocationSize()); | ||
assert(size % minimumAlignment() == 0); | ||
|
||
const auto freeRegion = FreeMemoryRegionFinder{ &firstFreeRegion }.findFreeRegion(size); | ||
if (freeRegion == nullptr) [[unlikely]] | ||
return nullptr; | ||
|
||
const auto result = reinterpret_cast<std::byte*>(*freeRegion); | ||
if ((*freeRegion)->size != size) | ||
*freeRegion = new (result + size) FreeMemoryRegion{ (*freeRegion)->size - size, (*freeRegion)->nextFreeRegion }; | ||
else | ||
*freeRegion = (*freeRegion)->nextFreeRegion; | ||
return result; | ||
} | ||
|
||
void deallocate(std::byte* pointer, std::size_t size) noexcept | ||
{ | ||
assert(MemorySection{ memory }.contains(reinterpret_cast<std::uintptr_t>(pointer), size) && "Invalid pointer or size provided for deallocation"); | ||
|
||
const auto nearestRegion = FreeMemoryRegionFinder{ &firstFreeRegion }.findNearestFreeRegion(reinterpret_cast<std::uintptr_t>(pointer)); | ||
const auto noFreeRegionsBeforeDeallocation = (*nearestRegion == nullptr); | ||
if (noFreeRegionsBeforeDeallocation || reinterpret_cast<std::uintptr_t>(*nearestRegion) > reinterpret_cast<std::uintptr_t>(pointer)) { | ||
*nearestRegion = new (pointer) FreeMemoryRegion{ size, *nearestRegion }; | ||
} else { | ||
(*nearestRegion)->appendRegion(pointer, size); | ||
} | ||
|
||
if (!noFreeRegionsBeforeDeallocation) | ||
(*nearestRegion)->tryMergeWithNextRegion(); | ||
} | ||
|
||
private: | ||
[[nodiscard]] static FreeMemoryRegion* createInitialFreeRegion(std::span<std::byte> memory) noexcept | ||
{ | ||
assert(memory.size() >= minimumAllocationSize()); | ||
assert(std::uintptr_t(memory.data()) % minimumAlignment() == 0); | ||
return new (memory.data()) FreeMemoryRegion{ memory.size(), nullptr }; | ||
} | ||
|
||
FreeMemoryRegion* firstFreeRegion; | ||
#ifndef NDEBUG | ||
std::span<std::byte> memory; | ||
#endif | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,30 @@ | ||
#pragma once | ||
|
||
#include <cstddef> | ||
#include <type_traits> | ||
|
||
#include <Utils/Align.h> | ||
|
||
#include "FreeMemoryRegionList.h" | ||
|
||
struct MemoryAllocator { | ||
[[nodiscard]] static std::byte* allocate(std::size_t size) noexcept; | ||
static void deallocate(std::byte* memory, std::size_t size) noexcept; | ||
|
||
template <typename T> | ||
[[nodiscard]] static auto memoryFor() noexcept | ||
{ | ||
static_assert(!std::is_array_v<T>); | ||
static_assert(alignof(T) <= FreeMemoryRegionList::minimumAlignment(), "Unsupported alignment"); | ||
return utils::align<sizeof(T), FreeMemoryRegionList::minimumAlignment()>(); | ||
} | ||
|
||
template <typename T> | ||
[[nodiscard]] static auto memoryFor(std::size_t numberOfElements) noexcept | ||
{ | ||
static_assert(std::is_array_v<T>); | ||
static_assert(alignof(T) <= FreeMemoryRegionList::minimumAlignment(), "Unsupported alignment"); | ||
assert(numberOfElements <= (std::numeric_limits<std::size_t>::max)() / sizeof(std::remove_extent_t<T>)); | ||
return utils::align(numberOfElements * sizeof(std::remove_extent_t<T>), FreeMemoryRegionList::minimumAlignment()); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#pragma once | ||
|
||
#include <cassert> | ||
#include <cstddef> | ||
#include <limits> | ||
|
||
namespace utils | ||
{ | ||
|
||
template <std::size_t Size, std::size_t Alignment> | ||
consteval std::size_t align() noexcept | ||
{ | ||
if constexpr (constexpr auto offset = Alignment - Size % Alignment; offset != Alignment) { | ||
static_assert(Size <= (std::numeric_limits<std::size_t>::max)() - offset, "Size is too large to align"); | ||
return Size + offset; | ||
} | ||
return Size; | ||
} | ||
|
||
[[nodiscard]] inline std::size_t align(std::size_t size, std::size_t alignment) noexcept | ||
{ | ||
if (const auto offset = alignment - size % alignment; offset != alignment) { | ||
assert(size <= (std::numeric_limits<std::size_t>::max)() - offset && "Size is too large to align"); | ||
return size + offset; | ||
} | ||
return size; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters