Ideally, a realloc()
would simply see if there's enough free memory beyond the current block and, if so, just adjust the arena data structures to expand the current block in-place. This gets rid of the expensive copy operation and reduces the chances of allocation failure. That's for increasing the size. For reducing, you should be able to do it in-place always, sending the remainder of the current block back to the free pool.
By doing a malloc/free, if you have 100K in the arena with a single 60K block allocated, calling your mm_realloc
to adjust the size to 50K will fail.
However, it is a workable solution, at least for a first attempt, so here's how I'd implement it:
void *mm_realloc (void *ptr, size_t size) {
int minsize;
void *newptr;
// Allocate new block, returning NULL if not possible.
newptr = malloc (size);
if (newptr == NULL) return NULL;
// Don't copy/free original block if it was NULL.
if (ptr != NULL) {
// Get size to copy - mm_getsize must give you the size of the current block.
// But, if new size is smaller, only copy that much. Many implementations
// actually reserve the 16 bytes in front of the memory to store this info, e.g.,
// +--------+--------------------------------+
// | Header | Your data |
// +--------+--------------------------------+
// ^
// +--- this is your pointer.
// <- This is the memory actually allocated ->
minsize = mm_getsize (ptr);
if (size < minsize)
minsize = size;
// Copy the memory, free the old block and return the new block.
memcpy (newptr, ptr, minsize);
free (ptr)
}
return newptr;
}
One thing you'll notice that's missing from yours is that it has to copy only enough bytes for the smallest of the old and new blocks. Otherwise you risk core dumps by overflowing one of them.
In addition, your loop didn't actually copy the data, it set each byte of the block to its offset, and it lost the old pointer when allocating the new one, hence my use of newptr
to keep them separate.