void umm_free( void *ptr ) { unsigned short int c; /* If we're being asked to free a NULL pointer, well that's just silly! */ if( (void *)0 == ptr ) { DBGLOG_DEBUG( "free a null pointer -> do nothing\n" ); return; } /* * FIXME: At some point it might be a good idea to add a check to make sure * that the pointer we're being asked to free up is actually within * the umm_heap! * * NOTE: See the new umm_info() function that you can use to see if a ptr is * on the free list! */ /* Protect the critical section... */ UMM_CRITICAL_ENTRY(); /* Figure out which block we're in. Note the use of truncated division... */ c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block); DBGLOG_DEBUG( "Freeing block %6i\n", c ); /* Now let's assimilate this block with the next one if possible. */ umm_assimilate_up( c ); /* Then assimilate with the previous block if possible */ if( UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK ) { DBGLOG_DEBUG( "Assimilate down to next block, which is FREE\n" ); c = umm_assimilate_down(c, UMM_FREELIST_MASK); } else { /* * The previous block is not a free block, so add this one to the head * of the free list */ DBGLOG_DEBUG( "Just add to head of free list\n" ); UMM_PFREE(UMM_NFREE(0)) = c; UMM_NFREE(c) = UMM_NFREE(0); UMM_PFREE(c) = 0; UMM_NFREE(0) = c; UMM_NBLOCK(c) |= UMM_FREELIST_MASK; } /* Release the critical section... */ UMM_CRITICAL_EXIT(); }
static unsigned short int umm_assimilate_down( unsigned short int c, unsigned short int freemask ) { UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask; UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c); return( UMM_PBLOCK(c) ); }
void umm_init_x( size_t size ) { uint32_t UMM_MALLOC_CFG_HEAP_SIZE = (size / sizeof(size_t)) * sizeof(size_t); if (UMM_MALLOC_CFG_HEAP_SIZE < (sizeof(umm_block) * 128)) fb_alloc_fail(); if (UMM_MALLOC_CFG_HEAP_SIZE > (sizeof(umm_block) * 32768)) UMM_MALLOC_CFG_HEAP_SIZE = sizeof(umm_block) * 32768; void *UMM_MALLOC_CFG_HEAP_ADDR = fb_alloc(UMM_MALLOC_CFG_HEAP_SIZE); /* init heap pointer and size, and memset it to 0 */ umm_heap = (umm_block *)UMM_MALLOC_CFG_HEAP_ADDR; umm_numblocks = (UMM_MALLOC_CFG_HEAP_SIZE / sizeof(umm_block)); memset(umm_heap, 0x00, UMM_MALLOC_CFG_HEAP_SIZE); /* setup initial blank heap structure */ { /* index of the 0th `umm_block` */ const unsigned short int block_0th = 0; /* index of the 1st `umm_block` */ const unsigned short int block_1th = 1; /* index of the latest `umm_block` */ const unsigned short int block_last = UMM_NUMBLOCKS - 1; /* setup the 0th `umm_block`, which just points to the 1st */ UMM_NBLOCK(block_0th) = block_1th; UMM_NFREE(block_0th) = block_1th; UMM_PFREE(block_0th) = block_1th; /* * Now, we need to set the whole heap space as a huge free block. We should * not touch the 0th `umm_block`, since it's special: the 0th `umm_block` * is the head of the free block list. It's a part of the heap invariant. * * See the detailed explanation at the beginning of the file. */ /* * 1th `umm_block` has pointers: * * - next `umm_block`: the latest one * - prev `umm_block`: the 0th * * Plus, it's a free `umm_block`, so we need to apply `UMM_FREELIST_MASK` * * And it's the last free block, so the next free block is 0. */ UMM_NBLOCK(block_1th) = block_last | UMM_FREELIST_MASK; UMM_NFREE(block_1th) = 0; UMM_PBLOCK(block_1th) = block_0th; UMM_PFREE(block_1th) = block_0th; /* * latest `umm_block` has pointers: * * - next `umm_block`: 0 (meaning, there are no more `umm_blocks`) * - prev `umm_block`: the 1st * * It's not a free block, so we don't touch NFREE / PFREE at all. */ UMM_NBLOCK(block_last) = 0; UMM_PBLOCK(block_last) = block_1th; } }
static void umm_make_new_block( unsigned short int c, unsigned short int blocks, unsigned short int freemask ) { UMM_NBLOCK(c+blocks) = UMM_NBLOCK(c) & UMM_BLOCKNO_MASK; UMM_PBLOCK(c+blocks) = c; UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c+blocks); UMM_NBLOCK(c) = (c+blocks) | freemask; }
/* * Split the block `c` into two blocks: `c` and `c + blocks`. * * - `new_freemask` should be `0` if `c + blocks` used, or `UMM_FREELIST_MASK` * otherwise. * * Note that free pointers are NOT modified by this function. */ static void umm_split_block( unsigned short int c, unsigned short int blocks, unsigned short int new_freemask ) { UMM_NBLOCK(c+blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask; UMM_PBLOCK(c+blocks) = c; UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c+blocks); UMM_NBLOCK(c) = (c+blocks); }
static void umm_disconnect_from_free_list( unsigned short int c ) { // Disconnect this block from the FREE list UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); // And clear the free block indicator UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK); }
static void umm_assimilate_up( unsigned short int c ) { if( UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK ) { // The next block is a free block, so assimilate up and remove it from // the free list DBG_LOG_DEBUG( "Assimilate up to next block, which is FREE\n" ); // Disconnect the next block from the FREE list umm_disconnect_from_free_list( UMM_NBLOCK(c) ); // Assimilate the next block with this one UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c; UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK; } }
void *umm_malloc( size_t size ) { unsigned short int blocks; unsigned short int blockSize = 0; unsigned short int bestSize; unsigned short int bestBlock; unsigned short int cf; // the very first thing we do is figure out if we're being asked to allocate // a size of 0 - and if we are we'll simply return a null pointer. if not // then reduce the size by 1 byte so that the subsequent calculations on // the number of blocks to allocate are easier... if( 0 == size ) { DBG_LOG_DEBUG( "malloc a block of 0 bytes -> do nothing\n" ); return( (void *)NULL ); } // Protect the critical section... // UMM_CRITICAL_ENTRY(); blocks = umm_blocks( size ); // Now we can scan through the free list until we find a space that's big // enough to hold the number of blocks we need. // // This part may be customized to be a best-fit, worst-fit, or first-fit // algorithm cf = UMM_NFREE(0); bestBlock = UMM_NFREE(0); bestSize = 0x7FFF; while( UMM_NFREE(cf) ) { blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; DBG_LOG_TRACE( "Looking at block %6i size %6i\n", cf, blockSize ); #if defined UMM_FIRST_FIT // This is the first block that fits! if( (blockSize >= blocks) ) break; #elif defined UMM_BEST_FIT if( (blockSize >= blocks) && (blockSize < bestSize) ) { bestBlock = cf; bestSize = blockSize; } #endif cf = UMM_NFREE(cf); } if( 0x7FFF != bestSize ) { cf = bestBlock; blockSize = bestSize; } if( UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK ) { // This is an existing block in the memory heap, we just need to split off // what we need, unlink it from the free list and mark it as in use, and // link the rest of the block back into the freelist as if it was a new // block on the free list... if( blockSize == blocks ) { // It's an exact fit and we don't neet to split off a block. DBG_LOG_DEBUG( "Allocating %6d blocks starting at %6d - exact\n", blocks, cf ); // Disconnect this block from the FREE list umm_disconnect_from_free_list( cf ); } else { // It's not an exact fit and we need to split off a block. DBG_LOG_DEBUG( "Allocating %6d blocks starting at %6d - existing\n", blocks, cf ); umm_make_new_block( cf, blockSize-blocks, UMM_FREELIST_MASK ); cf += blockSize-blocks; } } else { // We're at the end of the heap - allocate a new block, but check to see if // there's enough memory left for the requested block! Actually, we may need // one more than that if we're initializing the umm_heap for the first // time, which happens in the next conditional... if( UMM_NUMBLOCKS <= cf+blocks+1 ) { DBG_LOG_WARNING( "Can't allocate %5d blocks at %5d\n", blocks, cf ); // Release the critical section... // UMM_CRITICAL_EXIT(); return( (void *)NULL ); } // Now check to see if we need to initialize the free list...this assumes // that the BSS is set to 0 on startup. We should rarely get to the end of // the free list so this is the "cheapest" place to put the initialization! if( 0 == cf ) { DBG_LOG_DEBUG( "Initializing malloc free block pointer\n" ); UMM_NBLOCK(0) = 1; UMM_NFREE(0) = 1; cf = 1; } DBG_LOG_DEBUG( "Allocating %6d blocks starting at %6d - new \n", blocks, cf ); UMM_NFREE(UMM_PFREE(cf)) = cf+blocks; memcpy( &UMM_BLOCK(cf+blocks), &UMM_BLOCK(cf), sizeof(umm_block) ); UMM_NBLOCK(cf) = cf+blocks; UMM_PBLOCK(cf+blocks) = cf; } // Release the critical section... // UMM_CRITICAL_EXIT(); return( (void *)&UMM_DATA(cf) ); }
void umm_free( void *ptr ) { unsigned short int c; // If we're being asked to free a NULL pointer, well that's just silly! if( (void *)0 == ptr ) { DBG_LOG_DEBUG( "free a null pointer -> do nothing\n" ); return; } // FIXME: At some point it might be a good idea to add a check to make sure // that the pointer we're being asked to free up is actually within // the umm_heap! // // NOTE: See the new umm_info() function that you can use to see if a ptr is // on the free list! // Protect the critical section... // UMM_CRITICAL_ENTRY(); // Figure out which block we're in. Note the use of truncated division... c = (ptr-(void *)(&(umm_heap[0])))/sizeof(umm_block); DBG_LOG_DEBUG( "Freeing block %6d\n", c ); // Now let's assimilate this block with the next one if possible. umm_assimilate_up( c ); // Then assimilate with the previous block if possible if( UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK ) { DBG_LOG_DEBUG( "Assimilate down to next block, which is FREE\n" ); c = umm_assimilate_down(c, UMM_FREELIST_MASK); } else { // The previous block is not a free block, so add this one to the head // of the free list DBG_LOG_DEBUG( "Just add to head of free list\n" ); UMM_PFREE(UMM_NFREE(0)) = c; UMM_NFREE(c) = UMM_NFREE(0); UMM_PFREE(c) = 0; UMM_NFREE(0) = c; UMM_NBLOCK(c) |= UMM_FREELIST_MASK; } #if(0) // The following is experimental code that checks to see if the block we just // freed can be assimilated with the very last block - it's pretty convoluted in // terms of block index manipulation, and has absolutely no effect on heap // fragmentation. I'm not sure that it's worth including but I've left it // here for posterity. if( 0 == UMM_NBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK ) ) { if( UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) != UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) ) { UMM_NFREE(UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK)) = c; UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c); UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c); UMM_PFREE(c) = UMM_PFREE(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK); } UMM_NFREE(c) = 0; UMM_NBLOCK(c) = 0; } #endif // Release the critical section... // UMM_CRITICAL_EXIT(); }
void *umm_info( void *ptr, int force ) { unsigned short int blockNo = 0; // Protect the critical section... // UMM_CRITICAL_ENTRY(); // Clear out all of the entries in the heapInfo structure before doing // any calculations.. // memset( &heapInfo, 0, sizeof( heapInfo ) ); DBG_LOG_FORCE( force, "\n\nDumping the umm_heap...\n" ); DBG_LOG_FORCE( force, "|0x%08x|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", &UMM_BLOCK(blockNo), blockNo, UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, UMM_PBLOCK(blockNo), (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo, UMM_NFREE(blockNo), UMM_PFREE(blockNo) ); // Now loop through the block lists, and keep track of the number and size // of used and free blocks. The terminating condition is an nb pointer with // a value of zero... blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; while( UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK ) { ++heapInfo.totalEntries; heapInfo.totalBlocks += (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo; // Is this a free block? if( UMM_NBLOCK(blockNo) & UMM_FREELIST_MASK ) { ++heapInfo.freeEntries; heapInfo.freeBlocks += (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo; DBG_LOG_FORCE( force, "|0x%08x|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", &UMM_BLOCK(blockNo), blockNo, UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, UMM_PBLOCK(blockNo), (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo, UMM_NFREE(blockNo), UMM_PFREE(blockNo) ); // Does this block address match the ptr we may be trying to free? if( ptr == &UMM_BLOCK(blockNo) ) { // Release the critical section... // UMM_CRITICAL_EXIT(); return( ptr ); } } else { ++heapInfo.usedEntries; unsigned usedBlocks = (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo; heapInfo.usedBlocks += usedBlocks; DBG_LOG_FORCE( force, "|0x%08x|B %5d|NB %5d|PB %5d|Z %5d|\n", &UMM_BLOCK(blockNo), blockNo, UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, UMM_PBLOCK(blockNo), (UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK )-blockNo ); MIOS32_MIDI_SendDebugHexDump((unsigned char *)&UMM_BLOCK(blockNo), usedBlocks * sizeof(umm_block)); } blockNo = UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK; } // Update the accounting totals with information from the last block, the // rest must be free! heapInfo.freeBlocks += UMM_NUMBLOCKS-blockNo; heapInfo.totalBlocks += UMM_NUMBLOCKS-blockNo; DBG_LOG_FORCE( force, "|0x%08x|B %5d|NB %5d|PB %5d|Z %5d|NF %5d|PF %5d|\n", &UMM_BLOCK(blockNo), blockNo, UMM_NBLOCK(blockNo) & UMM_BLOCKNO_MASK, UMM_PBLOCK(blockNo), UMM_NUMBLOCKS-blockNo, UMM_NFREE(blockNo), UMM_PFREE(blockNo) ); DBG_LOG_FORCE( force, "Total Entries %5d Used Entries %5d Free Entries %5d\n", heapInfo.totalEntries, heapInfo.usedEntries, heapInfo.freeEntries ); DBG_LOG_FORCE( force, "Total Blocks %5d Used Blocks %5d Free Blocks %5d\n", heapInfo.totalBlocks, heapInfo.usedBlocks, heapInfo.freeBlocks ); DBG_LOG_FORCE( force, "Size of umm_heap is %d bytes, used: %d bytes, free: %d bytes\n", sizeof(umm_heap), heapInfo.usedBlocks*sizeof(umm_block), heapInfo.freeBlocks*sizeof(umm_block)); // Release the critical section... // UMM_CRITICAL_EXIT(); return( NULL ); }
void *umm_realloc( void *ptr, size_t size ) { unsigned short int blocks; unsigned short int blockSize; unsigned short int c; size_t curSize; // This code looks after the case of a NULL value for ptr. The ANSI C // standard says that if ptr is NULL and size is non-zero, then we've // got to work the same a malloc(). If size is also 0, then our version // of malloc() returns a NULL pointer, which is OK as far as the ANSI C // standard is concerned. if( ((void *)NULL == ptr) ) { DBG_LOG_DEBUG( "realloc the NULL pointer - call malloc()\n" ); return( umm_malloc(size) ); } // Now we're sure that we have a non_NULL ptr, but we're not sure what // we should do with it. If the size is 0, then the ANSI C standard says that // we should operate the same as free. if( 0 == size ) { DBG_LOG_DEBUG( "realloc to 0 size, just free the block\n" ); umm_free( ptr ); return( (void *)NULL ); } // Protect the critical section... // UMM_CRITICAL_ENTRY(); // Otherwise we need to actually do a reallocation. A naiive approach // would be to malloc() a new block of the correct size, copy the old data // to the new block, and then free the old block. // // While this will work, we end up doing a lot of possibly unnecessary // copying. So first, let's figure out how many blocks we'll need. blocks = umm_blocks( size ); // Figure out which block we're in. Note the use of truncated division... c = (ptr-(void *)(&(umm_heap[0])))/sizeof(umm_block); // Figure out how big this block is... blockSize = (UMM_NBLOCK(c) - c); // Figure out how many bytes are in this block curSize = (blockSize*sizeof(umm_block))-(sizeof(((umm_block *)0)->header)); // Ok, now that we're here, we know the block number of the original chunk // of memory, and we know how much new memory we want, and we know the original // block size... if( blockSize == blocks ) { // This space intentionally left blank - return the original pointer! DBG_LOG_DEBUG( "realloc the same size block - %d, do nothing\n", blocks ); // Release the critical section... // UMM_CRITICAL_EXIT(); return( ptr ); } // Now we have a block size that could be bigger or smaller. Either // way, try to assimilate up to the next block before doing anything... // // If it's still too small, we have to free it anyways and it will save the // assimilation step later in free :-) umm_assimilate_up( c ); // Now check if it might help to assimilate down, but don't actually // do the downward assimilation unless the resulting block will hold the // new request! If this block of code runs, then the new block will // either fit the request exactly, or be larger than the request. if( (UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) && (blocks <= (UMM_NBLOCK(c)-UMM_PBLOCK(c))) ) { // Check if the resulting block would be big enough... DBG_LOG_DEBUG( "realloc() could assimilate down %d blocks - fits!\n\r", c-UMM_PBLOCK(c) ); // Disconnect the previous block from the FREE list umm_disconnect_from_free_list( UMM_PBLOCK(c) ); // Connect the previous block to the next block ... and then // realign the current block pointer c = umm_assimilate_down(c, 0); // Move the bytes down to the new block we just created, but be sure to move // only the original bytes. memmove( (void *)&UMM_DATA(c), ptr, curSize ); // And don't forget to adjust the pointer to the new block location! ptr = (void *)&UMM_DATA(c); } // Now calculate the block size again...and we'll have three cases blockSize = (UMM_NBLOCK(c) - c); if( blockSize == blocks ) { // This space intentionally left blank - return the original pointer! DBG_LOG_DEBUG( "realloc the same size block - %d, do nothing\n", blocks ); } else if (blockSize > blocks ) { // New block is smaller than the old block, so just make a new block // at the end of this one and put it up on the free list... DBG_LOG_DEBUG( "realloc %d to a smaller block %d, shrink and free the leftover bits\n", blockSize, blocks ); umm_make_new_block( c, blocks, 0 ); umm_free( (void *)&UMM_DATA(c+blocks) ); } else { // New block is bigger than the old block... void *oldptr = ptr; DBG_LOG_DEBUG( "realloc %d to a bigger block %d, make new, copy, and free the old\n", blockSize, blocks ); // Now umm_malloc() a new/ one, copy the old data to the new block, and // free up the old block, but only if the malloc was sucessful! if( (ptr = umm_malloc( size )) ) { memcpy( ptr, oldptr, curSize ); } umm_free( oldptr ); } // Release the critical section... // UMM_CRITICAL_EXIT(); return( ptr ); }
void *umm_realloc( void *ptr, size_t size ) { unsigned short int blocks; unsigned short int blockSize; unsigned short int prevBlockSize = 0; unsigned short int nextBlockSize = 0; unsigned short int c; size_t curSize; if (umm_heap == NULL) { umm_init(); } /* * This code looks after the case of a NULL value for ptr. The ANSI C * standard says that if ptr is NULL and size is non-zero, then we've * got to work the same a malloc(). If size is also 0, then our version * of malloc() returns a NULL pointer, which is OK as far as the ANSI C * standard is concerned. */ if( ((void *)NULL == ptr) ) { DBGLOG_DEBUG( "realloc the NULL pointer - call malloc()\n" ); return( umm_malloc(size) ); } /* * Now we're sure that we have a non_NULL ptr, but we're not sure what * we should do with it. If the size is 0, then the ANSI C standard says that * we should operate the same as free. */ if( 0 == size ) { DBGLOG_DEBUG( "realloc to 0 size, just free the block\n" ); umm_free( ptr ); return( (void *)NULL ); } /* * Otherwise we need to actually do a reallocation. A naiive approach * would be to malloc() a new block of the correct size, copy the old data * to the new block, and then free the old block. * * While this will work, we end up doing a lot of possibly unnecessary * copying. So first, let's figure out how many blocks we'll need. */ blocks = umm_blocks( size ); /* Figure out which block we're in. Note the use of truncated division... */ c = (((char *)ptr)-(char *)(&(umm_heap[0])))/sizeof(umm_block); /* Figure out how big this block is ... the free bit is not set :-) */ blockSize = (UMM_NBLOCK(c) - c); /* Figure out how many bytes are in this block */ curSize = (blockSize*sizeof(umm_block))-(sizeof(((umm_block *)0)->header)); /* Protect the critical section... */ UMM_CRITICAL_ENTRY(); /* Now figure out if the previous and/or next blocks are free as well as * their sizes - this will help us to minimize special code later when we * decide if it's possible to use the adjacent blocks. * * We set prevBlockSize and nextBlockSize to non-zero values ONLY if they * are free! */ if ((UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK)) { nextBlockSize = (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) - UMM_NBLOCK(c); } if ((UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK)) { prevBlockSize = (c - UMM_PBLOCK(c)); } DBGLOG_DEBUG( "realloc blocks %i blockSize %i nextBlockSize %i prevBlockSize %i\n", blocks, blockSize, nextBlockSize, prevBlockSize ); /* * Ok, now that we're here we know how many blocks we want and the current * blockSize. The prevBlockSize and nextBlockSize are set and we can figure * out the best strategy for the new allocation as follows: * * 1. If the new block is the same size or smaller than the current block do * nothing. * 2. If the next block is free and adding it to the current block gives us * enough memory, assimilate the next block. * 3. If the prev block is free and adding it to the current block gives us * enough memory, remove the previous block from the free list, assimilate * it, copy to the new block. * 4. If the prev and next blocks are free and adding them to the current * block gives us enough memory, assimilate the next block, remove the * previous block from the free list, assimilate it, copy to the new block. * 5. Otherwise try to allocate an entirely new block of memory. If the * allocation works free the old block and return the new pointer. If * the allocation fails, return NULL and leave the old block intact. * * All that's left to do is decide if the fit was exact or not. If the fit * was not exact, then split the memory block so that we use only the requested * number of blocks and add what's left to the free list. */ if (blockSize >= blocks) { DBGLOG_DEBUG( "realloc the same or smaller size block - %i, do nothing\n", blocks ); /* This space intentionally left blank */ } else if ((blockSize + nextBlockSize) >= blocks) { DBGLOG_DEBUG( "realloc using next block - %i\n", blocks ); umm_assimilate_up( c ); blockSize += nextBlockSize; } else if ((prevBlockSize + blockSize) >= blocks) { DBGLOG_DEBUG( "realloc using prev block - %i\n", blocks ); umm_disconnect_from_free_list( UMM_PBLOCK(c) ); c = umm_assimilate_down(c, 0); memmove( (void *)&UMM_DATA(c), ptr, curSize ); ptr = (void *)&UMM_DATA(c); blockSize += prevBlockSize; } else if ((prevBlockSize + blockSize + nextBlockSize) >= blocks) { DBGLOG_DEBUG( "realloc using prev and next block - %i\n", blocks ); umm_assimilate_up( c ); umm_disconnect_from_free_list( UMM_PBLOCK(c) ); c = umm_assimilate_down(c, 0); memmove( (void *)&UMM_DATA(c), ptr, curSize ); ptr = (void *)&UMM_DATA(c); blockSize += (prevBlockSize + nextBlockSize); } else { DBGLOG_DEBUG( "realloc a completely new block %i\n", blocks ); void *oldptr = ptr; if( (ptr = umm_malloc( size )) ) { DBGLOG_DEBUG( "realloc %i to a bigger block %i, copy, and free the old\n", blockSize, blocks ); memcpy( ptr, oldptr, curSize ); umm_free( oldptr ); } else { DBGLOG_DEBUG( "realloc %i to a bigger block %i failed - return NULL and leave the old block!\n", blockSize, blocks ); /* This space intentionally left blnk */ } blockSize = blocks; } /* Now all we need to do is figure out if the block fit exactly or if we * need to split and free ... */ if (blockSize > blocks ) { DBGLOG_DEBUG( "split and free %i blocks from %i\n", blocks, blockSize ); umm_split_block( c, blocks, 0 ); umm_free( (void *)&UMM_DATA(c+blocks) ); } /* Release the critical section... */ UMM_CRITICAL_EXIT(); return( ptr ); }
void *umm_malloc( size_t size ) { unsigned short int blocks; unsigned short int blockSize = 0; unsigned short int bestSize; unsigned short int bestBlock; unsigned short int cf; if (umm_heap == NULL) { umm_init(); } /* * the very first thing we do is figure out if we're being asked to allocate * a size of 0 - and if we are we'll simply return a null pointer. if not * then reduce the size by 1 byte so that the subsequent calculations on * the number of blocks to allocate are easier... */ if( 0 == size ) { DBGLOG_DEBUG( "malloc a block of 0 bytes -> do nothing\n" ); return( (void *)NULL ); } /* Protect the critical section... */ UMM_CRITICAL_ENTRY(); blocks = umm_blocks( size ); /* * Now we can scan through the free list until we find a space that's big * enough to hold the number of blocks we need. * * This part may be customized to be a best-fit, worst-fit, or first-fit * algorithm */ cf = UMM_NFREE(0); bestBlock = UMM_NFREE(0); bestSize = 0x7FFF; while( cf ) { blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf; DBGLOG_TRACE( "Looking at block %6i size %6i\n", cf, blockSize ); #if defined UMM_BEST_FIT if( (blockSize >= blocks) && (blockSize < bestSize) ) { bestBlock = cf; bestSize = blockSize; } #elif defined UMM_FIRST_FIT /* This is the first block that fits! */ if( (blockSize >= blocks) ) break; #else # error "No UMM_*_FIT is defined - check umm_malloc_cfg.h" #endif cf = UMM_NFREE(cf); } if( 0x7FFF != bestSize ) { cf = bestBlock; blockSize = bestSize; } if( UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks ) { /* * This is an existing block in the memory heap, we just need to split off * what we need, unlink it from the free list and mark it as in use, and * link the rest of the block back into the freelist as if it was a new * block on the free list... */ if( blockSize == blocks ) { /* It's an exact fit and we don't neet to split off a block. */ DBGLOG_DEBUG( "Allocating %6i blocks starting at %6i - exact\n", blocks, cf ); /* Disconnect this block from the FREE list */ umm_disconnect_from_free_list( cf ); } else { /* It's not an exact fit and we need to split off a block. */ DBGLOG_DEBUG( "Allocating %6i blocks starting at %6i - existing\n", blocks, cf ); /* * split current free block `cf` into two blocks. The first one will be * returned to user, so it's not free, and the second one will be free. */ umm_split_block( cf, blocks, UMM_FREELIST_MASK /*new block is free*/ ); /* * `umm_split_block()` does not update the free pointers (it affects * only free flags), but effectively we've just moved beginning of the * free block from `cf` to `cf + blocks`. So we have to adjust pointers * to and from adjacent free blocks. */ /* previous free block */ UMM_NFREE( UMM_PFREE(cf) ) = cf + blocks; UMM_PFREE( cf + blocks ) = UMM_PFREE(cf); /* next free block */ UMM_PFREE( UMM_NFREE(cf) ) = cf + blocks; UMM_NFREE( cf + blocks ) = UMM_NFREE(cf); } } else { /* Out of memory */ DBGLOG_DEBUG( "Can't allocate %5i blocks\n", blocks ); /* Release the critical section... */ UMM_CRITICAL_EXIT(); return( (void *)NULL ); } /* Release the critical section... */ UMM_CRITICAL_EXIT(); return( (void *)&UMM_DATA(cf) ); }