-
-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Expand file tree
/
Copy pathlinear_allocator.cpp
More file actions
117 lines (103 loc) · 3.78 KB
/
linear_allocator.cpp
File metadata and controls
117 lines (103 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
* @file
* @brief Linear Allocator
* @details Implementation of a linear allocator that pre-allocates memory
* for high speed access. See [Region-based memory
* management](https://en.wikipedia.org/wiki/Region-based_memory_management).
*
* A linear (also known a bump) allocator works by allocating a buffer of
* memory upon initialization, and then handing it out per allocation call. An
* index is used to indicate from where to hand out memory, after doing so it
* will be "bumped up" to the next spot. For this implementation, the next spot
* is ensured to be aligned to the largest type on this plaform.
*
* Note: CPUs access data most efficiently when it is aligned (i.e. data of
* size N is located at a memory address that is a multiple of N). Alignment of
* data matters because depending on the platform, when accessing un-aligned
* data a CPU will have reduced performance or straight up crash. See
* https://en.wikipedia.org/wiki/Data_structure_alignment for more info.
*
* One of the biggest reasons for using a compile-time linear allocator is the
* fact that lifetime of data is limited to the scope of use. We don't have to
* worry about freeing up memory as the stack will take care of that for us, and
* for that reason there is no need for a de-allocate function.
*
*
* Key constraints and limitations of this example:
* - As mentioned above, this implementation has the buffer aligned to the
* std::max_align_t which ensures that fundamental types are aligned to the
* largest type on the current platform. However, when trying to allocate for
* complex types which may be larger, this doesn't work. One solution would be
* to take a note from
* [std::pmr::memory_resource::allocate](https://en.cppreference.com/cpp/memory/memory_resource/allocate)
* and have the alignment as a default parameter.
*
* - It is up to the user to ensure that they do not write past the returned
* bytes. This is the same behaviour as "malloc" or "new".
*
*
* @author [Abhi Patel](https://github.com/B33Boy/)
*/
#include "linear_allocator.hpp"
#include <cassert> /// for std::assert
#include <iostream> /// for IO operations
/**
* @brief Tests that the allocator hands out valid aligned memory
* @returns void
*/
static void test_allocator_allocates_data() {
static const size_t CAPACITY = 64;
memory::allocator::linear_allocator<CAPACITY> la{};
// Allocate space for an int
auto* mem = la.allocate(sizeof(int));
assert(mem != nullptr);
// construct the int at the mem with placement new
assert(69 == *new (mem) int{69});
assert(la.offset() > 0);
assert(la.offset() <= CAPACITY);
assert(0 == la.offset() % alignof(std::max_align_t));
}
/**
* @brief Tests that reset returns the offset to zero
* @returns void
*/
static void test_allocator_resets() {
static const size_t CAPACITY = 64;
memory::allocator::linear_allocator<CAPACITY> la{};
auto* mem = la.allocate(sizeof(int));
assert(mem != nullptr);
new (mem) int{69};
assert(la.offset() > 0);
la.reset();
assert(0 == la.offset());
}
/**
* @brief Tests that a full allocator returns nullptr
* @returns void
*/
static void test_allocate_when_full_returns_nullptr() {
static const size_t CAPACITY = alignof(std::max_align_t);
memory::allocator::linear_allocator<CAPACITY> la{};
auto* mem = la.allocate(sizeof(char));
assert(mem != nullptr);
new (mem) char{'A'};
assert(nullptr == la.allocate(CAPACITY));
}
/**
* @brief Self-test implementations
* @returns void
*/
static void test() {
test_allocator_allocates_data();
test_allocator_resets();
test_allocate_when_full_returns_nullptr();
std::cout << "All tests have successfully passed!\n";
}
/**
* @brief Main function
* @returns 0 on exit
*/
int main() {
test();
return 0;
}