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