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