/* * libc/stdlib/malloc/realloc.c -- realloc function * * Copyright (C) 2002 NEC Corporation * Copyright (C) 2002 Miles Bader <miles@gnu.org> * * This file is subject to the terms and conditions of the GNU Lesser * General Public License. See the file COPYING.LIB in the main * directory of this archive for more details. * * Written by Miles Bader <miles@gnu.org> */ #include <stdlib.h> #include <string.h> #include <errno.h> #include "malloc.h" #include "heap.h" void * realloc (void *mem, size_t new_size) { size_t size; char *base_mem; /* Check for special cases. */ if (! new_size) { free (mem); return malloc (new_size); } if (! mem) return malloc (new_size); /* This matches the check in malloc() */ if (unlikely(((unsigned long)new_size > (unsigned long)(MALLOC_HEADER_SIZE*-2)))) return NULL; /* Normal realloc. */ base_mem = MALLOC_BASE (mem); size = MALLOC_SIZE (mem); /* Include extra space to record the size of the allocated block. Also make sure that we're dealing in a multiple of the heap allocation unit (SIZE is already guaranteed to be so).*/ new_size = HEAP_ADJUST_SIZE (new_size + MALLOC_HEADER_SIZE); if (new_size < sizeof (struct heap_free_area)) /* Because we sometimes must use a freed block to hold a free-area node, we must make sure that every allocated block can hold one. */ new_size = HEAP_ADJUST_SIZE (sizeof (struct heap_free_area)); MALLOC_DEBUG (1, "realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)", (long)mem, new_size, (long)base_mem, size); if (new_size > size) /* Grow the block. */ { size_t extra = new_size - size; __heap_lock (&__malloc_heap_lock); extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra); __heap_unlock (&__malloc_heap_lock); if (extra) /* Record the changed size. */ MALLOC_SET_SIZE (base_mem, size + extra); else /* Our attempts to extend MEM in place failed, just allocate-and-copy. */ { void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE); if (new_mem) { memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE); free (mem); } mem = new_mem; } } else if (new_size + MALLOC_REALLOC_MIN_FREE_SIZE <= size) /* Shrink the block. */ { __heap_lock (&__malloc_heap_lock); __heap_free (&__malloc_heap, base_mem + new_size, size - new_size); __heap_unlock (&__malloc_heap_lock); MALLOC_SET_SIZE (base_mem, new_size); } if (mem) MALLOC_DEBUG (-1, "realloc: returning 0x%lx (base:0x%lx, total_size:%d)", (long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem)); else MALLOC_DEBUG (-1, "realloc: returning 0"); return mem; }