heap.c

Go to the documentation of this file.
00001 
00041 /*#*
00042  *#* $Log$
00043  *#* Revision 1.9  2006/07/19 12:56:27  bernie
00044  *#* Convert to new Doxygen style.
00045  *#*
00046  *#* Revision 1.8  2005/11/04 16:20:02  bernie
00047  *#* Fix reference to README.devlib in header.
00048  *#*
00049  *#* Revision 1.7  2005/04/11 19:10:28  bernie
00050  *#* Include top-level headers from cfg/ subdir.
00051  *#*
00052  *#* Revision 1.6  2004/10/26 09:02:13  bernie
00053  *#* heap_free(): Handle NULL pointers like free(), write documentation.
00054  *#*
00055  *#* Revision 1.5  2004/10/03 20:43:22  bernie
00056  *#* Import changes from sc/firmware.
00057  *#*
00058  *#* Revision 1.1  2004/07/31 16:33:58  rasky
00059  *#* Spostato lo heap da kern/ a mware/
00060  *#*
00061  *#* Revision 1.2  2004/06/03 11:27:09  bernie
00062  *#* Add dual-license information.
00063  *#*
00064  *#* Revision 1.1  2004/05/23 17:27:00  bernie
00065  *#* Import kern/ subdirectory.
00066  *#*
00067  *#*/
00068 
00069 #include "heap.h"
00070 #include <string.h>           // memset()
00071 #include <cfg/macros.h>           // IS_POW2()
00072 #include <cfg/debug.h>            // ASSERT()
00073 
00074 /* NOTE: struct size must be a 2's power! */
00075 typedef struct _MemChunk
00076 {
00077     struct _MemChunk *next;
00078     size_t size;
00079 } MemChunk;
00080 
00081 STATIC_ASSERT(IS_POW2(sizeof(MemChunk)));
00082 
00083 #define FREE_FILL_CODE     0xDEAD
00084 #define ALLOC_FILL_CODE    0xBEEF
00085 
00086 void heap_init(struct Heap* h, void* memory, size_t size)
00087 {
00088 #ifdef _DEBUG
00089     memset(memory, FREE_FILL_CODE, size);
00090 #endif
00091 
00092     /* Initialize heap with a single big chunk */
00093     h->FreeList = (MemChunk *)memory;
00094     h->FreeList->next = NULL;
00095     h->FreeList->size = size;
00096 }
00097 
00098 
00099 void *heap_allocmem(struct Heap* h, size_t size)
00100 {
00101     MemChunk *chunk, *prev;
00102 
00103     /* Round size up to the allocation granularity */
00104     size = ROUND2(size, sizeof(MemChunk));
00105 
00106     /* Handle allocations of 0 bytes */
00107     if (!size)
00108         size = sizeof(MemChunk);
00109 
00110     /* Walk on the free list looking for any chunk big enough to
00111      * fit the requested block size.
00112      */
00113     for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
00114         chunk;
00115         prev = chunk, chunk = chunk->next)
00116     {
00117         if (chunk->size >= size)
00118         {
00119             if (chunk->size == size)
00120             {
00121                 /* Just remove this chunk from the free list */
00122                 prev->next = chunk->next;
00123                 #ifdef _DEBUG
00124                     memset(chunk, ALLOC_FILL_CODE, size);
00125                 #endif
00126                 return (void *)chunk;
00127             }
00128             else
00129             {
00130                 /* Allocate from the END of an existing chunk */
00131                 chunk->size -= size;
00132                 #ifdef _DEBUG
00133                     memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
00134                 #endif
00135                 return (void *)((uint8_t *)chunk + chunk->size);
00136             }
00137         }
00138     }
00139 
00140     return NULL; /* fail */
00141 }
00142 
00143 
00144 void heap_freemem(struct Heap* h, void *mem, size_t size)
00145 {
00146     MemChunk *prev;
00147 
00148     ASSERT(mem);
00149 
00150 #ifdef _DEBUG
00151     memset(mem, FREE_FILL_CODE, size);
00152 #endif
00153 
00154     /* Round size up to the allocation granularity */
00155     size = ROUND2(size, sizeof(MemChunk));
00156 
00157     /* Handle allocations of 0 bytes */
00158     if (!size)
00159         size = sizeof(MemChunk);
00160 
00161     /* Special case: first chunk in the free list */
00162     ASSERT((uint8_t*)mem != (uint8_t*)h->FreeList);
00163     if (((uint8_t *)mem) < ((uint8_t *)h->FreeList))
00164     {
00165         /* Insert memory block before the current free list head */
00166         prev = (MemChunk *)mem;
00167         prev->next = h->FreeList;
00168         prev->size = size;
00169         h->FreeList = prev;
00170     }
00171     else /* Normal case: not the first chunk in the free list */
00172     {
00173         /*
00174          * Walk on the free list. Stop at the insertion point (when mem
00175          * is between prev and prev->next)
00176          */
00177         prev = h->FreeList;
00178         while (prev->next < (MemChunk *)mem && prev->next)
00179             prev = prev->next;
00180 
00181         /* Make sure mem is not *within* prev */
00182         ASSERT((uint8_t*)mem >= (uint8_t*)prev + prev->size);
00183 
00184         /* Should it be merged with previous block? */
00185         if (((uint8_t *)prev) + prev->size == ((uint8_t *)mem))
00186         {
00187             /* Yes */
00188             prev->size += size;
00189         }
00190         else /* not merged with previous chunk */
00191         {
00192             MemChunk *curr = (MemChunk*)mem;
00193 
00194             /* insert it after the previous node
00195              * and move the 'prev' pointer forward
00196              * for the following operations
00197              */
00198             curr->next = prev->next;
00199             curr->size = size;
00200             prev->next = curr;
00201 
00202             /* Adjust for the following test */
00203             prev = curr;
00204         }
00205     }
00206 
00207     /* Also merge with next chunk? */
00208     if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
00209     {
00210         prev->size += prev->next->size;
00211         prev->next = prev->next->next;
00212 
00213         /* There should be only one merge opportunity, becuase we always merge on free */
00214         ASSERT((uint8_t*)prev + prev->size != (uint8_t*)prev->next);
00215     }
00216 }
00217 
00218 #if CONFIG_HEAP_MALLOC
00219 
00220 void *heap_malloc(struct Heap* h, size_t size)
00221 {
00222     size_t *mem;
00223 
00224     size += sizeof(size_t);
00225     if ((mem = (size_t*)heap_allocmem(h, size)))
00226         *mem++ = size;
00227 
00228     return mem;
00229 }
00230 
00231 void *heap_calloc(struct Heap* h, size_t size)
00232 {
00233     void *mem;
00234 
00235     if ((mem = heap_malloc(h, size)))
00236         memset(mem, 0, size);
00237 
00238     return mem;
00239 }
00240 
00254 void heap_free(struct Heap *h, void *mem)
00255 {
00256     size_t *_mem = (size_t *)mem;
00257 
00258     if (_mem)
00259     {
00260         --_mem;
00261         heap_freemem(h, _mem, *_mem);
00262     }
00263 }
00264 
00265 #endif /* CONFIG_HEAP_MALLOC */