1 //----------------------------------------------------------------
2 // Statically-allocated memory manager
3 //
4 // by Eli Bendersky (eliben@gmail.com)
5 //
6 // This code is in the public domain.
7 //----------------------------------------------------------------
8 #include "memmgr.h"
9
10 typedef ulong Align;
11
12 union mem_header_union
13 {
14 struct
15 {
16 // Pointer to the next block in the free list
17 //
18 union mem_header_union* next;
19
20 // Size of the block (in quantas of sizeof(mem_header_t))
21 //
22 ulong size;
23 } s;
24
25 // Used to align headers in memory to a boundary
26 //
27 Align align_dummy;
28 };
29
30 typedef union mem_header_union mem_header_t;
31
32 // Initial empty list
33 //
34 static mem_header_t base;
35
36 // Start of free list
37 //
38 static mem_header_t* freep = 0;
39
40 // Static pool for new allocations
41 //
42 static byte pool[POOL_SIZE] = {0};
43 static ulong pool_free_pos = 0;
44
45
memmgr_init()46 void memmgr_init()
47 {
48 base.s.next = 0;
49 base.s.size = 0;
50 freep = 0;
51 pool_free_pos = 0;
52 }
53
54
get_mem_from_pool(ulong nquantas)55 static mem_header_t* get_mem_from_pool(ulong nquantas)
56 {
57 ulong total_req_size;
58
59 mem_header_t* h;
60
61 if (nquantas < MIN_POOL_ALLOC_QUANTAS)
62 nquantas = MIN_POOL_ALLOC_QUANTAS;
63
64 total_req_size = nquantas * sizeof(mem_header_t);
65
66 if (pool_free_pos + total_req_size <= POOL_SIZE)
67 {
68 h = (mem_header_t*) (pool + pool_free_pos);
69 h->s.size = nquantas;
70 memmgr_free((void*) (h + 1));
71 pool_free_pos += total_req_size;
72 }
73 else
74 {
75 return 0;
76 }
77
78 return freep;
79 }
80
81
82 // Allocations are done in 'quantas' of header size.
83 // The search for a free block of adequate size begins at the point 'freep'
84 // where the last block was found.
85 // If a too-big block is found, it is split and the tail is returned (this
86 // way the header of the original needs only to have its size adjusted).
87 // The pointer returned to the user points to the free space within the block,
88 // which begins one quanta after the header.
89 //
memmgr_alloc(ulong nbytes)90 void* memmgr_alloc(ulong nbytes)
91 {
92 mem_header_t* p;
93 mem_header_t* prevp;
94
95 // Calculate how many quantas are required: we need enough to house all
96 // the requested bytes, plus the header. The -1 and +1 are there to make sure
97 // that if nbytes is a multiple of nquantas, we don't allocate too much
98 //
99 ulong nquantas = (nbytes + sizeof(mem_header_t) - 1) / sizeof(mem_header_t) + 1;
100
101 // First alloc call, and no free list yet ? Use 'base' for an initial
102 // denegerate block of size 0, which points to itself
103 //
104 if ((prevp = freep) == 0)
105 {
106 base.s.next = freep = prevp = &base;
107 base.s.size = 0;
108 }
109
110 for (p = prevp->s.next; ; prevp = p, p = p->s.next)
111 {
112 // big enough ?
113 if (p->s.size >= nquantas)
114 {
115 // exactly ?
116 if (p->s.size == nquantas)
117 {
118 // just eliminate this block from the free list by pointing
119 // its prev's next to its next
120 //
121 prevp->s.next = p->s.next;
122 }
123 else // too big
124 {
125 p->s.size -= nquantas;
126 p += p->s.size;
127 p->s.size = nquantas;
128 }
129
130 freep = prevp;
131 return (void*) (p + 1);
132 }
133 // Reached end of free list ?
134 // Try to allocate the block from the pool. If that succeeds,
135 // get_mem_from_pool adds the new block to the free list and
136 // it will be found in the following iterations. If the call
137 // to get_mem_from_pool doesn't succeed, we've run out of
138 // memory
139 //
140 else if (p == freep)
141 {
142 if ((p = get_mem_from_pool(nquantas)) == 0)
143 {
144 #ifdef DEBUG_MEMMGR_FATAL
145 printf("!! Memory allocation failed !!\n");
146 #endif
147 return 0;
148 }
149 }
150 }
151 }
152
153
154 // Scans the free list, starting at freep, looking the the place to insert the
155 // free block. This is either between two existing blocks or at the end of the
156 // list. In any case, if the block being freed is adjacent to either neighbor,
157 // the adjacent blocks are combined.
158 //
memmgr_free(void * ap)159 void memmgr_free(void* ap)
160 {
161 mem_header_t* block;
162 mem_header_t* p;
163
164 // acquire pointer to block header
165 block = ((mem_header_t*) ap) - 1;
166
167 // Find the correct place to place the block in (the free list is sorted by
168 // address, increasing order)
169 //
170 for (p = freep; !(block > p && block < p->s.next); p = p->s.next)
171 {
172 // Since the free list is circular, there is one link where a
173 // higher-addressed block points to a lower-addressed block.
174 // This condition checks if the block should be actually
175 // inserted between them
176 //
177 if (p >= p->s.next && (block > p || block < p->s.next))
178 break;
179 }
180
181 // Try to combine with the higher neighbor
182 //
183 if (block + block->s.size == p->s.next)
184 {
185 block->s.size += p->s.next->s.size;
186 block->s.next = p->s.next->s.next;
187 }
188 else
189 {
190 block->s.next = p->s.next;
191 }
192
193 // Try to combine with the lower neighbor
194 //
195 if (p + p->s.size == block)
196 {
197 p->s.size += block->s.size;
198 p->s.next = block->s.next;
199 }
200 else
201 {
202 p->s.next = block;
203 }
204
205 freep = p;
206 }
207