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