Merge remote-tracking branch 'upstream/edge' into feature/abc-axis
[clinton/Smoothieware.git] / src / libs / MemoryPool.cpp
CommitLineData
3077abb6
MM
1#include "MemoryPool.h"
2
8d54c34c
JM
3#include "StreamOutput.h"
4
3077abb6 5#include <mri.h>
1803076a 6#include <cstdio>
3077abb6 7
78d03fd7
JM
8// this catches all usages of delete blah. The object's destructor is called before we get here
9// it first checks if the deleted object is part of a pool, and uses free otherwise.
10void operator delete(void* p)
11{
12 MemoryPool* m = MemoryPool::first;
13 while (m)
14 {
15 if (m->has(p))
16 {
17 MDEBUG("Pool %p has %p, using dealloc()\n", m, p);
18 m->dealloc(p);
19 return;
20 }
21 m = m->next;
22 }
23
24 MDEBUG("no pool has %p, using free()\n", p);
25 free(p);
26}
27
28
3077abb6
MM
29#define offset(x) (((uint8_t*) x) - ((uint8_t*) this->base))
30
31typedef struct __attribute__ ((packed))
32{
33 uint32_t next :31;
34 uint32_t used :1;
35
36 uint8_t data[];
37} _poolregion;
38
39MemoryPool* MemoryPool::first = NULL;
40
41MemoryPool::MemoryPool(void* base, uint16_t size)
42{
43 this->base = base;
44 this->size = size;
56c83a64 45
3077abb6
MM
46 ((_poolregion*) base)->used = 0;
47 ((_poolregion*) base)->next = size;
48
3077abb6
MM
49 // insert ourselves into head of LL
50 next = first;
51 first = this;
52}
53
54MemoryPool::~MemoryPool()
55{
56 MDEBUG("Pool %p destroyed: region %p (%d)\n", this, base, size);
57
58 // remove ourselves from the LL
59 if (first == this)
60 { // special case: we're first
61 first = this->next;
62 return;
63 }
64
65 // otherwise search the LL for the previous pool
66 MemoryPool* m = first;
67 while (m)
68 {
69 if (m->next == this)
70 {
71 m->next = next;
72 return;
73 }
74 m = m->next;
75 }
76}
77
78void* MemoryPool::alloc(size_t nbytes)
79{
80 // nbytes = ceil(nbytes / 4) * 4
81 if (nbytes & 3)
82 nbytes += 4 - (nbytes & 3);
83
84 // start at the start
85 _poolregion* p = ((_poolregion*) base);
86
87 // find the allocation size including our metadata
88 uint16_t nsize = nbytes + sizeof(_poolregion);
89
90 MDEBUG("\tallocate %d bytes from %p\n", nsize, base);
91
92 // now we walk the list, looking for a sufficiently large free block
93 do {
94 MDEBUG("\t\tchecking %p (%s, %db)\n", p, (p->used?"used":"free"), p->next);
95 if ((p->used == 0) && (p->next >= nsize))
96 { // we found a free space that's big enough
97 MDEBUG("\t\tFOUND free block at %p (%+d) with %d bytes\n", p, offset(p), p->next);
98 // mark it as used
99 p->used = 1;
100
101 // if there's free space at the end of this block
102 if (p->next > nsize)
103 {
104 // q = p->next
105 _poolregion* q = (_poolregion*) (((uint8_t*) p) + nsize);
106
107 MDEBUG("\t\twriting header to %p (%+d) (%d)\n", q, offset(q), p->next - nsize);
108 // write a new block header into q
109 q->used = 0;
110 q->next = p->next - nsize;
111
112 // set our next to point to it
113 p->next = nsize;
114
3077abb6 115 // sanity check
56c83a64 116 if (offset(q) >= size)
3077abb6
MM
117 {
118 // captain, we have a problem!
119 // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full
120 __debugbreak();
121 }
122 }
123
3077abb6
MM
124 // then return the data region for the block
125 return &p->data;
126 }
127
128 // p = p->next
129 p = (_poolregion*) (((uint8_t*) p) + p->next);
130
131 // make sure we don't walk off the end
56c83a64 132 } while (p <= (_poolregion*) (((uint8_t*)base) + size));
3077abb6
MM
133
134 // fell off the end of the region!
135 return NULL;
136}
137
138void MemoryPool::dealloc(void* d)
139{
140 _poolregion* p = (_poolregion*) (((uint8_t*) d) - sizeof(_poolregion));
141 p->used = 0;
142
143 MDEBUG("\tdeallocating %p (%+d, %db)\n", p, offset(p), p->next);
144
145 // combine next block if it's free
146 _poolregion* q = (_poolregion*) (((uint8_t*) p) + p->next);
147 if (q->used == 0)
148 {
149 MDEBUG("\t\tCombining with next free region at %p, new size is %d\n", q, p->next + q->next);
150
3077abb6 151 // sanity check
56c83a64 152 if (offset(q) > size)
3077abb6
MM
153 {
154 // captain, we have a problem!
155 // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full
156 __debugbreak();
157 }
158
159 p->next += q->next;
160 }
161
162 // walk the list to find previous block
163 q = (_poolregion*) base;
164 do {
165 // check if q is the previous block
166 if ((((uint8_t*) q) + q->next) == (uint8_t*) p) {
167 // q is the previous block.
168 if (q->used == 0)
169 { // if q is free
170 MDEBUG("\t\tCombining with previous free region at %p, new size is %d\n", q, p->next + q->next);
171
172 // combine!
173 q->next += p->next;
174
3077abb6 175 // sanity check
56c83a64 176 if ((offset(p) + p->next) >= size)
3077abb6
MM
177 {
178 // captain, we have a problem!
179 // this can only happen if something has corrupted our heap, since we should simply fail to find a free block if it's full
180 __debugbreak();
181 }
182 }
183
184 // we found previous block, return
185 return;
186 }
187
188 // return if last block
56c83a64 189 if (offset(q) + q->next >= size)
3077abb6
MM
190 return;
191
192 // q = q->next
193 q = (_poolregion*) (((uint8_t*) q) + q->next);
194
195 // if some idiot deallocates our memory region while we're using it, strange things can happen.
196 // avoid an infinite loop in that case, however we'll still leak memory and may corrupt things
197 if (q->next == 0)
198 return;
199
56c83a64
MM
200 // make sure we don't walk off the end
201 } while (q < (_poolregion*) (((uint8_t*) base) + size));
3077abb6
MM
202}
203
1803076a 204void MemoryPool::debug(StreamOutput* str)
3077abb6 205{
3077abb6 206 _poolregion* p = (_poolregion*) base;
1803076a
MM
207 uint32_t tot = 0;
208 uint32_t free = 0;
209 str->printf("Start: %ub MemoryPool at %p\n", size, p);
3077abb6 210 do {
1803076a
MM
211 str->printf("\tChunk at %p (%+4d): %s, %lu bytes\n", p, offset(p), (p->used?"used":"free"), p->next);
212 tot += p->next;
213 if (p->used == 0)
214 free += p->next;
215 if ((offset(p) + p->next >= size) || (p->next <= sizeof(_poolregion)))
3077abb6 216 {
1803076a 217 str->printf("End: total %lub, free: %lub\n", tot, free);
3077abb6
MM
218 return;
219 }
220 p = (_poolregion*) (((uint8_t*) p) + p->next);
221 } while (1);
3077abb6
MM
222}
223
224bool MemoryPool::has(void* p)
225{
226 return ((p >= base) && (p < (void*) (((uint8_t*) base) + size)));
227}
228
229uint32_t MemoryPool::free()
230{
56c83a64 231 uint32_t free = 0;
3077abb6
MM
232
233 _poolregion* p = (_poolregion*) base;
234
235 do {
236 if (p->used == 0)
56c83a64
MM
237 free += p->next;
238 if (offset(p) + p->next >= size)
239 return free;
240 if (p->next <= sizeof(_poolregion))
3077abb6
MM
241 return free;
242 p = (_poolregion*) (((uint8_t*) p) + p->next);
243 } while (1);
244}