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

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

/*
 * Definitions
 */

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

	#define	NUM_BITS			8
	#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	offset;
		Entry		next;
	};

/* This describes one names database */

	OpaqueDefn(Names)
	{
		char		*buf;
		unsigned	size;
		unsigned	next;
		Entry		buckets[NUM_BUCKETS];
	};

/*
 * INTERNAL FUNCTIONS
 */

	static	Entry			*Locate(Names db, char *string);

/*
 * NewNames()
 *
 * Create a new names database
 */

Names
NewNames(unsigned size)
{
	Names	db;

	db = NewOpaque(Names);
	db->buf = GetMem(db->size = size);
	db->next = 0;
	return(db);
}

/*
 * NameID()
 *
 * Return unique ID for a string, adding string if necessary
 */

unsigned
NameID(Names db, char *string)
{
	Entry	name, *namep;

	if (!(name = *(namep = Locate(db, string))))
	{

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

		*namep = name = NewOpaque(Entry);
		name->offset = db->next;
		name->next = NULL;

	/* Add string to library, check for overflow */

		while ((db->buf[db->next++] = *string++))
			if (db->next >= db->size)
				Panic("names buffer full");
	}

/* Unique ID is offset into string buffer */

	return(name->offset);
}

/*
 * Name()
 *
 * Return string associated with ID
 */

char	*
Name(Names db, unsigned id)
{
	if (id >= db->next)
		cerror("Name: %d out of range", id);
	return(db->buf + id);
}

/*
 * NamesLoad()
 *
 * Load from file
 */

Names
NamesLoad(FILE *fp, unsigned size)
{
	Names	db;

	db = NewNames(size);
	if (fread(db->buf, 1, db->size, fp) != db->size)
	{
		perror("fread");
		cerror("NamesLoad: can't read names");
	}
	db->next = db->size;
	return(db);
}

/*
 * NamesDump()
 *
 * Dump the buffer to a file, return length
 */

unsigned
NamesDump(FILE *fp, Names db)
{
	if (fwrite(db->buf, 1, db->next, fp) != db->next)
	{
		perror("fwrite");
		cerror("NamesDump: can't write names");
	}
	return(db->next);
}

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

static	Entry	*
Locate(Names db, char *string)
{
	Entry		*namep;
	unsigned	bucket;

	bucket = Hash(string, strlen(string)) & HASH_MASK;
	for (namep = db->buckets + bucket;
			*namep && strcmp(string, db->buf + (*namep)->offset);
			namep = &(*namep)->next);
	return(namep);
}

