/*
 *  Project   : tin - a Usenet reader
 *  Module    : hashstr.c
 *  Author    : I.Lea & R.Skrenta
 *  Created   : 01-04-91
 *  Updated   : 01-12-94
 *  Notes     :
 *  Copyright : (c) Copyright 1991-94 by Iain Lea & Rich Skrenta
 *              You may  freely  copy or  redistribute  this software,
 *              so  long as there is no profit made from its use, sale
 *              trade or  reproduction.  You may not change this copy-
 *              right notice, and it must be included in any copy made
 */

#include	"tin.h"

/*
 *  Maintain a table of all strings we have seen.
 *  If a new string comes in, add it to the table and return a pointer
 *  to it.  If we've seen it before, just return the pointer to it.
 *
 *  Usage:  hash_str("some string") returns char *
 *
 *  Spillovers are chained on the end
 */

/*
 *  Arbitrary table size, but make sure it's prime!
 */

#define		HASHNODE_TABLE_SIZE	2411

#ifdef M_AMIGA
struct t_hashnode **table=0;
#else
struct t_hashnode *table[HASHNODE_TABLE_SIZE];
#endif

char *
hash_str (s)
	char *s;
{
	long h;				/* result of hash:  index into hash table */
	struct t_hashnode *p;	/* used to descend the spillover structs */

	if (s == (char *) 0) {
		return ((char *) 0);
	}

	{
		unsigned char *t = (unsigned char *) s;

		h = *t++;
		while (*t)
			h = ((h << 1) ^ *t++) % (long) HASHNODE_TABLE_SIZE;
	}

	p = table[h];

	if (p == (struct t_hashnode *) 0) {
		table[h] = add_string (s);
		return table[h]->s;
	}

	while (TRUE) {
		if (STRCMPEQ(s, p->s)) {
			return (p->s);
		}

		if (p->next == (struct t_hashnode *) 0) {
			p->next = add_string (s);
			return p->next->s;
		} else {
			p = p->next;
		}
	}
	/* NOTREACHED */
}


struct t_hashnode *
add_string (s)
	char *s;
{
	int *iptr;
	struct t_hashnode *p;

	p = (struct t_hashnode *) my_malloc (sizeof (struct t_hashnode));

	p->next = (struct t_hashnode *) 0;
#ifndef OLD_THREADING
	iptr = (int *) my_malloc ((unsigned) strlen (s) + sizeof (int)*2 + 1);
	*iptr++ = -1;
	*iptr++ = -1;
#else
	iptr = (int *) my_malloc ((unsigned) strlen (s) + sizeof (int) + 1);
	*iptr++ = -1;
#endif
	p->s = (char *) iptr;
	strcpy (p->s, s);

	return p;
}


void 
hash_init ()
{
	int i;

#ifdef M_AMIGA
	if (! table) {
		table = (struct t_hashnode **) my_malloc (HASHNODE_TABLE_SIZE * sizeof (void *));
	}
#endif

	for (i = 0; i < HASHNODE_TABLE_SIZE; i++) {
		table[i] = (struct t_hashnode *) 0;
	}
}


void 
hash_reclaim ()
{
	int i;
	int *iptr;
	struct t_hashnode *p, *next;

#ifdef M_AMIGA
	if (! table) {
		return;
	}
#endif

	for (i = 0; i < HASHNODE_TABLE_SIZE; i++)
		if (table[i] != (struct t_hashnode *) 0) {
			p = table[i];
			while (p != (struct t_hashnode *) 0) {
				next = p->next;
				if (p->s != (char *) 0) {
					iptr = (int *) p->s;
#ifndef OLD_THREADING
					iptr-=2;
#else
					iptr--;
#endif
					free ((char *) iptr);
				}
				free ((char *) p);
				p = next;
			}
			table[i] = (struct t_hashnode *) 0;
		}
}
