Include top-level headers from cfg/ subdir.
[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.7  2005/04/11 19:10:28  bernie
19  *#* Include top-level headers from cfg/ subdir.
20  *#*
21  *#* Revision 1.6  2004/10/26 09:02:13  bernie
22  *#* heap_free(): Handle NULL pointers like free(), write documentation.
23  *#*
24  *#* Revision 1.5  2004/10/03 20:43:22  bernie
25  *#* Import changes from sc/firmware.
26  *#*
27  *#* Revision 1.1  2004/07/31 16:33:58  rasky
28  *#* Spostato lo heap da kern/ a mware/
29  *#*
30  *#* Revision 1.2  2004/06/03 11:27:09  bernie
31  *#* Add dual-license information.
32  *#*
33  *#* Revision 1.1  2004/05/23 17:27:00  bernie
34  *#* Import kern/ subdirectory.
35  *#*
36  *#*/
37
38 #include "heap.h"
39 #include <string.h>           // memset()
40 #include <cfg/macros.h>           // IS_POW2()
41 #include <cfg/debug.h>            // ASSERT()
42
43 /* NOTE: struct size must be a 2's power! */
44 typedef struct _MemChunk
45 {
46         struct _MemChunk *next;
47         size_t size;
48 } MemChunk;
49
50 STATIC_ASSERT(IS_POW2(sizeof(MemChunk)));
51
52 #define FREE_FILL_CODE     0xDEAD
53 #define ALLOC_FILL_CODE    0xBEEF
54
55 void heap_init(struct Heap* h, void* memory, size_t size)
56 {
57 #ifdef _DEBUG
58         memset(memory, FREE_FILL_CODE, size);
59 #endif
60
61         /* Initialize heap with a single big chunk */
62         h->FreeList = (MemChunk *)memory;
63         h->FreeList->next = NULL;
64         h->FreeList->size = size;
65 }
66
67
68 void *heap_allocmem(struct Heap* h, size_t size)
69 {
70         MemChunk *chunk, *prev;
71
72         /* Round size up to the allocation granularity */
73         size = ROUND2(size, sizeof(MemChunk));
74
75         /* Handle allocations of 0 bytes */
76         if (!size)
77                 size = sizeof(MemChunk);
78
79         /* Walk on the free list looking for any chunk big enough to
80          * fit the requested block size.
81          */
82         for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
83                 chunk;
84                 prev = chunk, chunk = chunk->next)
85         {
86                 if (chunk->size >= size)
87                 {
88                         if (chunk->size == size)
89                         {
90                                 /* Just remove this chunk from the free list */
91                                 prev->next = chunk->next;
92                                 #ifdef _DEBUG
93                                         memset(chunk, ALLOC_FILL_CODE, size);
94                                 #endif
95                                 return (void *)chunk;
96                         }
97                         else
98                         {
99                                 /* Allocate from the END of an existing chunk */
100                                 chunk->size -= size;
101                                 #ifdef _DEBUG
102                                         memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
103                                 #endif
104                                 return (void *)((uint8_t *)chunk + chunk->size);
105                         }
106                 }
107         }
108
109         return NULL; /* fail */
110 }
111
112
113 void heap_freemem(struct Heap* h, void *mem, size_t size)
114 {
115         MemChunk *prev;
116
117         ASSERT(mem);
118
119 #ifdef _DEBUG
120         memset(mem, FREE_FILL_CODE, size);
121 #endif
122
123         /* Round size up to the allocation granularity */
124         size = ROUND2(size, sizeof(MemChunk));
125
126         /* Handle allocations of 0 bytes */
127         if (!size)
128                 size = sizeof(MemChunk);
129
130         /* Special case: first chunk in the free list */
131         ASSERT((uint8_t*)mem != (uint8_t*)h->FreeList);
132         if (((uint8_t *)mem) < ((uint8_t *)h->FreeList))
133         {
134                 /* Insert memory block before the current free list head */
135                 prev = (MemChunk *)mem;
136                 prev->next = h->FreeList;
137                 prev->size = size;
138                 h->FreeList = prev;
139         }
140         else /* Normal case: not the first chunk in the free list */
141         {
142                 /*
143                  * Walk on the free list. Stop at the insertion point (when mem
144                  * is between prev and prev->next)
145                  */
146                 prev = h->FreeList;
147                 while (prev->next < (MemChunk *)mem && prev->next)
148                         prev = prev->next;
149
150                 /* Make sure mem is not *within* prev */
151                 ASSERT((uint8_t*)mem >= (uint8_t*)prev + prev->size);
152
153                 /* Should it be merged with previous block? */
154                 if (((uint8_t *)prev) + prev->size == ((uint8_t *)mem))
155                 {
156                         /* Yes */
157                         prev->size += size;
158                 }
159                 else /* not merged with previous chunk */
160                 {
161                         MemChunk *curr = (MemChunk*)mem;
162
163                         /* insert it after the previous node
164                          * and move the 'prev' pointer forward
165                          * for the following operations
166                          */
167                         curr->next = prev->next;
168                         curr->size = size;
169                         prev->next = curr;
170
171                         /* Adjust for the following test */
172                         prev = curr;
173                 }
174         }
175
176         /* Also merge with next chunk? */
177         if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
178         {
179                 prev->size += prev->next->size;
180                 prev->next = prev->next->next;
181
182                 /* There should be only one merge opportunity, becuase we always merge on free */
183                 ASSERT((uint8_t*)prev + prev->size != (uint8_t*)prev->next);
184         }
185 }
186
187 #if CONFIG_HEAP_MALLOC
188
189 void *heap_malloc(struct Heap* h, size_t size)
190 {
191         size_t *mem;
192
193         size += sizeof(size_t);
194         if ((mem = (size_t*)heap_allocmem(h, size)))
195                 *mem++ = size;
196
197         return mem;
198 }
199
200 void *heap_calloc(struct Heap* h, size_t size)
201 {
202         void *mem;
203
204         if ((mem = heap_malloc(h, size)))
205                 memset(mem, 0, size);
206
207         return mem;
208 }
209
210 /*!
211  * Free a block of memory, determining its size automatically.
212  *
213  * \param h    Heap from which the block was allocated.
214  * \param mem  Pointer to a block of memory previously allocated with
215  *             either heap_malloc() or heap_calloc().
216  *
217  * \note If \a mem is a NULL pointer, no operation is performed.
218  *
219  * \note Freeing the same memory block twice has undefined behavior.
220  *
221  * \note This function works like the ANSI C free().
222  */
223 void heap_free(struct Heap *h, void *mem)
224 {
225         size_t *_mem = (size_t *)mem;
226
227         if (_mem)
228         {
229                 --_mem;
230                 heap_freemem(h, _mem, *_mem);
231         }
232 }
233
234 #endif /* CONFIG_HEAP_MALLOC */