/* malloc(3) implementation
   Copyright 1993, 1994 Tristan Gingold
		  Written December 1993 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"

struct malloc_header *_heapinfo[HASH_SIZE];
#ifndef NO_HEAPINDEX
unsigned int _heapindex;
#endif /* NO_HEAPINDEX */
struct malloc_header *_firstblock;
struct malloc_header *_lastblock;
int __malloc_initialized = 0;

/* malloc: return the address of a block of size bytes.
 * malloc(0) returns 0
 */ 
__ptr_t
_malloc(size_t real_size)
{
  size_t round_size;
  struct malloc_header *tmp;
  struct malloc_header *tmp1;
  int i;

#if 1
  if (real_size == 0)
    return (__ptr_t)0;
#endif      
  /* initialize all, if necessary */
  if (__malloc_initialized == 0)
    {
      __malloc_initialized = 1;
      chkr_initialize();	/* which called init_morecore */
    }
  round_size = real_size + be_red_zone + af_red_zone;
  round_size = (round_size + (LITTLE_SPACE-1)) & ~(LITTLE_SPACE-1);
  
  /* Try to find a free block, in the lists of free blocks */
  tmp1 = NULL_HEADER;
  /* Begins with the smallest list */
#ifdef NO_HEAPINDEX  
  for(i = log_size(round_size); i < HASH_SIZE; i++)
#else /* !NO_HEAPINDEX */
  for(i = log_size(round_size); i <= _heapindex; i++)  
#endif /* NO_HEAPINDEX */
    {
      if (_heapinfo[i] == NULL_HEADER)
        continue;	/* empty list */
      tmp = _heapinfo[i];
      for(tmp = _heapinfo[i]; tmp != NULL_HEADER; tmp = tmp->info.free.next)
        {
          if (tmp->size < round_size)
            continue;	/* block is too little */
          tmp->state = MDBUSY;	/* This block will be used */
          if (tmp->size > (round_size + HEADER_SIZE + LITTLE_SPACE))
            {
              /* split the block: tmp1 is a new block */
              tmp1 = (struct malloc_header*)((char*)tmp + round_size + HEADER_SIZE);
              tmp1->prev = tmp;
              tmp1->next = tmp->next;
              if (tmp->next)
                tmp->next->prev = tmp1;
              tmp->next = tmp1;        
              tmp1->size = tmp->size - HEADER_SIZE - round_size;
              tmp->size = round_size;
              if (_lastblock == tmp)
                _lastblock = tmp1;
            }
          /* remove the block from the list */
          if (tmp->info.free.next)
            tmp->info.free.next->info.free.prev = tmp->info.free.prev;
          if (tmp->info.free.prev)
            tmp->info.free.prev->info.free.next = tmp->info.free.next;
          if (_heapinfo[i] == tmp)
            _heapinfo[i] = tmp->info.free.next;

#ifndef NO_HEAPINDEX        
          /* Compute _heapindex, if necessary */
          if(_heapindex == i && _heapinfo[i] == NULL_HEADER)
            while(_heapinfo[_heapindex] == NULL_HEADER)
              {
                if(_heapindex)
                  _heapindex--;
                else
                  break;
              }
#endif /* NO_HEAPINDEX */
          /* free the split block, if it exists */
          if (tmp1)
            {
              tmp1->state = MDBUSY;	/* cheats free() */
              /* The new block can't be coalised. So call _internal_free() */
              _internal_free(tmp1);	/* save it */
              tmp1 = NULL_HEADER;
            }
          goto done;
        }  
    }
  /* No free block: must allocate memory */
  /* first, try the last block */
  if (_lastblock->state == MDFREE)
    {
      tmp = _lastblock;
      if (morecore(round_size - tmp->size) == (__ptr_t)0)
        return (__ptr_t)0;
      i = log_size(tmp->size);
      tmp->size = round_size;
      /* remove the block from the list */
      if (tmp->info.free.next)
        tmp->info.free.next->info.free.prev = tmp->info.free.prev;
      if (tmp->info.free.prev)
        tmp->info.free.prev->info.free.next = tmp->info.free.next;
      if (_heapinfo[i] == tmp)
        _heapinfo[i] = tmp->info.free.next;
    }
  else
    {
      tmp = (struct malloc_header*)morecore(round_size + HEADER_SIZE);
      if (tmp == NULL_HEADER)
        return (__ptr_t)0;	/* Not enough memory */
      tmp->size = round_size;
      tmp->prev = _lastblock;
      if (_lastblock)
        _lastblock->next = tmp;
      tmp->next = NULL_HEADER;
      _lastblock = tmp;
      if(!_firstblock)
        _firstblock = tmp;
    }
 done:
  /* all is right */
  tmp->state = MDBUSY;
  tmp->garbage_t = POINT_NOT;
  tmp->info.busy.real_size = real_size;
  tmp->s_diff = tmp->size - real_size - be_red_zone - af_red_zone;
#ifdef CHKR_SAVESTACK
  chkr_save_stack(	/* save the stack */
    	(__ptr_t*)((u_int)tmp + HEADER_SIZE + round_size - af_red_zone),
    	0, /* number of frame to forget. with 0, show malloc */
    	af_red_zone / sizeof(void*)); /* number of frames to save */
#endif /* CHKR_SAVESTACK */
#ifdef CHKR_HEAPBITMAP
  chkr_set_right((__ptr_t)tmp + HEADER_SIZE + be_red_zone, real_size, CHKR_WO);
#endif /* CHKR_HEAPBITMAP */
  return (__ptr_t)tmp + HEADER_SIZE + be_red_zone;
}

/* malloc: return the address of a block of size bytes.
 * malloc(0) returns 0
 */ 
__ptr_t
malloc(size_t real_size)
{
#ifdef CHKR_STACKBITMAP
  chkr_check_addr(&real_size, sizeof (size_t), CHKR_RO);
#endif
  return _malloc(real_size);
}
