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