#include <stdio.h>
#define _MALLOC_INTERNAL
/*#include <stdlib.h>*/
#include "ansidecl.h"
#include "gmalloc.h"

/* The routine check_malloc_heap() does a number of consistency
 * checks on the malloc heap.  You can call it (perhaps invoked
 * by gdb) when you suspect memory is corrupted.
 * Alternatively, you can have it be called before every malloc,
 * before every 10'th malloc, every 5th malloc after the 500'th etc.
 * To do that, call monitor_malloc() (perhaps from the debugger).
 * This causes every call to malloc() to be counted
 * (in the variable 'malloc_count'), and sometimes call check_malloc_heap().
 *
 * Because check_malloc_heap() takes a while to execute you may
 * not want to have it run on every malloc(). You can use
 * the following three variables to control when check_malloc_heap is
 * called:
 *
 * While MALLOC_COUNT is between START_MALLOC_CHECK and END_MALLOC_CHECK,
 * call check_malloc_heap() for every COUNT_MALLOC_CHECK calls to malloc.
 */
unsigned long start_malloc_check = 0;
unsigned long end_malloc_check = (unsigned long)(-1);
unsigned long count_malloc_check = 1;

/* Count of calls to malloc. */
static unsigned long count_malloc = 0;

/* Used to count from COUNT_MALLOC_CHECK down to 0. */
static unsigned long step_malloc_check = 1;

void static
malloc_fatal (message)
     char *message;
{
  fprintf (stderr, "malloc-botch: %s\n", message);
  abort ();
}

void
check_malloc_heap()
{
  size_t log = 1;
  size_t start, block, next;
  int cycles = 0;
  for (log = 1; log < BLOCKLOG; log++)
    {
      /* Check fragment free list. */
      struct list *list = &_fraghead[log];
      struct list *next;
      for (;;) {
	  next = list->next;
	  if (next == NULL)
	      break;
	  if (next->prev != list)
	    malloc_fatal ("Bad fragment free-list backpointer!");
	  block = BLOCK(next);
	  if (block > _heaplimit)
	    malloc_fatal ("Bad fragment free-list block index!");
          if (_heapinfo[block].busy.type != log)
	    malloc_fatal ("Bad fragment free-list block size!");
	  list = next;
      }
    }

  /* Check freelist of large blocks. */
  start = block = MALLOC_SEARCH_START;
  for (;;)
    {
      next = _heapinfo[block].free.next;
      if (_heapinfo[next].free.prev != block)
	malloc_fatal ("Bad block free-list backpointer!");
      if (next > block && block + _heapinfo[block].free.size > next)
	malloc_fatal ("Bad block free-list block size!");
      if (next <= block)
	if (cycles == 0)
	  cycles = 1;
	else
	  malloc_fatal ("Bad block free-list cycle!");
      block = next;
      if (block == start)
	  break;
    }

  /* Check busy blocks. */
  block = 0;
  for (;;)
    {
      int size = _heapinfo[block].free.size;
      next = _heapinfo[block].free.next;
      if (next == 0)
	next = _heaplimit;
      
      /* If the size is 0, it's not available. (???) */
      if (size == 0)  size = 1;
      
      block += size;

      /* All blocks between block+size (inclusive) and next (exclusive)
	 are in use (busy).  Check them all. */
      
      while (block < next)
	{
	  int size_log = _heapinfo[block].busy.type;
	  if (size_log == 0)
	    { /* A large block. */
	      size = _heapinfo[block].busy.info.size;
	      if (size == 0) size = 1;
	      block += size; /* Skip cluster. */
	    }
	  else /* A block of small fragments. */
	    {
	      int nfree = _heapinfo[block].busy.info.frag.nfree;
	      struct list *link;
	      struct list *block_start = (struct list*) ADDRESS(block);
	      struct list *block_end = (struct list*) ADDRESS(block+1);
	      /* Get the address of the first free fragment in this block.  */
	      link = (struct list *) ((char *) ADDRESS(block) +
				      (_heapinfo[block].busy.info.frag.first
				       << size_log));
	      if ((1 << size_log) > (BLOCKSIZE/2)
		|| (1 << size_log) < sizeof(struct list))
		  malloc_fatal ("Bad fragment size");
	      if (nfree >= (BLOCKSIZE >> size_log))
		  malloc_fatal ("Bad count of free fragments.");

	      if (nfree)
		{
		  for (; nfree > 0; nfree--)
		    {
		      /* The current element of the free list
			 must be in the current block. */
			if (link < block_start || link >= block_end)
			    malloc_fatal("Bad fragment free list count.");
			/* The address of a fragment must be a multiple
			   of (1<<size_log). */
			if ((unsigned long)link & ((1 << size_log)-1))
			  malloc_fatal ("Unaligned free fragment.");
			if (link->prev->next != link)
			  malloc_fatal ("Bad fragment free list.");
			link = link->next;
		    }
		  /* If the current element of the list (link) is
		     within the current block, somthing is confused. */
		  if (link != NULL
		    && (link >= block_start && link < block_end))
		      malloc_fatal("Bad fragment free list tail.");
	        }
	      block++;
	    }
	}
      
      block = next;
      if (block == _heaplimit)
	  break;
    }
}

/* Old hook values.  */
static void (*old_free_hook)(void * ptr);
static void* (*old_malloc_hook)(size_t size);
static void* (*old_realloc_hook)(void* ptr, size_t size);

static void *my_malloc(size_t size)
{
  void *ptr;
  count_malloc++;
  if (count_malloc < end_malloc_check && count_malloc >= start_malloc_check)
    if (--count_malloc_check <= 0)
      {
	check_malloc_heap();
	count_malloc_check = step_malloc_check;
      }
  __malloc_hook = old_malloc_hook;
  ptr = (void*)malloc(size);
  __malloc_hook = my_malloc;
  return ptr;
}

void monitor_malloc ()
{
    __malloc_hook = my_malloc;
}
