
/*
 * sym.c
 *
 * (C) Copyright 1995 Archie L. Cobbs
 */

#include <stdio.h>
#include "util.h"
#include "error.h"
#include "hash.h"
#include "names.h"
#include "sym.h"

/*
 * DEFINITIONS
 */

/* Hashing: how many bits -> how many buckets? */

	#define	NUM_BITS			10
	#define	NUM_BUCKETS			(1 << NUM_BITS)
	#define	HASH_MASK			(NUM_BUCKETS - 1)

/* One entry in a hash chain looks like this */

	OpaqueDecl(Entry);
	OpaqueDefn(Entry)
	{
		unsigned	id;			/* Name id in database */
		void		*item;		/* Pointer to user structure */
		Entry		next;		/* Next in hash chain */
	};

/* This describes a symbol table */

	OpaqueDefn(Symtab)
	{
		Names		names;
		unsigned	itemsize;
		Entry		buckets[NUM_BUCKETS];
	};

/*
 * INTERNAL FUNCTIONS
 */

	static	Entry			*Locate(Symtab tab, unsigned id);

/*
 * SymNew()
 *
 * Create a new symbol table. Use the given
 * Names database, and associate an opaque
 * structure of size "itemsize" with each symbol.
 */

Symtab
SymNew(Names db, unsigned itemsize)
{
	Symtab	tab;

	tab = GetMem(sizeof(*tab));
	tab->names = db;
	tab->itemsize = itemsize;
	return(tab);
}

/*
 * SymAdd()
 *
 * Assumes symbol does not already exist in table.
 * Returns pointer to a fresh item structure.
 */

void	*
SymAdd(Symtab tab, unsigned id)
{
	Entry	sym, *symp;

/* Error if previously defined (sometimes) */

	if (*(symp = Locate(tab, id)))
		Panic("SymAdd: symbol \"%s\" multiply defined", Name(tab->names, id));

/* Make a new entry in the hash chain */

	sym = *symp = NewOpaque(Entry);

/* Initialize */

	sym->id = id;
	sym->item = GetMem(tab->itemsize);

/* Done */

	return(sym->item);
}

/*
 * GetSym()
 *
 * Get symbol table item or NULL if does not exist
 */

void	*
SymFind(Symtab tab, unsigned id)
{
	Entry	sym;

	if ((sym = *Locate(tab, id)))
		return(sym->item);
	return(NULL);
}

/*
 * SymDump()
 *
 * Traverse entire symbol table. Aborts
 * on a negative return from Func().
 */

void
SymDump(Symtab tab, int (*Func)(void *item))
{
	Entry		entry;
	unsigned	bucket;

	for (bucket = 0; bucket < NUM_BUCKETS; bucket++)
		for (entry = tab->buckets[bucket]; entry; entry = entry->next)
			if (Func(entry->item) < 0)
				return;
}

/*
 * Locate()
 *
 * Locate (possibly non-existent) entry for given name
 */

static	Entry	*
Locate(Symtab tab, unsigned id)
{
	Entry		*symp;
	unsigned	bucket;

	bucket = Hash(&id, sizeof(id)) & HASH_MASK;
	for (symp = tab->buckets + bucket;
			*symp && (*symp)->id != id;
			symp = &(*symp)->next);
	return(symp);
}

