/*
* Copyright (c) Jon Santmyer.
* This source file is released under the LGPL Version 3 as detailed in
* the LICENSE file provided in the following repository:
* https://git.jonsantmyer.com/heap/
* */
#include "malloc.h"
#include <assert.h>
#include <string.h>
void*
__realloc_impl(heap_cache_t *cache, void *ptr, size_t w)
{
if(w == 0) {
DBGPRINT("realloc: called with w == 0, freeing %p\n", ptr);
__free_impl(cache, ptr);
return NULL;
}
if(ptr == NULL) {
DBGPRINT("realloc: called with NULL ptr, alloc %i\n", w);
return __malloc_impl(cache, w);
}
w = REALW(w);
bucket_obj_t *obj = OBJ_FROM_PTR(ptr);
assert(OBJ_VALID(obj));
size_t objw = OBJ_WIDTH(obj);
if(objw > w) return ptr;
size_t wdiff = w - objw;
DBGPRINT("realloc: ptr %p needs %i new bytes\n", ptr, wdiff);
bucket_obj_t *after = OBJ_AFTER(obj);
size_t afterw = 0;
bucket_t *bucket = __bucket_from_obj(cache, obj);
void *newptr = NULL;
if(bucket == NULL)
goto resize_fail;
if((uintptr_t)after > bucket->limit) goto resize_fail;
assert(OBJ_VALID(after));
afterw = OBJ_WIDTH(after);
if(OBJ_TAKEN(after))
goto resize_fail;
DBGPRINT("realloc: obj %p after obj %p is free (w %i)\n", after, obj, afterw);
if(objw + sizeof(bucket_obj_t) + afterw < w)
goto resize_fail;
obj->size_taken = w | 1;
if(bucket->firstfree == after)
bucket->firstfree = OBJ_AFTER(obj);
after = OBJ_AFTER(obj);
*after = (bucket_obj_t){
.checksum = OBJ_CHECKSUM,
.size_taken = afterw - wdiff
};
DBGPRINT("realloc: moved obj %p (%i) to resize obj %p (%i)\n",
after, after->size_taken, obj, obj->size_taken);
return ptr;
resize_fail:
DBGPRINT("realloc: could not resize existing object, calling malloc\n");
newptr = __malloc_impl(cache, w);
memcpy(newptr, ptr, obj->size_taken & ~1);
__free_impl(cache, ptr);
return newptr;
}