-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStackAlloc.hpp
More file actions
145 lines (120 loc) · 3.4 KB
/
StackAlloc.hpp
File metadata and controls
145 lines (120 loc) · 3.4 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
StackAlloc.hpp
Allows allocation for different data types
Gives away memory like a stack, ie. it destroys its elements in reverse order of their creation
*/
#pragma once
#include <vector>
#include <iostream>
#include "MemoryConfig.hpp"
#include "Errors.hpp"
namespace moe {
namespace memory {
/* class to "save" the destructors of the objects using Memory from StackAlloc */
class StackAllocDestructor {
public:
template <typename T>
explicit StackAllocDestructor(const T& data)
: _data(moe_addressof(data))
{
_destructor = [](const void* lambdaData) {
auto type = static_cast<const T*>(lambdaData);
type->~T();
};
}
void operator()() {
_destructor(_data);
}
private:
const void* _data;
void(*_destructor)(const void*); //lambda-expression
};
struct StackAllocMarker {
StackAllocMarker(byte* val, size_t handle)
: value{ val }, destructorHandle{ handle } {}
byte* value;
size_t destructorHandle;
};
class StackAlloc {
public:
explicit StackAlloc(size_t size = ALLOC_DEFAULT_SIZE)
: _size{ size },
_head{ nullptr },
_data{ nullptr }
{
_data = new byte[_size];
_head = _data;
}
~StackAlloc() {
//to do: give user a hint if he deletes a non-empty Allocator
delete[] _data;
_data = nullptr;
_head = nullptr;
}
//no copy and no move:
StackAlloc(const StackAlloc&) = delete;
StackAlloc(StackAlloc&&) = delete;
StackAlloc operator = (const StackAlloc&) = delete;
StackAlloc operator = (StackAlloc&&) = delete;
void* alloc(size_t numBytes, size_t alignment = 1) {
byte* location = (byte*)nextMultiple(alignment, (size_t)_head);
byte* headPtr = location + numBytes;
//check if we have enough memory:
if (headPtr > _data + _size) {
throw errors::MemoryError(("<StackAlloc::alloc> " + OUT_OF_MEMORY).c_str());
}
_head = headPtr;
return location;
}
template <typename T, typename... ARGS>
T* make(size_t numObjs = 1, ARGS&&... args) {
byte* location = (byte*)nextMultiple(alignof(T), (size_t)_head);
byte* headPtr = location + numObjs * sizeof(T);
if (headPtr > _data + _size) {
throw errors::MemoryError(("<StackAlloc::make> " + OUT_OF_MEMORY).c_str());
}
T* objPointer = reinterpret_cast<T*>(location);
_head = headPtr;
//create objs:
for (size_t i = 0; i < numObjs; i++) {
T* obj = new(moe_addressof(objPointer[i])) T(std::forward<ARGS>(args)...);
//keep track of the destructors of our objs:
_destructors.push_back(StackAllocDestructor{ *obj });
}
return objPointer;
}
void destroyAll() {
_head = _data;
for(size_t i = 0; i < _destructors.size(); i++) {
_destructors[i]();
}
_destructors.clear();
}
void destroyToMarker(StackAllocMarker marker) {
_head = marker.value;
while (_destructors.size() > marker.destructorHandle) {
_destructors.back()();
_destructors.pop_back();
}
}
StackAllocMarker getMarker() {
return StackAllocMarker{ _head, _destructors.size() };
}
private:
//only store destructors non-trivially-destructable objs:
template <typename T>
inline typename std::enable_if<std::is_trivially_destructible<T>::value>::type
addToList(T* obj) {
//do nothing
}
template <typename T>
inline typename std::enable_if<!std::is_trivially_destructible<T>::value>::type
addToList(T* obj) {
_destructors.push_back(StackAllocDestructor{ *obj });
}
size_t _size;
byte* _head;
byte* _data;
std::vector<StackAllocDestructor> _destructors;
};
}} //end of namespace moe::memory