/* Change the size of a block allocated by `malloc'.
   Copyright 1994 Tristan Gingold
		     Written January 1994 by Tristan Gingold
		     
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE
*/

#define _MALLOC_INTERNAL
#include "malloc.h"

#ifdef CHECKER
#include "errlist.h"
#endif

#ifdef CHKR_HEAPBITMAP
void chkr_copy_bm(__ptr_t dest, __ptr_t src, unsigned int len);
#endif

/* Resize the given region to the new size, returning a pointer
 * to the (possibly moved) region.
 */
__ptr_t
realloc (__ptr_t ptr, size_t size)
{
  __ptr_t result;
  struct malloc_header *block;
  size_t old_size;
  
#ifdef CHKR_STACKBITMAP
  chkr_check_addr(&ptr, sizeof(__ptr_t), CHKR_RO);
  chkr_check_addr(&size, sizeof(size_t), CHKR_RO);
#endif
  
  if (ptr == NULL)
    return _malloc(size);
    
  if(!__malloc_initialized)	/* Imagine you call realloc before malloc */
    {
      chkr_errno = E_FREEBMALLOC;
      chkr_perror();
      return NULL;
    }
  
  /* Compute the address of the header from ptr */
  block = (struct malloc_header*)(ptr - HEADER_SIZE - be_red_zone);
  /* Check if the address is good */
  if (block != find_header(ptr))
    {
      chkr_errno = E_BADADDR;
      chkr_perror();
      return NULL;
    }
  /* A little check: the block must be busy, to be freed */
  if (block->state != MDBUSY)
    {
      chkr_errno = E_FREEFFRAG;
      chkr_perror();
      return NULL;
    }

  if (size == 0)
    {
      _free (ptr);
      return _malloc (0);
    }
    
   /* Very simple realloc implementation 
      But easy to do and bugs free ( I hope !)
      Anyway, realloc is very rarely used ...
      memory management in C++ implement only malloc (new) and free (delete).
   */
   result = _malloc (size);
   if (result != NULL)
     {
       memcpy (result, ptr, size);
       old_size = block->info.busy.real_size;
#ifdef CHKR_HEAPBITMAP
	/* This was the first bug !!! */
       chkr_copy_bm(result, ptr, old_size > size ? size : old_size);
       /* Disable reading and writing right in the block */
       chkr_set_right((__ptr_t)block + HEADER_SIZE + be_red_zone,
                 block->info.busy.real_size, CHKR_UN);
#endif /* CHKR_HEAPBITMAP */
       _free (ptr);
     }
   return result;
}
