-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsingle_allocator.hpp
More file actions
125 lines (102 loc) · 3.47 KB
/
Copy pathsingle_allocator.hpp
File metadata and controls
125 lines (102 loc) · 3.47 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
#pragma once
#include <type_traits>
#include <concepts>
#include <compare>
#include <cassert>
#include <utility>
#include "concept_extras.hpp"
#include "allocator_info.hpp"
#include "smart_pointers.hpp"
namespace compile_time::allocator {
template<typename U, std::size_t amnt> struct single_allocator;
template<typename U> struct single_allocator<U, 0>{
//initial allocations. Find and track high water mark.
struct plain_delete : public destructor<U>{
single_info<U> *this_info{nullptr};
constexpr plain_delete() = default;
constexpr ~plain_delete() = default;
constexpr void destroy(U* tofree){
this_info->current_amount--;
delete tofree;
}
};
plain_delete deleter;
template<typename ThisInfo, typename... Args>
constexpr allocated_ptr<U> alloc(ThisInfo &info, Args && ...args) {
single_info<U> &this_info = info;
if (!deleter.this_info) deleter.this_info = &this_info;
this_info.current_amount++;
if (this_info.current_amount > this_info.high_water_mark){
this_info.high_water_mark = this_info.current_amount;
}
return allocated_ptr<U>{
new U(std::forward<Args>(args)...),
&deleter
};
}
template<typename ThisInfo>
constexpr allocated_ptr<U> rewrap(U* tofree){
return allocated_ptr<U>{tofree,deleter.ptr};
}
};
template<typename U, std::size_t amnt> struct single_allocator{
//the exact-size case
struct free_list_node : public destructor<U>{
free_list_node* next {nullptr};
U this_payload;
std::size_t refcount{0};
free_list_node ** free_list{nullptr};
constexpr free_list_node() = default;
constexpr free_list_node(const free_list_node&) = delete;
constexpr free_list_node(free_list_node&&) = delete;
constexpr ~free_list_node() = default;
constexpr void destroy(U* _this){
assert(_this == &this_payload);
if (refcount == 0){
assert(free_list);
next = *free_list;
*free_list = this;
} else --refcount;
}
constexpr void share(U* _this){
assert(_this == &this_payload);
++refcount;
}
};
free_list_node free_list_storage[amnt];
//remember: guaranteed amnt > 0
free_list_node *free_list = &free_list_storage[0];
constexpr single_allocator(){
for (auto i = 0u; i + 1 < amnt; ++i){
free_list_storage[i].next = &free_list_storage[i+1];
}
for (auto &ln : free_list_storage){
ln.free_list = &free_list;
}
}
constexpr single_allocator(const single_allocator&) = delete;
constexpr single_allocator(single_allocator&&) = delete;
constexpr ~single_allocator() = default;
template<typename... Args>
constexpr allocated_ptr<U> alloc(auto&&, Args && ...args) {
assert(free_list);
/*
decided not to do the fancy version where we
explicitly know which nodes are destined to survive,
so there should always be enough pre-reserved space
here :)
*/
auto &selected_free_ln = *free_list;
free_list = selected_free_ln.next;
selected_free_ln.this_payload = U(std::forward<Args>(args)...);
return allocated_ptr<U>{&selected_free_ln.this_payload,&selected_free_ln};
}
constexpr allocated_ptr<U> rewrap(U* tofree){
for (auto &potential_freed_node : free_list_storage){
if (tofree == &potential_freed_node.this_payload){
return allocated_ptr<U>{tofree,&potential_freed_node};
}
}
}
};
}