/* ,file-id archive://[lord]/453/rx/super.c/1998-05-18
 */


/*	Copyright (C) 1997 Tom Lord
 * 
 * This program is provided to you under the terms of the Liberty Software
 * License.  You are NOT permitted to redistribute, modify, or use it
 * except in very specific ways described by that license.
 *
 * This software comes with NO WARRANTY.
 * 
 * You should have received a copy of the Liberty Software License
 * along with this software; see the file =LICENSE.  If not, write to
 * the Tom Lord, 1810 Francisco St. #2, Berkeley CA, 94703, USA.  
 */





#include <errno.h>

#include "vu/bitset.h"
#include "vu/hashtab.h"
#include "vu/xmalloc.h"
#include "vu/dstr.h"
#include "vu/str.h"

#include "rexp.h"
#include "nfa.h"
#include "super.h"



struct rx_cache;
typedef void (*rx_morecore_fn)(struct rx_cache *);

struct rx_cache
{
  struct hashtab_rules superset_hashtab_rules;
  struct rx_superstate * lru_superstate;
  struct rx_superstate * semifree_superstate;
  struct rx_superset * empty_superset;
  int superstates;
  int semifree_superstates;
  int hits;
  int misses;
  int bytes_allowed;
  int bytes_used;
  int local_cset_size;
  struct hashtab superset_table;
};

static struct rx_cache * rx_default_cache;


int rx_superstate_counter = 0;


#ifndef RX_DEFAULT_DFA_CACHE_SIZE
/* This is an upper bound on the number of bytes that may (normally)
 * be allocated for DFA states.  If this threshold would be exceeded,
 * Rx tries to flush some DFA states from the cache.
 *
 * This value is used whenever the rx_default_cache is used (for example,
 * with the Posix entry points).
 */
#define RX_DEFAULT_DFA_CACHE_SIZE (1 << 19)
#endif




/* The partially instantiated superstate graph has a transition 
 * table at every node.  There is one entry for every character.
 * This fills in the transition for a set.
 */
static void 
install_transition (struct rx_superstate * super,
		    struct rx_inx * answer,
		    bitset trcset) 
{
  struct rx_inx * transitions;
  int chr;

  transitions = super->transitions;
  chr = 0;

  while (chr < 256)
    if (!*trcset)
      {
	++trcset;
	chr += bits_per_subset;
      }
    else
      {
	subset sub;
	subset mask;
	int bound;

	sub = *trcset;
	mask = 1;
	bound = chr + bits_per_subset;

	while (chr < bound)
	  {
	    if (sub & mask)
	      transitions [chr] = *answer;
	    ++chr;
	    mask <<= 1;
	  }
	++trcset;
      }
}

static int
qlen (struct rx_superstate * q)
{
  int count = 1;
  struct rx_superstate * it;
  if (!q)
    return 0;
  for (it = q->next_recyclable; it != q; it = it->next_recyclable)
    ++count;
  return count;
}

static void
check_cache ()
{
  struct rx_cache * cache = rx_default_cache;
  int total = cache->superstates;
  int semi = cache->semifree_superstates;


  total = cache->superstates;
  semi = cache->semifree_superstates;

  if (semi != qlen (cache->semifree_superstate))
    panic ("failed cache check in rx");

  if ((total - semi) != qlen (cache->lru_superstate))
    panic ("failed cache check in rx (2)");
}

static char *
rx_cache_malloc (int size)
{
  struct rx_cache * cache = rx_default_cache;
  char * answer;
  answer = (char *)malloc (size);
  if (answer)
    cache->bytes_used += size;
  return answer;
}

static void
rx_cache_free (int size, char * mem)
{
  struct rx_cache * cache = rx_default_cache;
  free (mem);
  cache->bytes_used -= size;
}

static void
semifree_superstate ()
{
  struct rx_cache * cache = rx_default_cache;
  int disqualified;

  disqualified = cache->semifree_superstates;
  if (disqualified == cache->superstates)
    return;

  while (cache->lru_superstate->locks)
    {
      cache->lru_superstate = cache->lru_superstate->next_recyclable;
      ++disqualified;
      if (disqualified == cache->superstates)
	return;
    }

  {
    struct rx_superstate * it;

    it = cache->lru_superstate;
    it->next_recyclable->prev_recyclable = it->prev_recyclable;
    it->prev_recyclable->next_recyclable = it->next_recyclable;
    
    cache->lru_superstate = (it == it->next_recyclable
			     ? 0
			     : it->next_recyclable);

    if (!cache->semifree_superstate)
      {
	cache->semifree_superstate = it;
	it->next_recyclable = it;
	it->prev_recyclable = it;
      }
    else
      {
	it->prev_recyclable = cache->semifree_superstate->prev_recyclable;
	it->next_recyclable = cache->semifree_superstate;
	it->prev_recyclable->next_recyclable = it;
	it->next_recyclable->prev_recyclable = it;
      }
    {
      struct rx_super_edge *e;

      it->is_semifree = 1;
      ++cache->semifree_superstates;

      e = it->incoming_edges;
      if (e)
	{
	  e->prev_same_dest->next_same_dest = 0;
	  while (e)
	    {
	      struct rx_inx cache_miss_instruction;
	      cache_miss_instruction.data = 0;
	      cache_miss_instruction.data_2 = (void *)e;
	      cache_miss_instruction.inx = (void *)rx_cache_miss;
	      install_transition (e->present, &cache_miss_instruction, e->cset);
	      e = e->next_same_dest;
	    }
	  e = it->incoming_edges;
	  e->prev_same_dest->next_same_dest = e;
	}
    }
  }
}

static void 
refresh_semifree_superstate (struct rx_superstate * super)
{
  struct rx_cache * cache = rx_default_cache;
  struct rx_super_edge * e;

  if (super->incoming_edges)
    {
      super->incoming_edges->prev_same_dest->next_same_dest = 0; 
      for (e = super->incoming_edges; e; e = e->next_same_dest)
	{
	  struct rx_inx next_char_instruction;
	  next_char_instruction.data = (void *)&super->transitions;
	  next_char_instruction.data_2 = (void *)(super->members->state_label);
	  next_char_instruction.inx = (void *)rx_next_char;
	  install_transition (e->present, &next_char_instruction, e->cset);
	}
      super->incoming_edges->prev_same_dest->next_same_dest = super->incoming_edges;
    }

  if (cache->semifree_superstate == super)
    cache->semifree_superstate = (super->prev_recyclable == super
				  ? 0
				  : super->prev_recyclable);
  super->next_recyclable->prev_recyclable = super->prev_recyclable;
  super->prev_recyclable->next_recyclable = super->next_recyclable;

  if (!cache->lru_superstate)
    (cache->lru_superstate
     = super->next_recyclable
     = super->prev_recyclable
     = super);
  else
    {
      super->next_recyclable = cache->lru_superstate;
      super->prev_recyclable = cache->lru_superstate->prev_recyclable;
      super->next_recyclable->prev_recyclable = super;
      super->prev_recyclable->next_recyclable = super;
    }
  super->is_semifree = 0;
  --cache->semifree_superstates;
}

void
rx_refresh_this_superstate (struct rx_superstate * superstate)
{
  struct rx_cache * cache = rx_default_cache;
  if (superstate->is_semifree)
    refresh_semifree_superstate (superstate);
  else if (cache->lru_superstate == superstate)
    cache->lru_superstate = superstate->next_recyclable;
  else if (superstate != cache->lru_superstate->prev_recyclable)
    {
      superstate->next_recyclable->prev_recyclable
	= superstate->prev_recyclable;
      superstate->prev_recyclable->next_recyclable
	= superstate->next_recyclable;
      superstate->next_recyclable = cache->lru_superstate;
      superstate->prev_recyclable = cache->lru_superstate->prev_recyclable;
      superstate->next_recyclable->prev_recyclable = superstate;
      superstate->prev_recyclable->next_recyclable = superstate;
    }
}

static void 
release_superset_low (struct rx_superset *set)
{
  struct rx_cache * cache = rx_default_cache;
  if (!--set->refs)
    {
      if (set->starts_for)
	set->starts_for->start_set = 0;
      if (set->cdr)
	release_superset_low (set->cdr);
      hashtab_delete (&set->hash_item, &cache->superset_hashtab_rules);
      rx_cache_free (sizeof (struct rx_superset), (char *)set);
    }
}

void 
rx_release_superset (struct rx_nfa *rx, struct rx_superset *set)
{
  release_superset_low (set);
}

/* This tries to add a new superstate to the superstate freelist.
 * It might, as a result, free some edge pieces or hash tables.
 * If nothing can be freed because too many locks are being held, fail.
 */
static int
rx_really_free_superstate ()
{
  struct rx_cache * cache = rx_default_cache;
  struct rx_superstate * it;

  if (!cache->superstates)
    return -1;

  /* scale these */
  while ((cache->hits + cache->misses) > cache->superstates)
    {
      cache->hits >>= 1;
      cache->misses >>= 1;
    }

  semifree_superstate (cache);
  semifree_superstate (cache);
  semifree_superstate (cache);

  if (!cache->semifree_superstate)
    return -1;
  
  {
    it = cache->semifree_superstate;
    it->next_recyclable->prev_recyclable = it->prev_recyclable;
    it->prev_recyclable->next_recyclable = it->next_recyclable;
    cache->semifree_superstate = ((it == it->next_recyclable)
				  ? 0
				  : it->next_recyclable);
    --cache->semifree_superstates;
  }


  if (it->incoming_edges)
    {
      struct rx_super_edge * e;
      it->incoming_edges->prev_same_dest->next_same_dest = 0;
      e = it->incoming_edges;
      while (e)
	{
	  struct rx_super_edge * et;
	  et = e;
	  e = e->next_same_dest;
	  et->prev_same_dest = et->next_same_dest = 0;
	  et->future = 0;
	}
    }

  {
    struct rx_super_edge *tc;
    tc = it->outgoing_edges;
    while (tc)
      {
	struct rx_super_edge *tct;

	tct = tc;
	tc = tc->next_same_present;

	if (tct->future)
	  {
	    if (tct->future->incoming_edges == tct)
	      {
		tct->future->incoming_edges = tct->next_same_dest;
		if (tct->future->incoming_edges == tct)
		  tct->future->incoming_edges = 0;
	      }
	    tct->next_same_dest->prev_same_dest = tct->prev_same_dest;
	    tct->prev_same_dest->next_same_dest = tct->next_same_dest;
	    rx_cache_free (sizeof (struct rx_super_edge), (char *)tct);
	  }
      }
  }
  
  if (it->members->superstate == it)
    it->members->superstate = 0;

  release_superset_low (it->members);

  rx_cache_free ((sizeof (struct rx_superstate) + cache->local_cset_size * sizeof (struct rx_inx)),
		 (char *)it);
  --cache->superstates;
  return 0;
}


static char *
rx_cache_malloc_or_get (int size)
{
  struct rx_cache * cache = rx_default_cache;
  char * it;
  while (   (cache->bytes_used + size > cache->bytes_allowed)
	 && (0 <= rx_really_free_superstate (cache)))
    ;

  it = rx_cache_malloc (size);
  if (!it)
    panic ("out of memory in rx_cache_malloc");
  return it;
}



static int
supersetcmp (void * va, void * vb)
{
  struct rx_superset * a = (struct rx_superset *)va;
  struct rx_superset * b = (struct rx_superset *)vb;

  a = (struct rx_superset *)va;
  b = (struct rx_superset *)vb;
  return (   (a == b)
	  || (   a
	      && b
	      && (a->id == b->id)
	      && (a->car == b->car)
	      && (a->cdr == b->cdr)));
}

#define rx_abs(A) (((A) > 0) ? (A) : -(A))

static int
superset_allocator (int * errn,
		    struct hashtab_item ** retv,
		    struct hashtab_rules * rules,
		    void * val)
{
  struct rx_cache * cache;
  struct rx_superset * template;
  struct rx_superset * newset;
  
  cache = ((struct rx_cache *)
	   ((char *)rules
	    - (unsigned long)(&((struct rx_cache *)0)->superset_hashtab_rules)));
  template = (struct rx_superset *)val;
  newset = ((struct rx_superset *)
	    rx_cache_malloc (sizeof (*template)));

  if (!newset)
    {
      return panic ("out of memory in rx superset_allocator");
    }

  {
    int cdrfinal;
    int cdredges;

    cdrfinal = (template->cdr
		? template->cdr->state_label
		: 0);
    cdredges = (template->cdr
		? template->cdr->has_cset_edges
		: 0);

    newset->state_label = (rx_abs (template->car->state_label) > rx_abs (cdrfinal)
			   ? template->car->state_label
			   : template->cdr->state_label);
    newset->has_cset_edges = (template->car->has_cset_edges || cdredges);
  }

  newset->refs = 0;
  newset->id = template->id;
  newset->car = template->car;
  newset->cdr = template->cdr;
  rx_protect_superset (rx, template->cdr);
  newset->superstate = 0;
  newset->starts_for = 0;
  newset->hash_item.data = (void *)newset;
  newset->hash_item.binding = 0;
  *retv = &newset->hash_item;
  return 0;
}

static int
super_hash_allocator (int * errn, struct hashtab ** retv, struct hashtab_rules * rules)
{
  struct rx_cache * cache;

  cache = ((struct rx_cache *)
	   ((char *)rules
	    - (unsigned long)(&((struct rx_cache *)0)->superset_hashtab_rules)));
  *retv = ((struct hashtab *)
	   rx_cache_malloc (sizeof (struct hashtab)));
  if (!*retv)
    {
      return panic ("out of memory in rx super_hash_allocator");
    }
  return 0;
}


static void
super_hash_liberator (struct hashtab * hash, struct hashtab_rules * rules)
{
  struct rx_cache * cache
    = ((struct rx_cache *)
       (char *)rules - (long)(&((struct rx_cache *)0)->superset_hashtab_rules));
  rx_cache_free (sizeof (struct hashtab), (char *)hash);
}

static void
superset_hash_item_liberator (struct hashtab_item * it,
			      struct hashtab_rules * rules)
{
}

static struct rx_cache default_cache = 
{
  {
    supersetcmp,
    super_hash_allocator,
    super_hash_liberator,
    superset_allocator,
    superset_hash_item_liberator,
  },
  0,
  0,
  0,
  0,
  0,
  0,
  0,
  RX_DEFAULT_DFA_CACHE_SIZE,
  0,
  256,
  {
    0,
    0,
    0,
    {0}
  }
};
static struct rx_cache * rx_default_cache = &default_cache;

/* This adds an element to a superstate set.  These sets are lists, such
 * that lists with == elements are ==.  The empty set is returned by
 * superset_cons (rx, 0, 0) and is NOT equivelent to 
 * (struct rx_superset)0.
 */

struct rx_superset *
rx_superset_cons (struct rx_nfa * rx, struct rx_nfa_state *car, struct rx_superset *cdr)
{
  struct rx_cache * cache = rx_default_cache;
  if (!car && !cdr)
    {
      if (!cache->empty_superset)
	{
	  cache->empty_superset = ((struct rx_superset *)
				   rx_cache_malloc_or_get (sizeof (struct rx_superset)));
	  memset0 ((char *)cache->empty_superset, sizeof (struct rx_superset));
	  cache->empty_superset->refs = 1000;
	}
      return cache->empty_superset;
    }
  {
    struct rx_superset template;
    struct hashtab_item * hit;

    template.car = car;
    template.cdr = cdr;
    template.id = rx->rx_id;
    rx_protect_superset (rx, template.cdr);
    hit = hashtab_store (&cache->superset_table,
			 (unsigned long)car ^ car->id ^ (unsigned long)cdr,
			 (void *)&template,
			 &cache->superset_hashtab_rules);
    rx_protect_superset (rx, template.cdr);
    return (struct rx_superset *)hit->data;
  }
}

/* This computes a union of two NFA state sets.  The sets do not have the
 * same representation though.  One is a RX_SUPERSET structure (part
 * of the superstate NFA) and the other is an NFA_STATE_SET (part of the NFA).
 */
struct rx_superset *
rx_superstate_eclosure_union (struct rx_nfa * rx,
			      struct rx_superset *set,
			      struct rx_nfa_state_set *ecl) 
{
  if (!ecl)
    return set;

  if (!set->car)
    return rx_superset_cons (rx, ecl->car,
			     rx_superstate_eclosure_union (rx, set, ecl->cdr));
  if (set->car == ecl->car)
    return rx_superstate_eclosure_union (rx, set, ecl->cdr);

  {
    struct rx_superset * tail;
    struct rx_nfa_state * first;

    if (set->car->id < ecl->car->id)
      {
	tail = rx_superstate_eclosure_union (rx, set->cdr, ecl);
	first = set->car;
      }
    else
      {
	tail = rx_superstate_eclosure_union (rx, set, ecl->cdr);
	first = ecl->car;
      }

    return rx_superset_cons (rx, first, tail);
  }
}




/* This constructs a new superstate from its state set.  The only 
 * complexity here is memory management.
 */
struct rx_superstate *
rx_superstate (struct rx_nfa *rx, struct rx_superset *set)
{
  struct rx_cache * cache;
  struct rx_superstate * superstate;

  cache = rx_default_cache;
  superstate = 0;

  /* Does the superstate already exist in the cache? */
  if (set->superstate)
    {
      if (set->superstate->rx_id != rx->rx_id)
	{
	  /* Aha.  It is in the cache, but belongs to a superstate
	   * that refers to an NFA that no longer exists.
	   * (We know it no longer exists because it was evidently
	   *  stored in the same region of memory as the current nfa
	   *  yet it has a different id.)
	   */
	  superstate = set->superstate;
	  if (!superstate->is_semifree)
	    {
	      if (cache->lru_superstate == superstate)
		{
		  cache->lru_superstate = superstate->next_recyclable;
		  if (cache->lru_superstate == superstate)
		    cache->lru_superstate = 0;
		}
	      {
		superstate->next_recyclable->prev_recyclable = superstate->prev_recyclable;
		superstate->prev_recyclable->next_recyclable = superstate->next_recyclable;
		if (!cache->semifree_superstate)
		  {
		    (cache->semifree_superstate
		     = superstate->next_recyclable
		     = superstate->prev_recyclable
		     = superstate);
		  }
		else
		  {
		    superstate->next_recyclable = cache->semifree_superstate;
		    superstate->prev_recyclable = cache->semifree_superstate->prev_recyclable;
		    superstate->next_recyclable->prev_recyclable = superstate;
		    superstate->prev_recyclable->next_recyclable = superstate;
		    cache->semifree_superstate = superstate;
		  }
		++cache->semifree_superstates;
	      }
	    }
	  set->superstate = 0;
	  goto handle_cache_miss;
	}
      ++cache->hits;
      superstate = set->superstate;
      rx_refresh_this_superstate (superstate);
      return superstate;
    }

 handle_cache_miss:

  /* This point reached only for cache misses. */
  ++cache->misses;

  {
    int superstate_size;
    superstate_size = (  sizeof (*superstate)
		       + (  sizeof (struct rx_inx)
			  * rx->local_cset_size));
    superstate = ((struct rx_superstate *)
		  rx_cache_malloc_or_get (superstate_size));
    ++cache->superstates;
    superstate->seq = rx_superstate_counter++;
  }

  if (!superstate)
    return 0;

  if (!cache->lru_superstate)
    (cache->lru_superstate
     = superstate->next_recyclable
     = superstate->prev_recyclable
     = superstate);
  else
    {
      superstate->next_recyclable = cache->lru_superstate;
      superstate->prev_recyclable = cache->lru_superstate->prev_recyclable;
      (  superstate->prev_recyclable->next_recyclable
       = superstate->next_recyclable->prev_recyclable
       = superstate);
    }
  superstate->outgoing_edges = 0;
  superstate->incoming_edges = 0;
  superstate->members = set;
  set->superstate = superstate;
  rx_protect_superset (rx, set);
  superstate->is_semifree = 0;
  superstate->rx_id = rx->rx_id;
  superstate->locks = 0;
  {
    int x;
    int bound;
    bound = rx->local_cset_size;

    /* None of the transitions from this superstate are known yet.
     */
    for (x = 0; x < bound; ++x)
      {
	struct rx_inx * ifr;
	ifr = &superstate->transitions[x];
	ifr->inx = (void *)rx_cache_miss;
	ifr->data = ifr->data_2 = 0;
      }
  }
  return superstate;
}

static void
solve_destination (struct rx_nfa * rx, struct rx_inx * inx_out, struct rx_super_edge * e)
{
  struct rx_superset * nfa_state;
  struct rx_superset * nil_set;
  struct rx_superset * solution;
  struct rx_superstate *dest;

  nil_set = rx_superset_cons (rx, 0, 0);
  solution = nil_set;
  rx_protect_superset (rx, solution);

  /* Iterate over all NFA states in the state set of this superstate. */
  for (nfa_state = e->present->members; nfa_state->car; nfa_state = nfa_state->cdr)
    {
      struct rx_nfa_edge * ne;

      /* Iterate over all edges of each NFA state. */
      for (ne = nfa_state->car->edges; ne; ne = ne->next)

        /* If we find an edge that is labeled with 
	 * the characters we are solving for.....
	 */
	if ((ne->type == ne_cset) && bitset_is_subset (rx->local_cset_size, e->cset, ne->cset))
	  {
	    struct rx_nfa_state * n;
	    struct rx_superset * old_sol;

	    n = ne->dest;
	    old_sol = solution;
	    if (!n->closure_computed)
	      rx_state_closure (rx, n);

	    solution = rx_superstate_eclosure_union (rx, solution, n->closure);
	    rx_protect_superset (rx, solution);
	    rx_release_superset (rx, old_sol);
	  }
    }

  if (solution == nil_set)
    {
      inx_out->inx = (void *) rx_backtrack;
      inx_out->data = 0;
      inx_out->data_2 = 0;
      return;
    }

  dest = rx_superstate (rx, solution);
  rx_release_superset (rx, solution);
  e->future = dest;

  if (!dest->incoming_edges)
    {
      dest->incoming_edges = e;
      e->next_same_dest = e->prev_same_dest = e;
    }
  else
    {
      e->next_same_dest = dest->incoming_edges;
      e->prev_same_dest = dest->incoming_edges->prev_same_dest;
      e->next_same_dest->prev_same_dest = e;
      e->prev_same_dest->next_same_dest = e;
    }
  inx_out->data = (void *)&dest->transitions;
  inx_out->data_2 = (void *) dest->members->state_label;
  inx_out->inx = (void *) rx_next_char;
}

static int
compute_super_edge_cset (struct rx_nfa *rx,
			 bitset csetout,
			 struct rx_superstate * superstate,
			 unsigned char chr)
{
  int n_nfa_edges;
  struct rx_superset * stateset;

  stateset = superstate->members;

  bitset_fill (rx->local_cset_size, csetout);
  n_nfa_edges = 0;
  while (stateset->car)
    {
      struct rx_nfa_edge *e;

      for (e = stateset->car->edges; e; e = e->next)
	if (e->type == ne_cset)
	  {
	    if (!bitset_member (e->cset, chr))
	      bitset_difference (rx->local_cset_size, csetout, e->cset);
	    else
	      bitset_intersection (rx->local_cset_size, csetout, e->cset);
	    ++n_nfa_edges;
	  }
      stateset = stateset->cdr;
    }
  return n_nfa_edges;
}

static struct rx_super_edge *
rx_super_edge (struct rx_nfa *rx, struct rx_superstate *super, bitset cset)
{
  struct rx_super_edge *tc;
  int tc_size;

  tc_size = sizeof (struct rx_super_edge) + sizeof_bitset (rx->local_cset_size);
  tc = ((struct rx_super_edge *) rx_cache_malloc_or_get (tc_size));
  memset0 (tc, tc_size);
  tc->next_same_present = super->outgoing_edges;
  super->outgoing_edges = tc;
  tc->cset = (bitset) ((char *) tc + sizeof (*tc));
  bitset_assign (rx->local_cset_size, tc->cset, cset);
  tc->present = super;
  return tc;
}


static void
install_partial_transition  (struct rx_superstate *super,
			     struct rx_inx *answer,
			     subset set,
			     int offset)
{
  int start;
  int end;
  subset pos;
  struct rx_inx * transitions;

  start = offset;
  end = start + 32;
  pos = 1;
  transitions = super->transitions;
  
  while (start < end)
    {
      if (set & pos)
	transitions[start] = *answer;
      pos <<= 1;
      ++start;
    }
}


/* 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_SUPER_EDGE_CSET.  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 superstate.
 * 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 rx_inx *
rx_handle_cache_miss (struct rx_nfa *rx,
		      struct rx_superstate *super,
		      unsigned char chr,
		      void *data) 
{
  int offset;
  struct rx_super_edge * e;

  offset = chr / bits_per_subset;
  e = data;

  if (!e)
    {
      for (e = super->outgoing_edges; e; e = e->next_same_present)
	if (bitset_member (e->cset, chr))
	  {
	    struct rx_inx answer;
	    if (e->future)
	      {
		answer.data = (void *)&e->future->transitions;
		answer.data_2 = (void *)e->future->members->state_label;
		answer.inx = (void *)rx_next_char;
	      }
	    else
	      {
		answer.data = 0;
		answer.data_2 = (void *)e;
		answer.inx = (void *)rx_cache_miss;
	      }
	    install_partial_transition (super, &answer, e->cset [offset], offset * 32);
	    return &super->transitions[chr];
	  }

      {
	char cset_space[1024];
	bitset trcset;

	if (sizeof_bitset (rx->local_cset_size) > sizeof (cset_space))
	  panic ("character set size too large in rx_handle_cache_miss");
	trcset = (bitset)cset_space;
	rx_lock_superstate (rx, super);
	if (!compute_super_edge_cset (rx, trcset, super, chr))
	  {
	    static struct rx_inx backtrack = { 0, 0, (void *)rx_backtrack, 0 };
	    rx_unlock_superstate (rx, super);
	    return &backtrack;
	  }
	else
	  {
	    e = rx_super_edge (rx, super, trcset);
	    rx_unlock_superstate (rx, super);
	    goto compute_destination;
	  }
      }
    }
  else if (e->future)
    {				
      refresh_semifree_superstate (e->future);
      return &super->transitions[chr];
    }
  else
    {
      struct rx_inx inx;
    compute_destination:
      rx_lock_superstate (rx, super);
      solve_destination (rx, &inx, e);
      install_partial_transition (super, &inx, e->cset[offset], offset * 32);
      rx_unlock_superstate (rx, super);
      return &super->transitions[chr];
    }
}


struct rx_superstate *
rx_next_superstate (struct rx_nfa * rx, struct rx_superstate * superstate, int x)
{
  struct rx_inx * inx;

  inx = &superstate->transitions[x];

  while (1)
    {
      if (inx->data)
	{
	  struct rx_inx * next_transitions;
	  struct rx_superstate * next_superstate;

	  next_transitions = (struct rx_inx *)inx->data;
	  next_superstate = ((struct rx_superstate *)
			     ((char *)next_transitions
			      - (unsigned long)&(((struct rx_superstate *)0)->transitions)));
	  return next_superstate;
	}

      if (inx->inx == (void *)rx_backtrack)
	return 0;

      inx = rx_handle_cache_miss (rx, superstate, x, inx->data_2);
      
      if (!inx)
	return 0;
    }
}

