Add a simple heap test.
[bertos.git] / bertos / struct / heap.c
1 /**
2  * \file
3  * <!--
4  * This file is part of BeRTOS.
5  *
6  * Bertos is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * As a special exception, you may use this file as part of a free software
21  * library without restriction.  Specifically, if other files instantiate
22  * templates or use macros or inline functions from this file, or you compile
23  * this file and link it with other files to produce an executable, this
24  * file does not by itself cause the resulting executable to be covered by
25  * the GNU General Public License.  This exception does not however
26  * invalidate any other reasons why the executable file might be covered by
27  * the GNU General Public License.
28  *
29  * Copyright 2004 Develer S.r.l. (http://www.develer.com/)
30  * Copyright 1999, 2000, 2001, 2008 Bernie Innocenti <bernie@codewiz.org>
31  * -->
32  *
33  * \brief Heap subsystem (public interface).
34  *
35  * \version $Id$
36  * \author Bernie Innocenti <bernie@codewiz.org>
37  */
38
39 #include "heap.h"
40
41 #include <cfg/debug.h> // ASSERT()
42 #include <string.h>    // memset()
43
44 #define FREE_FILL_CODE     0xDEAD
45 #define ALLOC_FILL_CODE    0xBEEF
46
47
48 /*
49  * This function prototype is deprecated, will change in:
50  * void heap_init(struct Heap* h, heap_buf_t* memory, size_t size)
51  * in the nex BeRTOS release.
52  */
53 void heap_init(struct Heap* h, void* memory, size_t size)
54 {
55         #ifdef _DEBUG
56         memset(memory, FREE_FILL_CODE, size);
57         #endif
58
59         ASSERT2((((uintptr_t)memory % sizeof(heap_buf_t)) == 0),
60         "memory buffer is unaligned, please use the HEAP_DEFINE_BUF() macro to declare heap buffers!\n");
61
62         /* Initialize heap with a single big chunk */
63         h->FreeList = (MemChunk *)memory;
64         h->FreeList->next = NULL;
65         h->FreeList->size = size;
66 }
67
68
69 void *heap_allocmem(struct Heap* h, size_t size)
70 {
71         MemChunk *chunk, *prev;
72
73         /* Round size up to the allocation granularity */
74         size = ROUND_UP2(size, sizeof(MemChunk));
75
76         /* Handle allocations of 0 bytes */
77         if (!size)
78                 size = sizeof(MemChunk);
79
80         /* Walk on the free list looking for any chunk big enough to
81          * fit the requested block size.
82          */
83         for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
84                 chunk;
85                 prev = chunk, chunk = chunk->next)
86         {
87                 if (chunk->size >= size)
88                 {
89                         if (chunk->size == size)
90                         {
91                                 /* Just remove this chunk from the free list */
92                                 prev->next = chunk->next;
93                                 #ifdef _DEBUG
94                                         memset(chunk, ALLOC_FILL_CODE, size);
95                                 #endif
96                                 return (void *)chunk;
97                         }
98                         else
99                         {
100                                 /* Allocate from the END of an existing chunk */
101                                 chunk->size -= size;
102                                 #ifdef _DEBUG
103                                         memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
104                                 #endif
105                                 return (void *)((uint8_t *)chunk + chunk->size);
106                         }
107                 }
108         }
109
110         return NULL; /* fail */
111 }
112
113
114 void heap_freemem(struct Heap* h, void *mem, size_t size)
115 {
116         MemChunk *prev;
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 = ROUND_UP2(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 */