bool test_integrity_check(void) { size_t size; for (size = 1; size <= 16; size++) { { umm_init(); corruption_cnt = 0; char *ptr = wrap_malloc(size); memset(ptr, 0xfe, size + 8 /* size of umm_block*/); /* * NOTE: we don't use wrap_free here, because we've just corrupted the * heap, and gathering of the umm info on corrupted heap can cause * segfault */ umm_free(ptr); if (corruption_cnt == 0) { printf("corruption_cnt should not be 0, but it is\n"); return false; } } { umm_init(); corruption_cnt = 0; char *ptr = wrap_calloc(1, size); ptr[-1]++; /* * NOTE: we don't use wrap_free here, because we've just corrupted the * heap, and gathering of the umm info on corrupted heap can cause * segfault */ umm_free(ptr); if (corruption_cnt == 0) { printf("corruption_cnt should not be 0, but it is\n"); return false; } } } return true; }
/* * Wrapper for `umm_free()` which performs additional checks */ static void wrap_free(void *ptr) { free_blocks_check(); umm_free(ptr); free_blocks_check(); }
void vPortFree(void *ptr, const char *file, unsigned line) { (void) file; (void) line; umm_free(ptr); }
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 ICACHE_RAM_ATTR free(void *pv){ umm_free(pv); }