| /* |
| * 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; |
| } |