heap.c

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