heap.c

Go to the documentation of this file.
00001 
00039 #include "heap.h"
00040 
00041 #include <cfg/macros.h>           // IS_POW2()
00042 #include <cfg/debug.h>            // ASSERT()
00043 
00044 #include <string.h>           // memset()
00045 
00046 /* NOTE: struct size must be a 2's power! */
00047 typedef struct _MemChunk
00048 {
00049     struct _MemChunk *next;
00050     size_t size;
00051 } MemChunk;
00052 
00053 STATIC_ASSERT(IS_POW2(sizeof(MemChunk)));
00054 
00055 #define FREE_FILL_CODE     0xDEAD
00056 #define ALLOC_FILL_CODE    0xBEEF
00057 
00058 void heap_init(struct Heap* h, void* memory, size_t size)
00059 {
00060 #ifdef _DEBUG
00061     memset(memory, FREE_FILL_CODE, size);
00062 #endif
00063 
00064     /* Initialize heap with a single big chunk */
00065     h->FreeList = (MemChunk *)memory;
00066     h->FreeList->next = NULL;
00067     h->FreeList->size = size;
00068 }
00069 
00070 
00071 void *heap_allocmem(struct Heap* h, size_t size)
00072 {
00073     MemChunk *chunk, *prev;
00074 
00075     /* Round size up to the allocation granularity */
00076     size = ROUND_UP2(size, sizeof(MemChunk));
00077 
00078     /* Handle allocations of 0 bytes */
00079     if (!size)
00080         size = sizeof(MemChunk);
00081 
00082     /* Walk on the free list looking for any chunk big enough to
00083      * fit the requested block size.
00084      */
00085     for (prev = (MemChunk *)&h->FreeList, chunk = h->FreeList;
00086         chunk;
00087         prev = chunk, chunk = chunk->next)
00088     {
00089         if (chunk->size >= size)
00090         {
00091             if (chunk->size == size)
00092             {
00093                 /* Just remove this chunk from the free list */
00094                 prev->next = chunk->next;
00095                 #ifdef _DEBUG
00096                     memset(chunk, ALLOC_FILL_CODE, size);
00097                 #endif
00098                 return (void *)chunk;
00099             }
00100             else
00101             {
00102                 /* Allocate from the END of an existing chunk */
00103                 chunk->size -= size;
00104                 #ifdef _DEBUG
00105                     memset((uint8_t *)chunk + chunk->size, ALLOC_FILL_CODE, size);
00106                 #endif
00107                 return (void *)((uint8_t *)chunk + chunk->size);
00108             }
00109         }
00110     }
00111 
00112     return NULL; /* fail */
00113 }
00114 
00115 
00116 void heap_freemem(struct Heap* h, void *mem, size_t size)
00117 {
00118     MemChunk *prev;
00119     ASSERT(mem);
00120 
00121 #ifdef _DEBUG
00122     memset(mem, FREE_FILL_CODE, size);
00123 #endif
00124 
00125     /* Round size up to the allocation granularity */
00126     size = ROUND_UP2(size, sizeof(MemChunk));
00127 
00128     /* Handle allocations of 0 bytes */
00129     if (!size)
00130         size = sizeof(MemChunk);
00131 
00132     /* Special case: first chunk in the free list */
00133     ASSERT((uint8_t*)mem != (uint8_t*)h->FreeList);
00134     if (((uint8_t *)mem) < ((uint8_t *)h->FreeList))
00135     {
00136         /* Insert memory block before the current free list head */
00137         prev = (MemChunk *)mem;
00138         prev->next = h->FreeList;
00139         prev->size = size;
00140         h->FreeList = prev;
00141     }
00142     else /* Normal case: not the first chunk in the free list */
00143     {
00144         /*
00145          * Walk on the free list. Stop at the insertion point (when mem
00146          * is between prev and prev->next)
00147          */
00148         prev = h->FreeList;
00149         while (prev->next < (MemChunk *)mem && prev->next)
00150             prev = prev->next;
00151 
00152         /* Make sure mem is not *within* prev */
00153         ASSERT((uint8_t*)mem >= (uint8_t*)prev + prev->size);
00154 
00155         /* Should it be merged with previous block? */
00156         if (((uint8_t *)prev) + prev->size == ((uint8_t *)mem))
00157         {
00158             /* Yes */
00159             prev->size += size;
00160         }
00161         else /* not merged with previous chunk */
00162         {
00163             MemChunk *curr = (MemChunk*)mem;
00164 
00165             /* insert it after the previous node
00166              * and move the 'prev' pointer forward
00167              * for the following operations
00168              */
00169             curr->next = prev->next;
00170             curr->size = size;
00171             prev->next = curr;
00172 
00173             /* Adjust for the following test */
00174             prev = curr;
00175         }
00176     }
00177 
00178     /* Also merge with next chunk? */
00179     if (((uint8_t *)prev) + prev->size == ((uint8_t *)prev->next))
00180     {
00181         prev->size += prev->next->size;
00182         prev->next = prev->next->next;
00183 
00184         /* There should be only one merge opportunity, becuase we always merge on free */
00185         ASSERT((uint8_t*)prev + prev->size != (uint8_t*)prev->next);
00186     }
00187 }
00188 
00189 #if CONFIG_HEAP_MALLOC
00190 
00191 void *heap_malloc(struct Heap* h, size_t size)
00192 {
00193     size_t *mem;
00194 
00195     size += sizeof(size_t);
00196     if ((mem = (size_t*)heap_allocmem(h, size)))
00197         *mem++ = size;
00198 
00199     return mem;
00200 }
00201 
00202 void *heap_calloc(struct Heap* h, size_t size)
00203 {
00204     void *mem;
00205 
00206     if ((mem = heap_malloc(h, size)))
00207         memset(mem, 0, size);
00208 
00209     return mem;
00210 }
00211 
00225 void heap_free(struct Heap *h, void *mem)
00226 {
00227     size_t *_mem = (size_t *)mem;
00228 
00229     if (_mem)
00230     {
00231         --_mem;
00232         heap_freemem(h, _mem, *_mem);
00233     }
00234 }
00235 
00236 #endif /* CONFIG_HEAP_MALLOC */