/***************************************************************************
 * 
 * Copyright (c) 1997, 1998 Newcode Technology, Inc.  All rights reserved.
 * Copyright (c) 1999, Geodesic Systems, Inc. All rights reserved.
 *
 * MTS speed test demo program
 ***************************************************************************
 */
#include <stdio.h>
#if M_SOLARIS || M_HP700 || M_DECALPHA
#include <malloc.h>
#elif M_IBM_R6000
#include <stdlib.h>
#elif M_NT
#include <windows.h>
showtime() {
    static iter = 0;
    static long firsttime;
    SYSTEMTIME systime;
    GetSystemTime(&systime);
    if (iter == 0) {
      firsttime = 
             (systime.wHour            * 60 * 60 * 1000 +
              systime.wMinute               * 60 * 1000 +
              systime.wSecond                    * 1000 +
              systime.wMilliseconds);
      iter++;
    }
    else {
      int ms;
      int secs;
      int mins = 0;
      ms = (systime.wHour            * 60 * 60 * 1000 +
            systime.wMinute               * 60 * 1000 +
            systime.wSecond                    * 1000 +
            systime.wMilliseconds) - firsttime;
      secs = ms / 1000;
      ms = ms % 1000;
      mins = secs / 60;
      secs = secs % 60;
      printf("\nElapsed time: %dm%d.%3.3ds\n", mins, secs, ms); 
    }
}
#endif

/*
 *  MEM_ARRAY_SIZE defines how many mallocs occur before calls to free are
 *  made.  MEM_SAVE_SIZE defines what number of mallocs are not freed in
 *  the current free cycle.  Increasing the ARRAY_SIZE increases the amount
 *  of memory that is malloced and freed.  Decreasing the SAVE_SIZE divisor
 *  also increases the amount of memory needed to run this test.
 *  This test assumes that 1/15th of the mallocs are not freed on each cycle.
 */

#define	MEM_ARRAY_SIZE	100000
#define	MEM_SAVE_SIZE	(MEM_ARRAY_SIZE / 5)

char           *memory[MEM_ARRAY_SIZE];

int main ()
{ int		i; 
  int		j;
  int		k;
  int		k_max;
  int		repeat = 100;
  char        **mp = memory;
  char        **mpe = memory + sizeof memory / sizeof memory[0];
  char        **save_start = mpe;
  char        **save_end = mpe;
  long		low_mark;
  long		high_mark;
  long		print_high_mark;

/*
 *  prints different messages depending on whether this test is compiled
 *  with the MTS library or libc.a
 */
#if defined(COMPILE_ID)
  write (1, "Allocating memory using ", 
           sizeof "Allocating memory using " - 1);
  write (1, COMPILE_ID, sizeof COMPILE_ID - 1);
  write (1, "\n", 1);
#endif

/*
 *  low_mark and high_mark hold the lowest and highest pointers to memory
 *  that has been malloced.  They are printed out as the difference increases 
 *  between them in 1 meg intervals.
 */
  low_mark = (long) malloc (1);
  high_mark = low_mark;
  print_high_mark = low_mark;
  free ((char *) low_mark);

#if M_NT
  showtime();
#endif

  while (repeat--)           /* after every cycle print a '.' to the screen */
  { for (i = 1; i < 1000000; i = i * 3 / 2 + 1)
    { for (j = i; j; j /= 2)
      { k_max = 1;           /* k_max controls the size allocation bias.
				Large things are allocated once.  Sizes less
				than 100 are allocated 250 times.
                              */

	if (j < 10000)
	  k_max = 10;

	if (j < 1000)
	  k_max *= 5;

	if (j < 100)
	  k_max *= 5;

	for (k = 0; k < k_max; k ++)
  	{ if ( ! (*mp ++ = (char *) malloc (j)))
  	  { write (2, "? malloc failed\n", sizeof "? malloc failed\n");
  	    _exit (1);
  	  }
	  /* set the high_mark and check if it should be printed */
	  if (((long) *(mp - 1)) + j > high_mark)
	  { high_mark = (long) *(mp - 1) + j;
	    
	    if (high_mark > print_high_mark + 5L * 1024L * 1024L)
	    { printf ("\nheap size %ld", high_mark - low_mark);
#if !M_NT
	      fflush (stdout);
#endif
	      print_high_mark = high_mark;
	    }
	  
	  }
	  /* while allocating skip over that portion of the buffer that still
	     holds pointers from the previous cycle
           */
	  if (mp == save_start)
	    mp = save_end;

	  if (mp >= mpe)   /* if we've reached the end of the malloc buffer */
	  { mp = memory;
            
	    /* mark the next portion of the buffer */
	    save_start = save_end;  
	    if (save_start >= mpe)	save_start = mp;
	    save_end = save_start + MEM_SAVE_SIZE;
	    if (save_end > mpe)		save_end = mpe;
            
	    /* free the bottom and top parts of the buffer.
	     * The bottom part is freed in the order of allocation.
	     * The top part is free in reverse order of allocation.
	     */
	    while (mp < save_start)
	      free (*mp ++);
	    mp = mpe;

	    while (mp > save_end)
	      free (*--mp);

	    mp = memory;
	  }
	}
      }
    }
  }
  printf ("\nheap size %ld\n", high_mark - low_mark);
#if !M_NT
  fflush (stdout);
#endif

  /* free the residual allocations */
  mpe = mp;
  mp = memory;

  while (mp < mpe)
    free (*mp ++);

#if M_NT
  showtime();
#endif

  return 0;
}
