#include <stdio.h>
#include <stdlib.h>
#include "grope.h"
#include "files.h"
#ifdef MEMDEBUG
#include <mnemosyn.h>
#endif
#include "freel.h"
#include "sperror.h"

static void free_freel(FREEL **fl);
static void add_freel(FREEL **fl,void *member);
static void *sub_freel(FREEL **fl);


#ifdef TESTING
void check_val(FREEL *p)
{
 if (p->check_value != CHECK_VALUE)
 {
 fprintf(stderr,"Bad value at @ %p\n",p);
 exit(1);
 }
 return;
}
#endif
void CloseFMem(FREEL **fl)
{
 free_freel(fl);
}

#define MAX_ALLOC (63U*1024U)
#define MIN_SIZE sizeof(void *)

FREEL *SetUpFMem(long nitems,size_t size)
{
char *p;
FREEL *fl = NULL;
size_t this_alloc;

 size = max(size,MIN_SIZE);

 while (nitems)
 {
 this_alloc = (size_t)min(((long)MAX_ALLOC/(long)size),nitems);
 nitems -= this_alloc;
 if ((p = malloc(this_alloc * size)) == NULL)
 {
 RAM_ERROR;
 exit(1);
 }

 while (this_alloc--)
 {
 add_freel(&fl,p);
 p += size;
 }
 }
 return (void *)fl;
}

void *GetFMem(FREEL **fl)
{
void *p;
 p = sub_freel(fl);
 return p;
}

void PutFMem(FREEL **fl,void *memb)
{
 add_freel(fl,memb);
 return ;
}


static void free_freel(FREEL **fl)
{
#ifdef never
void *temp;
#endif
#ifdef TESTING
 check_val(*fl);
#endif
#ifdef never
 while (*fl != NULL)
 {
 temp = *fl;
 *fl = (*fl)->next;
 free(temp);
 }
#endif
 *fl = NULL;
}

static void add_freel(FREEL **fl,void *member)
{
#ifdef TESTING
 check_val(member);
#endif
 ((FREEL *)member)->next = *fl;
 *fl = member;
 return;
}


static void *sub_freel(FREEL **fl)
{
void *member;
#ifdef TESTING
 check_val(*fl);
#endif
 if (*fl == NULL)
 return NULL;
 member = *fl;
 *fl = (*fl)->next;
 return member;
}

#ifdef DBMAIN
static int size_freel(FREEL *fl)
{
int i = 0;

#ifdef TESTING
 check_val(fl);
#endif
 for (;;)
 {
 if (fl == NULL)
 return i;
 i++;
 fl = fl->next;
 }
}


static void print_freel(FREEL *fl)
{
int i = 0;

 for (;;)
 {
 if (fl == NULL)
 return;
#ifdef TESTING
 check_val(fl);
#endif
 i++;
 fprintf(stderr,"M %d @ %p\n",i,fl);
 fl = fl->next;
 }
}


#define NITS 30
#define ELEM_SIZE 256
static void randomise(void *p[],int nelem)
{
int i;
void *temp;

 for (;;)
 {
 nelem--;
 i = rand() % nelem;
 temp = p[nelem];
 p[nelem] = p[i];
 p[i] = temp;
 if (nelem < 2)
 break;
 }
 return;
}
FREEL *store[NITS];
#define N_ITEMS 2L
#define SIZE (1024*8)
void mem_test()
{
 void *array[N_ITEMS+1]; /* extra 1 for while loop below */
 void *fl;
 int i=0;

 fprintf(stderr,"Set up\n");
 fl = SetUpFMem(N_ITEMS,SIZE);
 i = 0;
 fprintf(stderr,"Getting\n");
 while ((array[i] = GetFMem(&fl)) != NULL)
 {
#ifdef MEMDEBUG
 CHECKPTR(array[i]);
#endif
 i++;
 }

 fprintf(stderr,"randomise\n");
 randomise(array,N_ITEMS);

 fprintf(stderr,"putting\n");
 i = 0;
 while (array[i] != NULL)
 {
#ifdef MEMDEBUG
 CHECKPTR(array[i]);
#endif
 PutFMem(&fl,array[i]);
 i++;
 }

 fprintf(stderr,"closing\n");
 CloseFMem(&fl);
 fprintf(stderr,"Done!!\n");
 return;
}


main(int argc ,char *argv[])
{
int j,i = 0;
FREEL *fl=NULL;

 for (i = 0;i < NITS;i++)
 {
 if ((store[i] = malloc(ELEM_SIZE)) == NULL)
 {
 RAM_ERROR;
 exit(0);
 }
#ifdef TESTING
 store[i]->check_value = CHECK_VALUE;
#endif
 }

 randomise(store,NITS);

/* main test */
 fprintf(stderr,"Main test\n");

 for (i = 0;i < NITS;i++)
 {
 fprintf(stderr,"Adding element %d\n",i);
 add_freel(&fl,store[i]);
#ifdef PARANOID
 print_freel(fl);
#endif
 }

 for (i = 2;i < NITS;i++)
 {
 fprintf(stderr,"Test radix %d:- ",i);

 for (j = 0;j < i;j++)
 {
 store[j] = sub_freel(&fl);
#ifdef TESTING
 check_val(store[j]);
#endif
 putc('-',stderr);
#ifdef PARANOID
 print_freel(fl);
#endif
 }

 if (size_freel(fl) != (NITS - i))
 {
 fprintf(stderr,"List contents inconsistency\n");
 exit(1);
 }
 randomise(store,i);

 for (j = 0;j < i;j++)
 {
 add_freel(&fl,store[j]);
 putc('+',stderr);
#ifdef PARANOID
 print_freel(fl);
#endif
 }

 fprintf(stderr,"... Done\n");
 }
 free_freel(&fl);
 fprintf(stderr,"Done\n");

 mem_test();

 exit(0);
 return 0;
}
#endif
