/*	Copyright (C) 1992 Free Software Foundation, Inc.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this software; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */




#include "rxrun.h"

void * rx_id_instruction_table[num_instructions] =
{
  (void *) backtrack_point,
  (void *) do_side_effects,
  (void *) cache_miss,
  (void *) next_char,
  (void *) backtrack,
  (void *) error_inx
};


struct superstate_contents *
nil_superstate_contents (rx)
     struct rx *rx;
{
  int arsize = (sizeof (struct superstate_contents *)
		* (rx->nodec + rx->epsnodec));
  return ((struct superstate_contents *)
	  calloc (sizeof (struct superstate_contents) + arsize, 1));
}

#define NFAINDEX(RX,ST) (ST - (struct nfa_state *)(RX)->buffer)
static struct superstate_contents *
superstate_cons (rx, car, cdr)
     struct rx *rx;
     struct nfa_state *car;
     struct superstate_contents *cdr;
{
  int i = NFAINDEX (rx, car);
  if (!cdr->kids[i])
    {
      int arsize = (sizeof (struct superstate_contents *)
		    * (rx->nodec + rx->epsnodec));
      struct superstate_contents *sc = cdr->kids[i] =
      (struct superstate_contents *) calloc (sizeof (*sc) + arsize, 1);
      sc->car = car;
      sc->cdr = cdr;
      ++cdr->refs;
    }
  return cdr->kids[i];
}

struct superstate_contents *
superstate_enjoin (rx, car, set)
     struct rx *rx;
     struct nfa_state *car;
     struct superstate_contents *set;
{
  if (!set->car)
    return superstate_cons (rx, car, set);
  else
    {
      int i = NFAINDEX (rx, car);
      int j = NFAINDEX (rx, set->car);
      struct superstate_contents *newset;

      return ((i == j)
	      ? set
	      : ((i > j)
		 ? superstate_cons (rx, car, set)
		 : ((set == (newset = superstate_enjoin (rx, car, set->cdr)))
		    ? set
		    : superstate_cons (rx, car, newset))));
    }
}

struct superstate_contents *
superstate_eclosure_union (rx, set, ecl)
     struct rx *rx;
     struct superstate_contents *set;
     struct nfa_state_set *ecl;
{
  if (!ecl)
    return set;
  if (!set->car)
    return superstate_cons (rx, ecl->car,
			    superstate_eclosure_union (rx, set, ecl->cdr));
  {
    int i = NFAINDEX (rx, set->car);
    int j = NFAINDEX (rx, ecl->car);
    if (i == j)
      return superstate_eclosure_union (rx, set, ecl->cdr);
    if (i > j)
      return superstate_cons (rx, set->car,
			      superstate_eclosure_union (rx, set->cdr, ecl));
    return superstate_cons (rx, ecl->car,
			    superstate_eclosure_union (rx, set, ecl->cdr));
  }
}

void 
release_superstate_contents (rx, set)
     struct rx *rx;
     struct superstate_contents *set;
{
  if (!--set->refs)
    {
      if (set->cdr)
	{
	  int i = NFAINDEX (rx, set->car);
	  set->cdr->kids[i] = 0;
	  release_superstate_contents (rx, set->cdr);
	}
      free (set);
    }
}


/*
 * This makes sure that a list of discernable_futures contains
 * a future for each possible set of side effects in the eclosure
 * of a given state.  This is some of the work of filling in a
 * superstate transition.
 *
 * What this function doesn't do is compute the destination superstate.
 * It records a cache_miss for the destination.
 *
 * There are two reasons for this split.  One is that the lifetime of the
 * discernable_futures is independent of the lifetimes of their destination
 * superstates.  The other reason is that the separation helps to facilitate
 * bootstrapping the match engine.
 */

struct discernable_future *
include_futures (rx, df, state, superstate)
     struct rx *rx;
     struct discernable_future *df;
     struct nfa_state *state;
     struct superstate *superstate;
{
  struct possible_future *future;
  for (future = state->futures; future; future = future->next)
    {
      struct discernable_future *dfp;
      for (dfp = df; dfp; dfp = dfp->next_same_transition_class)
	if (dfp->effects == future->effects)
	  break;
      if (!dfp)
	{
	  dfp = (struct discernable_future *) malloc (sizeof (*dfp));
	  if (!dfp)
	    return 0;
	  dfp->next_same_transition_class = df;
	  df = dfp;
	  dfp->next_same_dest = dfp->prev_same_dest = dfp;
	  dfp->future = 0;
	  dfp->present = superstate;
	  dfp->future_frame.inx = rx->instruction_table[cache_miss];
	  dfp->future_frame.data = 0;
	  dfp->future_frame.data_2 = (void *) dfp;
	  dfp->side_effects_frame.inx = rx->instruction_table[do_side_effects];
	  dfp->side_effects_frame.data = 0;
	  dfp->side_effects_frame.data_2 = (void *) dfp;
	  dfp->effects = future->effects;
	}
    }
  return df;
}

/* Memory Management for Superstates.
 *
 * Unlocked superstates are kept in a doubly-linked ring pointed to by
 * [min/max]_free_superstate in a struct rx.  States inclusively
 * between min_free and max_free are in a semi-free state.  If a
 * transition into a semi-free state occurs, the state is converted from
 * semi-free to live, and moved just beyond the max_free position in
 * the ring.  If the cache is full, but a new state is needed, the
 * max_free state is recycled.  The system keeps track of the ratio of
 * cache-hits to cache-misses (weighted in favor of recent history).
 * That ratio is used as feedback to control adjustments to the size
 * of the pool of semi-free states.
 */

static struct instruction_frame shared_cache_miss_frame =
{(void *) cache_miss, 0};
static struct instruction_frame shared_backtrack_frame =
{(void *) backtrack, 0};

static void install1_transition ();

static void 
semifree_superstate (rx, super)
     struct rx *rx;
     struct superstate *super;
{
  struct discernable_future *df;
  if (rx->superstate_semifree == rx->superstate_count)
    return;
  df = super->transition_refs;
  if (df)
    {
      df->prev_same_dest->next_same_dest = 0;
      for (df = super->transition_refs; df; df = df->next_same_dest)
	{
	  df->future_frame.inx = rx->instruction_table[cache_miss];
	  df->future_frame.data = 0;
	  df->future_frame.data_2 = (void *) df;
	  if (!df->effects && !df->edge->options->next_same_transition_class)
	    install1_transition (rx, df->present, &df->future_frame,
				 df->edge->cset);
	}
      df = super->transition_refs;
      df->prev_same_dest->next_same_dest = df;
    }
  ++rx->superstate_semifree;
}

int x = 0;
void
lose ()
{
 x = 3;
}


static void 
unfree_superstate (rx, super)
     struct rx *rx;
     struct superstate *super;
{
  struct discernable_future *df;
  super->transition_refs->prev_same_dest->next_same_dest = 0;
  for (df = super->transition_refs; df; df = df->next_same_dest)
    {
      df->future_frame.inx = rx->instruction_table[next_char];
      df->future_frame.data = (void *) super;
      if (!df->effects && !df->edge->options->next_same_transition_class)
	install1_transition (rx, df->present, &df->future_frame,
			     df->edge->cset);
    }
  super->transition_refs->prev_same_dest->next_same_dest
    = super->transition_refs;
  if (rx->min_free_superstate == super)
    rx->min_free_superstate = super->next_recyclable;
  if (rx->max_free_superstate == super)
    rx->max_free_superstate = super->prev_recyclable;

  super->next_recyclable->prev_recyclable
    = super->prev_recyclable;
  super->prev_recyclable->next_recyclable
    = super->next_recyclable;
  super->next_recyclable =
    rx->max_free_superstate->next_recyclable->next_recyclable;
  super->prev_recyclable = rx->max_free_superstate;
  super->next_recyclable->prev_recyclable = super;
  super->prev_recyclable->next_recyclable = super;
  --rx->superstate_semifree;
}


struct superstate *
superstate (rx, set)
     struct rx *rx;
     struct superstate_contents *set;
{
  struct superstate *superstate;

  if ((rx->superstate_hits + rx->superstate_misses) > rx->superstate_count)
    {
      rx->superstate_hits >>= 1;
      rx->superstate_misses >>= 1;
    }

  if (set->superstate)
    {
      ++rx->superstate_hits;
      superstate = set->superstate;
      if (!superstate->next_recyclable)
	return superstate;
      if (rx->max_free_superstate == superstate)
	{
	  /* This only happens when no superstates are free. */
	  rx->min_free_superstate = rx->max_free_superstate
	    = superstate->prev_recyclable;
	}
      superstate->next_recyclable->prev_recyclable
	= superstate->prev_recyclable;
      superstate->prev_recyclable->next_recyclable
	= superstate->next_recyclable;
      superstate->next_recyclable = rx->max_free_superstate->next_recyclable;
      superstate->prev_recyclable = rx->max_free_superstate;
      superstate->next_recyclable->prev_recyclable = superstate;
      superstate->prev_recyclable->next_recyclable = superstate;
      return superstate;
    }
  ++rx->superstate_misses;
  if ((rx->superstate_count < (3 * (rx->nodec + rx->epsnodec)))
      || (rx->superstate_count < MIN_LIVE_SUPERSTATES))
    {
      superstate = (struct superstate *)
	malloc (sizeof (*superstate)
		+ rx->local_cset_size * sizeof (struct instruction_frame));

      if (!superstate)
	return 0;
      if (!rx->superstate_count++)
	superstate->next_recyclable = superstate->prev_recyclable
	  = rx->min_free_superstate = rx->max_free_superstate = superstate;
      else
	{
	  superstate->prev_recyclable = rx->max_free_superstate;
	  superstate->next_recyclable =
	    rx->max_free_superstate->next_recyclable;
	  superstate->next_recyclable->prev_recyclable = superstate;
	  superstate->prev_recyclable->next_recyclable = superstate;
	}
    }

  else
    {
      if (((rx->superstate_semifree - 1) * rx->superstate_hits)
	  < (rx->superstate_misses * rx->superstate_count))
	{ /* See the comment `Memory Management for Superstates', above. */
	  semifree_superstate (rx, rx->min_free_superstate);
	  rx->min_free_superstate = rx->min_free_superstate->prev_recyclable;
	  semifree_superstate (rx, rx->min_free_superstate);
	  rx->min_free_superstate = rx->min_free_superstate->prev_recyclable;
	}
      superstate = rx->max_free_superstate;
      rx->max_free_superstate = superstate->prev_recyclable;
      --rx->superstate_semifree;
      superstate->contents->superstate = 0;
      release_superstate_contents (rx, superstate->contents);

      if (superstate->transition_refs)
	{
	  struct discernable_future *df;
	  struct transition_class *tc;
	  for (df = superstate->transition_refs,
	       df->prev_same_dest->next_same_dest = 0;
	       df;
	       df = df->next_same_dest)
	    {
	      struct discernable_future *dft;
	      df->future_frame.inx = rx->instruction_table[cache_miss];
	      df->future_frame.data = 0;
	      df->future_frame.data_2 = (void *) df;
	      df->future = 0;
	    }
	  superstate->transition_refs->prev_same_dest->next_same_dest =
	    superstate->transition_refs;
	}
      {
	struct transition_class *tc = superstate->edges;
	while (tc)
	  {
	    struct discernable_future * df;
	    struct transition_class *tct = tc->next;
	    df = tc->options;
	    while (df)
	      {
		struct discernable_future *dft = df;
		df = df->next_same_transition_class;
		if (dft->future && dft->future->transition_refs == dft)
		  {
		    dft->future->transition_refs = dft->next_same_dest;
		    if (dft->future->transition_refs == dft)
		      dft->future->transition_refs = 0;
		  }
		dft->next_same_dest->prev_same_dest = dft->prev_same_dest;
		dft->prev_same_dest->next_same_dest = dft->next_same_dest;
		free (dft);
	      }
	    free (tc);
	    tc = tct;
	  }
      }
    }
  superstate->transition_refs = 0;
  superstate->locks = 0;
  set->superstate = superstate;
  superstate->contents = set;
  protect_superstate_contents (rx, set);
  superstate->edges = 0;
  {
    int x;
    for (x = 0; x < rx->local_cset_size; ++x)
      superstate->transitions[x] = shared_cache_miss_frame;
  }
  return superstate;
}


void 
lock_superstate (rx, super)
     struct rx *rx;
     struct superstate *super;
{
  ++super->locks;
  ++rx->locks;
  if (!super->next_recyclable)
    return;
  --rx->superstate_count;
  if (rx->max_free_superstate == super)
    {
      rx->max_free_superstate = super->next_recyclable;
      if (rx->max_free_superstate == super)
	rx->max_free_superstate = rx->min_free_superstate = 0;
    }
  if (rx->min_free_superstate == super)
    rx->min_free_superstate = super->next_recyclable;
  super->next_recyclable->prev_recyclable = super->prev_recyclable;
  super->prev_recyclable->next_recyclable = super->next_recyclable;
  super->next_recyclable = super->prev_recyclable = 0;
}

void 
unlock_superstate (rx, super)
     struct rx *rx;
     struct superstate *super;
{
  if (--super->locks)
    return;
  --rx->locks;
  if (super->next_recyclable)
    return;
  ++rx->superstate_count;
  if (!rx->max_free_superstate)
    {
      rx->max_free_superstate = rx->min_free_superstate = super;
      super->next_recyclable = super->prev_recyclable = super;
    }
  super->next_recyclable = rx->max_free_superstate->next_recyclable;
  super->prev_recyclable = rx->max_free_superstate;
  super->next_recyclable->prev_recyclable = super;
  super->prev_recyclable->next_recyclable = super;
}


static int 
solve_destination (rx, df)
     struct rx *rx;
     struct discernable_future *df;
{
  struct transition_class *tc = df->edge;
  struct superstate_contents *nfa_state;
  struct superstate_contents *solution = rx->nil_set;
  struct superstate *dest;
  struct superstate *present = df->present;

  for (nfa_state = df->present->contents;
       nfa_state->car;
       nfa_state = nfa_state->cdr)
    {
      struct nfa_edge *e;
      for (e = nfa_state->car->edges; e; e = e->next)
	if (ut_bitset_is_subset (rx->local_cset_size,
				 tc->cset, e->params.cset))
	  {
	    struct nfa_state *n = e->dest;
	    struct possible_future *pf;
	    for (pf = n->futures; pf; pf = pf->next)
	      if (pf->effects == df->effects)
		{
		  solution =
		    superstate_eclosure_union (rx, solution, pf->destset);
		  if (!solution)
		    return 0;
		}
	  }
    }
  if (solution == rx->nil_set)
    {
      df->future_frame.inx = (void *) backtrack;
      df->future_frame.data = 0;
      df->future_frame.data_2 = 0;
      return 1;
    }
  dest = superstate (rx, solution);
  if (!dest)
    return 0;

  {
    struct discernable_future *dft;
    dft = df;
    df->prev_same_dest->next_same_dest = 0;
    while (dft)
      {
	dft->future = dest;
	dft->future_frame.inx = rx->instruction_table[next_char];
	dft->future_frame.data = (void *) dest;
	dft = dft->next_same_dest;
      }
    df->prev_same_dest->next_same_dest = df;
  }
  if (!dest->transition_refs)
    dest->transition_refs = df;
  else
    {
      struct discernable_future *dft = dest->transition_refs->next_same_dest;
      dest->transition_refs->next_same_dest = df->next_same_dest;
      df->next_same_dest->prev_same_dest = dest->transition_refs;
      df->next_same_dest = dft;
      dft->prev_same_dest = df;
    }
  return 1;
}


static int 
compute_transition_class (rx, dfout, csetout, superstate, chr)
     struct rx *rx;
     struct discernable_future **dfout;
     ut_Bitset csetout;
     struct superstate *superstate;
     unsigned char chr;
{
  struct superstate_contents *stateset = superstate->contents;
  ut_bitset_universe (rx->local_cset_size, csetout);
  *dfout = 0;
  while (stateset->car)
    {
      struct nfa_edge *e;
      for (e = stateset->car->edges; e; e = e->next)
	if (UT_bitset_member (e->params.cset, chr))
	  {
	    *dfout = include_futures (rx, *dfout, e->dest, superstate);
	    if (!*dfout)
	      return 0;
	    ut_bitset_intersection (rx->local_cset_size,
				    csetout, e->params.cset);
	  }
	else
	  ut_bitset_difference (rx->local_cset_size, csetout, e->params.cset);
      stateset = stateset->cdr;
    }
  return 1;
}


struct transition_class *
transition_class (rx, super, cset, df)
     struct rx *rx;
     struct superstate *super;
     ut_Bitset cset;
     struct discernable_future *df;
{
  struct transition_class *tc =
  ((struct transition_class *)
   malloc (sizeof (*tc) + ut_sizeof_bitset (rx->local_cset_size)));
  tc->next = super->edges;
  super->edges = tc;
  tc->backtrack_frame.inx = rx->instruction_table[backtrack_point];
  tc->backtrack_frame.data = 0;
  tc->backtrack_frame.data_2 = (void *) tc;
  tc->cset = (ut_Bitset) ((char *) tc + sizeof (*tc));
  tc->options = df;
  ut_bitset_assign (rx->local_cset_size, tc->cset, cset);
  while (df)
    {
      df->edge = tc;
      df = df->next_same_transition_class;
    }
  return tc;
}


static void 
install1_transition (rx, super, answer, trcset)
     struct rx *rx;
     struct superstate *super;
     struct instruction_frame *answer;
     ut_Bitset trcset;
{
  int x;
  for (x = 0; x < rx->local_cset_size; ++x)
    if (UT_bitset_member (trcset, x))
      super->transitions[x] = *answer;
}


/* There are three kinds of cache miss.  The first occurs when a
 * transition is taken that has never been computed during the
 * lifetime of the source superstate.  That cache miss is handled by
 * calling COMPUTE_TRANSITION_CLASS.  The second kind of cache miss
 * occurs when the destination superstate of a transition doesn't
 * exist.  SOLVE_DESTINATION is used to construct the destination.
 * Finally, the third kind of cache miss occurs when the destination
 * superstate of a transition is in a `semi-free state'.  That case is
 * handled by UNFREE_SUPERSTATE.
 *
 * The function of HANDLE_CACHE_MISS is to figure out which of these
 * cases applies.
 */

struct instruction_frame *
handle_cache_miss (rx, super, chr, data)
     struct rx *rx;
     struct superstate *super;
     unsigned char chr;
     void *data;
{
  struct discernable_future *df = data;
  if (!df)			/* must be the shared_cache_miss_frame */
    {
      ut_Bitset trcset = cset (rx);
      struct transition_class *tc;
      struct instruction_frame *answer;
      if (!trcset)
	return 0;
      if (!compute_transition_class (rx, &df, trcset, super, chr))
	return 0;
      if (!df)			/* We just computed the fail transition. */
	answer = &shared_backtrack_frame;
      else
	{
	  tc = transition_class (rx, super, trcset, df);
	  if (!tc)
	    return 0;
	  answer = (tc->options->next_same_transition_class
		    ? &tc->backtrack_frame
		    : (df->effects
		       ? &df->side_effects_frame
		       : &df->future_frame));
	}
      install1_transition (rx, super, answer, trcset);
      free (trcset);
      return answer;
    }
  else if (df->future)		/* rescue a semifree superstate */
    {
      unfree_superstate (rx, df->future);
      return &df->future_frame;
    }
  else
    /* missing destination superstate */
    {
      lock_superstate (rx, super);
      if (!solve_destination (rx, df))
	return 0;
      if (!df->edge->options->next_same_transition_class
	  && !df->effects)
	install1_transition (rx, super, &df->future_frame, df->edge->cset);
      unlock_superstate (rx, super);
      return &df->future_frame;
    }
}
