comparison tmalloc.c @ 0:772086c29cc7

Initial import.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 16 Nov 2016 11:16:33 +0200
parents
children 33594243ce31
comparison
equal deleted inserted replaced
-1:000000000000 0:772086c29cc7
1 /*
2 * Tiny malloc
3 *
4 * Copyright (c) 2014 Fabrice Bellard
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 #include <stddef.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <inttypes.h>
28 #include <string.h>
29 #include <limits.h>
30 #ifndef MALLOC_TEST
31 #define NDEBUG
32 #endif
33 #include <assert.h>
34
35 /*
36 * Note: only works for 32 bit pointers
37 */
38 #define MALLOC_ALIGN 8
39 #define MALLOC_BLOCK_SIZE 32
40
41 #define STATE_FREE 0xaa
42 #define STATE_ALLOCATED 0x55
43
44 struct list_head {
45 struct list_head *prev, *next;
46 };
47
48 #define list_entry(el, type, member) \
49 ((type *)((uint8_t *)(el) - offsetof(type, member)))
50
51 /* Note: the 'state' byte is stored just before the MemBlock header,
52 so at most 23 bytes can be allocated in a single block. */
53 typedef struct MemBlock {
54 struct list_head link;
55 union {
56 uint8_t data[0] __attribute((aligned(MALLOC_ALIGN)));
57 struct list_head free_link;
58 } u;
59 } MemBlock;
60
61 void *sbrk(intptr_t increment);
62
63 /* Invariants: the last block is always a free block. The last free
64 block is always the last block. */
65 static struct list_head free_list;
66 static struct list_head block_list;
67 static uint8_t *mem_top;
68
69 /* insert 'el' after prev */
70 static void list_add(struct list_head *el, struct list_head *prev)
71 {
72 struct list_head *next = prev->next;
73 prev->next = el;
74 el->prev = prev;
75 el->next = next;
76 next->prev = el;
77 }
78
79 static void list_del(struct list_head *el)
80 {
81 struct list_head *prev, *next;
82 prev = el->prev;
83 next = el->next;
84 prev->next = next;
85 next->prev = prev;
86 }
87
88 static size_t get_alloc_size(size_t size)
89 {
90 size = offsetof(MemBlock, u.data) + size;
91 /* one more byte for the state byte from the next block */
92 size = (size + MALLOC_BLOCK_SIZE) & ~(MALLOC_BLOCK_SIZE - 1);
93 return size;
94 }
95
96 /* Note: this size includes the 'state' byte from the next block */
97 static size_t get_block_size(MemBlock *p)
98 {
99 uint8_t *end;
100 struct list_head *el;
101 el = p->link.next;
102 if (el == &block_list)
103 end = mem_top;
104 else
105 end = (uint8_t *)list_entry(el, MemBlock, link);
106 return end - (uint8_t *)p;
107 }
108
109 static inline void set_block_state(MemBlock *p, int state)
110 {
111 ((uint8_t *)p)[-1] = state;
112 }
113
114 static inline int get_block_state(const MemBlock *p)
115 {
116 return ((const uint8_t *)p)[-1];
117 }
118
119 void *malloc(size_t size)
120 {
121 MemBlock *p, *p1;
122 struct list_head *el;
123 size_t block_size;
124
125 if (size == 0 || size > (INT_MAX - 2 * MALLOC_BLOCK_SIZE))
126 return NULL;
127 if (free_list.next == NULL) {
128 /* init */
129 p = sbrk(MALLOC_BLOCK_SIZE * 2);
130 if (p == (void *)-1)
131 return NULL;
132
133 mem_top = sbrk(0);
134 free_list.prev = free_list.next = &free_list;
135 block_list.prev = block_list.next = &block_list;
136 p++;
137 set_block_state(p, STATE_FREE);
138 list_add(&p->link, &block_list);
139 list_add(&p->u.free_link, &free_list);
140 }
141
142 size = get_alloc_size(size);
143 el = free_list.next;
144 for(;;) {
145 p = list_entry(el, MemBlock, u.free_link);
146 assert(get_block_state(p) == STATE_FREE);
147 block_size = get_block_size(p);
148 if (size < block_size) {
149 goto done1;
150 } else if (el == free_list.prev) {
151 /* last free block: increase its size */
152 if (sbrk(size + MALLOC_BLOCK_SIZE - block_size) == (void *)-1)
153 return NULL;
154 mem_top = sbrk(0);
155 done1:
156 p1 = (MemBlock *)((uint8_t *)p + size);
157 list_add(&p1->link, &p->link);
158 list_add(&p1->u.free_link, &p->u.free_link);
159 set_block_state(p1, STATE_FREE);
160 list_del(&p->u.free_link);
161 done:
162 set_block_state(p, STATE_ALLOCATED);
163 return p->u.data;
164 } else if (size == block_size) {
165 list_del(&p->u.free_link);
166 goto done;
167 }
168 el = el->next;
169 }
170 }
171
172 void free(void *ptr)
173 {
174 MemBlock *p, *p1;
175 struct list_head *el;
176
177 if (!ptr)
178 return;
179 p = (MemBlock *)((uint8_t *)ptr - offsetof(MemBlock, u.data));
180 assert(get_block_state(p) == STATE_ALLOCATED);
181
182 /* mark as free */
183 list_add(&p->u.free_link, &free_list);
184 set_block_state(p, STATE_FREE);
185
186 /* merge with previous free block if possible */
187 el = p->link.prev;
188 if (el != &block_list) {
189 p1 = list_entry(el, MemBlock, link);
190 if (get_block_state(p1) == STATE_FREE) {
191 list_del(&p->link);
192 list_del(&p->u.free_link);
193 p = p1;
194 }
195 }
196 /* merge with next block if possible */
197 el = p->link.next;
198 if (el != &block_list) {
199 p1 = list_entry(el, MemBlock, link);
200 if (get_block_state(p1) == STATE_FREE) {
201 list_del(&p1->link);
202 /* keep p in the same position in free_list as p1 */
203 list_del(&p->u.free_link);
204 list_add(&p->u.free_link, &p1->u.free_link);
205 list_del(&p1->u.free_link);
206 }
207 }
208 }
209
210 void *realloc(void *ptr, size_t size)
211 {
212 MemBlock *p;
213 void *ptr1;
214 size_t size1;
215
216 if (ptr == NULL) {
217 return malloc(size);
218 } else if (size == 0) {
219 free(ptr);
220 return NULL;
221 } else {
222 p = (MemBlock *)((uint8_t *)ptr - offsetof(MemBlock, u.data));
223 assert(get_block_state(p) == STATE_ALLOCATED);
224 ptr1 = malloc(size);
225 if (!ptr1)
226 return NULL;
227 /* Note: never the last block so it is valid */
228 size1 = (uint8_t *)list_entry(p->link.next, MemBlock, link) -
229 p->u.data - 1;
230 if (size < size1)
231 size1 = size;
232 memcpy(ptr1, ptr, size1);
233 free(ptr);
234 return ptr1;
235 }
236 }
237
238 #ifdef MALLOC_TEST
239 static void malloc_check(void)
240 {
241 MemBlock *p;
242 struct list_head *el;
243 int state;
244
245 for(el = block_list.next; el != &block_list; el = el->next) {
246 p = list_entry(el, MemBlock, link);
247 state = get_block_state(p);
248 assert(state == STATE_FREE || state == STATE_ALLOCATED);
249 if (el->next != &block_list)
250 assert(el->next > el);
251 }
252 for(el = free_list.next; el != &free_list; el = el->next) {
253 p = list_entry(el, MemBlock, u.free_link);
254 assert(get_block_state(p) == STATE_FREE);
255 }
256
257 /* check invariant */
258 el = free_list.prev;
259 if (el != &free_list) {
260 p = list_entry(el, MemBlock, u.free_link);
261 assert(&p->link == block_list.prev);
262 }
263 }
264
265 static void malloc_dump(void)
266 {
267 MemBlock *p;
268 struct list_head *el;
269
270 printf("blocks:\n");
271 for(el = block_list.next; el != &block_list; el = el->next) {
272 p = list_entry(el, MemBlock, link);
273 printf("block: %p next=%p free=%d size=%u\n", p, p->link.next,
274 get_block_state(p) == STATE_FREE,
275 (unsigned int)get_block_size(p));
276 }
277 printf("free list:\n");
278 for(el = free_list.next; el != &free_list; el = el->next) {
279 p = list_entry(el, MemBlock, u.free_link);
280 printf("block: %p size=%u\n", p, (unsigned int)get_block_size(p));
281 }
282 }
283
284 int main(int argc, char **argv)
285 {
286 int i, n, j, size;
287 void **tab;
288
289 n = 100;
290 tab = malloc(sizeof(void *) * n);
291 memset(tab, 0, n * sizeof(void *));
292
293 for(i = 0; i < n * 1000; i++) {
294 j = random() % n;
295
296 free(tab[j]);
297
298 malloc_check();
299
300 size = random() % 500;
301 tab[j] = malloc(size);
302 memset(tab[j], 0x11, size);
303
304 malloc_check();
305 }
306
307 malloc_dump();
308
309 for(i = 0; i < n; i++) {
310 free(tab[i]);
311 }
312 return 0;
313 }
314 #endif