
/* 
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/
#ident "$Header: names.c 1.1 90/01/19 $@(#)names.c	1.1 89/12/01"
#include "xs.h"
#include "names.h"
#include "stack.h"
#include "types.h"
#include "anal.h"
#include "xdefs.h"

/*
 * Names & strings storage & access
 * Storage is organized in name tables.
 * Actual strings & names are held in 'names'
 * Other tables are created to help retrieving names at each C nesting levels
 * they use references into the 'names' table.
 * 'mem' is a memory table containing actual memory space.
 *
 * A name table (NAMETAB) is a dynamically growing table (HASH_LEN step)
 * of fixed size hash tables (struct na_hash) (HASH_INC entries)
 *
 * Memory blocks are NS_SIZE chars long. references are kept in a dynamically
 * growing table (struct na_store) (step NS_LEN)
 */
extern char *calloc();
extern char *gettxt();

struct na_hash {
  char **nt_low;
  char **nt_high;
  TYPEP *nt_data;
  int nt_used;
};
  
struct na_access {
  struct na_hash *na_tab;
  int na_ntab;
};
  
/*
 * Actual memory storage organization
 */
struct na_store {
  int ns_free;
  char *ns_mem;
};

/*
 * na_isin access cache to speed up successive accesses with same value
 */
static bool no_changes = FALSE;
static char *prev_name;
static NAMETAB *prev_nt;
static TYPEP prev_ret;

/*
 * Memory allocation saving
 * buffers are never freed, they are put in free lists, according to their
 * types.
 */
static struct na_buf {
  int na_nelem;
  struct na_buf *na_next;
} *na_start, *na_data_start, *ns_start, *nt_start, *na_tab_start, *mem_start;

static NAMETAB names = { 0, 0 };
static struct na_store *mem;
static int ns_free;
static int ns_cur = -1;
static int ns_max;

/*
 * Enlarge a table. Add entries for hash tables.
 */
static
na_enlarge(nt)
  NAMETAB *nt;
{
  register int old;

  if (nt->na_ntab == 0){
    /*
     * No hash entries allocated yet
     */
    if (na_tab_start != 0){
      /*
       * Use previously free'd table
       */
      nt->na_tab = (struct na_hash *)na_tab_start;
      nt->na_ntab = na_tab_start->na_nelem;
      na_tab_start = na_tab_start->na_next;
      for (old = 0 ; old < nt->na_ntab ; old++)
        nt->na_tab[old].nt_low = 0;
    } else {
      /*
       * Allocate a new table
       */
      nt->na_tab = (struct na_hash *)calloc(sizeof(struct na_hash), HASH_SIZE);
      if (nt->na_tab == 0)
        fatal(gettxt("xs:38","Out of memory (names.c (1))"));
      nt->na_ntab = HASH_SIZE;
    }
  } else {
    /*
     * More hash entries needed
     */
    old = nt->na_ntab;
    nt->na_ntab += HASH_SIZE;
    nt->na_tab = (struct na_hash *)realloc((char *)nt->na_tab,
                    sizeof(struct na_hash) * nt->na_ntab);
    if (nt->na_tab == 0)
      fatal(gettxt("xs:39","Out of memory (names.c (2))"));
    for ( ; old < nt->na_ntab ; old++)
      nt->na_tab[old].nt_low = 0;
  }
}

/*
 * Allocate a new memory block
 */
static
ns_enlarge()
{
  if (ns_free == ns_max){
    ns_max += NS_LEN;
    if (mem == 0){
      /*
       * Allocate first memory blocks reference table
       */
      if (mem_start != 0){
        /*
         * Use previously free'd table
         */
        mem = (struct na_store *)mem_start;
        ns_max = mem_start->na_nelem;
        mem_start = mem_start->na_next;
      } else {
        /*
         * Allocate a new table
         */
        if ((mem = (struct na_store *)malloc(sizeof (struct na_store) *
              NS_LEN)) == 0)
          fatal(gettxt("xs:40","Out of memory (names.c (3))"));
      }
    } else {
      /*
       * Enlarge table
       */
      if ((mem = (struct na_store *)realloc(mem, sizeof (struct na_store)
           * ns_max)) == 0)
        fatal(gettxt("xs:41","Out of memory (names.c (4))"));
    }
  }
  /*
   * Allocate a memory block
   */
  if (ns_start != 0){
    /*
     * Use previously free'd block
     */
    mem[ns_free].ns_mem = (char *)ns_start;
    ns_start = ns_start->na_next;
  } else {
    /*
     * Allocate a new block
     */
    if ((mem[ns_free].ns_mem = malloc(NS_SIZE)) == 0)
      fatal(gettxt("xs:42","Out of memory (names.c (5))"));
  }
  mem[ns_free].ns_free = 0;
  ns_free++;
  ns_cur++;
}

/*
 * Add a name/string in the main table (no data associated)
 */
char *
na_store(name)
  char *name;
{
  return na_add(name, &names, type_null);
}

/*
 * Add an entry in a names table
 */
char *
na_add(name, nt, data)
  char *name;
  NAMETAB *nt;
  TYPEP data;
{
  struct na_hash *cur_tab;
  register char **h;
  register int i, j;
  char *ptr;
  int len, pos;
  
  no_changes = FALSE;
  for (i = 0, len = 0, ptr = name ; *ptr != '\0' ; ptr++, len++)
    i = (i << 1) + *ptr;

  if (len >= NS_SIZE){
    werror(gettxt("xs:43","String too long -- truncated to %d chars"), NS_SIZE - 1);
    len = NS_SIZE - 1;
    name[NS_SIZE - 1] = '\0';
  }
  pos = (i & 077777) % HASH_INC;

  /*
   * Scan each hash table in turn
   */
  for (j = 0 ; ; j++){
    if (j >= nt->na_ntab)
      /*
       * Need more tables
       */
      na_enlarge(nt);

    cur_tab = nt->na_tab + j;

    if (cur_tab->nt_low == 0){
      /*
       * entry for hash table exists, but table is not yet allocated
       */
      register char **hp;
      register TYPEP *dp;
      if (na_start != 0){
        /*
         * Use previously free'd hash table
         * Set contents to 0 (corresponding to calloc)
         */
      	register int i;
        hp = (char **)na_start;
        na_start = na_start->na_next;
        for (i = 0 ; i < HASH_INC ; i++)
          hp[i] = 0;
      } else {
        /*
         * Allocate a new table
         * Set contents to 0 (calloc)
         */
        if ((hp = (char **)calloc(sizeof(char *), HASH_INC)) == 0)
          fatal(gettxt("xs:44","Out of memory (names.c (6))"));
      }
      cur_tab->nt_low = hp;
      cur_tab->nt_high = hp + HASH_INC;
      cur_tab->nt_used = 0;
      if (nt != &names){
        /*
         * All name tables but the main one (names) have data associated
         */
        if (na_data_start != 0){
          /*
           * Use previously free'd data table
           */
          dp = (TYPEP *)na_data_start;
          na_data_start = na_data_start->na_next;
        } else {
	  /*
	   * Allocate a new table
	   */
          if ((dp = (TYPEP *)malloc(sizeof(TYPEP) * HASH_INC)) == 0)
            fatal(gettxt("xs:45","Out of memory (names.c (7))"));
        }
        cur_tab->nt_data = dp;
      }
    }
    /*
     * Use quadratic hashing
     */
    h = cur_tab->nt_low + pos;
    i = 1;
    do {
      if (*h == 0){
        /*
         * Free slot found : insert here
         */
        struct na_store *aux;

        if (cur_tab->nt_used > (HASH_INC * 3) / 4)
          /*
           * Insert in another table, the current one is 'full'
           */
          break;
        cur_tab->nt_used++;

        if (nt == &names){
          /*
           * Main name table : copy name (or string) in memory
           */
          len++;
          if (mem == 0 || len + mem[ns_cur].ns_free >= NS_SIZE)
            ns_enlarge();

          aux = mem + ns_cur;
          *h  = aux->ns_mem + aux->ns_free;
          strcpy(*h, name);
          aux->ns_free += len;
        } else {
          /*
           * Auxiliary name table. No copy necessary
           */
          *h = name;
          pos = h - cur_tab->nt_low;
          cur_tab->nt_data[pos] = data;
        }
        return *h;
      }
      if (**h == *name && strcmp(*h, name) == 0)
        /*
         * Entry is already in the table
         */
        return *h;

      h += i;
      i += 2;

      if (h > cur_tab->nt_high)
        h -= HASH_INC;

    } while (i < HASH_INC);
  }
}

/*
 * Create an auxiliary name table
 */
NAMETAB *
na_creat()
{
  NAMETAB *newtbl;
  
  if (nt_start != 0){
    /*
     * Use previously free'd table
     */
    newtbl = (NAMETAB *)nt_start;
    nt_start = nt_start->na_next;
  } else {
    /*
     * Allocate a new table
     */
    if ((newtbl = (NAMETAB *)malloc(sizeof(NAMETAB))) == 0)
      fatal(gettxt("xs:46","Out of memory (names.c (8))"));
  }
  newtbl->na_ntab = 0;
  no_changes = FALSE;
  return newtbl;
}

/*
 * Free an auxiliary name table
 */
void
na_free(nt)
  NAMETAB *nt;
{
  register int i;
  struct na_buf *ptr;
  if (nt->na_ntab != 0){
    /*
     * Free associated tables
     */
    for (i = 0 ; i < nt->na_ntab ; i++){
      if (nt->na_tab[i].nt_low == 0)
        break;
      /*
       * Insert hash table in free list
       */
      ptr = (struct na_buf *)nt->na_tab[i].nt_low;
      ptr->na_next = na_start;
      na_start = ptr;
      /*
       * Insert data table in free list
       */
      ptr = (struct na_buf *)nt->na_tab[i].nt_data;
      ptr->na_next = na_data_start;
      na_data_start = ptr;
    }
    /*
     * Insert hash table entries table in free list
     */
    ptr = (struct na_buf *)nt->na_tab;
    ptr->na_next = na_tab_start;
    ptr->na_nelem = nt->na_ntab;
    na_tab_start = ptr;
  }
  /*
   * Insert name table in free list
   */
  ptr = (struct na_buf *)nt;
  ptr->na_next = nt_start;
  nt_start = ptr;
}

/*
 * Free main table and memory blocks
 */
void
na_freeall()
{
}

/*
 * Locate a name in a table (for auxiliary names tables only)
 */
TYPEP
na_isin(nt, name)
  NAMETAB *nt;
  char *name;
{
  struct na_hash *cur_tab;
  register char **h;
  register int i, j;
  char *ptr;
  int len, pos;
  
  if (no_changes && prev_nt == nt && name == prev_name){
    /*
     * Fast call : current entry is same as previous
     */
    return prev_ret;
  }
  no_changes = TRUE;
  prev_nt = nt;
  prev_name = name;

  for (i = 0, len = 0, ptr = name ; *ptr != '\0' ; ptr++, len++)
    i = (i << 1) + *ptr;

  pos = (i & 077777) % HASH_INC;

  /*
   * Scan each hash table in turn
   */
  for (j = 0 ; ; j++){
    if (j >= nt->na_ntab){
      /*
       * not found in any table
       */
      prev_ret = type_null;
      return type_null;
    }

    cur_tab = nt->na_tab + j;

    if (cur_tab->nt_low == 0){
      /*
       * Reached last actually allocated table
       * Entry is not present
       */
      prev_ret = type_null;
      return type_null;
    }

    h = cur_tab->nt_low + pos;
    i = 1;
    do {
      if (*h == 0)
        break;

      if (*h == name){
        /*
         * Found entry;
         */
        pos = h - cur_tab->nt_low;
        prev_ret = cur_tab->nt_data[pos];
        return prev_ret;
      }

      h += i;
      i += 2;

      if (h > cur_tab->nt_high)
        h -= HASH_INC;

    } while (i < HASH_INC);
  }
}

/*
 * Unique vars names generation
 */
static int na_count;
static char loc_buf[10];

reset_var()
{
  na_count = 0;
}

char *
na_unique()
{
  sprintf(loc_buf, "%s%d", strings_unique, ++na_count);
  return na_store(loc_buf);
}
