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 *
realloc(void * mem,size_t new_size)24 realloc (void *mem, size_t new_size)
25 {
26   size_t size;
27   char *base_mem;
28 
29   if (! mem)
30     return malloc (new_size);
31 
32   /* Check for special cases.  */
33   if (! new_size)
34     {
35       free (mem);
36       return NULL;
37     }
38 
39   /* This matches the check in malloc() */
40   if (unlikely(((unsigned long)new_size > (unsigned long)(MALLOC_HEADER_SIZE*-2))))
41     return NULL;
42 
43   /* Normal realloc.  */
44 
45   base_mem = MALLOC_BASE (mem);
46   size = MALLOC_SIZE (mem);
47 
48   /* Include extra space to record the size of the allocated block.
49      Also make sure that we're dealing in a multiple of the heap
50      allocation unit (SIZE is already guaranteed to be so).*/
51   new_size = HEAP_ADJUST_SIZE (new_size + MALLOC_HEADER_SIZE);
52 
53   if (new_size < sizeof (struct heap_free_area))
54     /* Because we sometimes must use a freed block to hold a free-area node,
55        we must make sure that every allocated block can hold one.  */
56     new_size = HEAP_ADJUST_SIZE (sizeof (struct heap_free_area));
57 
58   MALLOC_DEBUG (1, "realloc: 0x%lx, %d (base = 0x%lx, total_size = %d)",
59 		(long)mem, new_size, (long)base_mem, size);
60 
61   if (new_size > size)
62     /* Grow the block.  */
63     {
64       size_t extra = new_size - size;
65 
66       __heap_lock (&__malloc_heap_lock);
67       extra = __heap_alloc_at (&__malloc_heap, base_mem + size, extra);
68       __heap_unlock (&__malloc_heap_lock);
69 
70       if (extra)
71 	/* Record the changed size.  */
72 	MALLOC_SET_SIZE (base_mem, size + extra);
73       else
74 	/* Our attempts to extend MEM in place failed, just
75 	   allocate-and-copy.  */
76 	{
77 	  void *new_mem = malloc (new_size - MALLOC_HEADER_SIZE);
78 	  if (new_mem)
79 	    {
80 	      memcpy (new_mem, mem, size - MALLOC_HEADER_SIZE);
81 	      free (mem);
82 	    }
83 	  mem = new_mem;
84 	}
85     }
86   else if (new_size + MALLOC_REALLOC_MIN_FREE_SIZE <= size)
87     /* Shrink the block.  */
88     {
89       __heap_lock (&__malloc_heap_lock);
90       __heap_free (&__malloc_heap, base_mem + new_size, size - new_size);
91       __heap_unlock (&__malloc_heap_lock);
92       MALLOC_SET_SIZE (base_mem, new_size);
93     }
94 
95   if (mem)
96     MALLOC_DEBUG (-1, "realloc: returning 0x%lx (base:0x%lx, total_size:%d)",
97 		  (long)mem, (long)MALLOC_BASE(mem), (long)MALLOC_SIZE(mem));
98   else
99     MALLOC_DEBUG (-1, "realloc: returning 0");
100 
101   return mem;
102 }
103