Import changes from sc/firmware.
[bertos.git] / mware / heap.c
1 /*!
2  * \file
3  * <!--
4  * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
5  * Copyright 1999,2000,2001 Bernardo Innocenti <bernie@develer.com>
6  * This file is part of DevLib - See devlib/README for information.
7  * -->
8  *
9  * \brief Heap subsystem (public interface).
10  *
11  * \version $Id$
12  *
13  * \author Bernardo Innocenti <bernie@develer.com>
14  */
15
16 /*#*
17  *#* $Log$
18  *#* Revision 1.5  2004/10/03 20:43:22  bernie
19  *#* Import changes from sc/firmware.
20  *#*
21  *#* Revision 1.1  2004/07/31 16:33:58  rasky
22  *#* Spostato lo heap da kern/ a mware/
23  *#*
24  *#* Revision 1.2  2004/06/03 11:27:09  bernie
25  *#* Add dual-license information.
26  *#*
27  *#* Revision 1.1  2004/05/23 17:27:00  bernie
28  *#* Import kern/ subdirectory.
29  *#*
30  *#*/
31
32 #include "heap.h"
33 #include <string.h>           // memset()
34 #include <macros.h>           // IS_POW2()
35 #include <debug.h>            // ASSERT()
36
37 /* NOTE: struct size must be a 2's power! */
38 typedef struct _MemChunk
39 {
40         struct _MemChunk *next;
41         size_t size;
42 } MemChunk;
43
44 STATIC_ASSERT(IS_POW2(sizeof(MemChunk)));
45
46 #define FREE_FILL_CODE     0xDEAD
47 #define ALLOC_FILL_CODE    0xBEEF
48
49 void heap_init(struct Heap* h, void* memory, size_t size)
50 {
51 #ifdef _DEBUG
52         memset(memory, FREE_FILL_CODE, size);
53 #endif
54
55         /* Initialize heap with a single big chunk */
56         h->FreeList = (MemChunk *)memory;
57         h->FreeList->next = NULL;
58         h->FreeList->size = size;
59 }
60
61
62 void *heap_allocmem(struct Heap* h, size_t size)
63 {
64         MemChunk *chunk, *prev;
65
66         /* Round size up to the allocation granularity */
67         size = ROUND2(size, sizeof(MemChunk));
68
69         /* Handle allocations of 0 bytes */
70         if (!size)
71                 size = sizeof(MemChunk);
72
73         /* Walk on the free list looking for any chunk big enough to
74          * fit the requested block size.
75          */
76         for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
77                 chunk;
78                 prev = chunk, chunk = chunk->next)
79         {
80                 if (chunk->size >= size)
81                 {
82                         if (chunk->size == size)
83                         {
84                                 /* Just remove this chunk from the free list */
85                                 prev->next = chunk->next;
86                                 #ifdef _DEBUG
87                                         memset(chunk, ALLOC_FILL_CODE, size);
88                                 #endif
89                                 return (void *)chunk;
90                         }
91                         else
92                         {
93                                 /* Allocate from the END of an existing chunk */
94                                 chunk->size -= size;
95                                 #ifdef _DEBUG
96                                         memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
97                                 #endif
98                                 return (void *)((uint8_t *)chunk + chunk->size);
99                         }
100                 }
101         }
102
103         return NULL; /* fail */
104 }
105
106
107 void heap_freemem(struct Heap* h, void *mem, size_t size)
108 {
109         MemChunk *prev;
110
111         ASSERT(mem);
112
113 #ifdef _DEBUG
114         memset(mem, FREE_FILL_CODE, size);
115 #endif
116
117         /* Round size up to the allocation granularity */
118         size = ROUND2(size, sizeof(MemChunk));
119
120         /* Handle allocations of 0 bytes */
121         if (!size)
122                 size = sizeof(MemChunk);
123
124         /* Special case: first chunk in the free list */
125         ASSERT((uint8_t*)mem != (uint8_t*)h->FreeList);
126         if (((uint8_t *)mem) < ((uint8_t *)h->FreeList))
127         {
128                 /* Insert memory block before the current free list head */
129                 prev = (MemChunk *)mem;
130                 prev->next = h->FreeList;
131                 prev->size = size;
132                 h->FreeList = prev;
133         }
134         else /* Normal case: not the first chunk in the free list */
135         {
136                 /*
137                  * Walk on the free list. Stop at the insertion point (when mem
138                  * is between prev and prev->next)
139                  */
140                 prev = h->FreeList;
141                 while (prev->next < (MemChunk *)mem && prev->next)
142                         prev = prev->next;
143
144                 /* Make sure mem is not *within* prev */
145                 ASSERT((uint8_t*)mem >= (uint8_t*)prev + prev->size);
146
147                 /* Should it be merged with previous block? */
148                 if (((uint8_t *)prev) + prev->size == ((uint8_t *)mem))
149                 {
150                         /* Yes */
151                         prev->size += size;
152                 }
153                 else /* not merged with previous chunk */
154                 {
155                         MemChunk *curr = (MemChunk*)mem;
156
157                         /* insert it after the previous node
158                          * and move the 'prev' pointer forward
159                          * for the following operations
160                          */
161                         curr->next = prev->next;
162                         curr->size = size;
163                         prev->next = curr;
164
165                         /* Adjust for the following test */
166                         prev = curr;
167                 }
168         }
169
170         /* Also merge with next chunk? */
171         if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
172         {
173                 prev->size += prev->next->size;
174                 prev->next = prev->next->next;
175
176                 /* There should be only one merge opportunity, becuase we always merge on free */
177                 ASSERT((uint8_t*)prev + prev->size != (uint8_t*)prev->next);
178         }
179 }
180
181 #if CONFIG_HEAP_MALLOC
182
183 void *heap_malloc(struct Heap* h, size_t size)
184 {
185         size_t *mem;
186
187         size += sizeof(size_t);
188         if ((mem = (size_t*)heap_allocmem(h, size)))
189                 *mem++ = size;
190
191         return mem;
192 }
193
194 void *heap_calloc(struct Heap* h, size_t size)
195 {
196         void *mem;
197
198         if ((mem = heap_malloc(h, size)))
199                 memset(mem, 0, size);
200
201         return mem;
202 }
203
204 void heap_free(struct Heap* h, void *mem_)
205 {
206         size_t* mem = (size_t*)mem_;
207         --mem;
208         heap_freemem(h, mem, *mem);
209 }
210
211 #endif /* CONFIG_HEAP_MALLOC */