//---------------------------------------------------------------- // Statically-allocated memory manager // // by Eli Bendersky (eliben@gmail.com) // // This code is in the public domain. //---------------------------------------------------------------- #include "memmgr.h" typedef ulong Align; union mem_header_union { struct { // Pointer to the next block in the free list // union mem_header_union* next; // Size of the block (in quantas of sizeof(mem_header_t)) // ulong size; } s; // Used to align headers in memory to a boundary // Align align_dummy; }; typedef union mem_header_union mem_header_t; // Initial empty list // static mem_header_t base; // Start of free list // static mem_header_t* freep = 0; // Static pool for new allocations // static byte pool[POOL_SIZE] = {0}; static ulong pool_free_pos = 0; void memmgr_init() { base.s.next = 0; base.s.size = 0; freep = 0; pool_free_pos = 0; } static mem_header_t* get_mem_from_pool(ulong nquantas) { ulong total_req_size; mem_header_t* h; if (nquantas < MIN_POOL_ALLOC_QUANTAS) nquantas = MIN_POOL_ALLOC_QUANTAS; total_req_size = nquantas * sizeof(mem_header_t); if (pool_free_pos + total_req_size <= POOL_SIZE) { h = (mem_header_t*) (pool + pool_free_pos); h->s.size = nquantas; memmgr_free((void*) (h + 1)); pool_free_pos += total_req_size; } else { return 0; } return freep; } // Allocations are done in 'quantas' of header size. // The search for a free block of adequate size begins at the point 'freep' // where the last block was found. // If a too-big block is found, it is split and the tail is returned (this // way the header of the original needs only to have its size adjusted). // The pointer returned to the user points to the free space within the block, // which begins one quanta after the header. // void* memmgr_alloc(ulong nbytes) { mem_header_t* p; mem_header_t* prevp; // Calculate how many quantas are required: we need enough to house all // the requested bytes, plus the header. The -1 and +1 are there to make sure // that if nbytes is a multiple of nquantas, we don't allocate too much // ulong nquantas = (nbytes + sizeof(mem_header_t) - 1) / sizeof(mem_header_t) + 1; // First alloc call, and no free list yet ? Use 'base' for an initial // denegerate block of size 0, which points to itself // if ((prevp = freep) == 0) { base.s.next = freep = prevp = &base; base.s.size = 0; } for (p = prevp->s.next; ; prevp = p, p = p->s.next) { // big enough ? if (p->s.size >= nquantas) { // exactly ? if (p->s.size == nquantas) { // just eliminate this block from the free list by pointing // its prev's next to its next // prevp->s.next = p->s.next; } else // too big { p->s.size -= nquantas; p += p->s.size; p->s.size = nquantas; } freep = prevp; return (void*) (p + 1); } // Reached end of free list ? // Try to allocate the block from the pool. If that succeeds, // get_mem_from_pool adds the new block to the free list and // it will be found in the following iterations. If the call // to get_mem_from_pool doesn't succeed, we've run out of // memory // else if (p == freep) { if ((p = get_mem_from_pool(nquantas)) == 0) { #ifdef DEBUG_MEMMGR_FATAL printf("!! Memory allocation failed !!\n"); #endif return 0; } } } } // Scans the free list, starting at freep, looking the the place to insert the // free block. This is either between two existing blocks or at the end of the // list. In any case, if the block being freed is adjacent to either neighbor, // the adjacent blocks are combined. // void memmgr_free(void* ap) { mem_header_t* block; mem_header_t* p; // acquire pointer to block header block = ((mem_header_t*) ap) - 1; // Find the correct place to place the block in (the free list is sorted by // address, increasing order) // for (p = freep; !(block > p && block < p->s.next); p = p->s.next) { // Since the free list is circular, there is one link where a // higher-addressed block points to a lower-addressed block. // This condition checks if the block should be actually // inserted between them // if (p >= p->s.next && (block > p || block < p->s.next)) break; } // Try to combine with the higher neighbor // if (block + block->s.size == p->s.next) { block->s.size += p->s.next->s.size; block->s.next = p->s.next->s.next; } else { block->s.next = p->s.next; } // Try to combine with the lower neighbor // if (p + p->s.size == block) { p->s.size += block->s.size; p->s.next = block->s.next; } else { p->s.next = block; } freep = p; }