/* /% C %/ */
/***********************************************************************
 * cint (C/C++ interpreter)
 ************************************************************************
 * Source file gcoll.c
 ************************************************************************
 * Description:
 *  Garbage Collection library and dictionary rewinding
 ************************************************************************
 * Copyright(c) 1995~1999  Masaharu Goto 
 *
 * Permission to use, copy, modify and distribute this software and its 
 * documentation for any purpose is hereby granted without fee,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.  The author makes no
 * representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 ************************************************************************/

#include "common.h"


#define G__IMMEDIATE_GARBAGECOLLECTION

/**************************************************************************
* Garbage collection library usage
*
*  G__init_garbagecollection();
*
*    T *a = malloc(sizeof(T));
*  G__add_alloctable(a,'u',G__defined_tagnum("T"));
*  G__add_refcount(a,&a);
*
*    T *b=a;
*  G__add_refcount(b,&b);
*
*  G__del_refcount(b,&b);
*    b=NULL;
*
*  G__del_alloctable(a);
*    free((void*)a);
*
*  G__garbagecollection();
*
*
**************************************************************************/



#define G__MAXREFTABLE 5
#define G__MAXALLOCTABLE 50

struct G__reflist {
  void **ref;
  struct G__reflist *prev;
  struct G__reflist *next;
};

struct G__alloclist {
  void *allocedmem;
  char type;
  short tagnum;
  struct G__reflist *reflist;
  struct G__alloclist *prev;
  struct G__alloclist *next;
};

static struct G__alloclist *G__alloctable;
static struct G__alloclist *G__p_alloc;
static unsigned int G__count_garbagecollection;

/**************************************************************************
* Static functions
*
*
**************************************************************************/

/**************************************************************************
* G__search_alloctable()
**************************************************************************/
static struct G__alloclist* G__search_alloctable(mem)
void *mem;
{
  struct G__alloclist *alloc;
  alloc = G__alloctable;
  while(alloc) {
    if(mem==alloc->allocedmem) return(alloc);
    alloc=alloc->next;
  }
  return((struct G__alloclist*)NULL);
}

/**************************************************************************
* G__free_reflist()
**************************************************************************/
static void G__free_reflist(reflist)
struct G__reflist *reflist;
{
  if(reflist) {
    if(reflist->next) {
      G__free_reflist(reflist->next);
    }
    if(reflist->ref) *reflist->ref=(void*)NULL;
    free((void*)reflist);
  }
}

/**************************************************************************
* G__delete_reflist()
*
*  1) delete entry from alloclist
*
**************************************************************************/
static struct G__reflist* G__delete_reflist(alloc,reflist)
struct G__alloclist *alloc;
struct G__reflist *reflist;
{
  struct G__reflist *freed;
  static struct G__reflist temp;
  
  if(reflist->prev) {
    freed=reflist;
    reflist->prev->next = reflist->next;
    if(reflist->next) reflist->next->prev=reflist->prev;
    reflist = reflist->prev;
  }
  else {
    freed=reflist;
    alloc->reflist = reflist->next;
    if(alloc->reflist) alloc->reflist->prev=(struct G__reflist*)NULL;
    temp.next = alloc->reflist;
    reflist = &temp;
  }
  free((void*)freed);
  
  return(reflist);
}

/**************************************************************************
* G__delete_alloctable()
*
*  1) delete entry from alloclist
*
**************************************************************************/
static struct G__alloclist* G__delete_alloctable(alloc)
struct G__alloclist *alloc;
{
  struct G__alloclist *freed;
  static struct G__alloclist temp;

  if(!alloc->next) { 
    G__p_alloc = alloc->prev;
  }

  if(alloc->prev) {
    freed=alloc;
    alloc->prev->next = alloc->next;
    if(alloc->next) alloc->next->prev=alloc->prev;
    alloc = alloc->prev;
  }
  else {
    freed=alloc;
    G__alloctable = alloc->next;
    if(G__alloctable) G__alloctable->prev=(struct G__alloclist*)NULL;
    temp.next = G__alloctable;
    alloc = &temp;
  }
  free((void*)freed);

  return(alloc);
}

/**************************************************************************
* G__destroy_garbageobject()
*
*  1) If class object call destructor
*  2) If interpreted class or fundamental class, free memory or close file
*
**************************************************************************/
void G__destroy_garbageobject(alloc)
struct G__alloclist *alloc;
{
  long store_tagnum;
  long store_struct_offset;
  long store_globalvarpointer;
  int done=0;
  char dtor[G__ONELINE];

  if(-1!=alloc->tagnum) {
    /* Call destructor if class object */
    sprintf(dtor,"~%s()",G__struct.name[alloc->tagnum]);
    store_globalvarpointer = G__globalvarpointer;
    store_tagnum = G__tagnum;
    store_struct_offset = G__store_struct_offset;
    G__tagnum = alloc->tagnum;
    G__store_struct_offset = (long)alloc->allocedmem;
    if(G__CPPLINK==G__struct.iscpplink[alloc->tagnum]) {
      G__globalvarpointer = G__store_struct_offset;
    }
    else {
      G__globalvarpointer = G__PVOID;
    }
    G__getfunction(dtor,&done ,G__TRYDESTRUCTOR);
    G__tagnum = store_tagnum;
    G__store_struct_offset = store_struct_offset;
    G__globalvarpointer = store_globalvarpointer;
    if(G__CPPLINK!=G__struct.iscpplink[alloc->tagnum]) {
      /* free resource , interpreted class */
      free((void*)alloc->allocedmem);
    }
  }
  else {
    /* Fundamental type, free resource */
    if('E'==alloc->type) {
      fclose((FILE*)alloc->allocedmem);
    }
    else {
      free((void*)alloc->allocedmem);
    }
  }

  /* delete entry from alloclist */
  /* alloc=G__delete_alloctable(alloc); */

  /* increment garbage collection count */
  ++G__count_garbagecollection;
}


/**************************************************************************
* Exported functions
*
*
**************************************************************************/

/**************************************************************************
* G__init_garbagecollection()
**************************************************************************/
void G__init_garbagecollection()
{
  static int state=1;
  if(state) {
    G__alloctable=(struct G__alloclist*)NULL;;
    G__p_alloc = G__alloctable;
    G__count_garbagecollection=0;
    state=0;
  }
}

/**************************************************************************
* G__garbagecollection()
**************************************************************************/
int G__garbagecollection()
{
  struct G__alloclist *alloc;
  struct G__reflist *reflist;
  unsigned int count;

#ifndef G__IMMEDIATE_GARBAGECOLLECTION
  G__fprinterr(G__serr,"!!! Reference Count Control start !!!\n");
#endif

  alloc = G__alloctable;
  while(alloc) {
    if(!alloc->reflist) {
      /* This object has no reference, means stray pointer */
      G__destroy_garbageobject(alloc);
      alloc=G__delete_alloctable(alloc);
    }
    else {
      /* Delete dummy reference count for returned pointer */
      reflist = alloc->reflist;
      while(reflist) {
	if((void**)NULL==reflist->ref) {
	  reflist=G__delete_reflist(alloc,reflist);
	}
	reflist=reflist->next;
      }
    }
    alloc=alloc->next;
  }

  G__fprinterr(G__serr,"!!! %d object(s) deleted by Reference Count Control !!!\n"
	  ,G__count_garbagecollection);
  count = G__count_garbagecollection;
  G__count_garbagecollection=0;

  return(count);
}


/**************************************************************************
* G__add_alloctable()
**************************************************************************/
void G__add_alloctable(allocedmem,type,tagnum)
void *allocedmem;
int type;
int tagnum;
{
  if(G__p_alloc) {
    G__p_alloc->next=(struct G__alloclist*)malloc(sizeof(struct G__alloclist));
    G__p_alloc->next->prev = G__p_alloc;
    G__p_alloc=G__p_alloc->next;
  }
  else {
    G__alloctable=(struct G__alloclist*)malloc(sizeof(struct G__alloclist));
    G__p_alloc = G__alloctable;
    G__p_alloc->prev =(struct G__alloclist*)NULL;
  }
  G__p_alloc->allocedmem = allocedmem;
  G__p_alloc->type = (char)type;
  G__p_alloc->tagnum = (short) tagnum;
  G__p_alloc->reflist=(struct G__reflist*)NULL;
  G__p_alloc->next=(struct G__alloclist*)NULL;
}

/**************************************************************************
* G__del_alloctable()
**************************************************************************/
int G__del_alloctable(allocmem)
void *allocmem;
{
  struct G__alloclist *alloc;

  alloc = G__search_alloctable(allocmem);
  if(alloc) {
    G__free_reflist(alloc->reflist);
    G__delete_alloctable(alloc);
    return(0);
  }
  else {
#ifndef G__FONS31
    G__fprinterr(G__serr,"Error: Can not free 0x%lx, not allocated."
	    ,(long)allocmem);
#else
    G__fprinterr(G__serr,"Error: Can not free 0x%x, not allocated.",allocmem);
#endif
    G__genericerror((char*)NULL);
    return(1);
  }
}

/**************************************************************************
* G__add_refcount()
**************************************************************************/
int G__add_refcount(allocedmem,storedmem)
void *allocedmem;
void **storedmem;
{
  struct G__alloclist *alloc;
  struct G__reflist *reflist;
  alloc = G__search_alloctable(allocedmem);
  if(alloc) {
    if(alloc->reflist) {
      reflist=alloc->reflist;
      while(reflist->next) reflist=reflist->next;
      reflist->next=(struct G__reflist*)malloc(sizeof(struct G__reflist));
      reflist->next->prev=reflist;
      reflist=reflist->next;
      reflist->next=(struct G__reflist*)NULL;
      reflist->ref=storedmem;
    }
    else {
      alloc->reflist=(struct G__reflist*)malloc(sizeof(struct G__reflist));
      reflist = alloc->reflist;
      reflist->prev=(struct G__reflist*)NULL;
      reflist->next=(struct G__reflist*)NULL;
      reflist->ref=storedmem;
    }
  }
  return(0);
}

/**************************************************************************
* G__del_refcount()
**************************************************************************/
int G__del_refcount(allocedmem,storedmem)
void *allocedmem;
void **storedmem;
{
  int flag=1;
  struct G__alloclist *alloc;
  struct G__reflist *reflist;
  alloc = G__search_alloctable(allocedmem);
  if(alloc) {
    reflist = alloc->reflist;
    while(reflist) {
      if(reflist->ref==storedmem) {
	reflist=G__delete_reflist(alloc,reflist);
      }
      else if((void**)NULL==reflist->ref) {
	reflist=G__delete_reflist(alloc,reflist);
	flag=0;
      }
      reflist=reflist->next;
    }
#ifdef G__IMMEDIATE_GARBAGECOLLECTION
    if(!alloc->reflist && flag) {
#ifdef G__DEBUG
#ifndef G__OLDIMPLEMENTATION401
      G__fprinterr(G__serr,"!!! %s object deleted by Reference Count Control !!!\n"
	      ,G__type2string(alloc->type,alloc->tagnum,-1,0,0));
#else
      G__fprinterr(G__serr,"!!! %s object deleted by Reference Count Control !!!\n"
	      ,G__type2string(alloc->type,alloc->tagnum,-1,0));
#endif
#endif
      G__destroy_garbageobject(alloc);
      G__delete_alloctable(alloc);
    }
#endif
  }
  return(0);
}


/**************************************************************************
* Debugging functions
*
*
**************************************************************************/

/**************************************************************************
* G__disp_garbagecollection()
**************************************************************************/
int G__disp_garbagecollection(fout)
FILE *fout;
{
  static struct G__alloclist *alloc;
  struct G__reflist *reflist;

  alloc = G__alloctable;
  fprintf(fout,"Allocated memory =========================================\n");
  fprintf(fout,"type                : location   : reference(s)\n");
  while(alloc) {
#ifndef G__OLDIMPLEMENTATION401
    fprintf(fout,"%-20s: 0x%lx :"
	    ,G__type2string(alloc->type,alloc->tagnum,-1,0,0)
	    ,(long)alloc->allocedmem);
#else
    fprintf(fout,"%-20s: 0x%lx :"
	    ,G__type2string(alloc->type,alloc->tagnum,-1,0)
	    ,alloc->allocedmem);
#endif
    reflist = alloc->reflist;
    while(reflist) {
#ifndef G__FONS31
      fprintf(fout," 0x%lx ,",(long)reflist->ref);
#else
      fprintf(fout," 0x%x ,",reflist->ref);
#endif
      reflist = reflist->next;
    }
    fprintf(fout,"\n");
    alloc=alloc->next;
  }
  return(0);
}




/*
 * Local Variables:
 * c-tab-always-indent:nil
 * c-indent-level:2
 * c-continued-statement-offset:2
 * c-brace-offset:-2
 * c-brace-imaginary-offset:0
 * c-argdecl-indent:0
 * c-label-offset:-2
 * compile-command:"make -k"
 * End:
 */
