Skip to content

Commit d67bb3d

Browse files
committed
Add week 8 project
1 parent 000317f commit d67bb3d

14 files changed

+986
-0
lines changed

CMakeLists.txt

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
cmake_minimum_required(VERSION 3.26)
2+
3+
project(memory_test)
4+
5+
set(CMAKE_CXX_STANDARD 20)
6+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
7+
8+
# add googletest - not needed on godbolt!
9+
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
10+
include(FetchContent)
11+
FetchContent_Declare(
12+
googletest
13+
URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip
14+
)
15+
# For Windows: Prevent overriding the parent project's compiler/linker settings
16+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
17+
FetchContent_MakeAvailable(googletest)
18+
endif()
19+
20+
# add cpp files for the allocator lib here
21+
add_library(allocator_lib
22+
allocators/MemoryOverrides.cpp
23+
allocators/SingleLinkAllocator.cpp
24+
allocators/FixedSizeAllocator.cpp
25+
allocators/FixedSizeAllocator.h
26+
allocators/StackAllocator.cpp
27+
allocators/StackAllocator.h
28+
)
29+
30+
# compile at max warning level + treat warnings as errors
31+
target_compile_options(allocator_lib PRIVATE
32+
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
33+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Werror>
34+
)
35+
36+
# add cpp files for the tests here
37+
add_executable(allocator_tests
38+
tests/SingleLinkAllocatorTest.cpp
39+
tests/main.cpp
40+
tests/FixedSizeAllocatorTest.cpp
41+
tests/StackAllocatorTest.cpp
42+
)
43+
44+
# compile at max warning level + treat warnings as errors
45+
target_compile_options(allocator_tests PRIVATE
46+
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
47+
$<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wall -Werror>
48+
)
49+
50+
target_include_directories(allocator_tests PRIVATE
51+
${CMAKE_CURRENT_SOURCE_DIR}/allocators/
52+
)
53+
54+
target_link_libraries(allocator_tests
55+
allocator_lib
56+
gtest
57+
)

allocators/FixedSizeAllocator.cpp

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "FixedSizeAllocator.h"
2+
3+
#include <stdexcept>
4+
5+
namespace dae
6+
{
7+
8+
FixedSizeAllocator::FixedSizeAllocator(uint32_t numOfBlock, uint32_t sizeOfEachBlock)
9+
: m_numOfBlocks{numOfBlock}
10+
, m_sizeOfEachBlock{sizeOfEachBlock}
11+
, m_numFreeBlocks{numOfBlock}
12+
, m_numInitialized{0}
13+
, m_memStart {new uint8_t[numOfBlock * sizeOfEachBlock]}
14+
, m_next{m_memStart}
15+
{
16+
}
17+
18+
FixedSizeAllocator::~FixedSizeAllocator()
19+
{
20+
delete[] m_memStart;
21+
}
22+
23+
void * FixedSizeAllocator::Acquire(size_t nbBytes)
24+
{
25+
if (nbBytes > m_sizeOfEachBlock or m_numFreeBlocks == 0)
26+
{
27+
throw std::bad_alloc();
28+
}
29+
if (m_numInitialized < m_numOfBlocks)
30+
{
31+
auto p = reinterpret_cast<uint32_t *>(AddFromIndex(m_numInitialized));
32+
*p = m_numInitialized + 1;
33+
m_numInitialized++;
34+
}
35+
void *ret = nullptr;
36+
if (m_numFreeBlocks > 0)
37+
{
38+
ret = m_next;
39+
--m_numFreeBlocks;
40+
if (m_numFreeBlocks != 0)
41+
{
42+
m_next = AddFromIndex(*m_next);
43+
}
44+
else
45+
{
46+
m_next = nullptr;
47+
}
48+
}
49+
return ret;
50+
}
51+
52+
void FixedSizeAllocator::Release(void *pointerToBuffer)
53+
{
54+
// Not sure if this is necessary
55+
// if (pointerToBuffer == nullptr)
56+
// {
57+
// throw std::runtime_error("Pointer is null");
58+
// }
59+
// if (m_numFreeBlocks == m_numOfBlocks)
60+
// {
61+
// throw std::runtime_error("All blocks are free");
62+
// }
63+
if (pointerToBuffer < m_memStart or pointerToBuffer >= m_memStart + m_numOfBlocks * m_sizeOfEachBlock)
64+
{
65+
throw std::runtime_error("Pointer is out of range");
66+
}
67+
if (m_next != nullptr)
68+
{
69+
*reinterpret_cast<uint32_t *>(pointerToBuffer) = IndexFromAddr(m_next);
70+
m_next = reinterpret_cast<uint8_t *>(pointerToBuffer);
71+
}
72+
else
73+
{
74+
*reinterpret_cast<uint32_t *>(pointerToBuffer) = m_numOfBlocks;
75+
m_next = reinterpret_cast<uint8_t *>(pointerToBuffer);
76+
}
77+
++m_numFreeBlocks;
78+
}
79+
80+
auto FixedSizeAllocator::AddFromIndex(uint32_t i) const -> uint8_t *
81+
{
82+
return m_memStart + i * m_sizeOfEachBlock;
83+
}
84+
85+
auto FixedSizeAllocator::IndexFromAddr(uint8_t const *p) const -> uint32_t
86+
{
87+
return static_cast<uint32_t>(p - m_memStart) / m_sizeOfEachBlock;
88+
}
89+
}

allocators/FixedSizeAllocator.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
3+
#include "MemoryAllocator.h"
4+
5+
#include <cstdint>
6+
7+
namespace dae
8+
{
9+
/*
10+
* Based on: Fast Efficient Fixed-Size Memory Pool - Ben Kenwright
11+
* https://arxiv.org/pdf/2210.16471
12+
*/
13+
class FixedSizeAllocator final : public MemoryAllocator
14+
{
15+
public:
16+
FixedSizeAllocator(uint32_t numOfBlock, uint32_t sizeOfEachBlock);
17+
~FixedSizeAllocator() override;
18+
19+
FixedSizeAllocator(const FixedSizeAllocator &) = delete;
20+
FixedSizeAllocator(FixedSizeAllocator &&) = delete;
21+
FixedSizeAllocator &operator=(const FixedSizeAllocator &) = delete;
22+
FixedSizeAllocator &operator=(const FixedSizeAllocator &&) = delete;
23+
24+
auto Acquire(size_t nbBytes) -> void * override;
25+
void Release(void *pointerToBuffer) override;
26+
27+
[[nodiscard]] auto AddFromIndex(uint32_t i) const -> uint8_t *;
28+
auto IndexFromAddr(uint8_t const *p) const -> uint32_t;
29+
30+
private:
31+
uint32_t m_numOfBlocks = 0;
32+
uint32_t m_sizeOfEachBlock = 0;
33+
uint32_t m_numFreeBlocks = 0;
34+
uint32_t m_numInitialized = 0;
35+
uint8_t *m_memStart = nullptr;
36+
uint8_t *m_next = nullptr;
37+
};
38+
39+
}

allocators/MemoryAllocator.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
#include <stddef.h> // for size_t
3+
4+
namespace dae
5+
{
6+
class MemoryAllocator
7+
{
8+
public:
9+
virtual ~MemoryAllocator() = default;
10+
11+
MemoryAllocator(const MemoryAllocator &) = delete;
12+
MemoryAllocator(MemoryAllocator &&) = delete;
13+
MemoryAllocator &operator=(const MemoryAllocator &) = delete;
14+
MemoryAllocator &operator=(const MemoryAllocator &&) = delete;
15+
16+
virtual auto Acquire(size_t = 0) -> void * = 0;
17+
virtual void Release(void *) = 0;
18+
19+
struct Tag
20+
{
21+
MemoryAllocator *pool;
22+
};
23+
24+
protected:
25+
MemoryAllocator() = default;
26+
};
27+
}

allocators/MemoryOverrides.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "MemoryOverrides.h"
2+
#include "MemoryAllocator.h"
3+
4+
#include <cstdlib>
5+
6+
using namespace dae;
7+
8+
void *operator new(size_t nbBytes, MemoryAllocator &storagePool)
9+
{
10+
if (nbBytes == 0) nbBytes = 1;
11+
while (true)
12+
{
13+
auto *const tag = reinterpret_cast<MemoryAllocator::Tag *>(storagePool.Acquire(
14+
nbBytes + sizeof(MemoryAllocator::Tag)));
15+
tag->pool = &storagePool;
16+
return tag + 1;
17+
}
18+
}
19+
20+
void *operator new[](size_t nbBytes, MemoryAllocator &storagePool)
21+
{
22+
return operator new(nbBytes, storagePool);
23+
}
24+
25+
void *operator new(size_t nbBytes)
26+
{
27+
if (nbBytes == 0) nbBytes = 1;
28+
while (true)
29+
{
30+
auto *const tag = reinterpret_cast<MemoryAllocator::Tag *>(malloc(nbBytes + sizeof(MemoryAllocator::Tag)));
31+
tag->pool = nullptr;
32+
return tag + 1;
33+
}
34+
}
35+
36+
void *operator new[](size_t nbBytes)
37+
{
38+
return operator new(nbBytes);
39+
}
40+
41+
void operator delete(void *pointerToBuffer, MemoryAllocator &storagePool) noexcept
42+
{
43+
if (pointerToBuffer != nullptr)
44+
{
45+
MemoryAllocator::Tag *const tag = reinterpret_cast<MemoryAllocator::Tag *>(pointerToBuffer) - 1;
46+
storagePool.Release(tag);
47+
}
48+
}
49+
50+
void operator delete(void *pointerToBuffer) noexcept
51+
{
52+
if (pointerToBuffer != nullptr)
53+
{
54+
MemoryAllocator::Tag *const tag = reinterpret_cast<MemoryAllocator::Tag *>(pointerToBuffer) - 1;
55+
if (tag->pool != nullptr)
56+
tag->pool->Release(tag);
57+
else
58+
free(tag);
59+
}
60+
}
61+
62+
void operator delete[](void *pointerToBuffer) noexcept
63+
{
64+
operator delete(pointerToBuffer);
65+
}
66+
67+
void operator delete(void *pointerToBuffer, long unsigned int) noexcept
68+
{
69+
operator delete(pointerToBuffer);
70+
}
71+
72+
void operator delete[](void *pointerToBuffer, long unsigned int) noexcept
73+
{
74+
operator delete(pointerToBuffer);
75+
}

allocators/MemoryOverrides.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#pragma once
2+
#include <stddef.h> // for size_t
3+
4+
// overloads global new and delete.
5+
6+
namespace dae
7+
{
8+
class MemoryAllocator;
9+
}
10+
11+
auto operator new(size_t nbBytes) -> void *;
12+
auto operator new(size_t nbBytes, dae::MemoryAllocator &storagePool) -> void *;
13+
auto operator new[](size_t nbBytes) -> void *;
14+
auto operator new[](size_t nbBytes, dae::MemoryAllocator &storagePool) -> void *;
15+
16+
void operator delete(void *pointerToBuffer) noexcept;
17+
void operator delete(void *pointerToBuffer, long unsigned int) noexcept;
18+
void operator delete[](void *pointerToBuffer) noexcept;
19+
void operator delete[](void *pointerToBuffer, long unsigned int) noexcept;
20+
void operator delete(void *pointerToBuffer, dae::MemoryAllocator &storagePool) noexcept;

0 commit comments

Comments
 (0)