lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame] | 1 | /* |
| 2 | * libc/stdlib/malloc/realloc.c -- realloc function |
| 3 | * |
| 4 | * Copyright (C) 2002 NEC Corporation |
| 5 | * Copyright (C) 2002 Miles Bader <miles@gnu.org> |
| 6 | * |
| 7 | * This file is subject to the terms and conditions of the GNU Lesser |
| 8 | * General Public License. See the file COPYING.LIB in the main |
| 9 | * directory of this archive for more details. |
| 10 | * |
| 11 | * Written by Miles Bader <miles@gnu.org> |
| 12 | */ |
| 13 | |
| 14 | #include <stdlib.h> |
| 15 | #include <string.h> |
| 16 | #include <errno.h> |
| 17 | |
| 18 | |
| 19 | #include "malloc.h" |
| 20 | #include "heap.h" |
| 21 | |
| 22 | |
| 23 | void * |
| 24 | realloc (void *mem, size_t new_size) |
| 25 | { |
| 26 | size_t size; |
| 27 | char *base_mem; |
| 28 | |
| 29 | /* Check for special cases. */ |
| 30 | if (! new_size) |
| 31 | { |
| 32 | free (mem); |
| 33 | return malloc (new_size); |
| 34 | } |
| 35 | if (! mem) |
| 36 | return malloc (new_size); |
| 37 | /* This matches the check in malloc() */ |
| 38 | if (unlikely(((unsigned long)new_size > (unsigned long)(MALLOC_HEADER_SIZE*-2)))) |
| 39 | return NULL; |
| 40 | |
| 41 | /* Normal realloc. */ |
| 42 | |
| 43 | base_mem = MALLOC_BASE (mem); |
| 44 | size = MALLOC_SIZE (mem); |
| 45 | |
| 46 | /* Include extra space to record the size of the allocated block. |
| 47 | Also make sure that we're dealing in a multiple of the heap |
| 48 | allocation unit (SIZE is already guaranteed to be so).*/ |
| 49 | new_size = HEAP_ADJUST_SIZE (new_size + MALLOC_HEADER_SIZE); |
| 50 | |
| 51 | if (new_size < sizeof (struct heap_free_area)) |
| 52 | /* Because we sometimes must use a freed block to hold a free-area node, |
| 53 | we must make sure that every allocated block can hold one. */ |
| 54 | new_size = HEAP_ADJUST_SIZE (sizeof (struct heap_free_area)); |
| 55 | |
| 56 | MALLOC_DEBUG (1, "realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)", |
| 57 | (long)mem, new_size, (long)base_mem, size); |
| 58 | |
| 59 | if (new_size > size) |
| 60 | /* Grow the block. */ |
| 61 | { |
| 62 | size_t extra = new_size - size; |
| 63 | |
| 64 | __heap_lock (&__malloc_heap_lock); |
| 65 | extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra); |
| 66 | __heap_unlock (&__malloc_heap_lock); |
| 67 | |
| 68 | if (extra) |
| 69 | /* Record the changed size. */ |
| 70 | MALLOC_SET_SIZE (base_mem, size + extra); |
| 71 | else |
| 72 | /* Our attempts to extend MEM in place failed, just |
| 73 | allocate-and-copy. */ |
| 74 | { |
| 75 | void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE); |
| 76 | if (new_mem) |
| 77 | { |
| 78 | memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE); |
| 79 | free (mem); |
| 80 | } |
| 81 | mem = new_mem; |
| 82 | } |
| 83 | } |
| 84 | else if (new_size + MALLOC_REALLOC_MIN_FREE_SIZE <= size) |
| 85 | /* Shrink the block. */ |
| 86 | { |
| 87 | __heap_lock (&__malloc_heap_lock); |
| 88 | __heap_free (&__malloc_heap, base_mem + new_size, size - new_size); |
| 89 | __heap_unlock (&__malloc_heap_lock); |
| 90 | MALLOC_SET_SIZE (base_mem, new_size); |
| 91 | } |
| 92 | |
| 93 | if (mem) |
| 94 | MALLOC_DEBUG (-1, "realloc: returning 0x%lx (base:0x%lx, total_size:%d)", |
| 95 | (long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem)); |
| 96 | else |
| 97 | MALLOC_DEBUG (-1, "realloc: returning 0"); |
| 98 | |
| 99 | return mem; |
| 100 | } |