/* htab.c - table hashing routines */
/* $Id: htab.c,v 1.13 1999/11/13 23:14:42 cvs Exp $ */
#include "copyright.h"
#include "autoconf.h"
#ifndef STANDALONE
#include "db.h"
#include "externs.h"
#include "htab.h"
#include "alloc.h"
#else
extern char * FDECL(strsave, (const char *));
#endif /* STANDALONE */
#include "mudconf.h"
/* ---------------------------------------------------------------------------
* hashval: Compute hash value of a string for a hash table.
*/
int hashval(str, hashmask)
char *str;
int hashmask;
{
int hash = 0;
char *sp;
/* If the string pointer is null, return 0. If not, add up the
* numeric value of all the characters and return the sum,
* modulo the size of the hash table.
*/
if (str == NULL)
return 0;
for (sp = str; *sp; sp++)
hash = (hash << 5) + hash + *sp;
return (hash & hashmask);
}
/* ----------------------------------------------------------------------
* get_hashmask: Get hash mask for mask-style hashing.
*/
int get_hashmask(size)
int *size;
{
int tsize;
/* Get next power-of-two >= size, return power-1 as the mask for
* ANDing
*/
for (tsize = 1; tsize < *size; tsize = tsize << 1) ;
*size = tsize;
return tsize - 1;
}
/* ---------------------------------------------------------------------------
* hashinit: Initialize a new hash table.
*/
void hashinit(htab, size)
HASHTAB *htab;
int size;
{
int i;
htab->mask = get_hashmask(&size);
htab->hashsize = size;
htab->checks = 0;
htab->scans = 0;
htab->max_scan = 0;
htab->hits = 0;
htab->entries = 0;
htab->deletes = 0;
htab->nulls = size;
htab->entry =
(HASHARR *) XMALLOC(size * sizeof(struct hashentry *),
"hashinit");
for (i = 0; i < size; i++)
htab->entry->element[i] = NULL;
}
/* ---------------------------------------------------------------------------
* hashreset: Reset hash table stats.
*/
void hashreset(htab)
HASHTAB *htab;
{
htab->checks = 0;
htab->scans = 0;
htab->hits = 0;
}
/* ---------------------------------------------------------------------------
* hashfind: Look up an entry in a hash table and return a pointer to its
* hash data.
*/
int *hashfind(str, htab)
char *str;
HASHTAB *htab;
{
int hval, numchecks;
HASHENT *hptr, *prev;
numchecks = 0;
htab->scans++;
hval = hashval(str, htab->mask);
for (prev = hptr = htab->entry->element[hval]; hptr != NULL; hptr = hptr->next) {
numchecks++;
if (strcmp(str, hptr->target) == 0) {
htab->hits++;
if (numchecks > htab->max_scan)
htab->max_scan = numchecks;
htab->checks += numchecks;
hptr->checks++;
/* If the string has been checked more than 20 times,
* move it to the head of the chain, and reset the
* check counter.
*/
if ((hptr->checks > 20) && (hptr != prev)) {
prev->next = hptr->next;
hptr->next = htab->entry->element[hval];
htab->entry->element[hval] = hptr;
hptr->checks = 0;
}
return hptr->data;
}
prev = hptr;
}
if (numchecks > htab->max_scan)
htab->max_scan = numchecks;
htab->checks += numchecks;
return NULL;
}
/* ---------------------------------------------------------------------------
* hashadd: Add a new entry to a hash table.
*/
int hashadd(str, hashdata, htab)
char *str;
int *hashdata;
HASHTAB *htab;
{
int hval;
HASHENT *hptr;
/*
* Make sure that the entry isn't already in the hash table. If it
* is, exit with an error. Otherwise, create a new hash block and
* link it in at the head of its thread.
*/
if (hashfind(str, htab) != NULL)
return (-1);
hval = hashval(str, htab->mask);
htab->entries++;
if (htab->entry->element[hval] == NULL)
htab->nulls--;
hptr = (HASHENT *) XMALLOC(sizeof(HASHENT), "hashadd");
hptr->target = (char *)strsave(str);
hptr->data = hashdata;
hptr->checks = 0;
hptr->next = htab->entry->element[hval];
htab->entry->element[hval] = hptr;
return (0);
}
/* ---------------------------------------------------------------------------
* hashdelete: Remove an entry from a hash table.
*/
void hashdelete(str, htab)
char *str;
HASHTAB *htab;
{
int hval;
HASHENT *hptr, *last;
hval = hashval(str, htab->mask);
last = NULL;
for (hptr = htab->entry->element[hval];
hptr != NULL;
last = hptr, hptr = hptr->next) {
if (strcmp(str, hptr->target) == 0) {
if (last == NULL)
htab->entry->element[hval] = hptr->next;
else
last->next = hptr->next;
free(hptr->target);
free(hptr);
htab->deletes++;
htab->entries--;
if (htab->entry->element[hval] == NULL)
htab->nulls++;
return;
}
}
}
/* ---------------------------------------------------------------------------
* hashflush: free all the entries in a hashtable.
*/
void hashflush(htab, size)
HASHTAB *htab;
int size;
{
HASHENT *hent, *thent;
int i;
for (i = 0; i < htab->hashsize; i++) {
hent = htab->entry->element[i];
while (hent != NULL) {
thent = hent;
hent = hent->next;
free(thent->target);
free(thent);
}
htab->entry->element[i] = NULL;
}
/* Resize if needed. Otherwise, just zero all the stats */
if ((size > 0) && (size != htab->hashsize)) {
free(htab->entry);
hashinit(htab, size);
} else {
htab->checks = 0;
htab->scans = 0;
htab->max_scan = 0;
htab->hits = 0;
htab->entries = 0;
htab->deletes = 0;
htab->nulls = htab->hashsize;
}
}
/* ---------------------------------------------------------------------------
* hashrepl: replace the data part of a hash entry.
*/
int hashrepl(str, hashdata, htab)
char *str;
int *hashdata;
HASHTAB *htab;
{
HASHENT *hptr;
int hval;
hval = hashval(str, htab->mask);
for (hptr = htab->entry->element[hval];
hptr != NULL;
hptr = hptr->next) {
if (strcmp(str, hptr->target) == 0) {
hptr->data = hashdata;
return 1;
}
}
return 0;
}
void hashreplall(old, new, htab)
int *old, *new;
HASHTAB *htab;
{
int hval;
HASHENT *hptr;
for (hval = 0; hval < htab->hashsize; hval++)
for (hptr = htab->entry->element[hval]; hptr != NULL; hptr = hptr->next) {
if (hptr->data == old)
hptr->data = new;
}
}
/* ---------------------------------------------------------------------------
* hashinfo: return an mbuf with hashing stats
*/
char *hashinfo(tab_name, htab)
const char *tab_name;
HASHTAB *htab;
{
char *buff;
buff = alloc_mbuf("hashinfo");
sprintf(buff, "%-15s %5d%8d%8d%8d%8d%8d%8d%8d",
tab_name, htab->hashsize, htab->entries, htab->deletes,
htab->nulls, htab->scans, htab->hits, htab->checks,
htab->max_scan);
return buff;
}
/* Returns the key for the first hash entry in 'htab'. */
int *hash_firstentry(htab)
HASHTAB *htab;
{
int hval;
for (hval = 0; hval < htab->hashsize; hval++)
if (htab->entry->element[hval] != NULL) {
htab->last_hval = hval;
htab->last_entry = htab->entry->element[hval];
return htab->entry->element[hval]->data;
}
return NULL;
}
int *hash_nextentry(htab)
HASHTAB *htab;
{
int hval;
HASHENT *hptr;
hval = htab->last_hval;
hptr = htab->last_entry;
if (hptr->next != NULL) { /* We can stay in the same chain */
htab->last_entry = hptr->next;
return hptr->next->data;
}
/* We were at the end of the previous chain, go to the next one */
hval++;
while (hval < htab->hashsize) {
if (htab->entry->element[hval] != NULL) {
htab->last_hval = hval;
htab->last_entry = htab->entry->element[hval];
return htab->entry->element[hval]->data;
}
hval++;
}
return NULL;
}
char *hash_firstkey(htab)
HASHTAB *htab;
{
int hval;
for (hval = 0; hval < htab->hashsize; hval++)
if (htab->entry->element[hval] != NULL) {
htab->last_hval = hval;
htab->last_entry = htab->entry->element[hval];
return htab->entry->element[hval]->target;
}
return NULL;
}
char *hash_nextkey(htab)
HASHTAB *htab;
{
int hval;
HASHENT *hptr;
hval = htab->last_hval;
hptr = htab->last_entry;
if (hptr->next != NULL) { /* We can stay in the same chain */
htab->last_entry = hptr->next;
return hptr->next->target;
}
/* We were at the end of the previous chain, go to the next one */
hval++;
while (hval < htab->hashsize) {
if (htab->entry->element[hval] != NULL) {
htab->last_hval = hval;
htab->last_entry = htab->entry->element[hval];
return htab->entry->element[hval]->target;
}
hval++;
}
return NULL;
}
/* ---------------------------------------------------------------------------
* hashresize: Resize a hash table, to double the number of keys in it.
*/
void hashresize(htab, min_size)
HASHTAB *htab;
int min_size;
{
int size, i;
HASHTAB new_htab;
HASHENT *hent, *thent;
size = (htab->entries) * HASH_FACTOR;
size = (size < min_size) ? min_size : size;
get_hashmask(&size);
if ((size > 512) && (size > htab->entries * 1.33 * HASH_FACTOR))
size /= 2;
if (size == htab->hashsize) {
/* We're already at the correct size. Don't do anything. */
return;
}
hashinit(&new_htab, size);
for (i = 0; i < htab->hashsize; i++) {
hent = htab->entry->element[i];
while (hent != NULL) {
thent = hent;
hent = hent->next;
hashadd(thent->target, thent->data, &new_htab);
free(thent->target);
free(thent);
}
htab->entry->element[i] = NULL;
}
free(htab->entry);
htab->hashsize = new_htab.hashsize;
htab->mask = new_htab.mask;
htab->checks = new_htab.checks;
htab->scans = new_htab.scans;
htab->max_scan = new_htab.max_scan;
htab->hits = new_htab.hits;
htab->entries = new_htab.entries;
htab->deletes = new_htab.deletes;
htab->nulls = new_htab.nulls;
htab->entry = new_htab.entry;
htab->last_hval = new_htab.last_hval;
htab->last_entry = new_htab.last_entry;
}
#ifndef STANDALONE
/* ---------------------------------------------------------------------------
* nhashfind: Look up an entry in a numeric hash table and return a pointer
* to its hash data.
*/
int *nhashfind(val, htab)
int val;
NHSHTAB *htab;
{
int hval, numchecks;
NHSHENT *hptr, *prev;
numchecks = 0;
htab->scans++;
hval = (val & htab->mask);
for (prev = hptr = htab->entry->element[hval]; hptr != NULL; hptr = hptr->next) {
numchecks++;
if (val == hptr->target) {
htab->hits++;
if (numchecks > htab->max_scan)
htab->max_scan = numchecks;
htab->checks += numchecks;
hptr->checks++;
if ((hptr->checks > 20) && (hptr != prev)) {
prev->next = hptr->next;
hptr->next = htab->entry->element[hval];
htab->entry->element[hval] = hptr;
hptr->checks = 0;
}
return hptr->data;
}
prev = hptr;
}
if (numchecks > htab->max_scan)
htab->max_scan = numchecks;
htab->checks += numchecks;
return NULL;
}
/* ---------------------------------------------------------------------------
* nhashadd: Add a new entry to a numeric hash table.
*/
int nhashadd(val, hashdata, htab)
int val, *hashdata;
NHSHTAB *htab;
{
int hval;
NHSHENT *hptr;
/*
* Make sure that the entry isn't already in the hash table. If it
* is, exit with an error. Otherwise, create a new hash block and
* link it in at the head of its thread.
*/
if (nhashfind(val, htab) != NULL)
return (-1);
hval = (val & htab->mask);
htab->entries++;
if (htab->entry->element[hval] == NULL)
htab->nulls--;
hptr = (NHSHENT *) XMALLOC(sizeof(NHSHENT), "nhashadd");
hptr->target = val;
hptr->data = hashdata;
hptr->checks = 0;
hptr->next = htab->entry->element[hval];
htab->entry->element[hval] = hptr;
return (0);
}
/* ---------------------------------------------------------------------------
* nhashdelete: Remove an entry from a numeric hash table.
*/
void nhashdelete(val, htab)
int val;
NHSHTAB *htab;
{
int hval;
NHSHENT *hptr, *last;
hval = (val & htab->mask);
last = NULL;
for (hptr = htab->entry->element[hval];
hptr != NULL;
last = hptr, hptr = hptr->next) {
if (val == hptr->target) {
if (last == NULL)
htab->entry->element[hval] = hptr->next;
else
last->next = hptr->next;
free(hptr);
htab->deletes++;
htab->entries--;
if (htab->entry->element[hval] == NULL)
htab->nulls++;
return;
}
}
}
/* ---------------------------------------------------------------------------
* nhashflush: free all the entries in a hashtable.
*/
void nhashflush(htab, size)
NHSHTAB *htab;
int size;
{
NHSHENT *hent, *thent;
int i;
for (i = 0; i < htab->hashsize; i++) {
hent = htab->entry->element[i];
while (hent != NULL) {
thent = hent;
hent = hent->next;
free(thent);
}
htab->entry->element[i] = NULL;
}
/* Resize if needed. Otherwise, just zero all the stats */
if ((size > 0) && (size != htab->hashsize)) {
free(htab->entry);
nhashinit(htab, size);
} else {
htab->checks = 0;
htab->scans = 0;
htab->max_scan = 0;
htab->hits = 0;
htab->entries = 0;
htab->deletes = 0;
htab->nulls = htab->hashsize;
}
}
/* ---------------------------------------------------------------------------
* nhashrepl: replace the data part of a hash entry.
*/
int nhashrepl(val, hashdata, htab)
int val, *hashdata;
NHSHTAB *htab;
{
NHSHENT *hptr;
int hval;
hval = (val & htab->mask);
for (hptr = htab->entry->element[hval];
hptr != NULL;
hptr = hptr->next) {
if (hptr->target == val) {
hptr->data = hashdata;
return 1;
}
}
return 0;
}
/* ---------------------------------------------------------------------------
* nhashresize: Resize a numeric hash table (double number of keys in it).
*/
void nhashresize(htab, min_size)
NHSHTAB *htab;
int min_size;
{
int size, i;
NHSHTAB new_htab;
NHSHENT *hent, *thent;
size = (htab->entries) * HASH_FACTOR;
size = (size < min_size) ? min_size : size;
get_hashmask(&size);
if ((size > 512) && (size > htab->entries * 1.33 * HASH_FACTOR))
size /= 2;
if (size == htab->hashsize) {
/* We're already at the correct size. Don't do anything. */
return;
}
nhashinit(&new_htab, size);
for (i = 0; i < htab->hashsize; i++) {
hent = htab->entry->element[i];
while (hent != NULL) {
thent = hent;
hent = hent->next;
nhashadd(thent->target, thent->data, &new_htab);
free(thent);
}
htab->entry->element[i] = NULL;
}
free(htab->entry);
htab->hashsize = new_htab.hashsize;
htab->mask = new_htab.mask;
htab->checks = new_htab.checks;
htab->scans = new_htab.scans;
htab->max_scan = new_htab.max_scan;
htab->hits = new_htab.hits;
htab->entries = new_htab.entries;
htab->deletes = new_htab.deletes;
htab->nulls = new_htab.nulls;
htab->entry = new_htab.entry;
htab->last_hval = new_htab.last_hval;
htab->last_entry = new_htab.last_entry;
}
/* ---------------------------------------------------------------------------
* search_nametab: Search a name table for a match and return the flag value.
*/
int search_nametab(player, ntab, flagname)
dbref player;
NAMETAB *ntab;
char *flagname;
{
NAMETAB *nt;
for (nt = ntab; nt->name; nt++) {
if (minmatch(flagname, nt->name, nt->minlen)) {
if (check_access(player, nt->perm)) {
return nt->flag;
} else
return -2;
}
}
return -1;
}
/* ---------------------------------------------------------------------------
* find_nametab_ent: Search a name table for a match and return a pointer to it.
*/
NAMETAB *find_nametab_ent(player, ntab, flagname)
dbref player;
NAMETAB *ntab;
char *flagname;
{
NAMETAB *nt;
for (nt = ntab; nt->name; nt++) {
if (minmatch(flagname, nt->name, nt->minlen)) {
if (check_access(player, nt->perm)) {
return nt;
}
}
}
return NULL;
}
/* ---------------------------------------------------------------------------
* display_nametab: Print out the names of the entries in a name table.
*/
void display_nametab(player, ntab, prefix, list_if_none)
dbref player;
NAMETAB *ntab;
char *prefix;
int list_if_none;
{
char *buf, *bp, *cp;
NAMETAB *nt;
int got_one;
buf = alloc_lbuf("display_nametab");
bp = buf;
got_one = 0;
for (cp = prefix; *cp; cp++)
*bp++ = *cp;
for (nt = ntab; nt->name; nt++) {
if (God(player) || check_access(player, nt->perm)) {
*bp++ = ' ';
for (cp = nt->name; *cp; cp++)
*bp++ = *cp;
got_one = 1;
}
}
*bp = '\0';
if (got_one || list_if_none)
notify(player, buf);
free_lbuf(buf);
}
/* ---------------------------------------------------------------------------
* interp_nametab: Print values for flags defined in name table.
*/
void interp_nametab(player, ntab, flagword, prefix, true_text, false_text)
dbref player;
NAMETAB *ntab;
int flagword;
char *prefix, *true_text, *false_text;
{
char *buf, *bp, *cp;
NAMETAB *nt;
buf = alloc_lbuf("interp_nametab");
bp = buf;
for (cp = prefix; *cp; cp++)
*bp++ = *cp;
nt = ntab;
while (nt->name) {
if (God(player) || check_access(player, nt->perm)) {
*bp++ = ' ';
for (cp = nt->name; *cp; cp++)
*bp++ = *cp;
*bp++ = '.';
*bp++ = '.';
*bp++ = '.';
if ((flagword & nt->flag) != 0)
cp = true_text;
else
cp = false_text;
while (*cp)
*bp++ = *cp++;
if ((++nt)->name)
*bp++ = ';';
}
}
*bp = '\0';
notify(player, buf);
free_lbuf(buf);
}
/* ---------------------------------------------------------------------------
* listset_nametab: Print values for flags defined in name table.
*/
void listset_nametab(player, ntab, flagword, prefix, list_if_none)
dbref player;
NAMETAB *ntab;
int flagword, list_if_none;
char *prefix;
{
char *buf, *bp, *cp;
NAMETAB *nt;
int got_one;
buf = bp = alloc_lbuf("listset_nametab");
for (cp = prefix; *cp; cp++)
*bp++ = *cp;
nt = ntab;
got_one = 0;
while (nt->name) {
if (((flagword & nt->flag) != 0) &&
(God(player) || check_access(player, nt->perm))) {
*bp++ = ' ';
for (cp = nt->name; *cp; cp++)
*bp++ = *cp;
got_one = 1;
}
nt++;
}
*bp = '\0';
if (got_one || list_if_none)
notify(player, buf);
free_lbuf(buf);
}
/* ---------------------------------------------------------------------------
* cf_ntab_access: Change the access on a nametab entry.
*/
extern void FDECL(cf_log_notfound, (dbref, char *, const char *, char *));
CF_HAND(cf_ntab_access)
{
NAMETAB *np;
char *ap;
for (ap = str; *ap && !isspace(*ap); ap++) ;
if (*ap)
*ap++ = '\0';
while (*ap && isspace(*ap))
ap++;
for (np = (NAMETAB *) vp; np->name; np++) {
if (minmatch(str, np->name, np->minlen)) {
return cf_modify_bits(&(np->perm), ap, extra,
player, cmd);
}
}
cf_log_notfound(player, cmd, "Entry", str);
return -1;
}
#endif