#include <stdio.h>
#include <stdlib.h>

typedef struct REFList {
 void *addr;
 void *refers_to;
 struct REFList *next;
} REFList;

/*TODO possibly use a wrapper that stores the TAIL and HEAD */
REFList *refs = NULL;

size_t bytes_allocated = 0;

void *check_alloc (size_t s) {
 void *m = malloc (s);

 if (NULL == m) {
  perror ("unable to malloc()");
  exit (EXIT_FAILURE);
 }
 return m;
}

void Collect (void) {
 REFList *cur = refs;
 REFList *last = NULL;

 while (NULL != cur) {
  printf ("cur ->addr %p ->refers_to %p\n", cur->addr, cur->refers_to);
  printf ("addr now contains %p\n", (*(unsigned int **)cur->addr));
  puts ("---------------");

  if ((*(unsigned int **)cur->addr != (unsigned int *)cur->refers_to)) {
   void *next = cur->next;

   if (cur == refs) {
    refs = next;
   }

   bytes_allocated -= 123;
   printf ("freeing %p\n", cur->refers_to);
   free (cur->refers_to);
   free (cur);

   if (NULL != last) {
    cur = last->next = next;
   } else {
    cur = next;       
   }   
  } else {
   last = cur;
   cur = cur->next;
  }
 }

 if (NULL == last) {
  refs = NULL;
 }
}

inline void *CollectedAlloc (size_t s) {
 void *r;
 if (bytes_allocated >= 400) {
  Collect ();
 }

 bytes_allocated += s;
 r = check_alloc (s);
 printf ("collect allocated %p\n", r);
 return r;
}

inline void REF (void *addr, void *refers_to) {
 REFList *cur;

 if (NULL == refs) {
  refs = check_alloc (sizeof (REFList)); 
  refs->addr = addr;
  refs->refers_to = *(unsigned int **)addr;
  refs->next = NULL;
  return;
 }

 cur = refs;

 while (NULL != cur->next) {
  cur = cur->next;
 }

#if 0
 fprintf (stderr, "REF addr %p refto %p\n", addr, refers_to);
#endif

 cur->next = check_alloc (sizeof (REFList));
 cur->next->addr = addr;
 cur->next->refers_to = refers_to;
 cur->next->next = NULL;
}

int main () { 
 char *s = NULL;
 int iterations = 0;

 printf ("s addr %p\n", &s);
  
 do {
  s = CollectedAlloc (123);
  REF(&s, s);
  s[0] = 'a';
  s[1] = 'b';
  s[2] = 'c';
  ++iterations;
  //usleep (400);
 } while (iterations < 200000);

 return EXIT_SUCCESS;
}
