/* rndheap.c (emx+gcc) */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <signal.h>
#include <time.h>
#include <getopt.h>
#include <errno.h>
#include <limits.h>

struct block
{
  void *mem;
  size_t size;
  int tile;
};

/* Not available when using emxlibc.dll! */

extern size_t *_malloc_bottom;
extern size_t *_malloc_top;

static long iter;
static size_t block_count;
static struct block *blocks;

static void usage (void)
{
  fputs ("Usage: rndheap [-c <count>] [-i <iterations>] [-s <size>] "
         "[-t] <seed>\n", stderr);
  exit (1);
}


static void show (void)
{
  long used_size;
  int i, count, tiled;

  used_size = 0; count = 0; tiled = 0;
  for (i = 0; i < block_count; ++i)
    if (blocks[i].mem != NULL)
      {
        ++count;
        if (blocks[i].tile)
          ++tiled;
        used_size += blocks[i].size;
      }
  printf ("Iterations: %10ld, blocks: %3d, tiled: %3d, "
          "used: %7ld, size: %8ld\n",
          iter, count, tiled, used_size,
          (size_t)_malloc_top - (size_t)_malloc_bottom);
}


static void handler (int sig)
{
  show ();
  signal (sig, SIG_ACK);
  alarm (60);
}


int main (int argc, char *argv[])
{
  int c, i, j, k, opt_t;
  size_t s, block_size;
  long n, iter_limit;
  void *p;
  char *q;

  opt_t = 0; iter_limit = LONG_MAX;
  block_size = 0x8000; block_count = 256;
  while ((c = getopt (argc, argv, "c:i:s:t")) != EOF)
    switch (c)
      {
      case 'c':
        errno = 0;
        block_count = strtol (optarg, &q, 0);
        if (errno != 0 || block_count < 2 || *q != 0)
          usage ();
        break;
      case 'i':
        errno = 0;
        iter_limit = strtol (optarg, &q, 0);
        if (errno != 0 || iter_limit < 1 || *q != 0)
          usage ();
        break;
      case 's':
        errno = 0;
        block_size = strtol (optarg, &q, 0);
        if (errno != 0 || block_size < 2 || *q != 0)
          usage ();
        break;
      case 't':
        opt_t = 1;
        break;
      default:
        usage ();
      }
  argv += optind;
  argc -= optind;
  if (argc != 1)
    usage ();

  errno = 0;
  n = strtol (argv[0], &q, 0);
  if (errno != 0 || *q != 0)
    usage ();

  srand ((unsigned)n);

  blocks = alloca (block_count * sizeof (*blocks));

  for (i = 0; i < block_count; ++i)
    {
      blocks[i].mem = NULL;
      blocks[i].size = 0;
      blocks[i].tile = 0;
    }

  signal (SIGALRM, handler);

  if (iter_limit != LONG_MAX)
    printf ("Start time: %ld\n", (long)clock ());

  show ();
  alarm (60);

  k = 0;
  for (iter = 0; iter < iter_limit; ++iter)
    {
      if (k == 97)
        {
          i = _heapchk ();
          if (i != _HEAPOK && i != _HEAPEMPTY)
            {
              fprintf (stderr, "Error in iteration %ld\n", iter);
              abort ();
            }
          k = 0;
        }
      i = rand () / 253;
      j = (rand () / 5) % block_count;
      switch (i % 3)
        {
        case 0:
          p = blocks[j].mem;
          if (p != NULL)
            {
              if (blocks[j].tile)
                _tfree (p);
              else
                free (p);
              blocks[j].mem = NULL;
            }
          break;
        case 1:
          p = blocks[j].mem;
          if (p != NULL)
            {
              if (blocks[j].tile)
                _tfree (p);
              else
                free (p);
            }
          s = rand () % block_size;
          blocks[j].tile = opt_t && ((i >> 2) & 1);
          blocks[j].size = s;
          if (blocks[j].tile)
            p = _tmalloc (s);
          else
            p = malloc (s);
          if (p == NULL)
            {
              fprintf (stderr, "Error in iteration %ld\n", iter);
              abort ();
            }
          blocks[j].mem = p;
          if (blocks[j].tile && s != 0)
            {
              if (((size_t)p & ~0xffff) != (((size_t)p + s - 1) & ~0xffff))
                {
                  fprintf (stderr, "Error in iteration %ld\n", iter);
                  abort ();
                }
            }
          memset (p, 0x55, s);
          break;
        case 2:
          p = blocks[j].mem;
          s = rand () % block_size;
          blocks[j].size = s;
          if (blocks[j].tile)
            p = _trealloc (p, s);
          else
            p = realloc (p, s);
          if (p == NULL)
            {
              if (s != 0)
                {
                  fprintf (stderr, "Error in iteration %ld\n", iter);
                  abort ();
                }
            }
          blocks[j].mem = p;
          if (p != NULL)
            {
              memset (blocks[j].mem, 0x55, s);
              if (blocks[j].tile)
                {
                  if (((size_t)p & ~0xffff) != (((size_t)p + s - 1) & ~0xffff))
                    {
                      fprintf (stderr, "Error in iteration %ld\n", iter);
                      abort ();
                    }
                }
            }
          break;
        }
    }
  printf ("End time:   %ld\n", (long)clock ());
  return (0);
}
