/* Copyright 1988 Regents of the University of California */

#ifndef lint
static char SCCSid[] = "@(#)savestr.c 2.2 7/3/92 LBL";
#endif

/*
 *  savestr.c - routines for efficient string storage.
 *
 *	Savestr(s) stores a shared read-only string.
 *  Freestr(s) indicates a client is finished with a string.
 *	All strings must be null-terminated.  There is
 *  no imposed length limit.
 *	Strings stored with savestr(s) can be equated
 *  reliably using their pointer values.  A tailored version
 *  of strcmp(s1,s2) is included.
 *	Calls to savestr(s) and freestr(s) should be
 *  balanced (obviously).  The last call to freestr(s)
 *  frees memory associated with the string; it should
 *  never be referenced again.
 *
 *     5/14/87
 */

#ifndef  NHASH
#define  NHASH		509		/* hash table size (prime!) */
#endif

typedef struct s_head {
	struct s_head  *next;		/* next in hash list */
	int  nl;			/* links count */
}  S_HEAD;				/* followed by the string itself */

static S_HEAD  *stab[NHASH];

static int  shash();

extern char  *savestr(), *strcpy(), *malloc();

#define  NULL		0

#define  string(sp)	((char *)((sp)+1))

#define  salloc(str)	(S_HEAD *)malloc(sizeof(S_HEAD)+1+strlen(str))

#define  sfree(sp)	free((char *)(sp))


char *
savestr(str)				/* save a string */
char  *str;
{
	register int  hval;
	register S_HEAD  *sp;

	if (str == NULL)
		return(NULL);
	hval = shash(str);
	for (sp = stab[hval]; sp != NULL; sp = sp->next)
		if (!strcmp(str, string(sp))) {
			sp->nl++;
			return(string(sp));
		}
	if ((sp = salloc(str)) == NULL) {
		eputs("Out of memory in savestr\n");
		quit(1);
	}
	strcpy(string(sp), str);
	sp->nl = 1;
	sp->next = stab[hval];
	stab[hval] = sp;
	return(string(sp));
}


freestr(s)				/* free a string */
char  *s;
{
	int  hval;
	register S_HEAD  *spl, *sp;

	if (s == NULL)
		return;
	hval = shash(s);
	for (spl = NULL, sp = stab[hval]; sp != NULL; spl = sp, sp = sp->next)
		if (s == string(sp)) {
			if (--sp->nl > 0)
				return;
			if (spl != NULL)
				spl->next = sp->next;
			else
				stab[hval] = sp->next;
			sfree(sp);
			return;
		}
}


static int
shash(s)				/* hash a string */
register char  *s;
{
	register int  h = 0;

	while (*s)
		h += *s++;

	return(h % NHASH);
}
