/* morecore.c - C library support routine for UNIX.
   Copyright (c) 1989, 1993  Michael J. Haertel
   You may redistribute this library under the terms of the
   GNU Library General Public License (version 2 or any later
   version) as published by the Free Software Foundation.
   THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED
   WARRANTY.  IN PARTICULAR, THE AUTHOR MAKES NO REPRESENTATION OR
   WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY OF THIS
   SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. */

#include <limits.h>
#include <stddef.h>
#include "malloc.h"
#include "chkrlib.h"

extern char etext;
static __ptr_t safe_sbrk(int incr);

#ifdef CHKR_HEAPBITMAP
__ptr_t chkr_heap_begin;
static __ptr_t heap_end;
static __ptr_t real_heap_end;
unsigned char *chkr_heap_bitmap;
static int heap_bitmap_size;
#endif /* CHKR_HEAPBITMAP */
#ifdef CHKR_DATABITMAP
unsigned char *chkr_data_bitmap;
static int data_bitmap_size;
#endif /* CHKR_DATABITMAP */
static int bytes_per_state = 1;	/* Number of bytes regrouped by a state */
int bm_round;			/* number of bytes per byte of bitmap -1 */
int bm_log_size;
static int initialized;

/* sbrk() return the old break address. ie brk(0) == brk(100) */

/* Note that morecore has to take a signed argument so
   that negative values can return memory to the system. */
void *
__default_morecore(int size)
{
 void *result;
#ifdef CHKR_HEAPBITMAP 
 int size1;
#endif /* CHKR_HEAPBITMAP */

#ifdef CHKR_USE_BITMAP
 if(!initialized)
 {
   /* initialisation */
   if (bytes_per_state == 0)	/* Can't be used */
   {
      chkr_errno = 8;	/* error */
      chkr_perror();
      bytes_per_state = 1;	/* default value */
   }
   while (bytes_per_state != 1)
   {
     bm_log_size++;
     bytes_per_state >>= 1;
   }
   bytes_per_state = 1 << bm_log_size;
   bm_log_size += 2;
   bm_round = (1 << bm_log_size) -1;
#ifdef CHKR_DATABITMAP
   result = sbrk(0);
   data_bitmap_size = (result - (__ptr_t)&etext + bm_round) >> bm_log_size;
   chkr_data_bitmap = sbrk(data_bitmap_size);
   if (chkr_data_bitmap == (__ptr_t)-1)
     chkr_data_bitmap = NULL;	/* Won't be checked */
   memset(chkr_data_bitmap, 0xff, data_bitmap_size);
#endif /* CHKR_DATABITMAP */
#ifdef CHKR_HEAPBITMAP
   chkr_heap_begin = heap_end = sbrk(0); /* Can sbrk(0) return -1 ??? */
   chkr_heap_bitmap = real_heap_end = heap_end;
   heap_bitmap_size = 0;
#endif /* CHKR_HEAPBITMAP */   
   initialized = 1;	/* really useful... */
 }
#endif /* CHKR_USE_BITMAP */
#if !defined(CHKR_HEAPBITMAP)
 /* really stupid morecore */
 result = sbrk(size);
 if (result == (void *) -1)
   return NULL;
 return result;
#else
 if (size == 0)
 {
   safe_sbrk(0);
   return heap_end;	/* morecore(0) cheats the caller... */
 }
 if (size > 0)
 {
   /* We must grow the bitmap */
   size1 = (size + bm_round) >> bm_log_size;
   
   result = safe_sbrk(size + size1);
   if (result == NULL)
     return NULL;
   /* Move the bitmap */
   memmove(chkr_heap_bitmap + size, chkr_heap_bitmap, heap_bitmap_size);
   /* initialize the new bitmap */
   chkr_heap_bitmap += size;   
   memset(chkr_heap_bitmap + heap_bitmap_size, 0, size1);
   /* Adjust the bitmap info */
   heap_bitmap_size += size1;
   /* Clean the new area */
   memset(heap_end, 0, size);
   heap_end += size;
   real_heap_end += size + size1;
   return heap_end - size;	/* return the old address */
 }
 else
 { 
   /* We must reduce the bitmap */
   size1 = (abs(size) + bm_round) >> bm_log_size;
   /* Move the bitmap */
   memmove(chkr_heap_bitmap + size, chkr_heap_bitmap, heap_bitmap_size - size1);
   /* return memory to the system */
   result = safe_sbrk(size - size1);
   if (result == NULL)	/* Is it possible ?? */
     return NULL;
   /* Adjust the bitmap info */
   chkr_heap_bitmap += size;
   heap_bitmap_size -= size1;
   heap_end += size;
   real_heap_end += size - size1;
   return heap_end - size;	/* return the old address */
 }
#endif
}

#ifdef CHKR_HEAPBITMAP
static __ptr_t safe_sbrk(int incr)
{
 __ptr_t result;
 result = sbrk(incr);
 if (result == (void*)-1)	/* Is it possible ?? */
   return NULL;
 if (result != real_heap_end)
 {
   chkr_errno = 7;	/* brk(2) has been called */
   chkr_perror();	/* We can't continue. */
   chkr_abort();
 }
 return result;
}
#endif /* CHKR_HEAPBITMAP */