/* hash.c
*/
/* This file is an altered version of a set of hash routines by
** Geoffrey Collyer. See the end of the file for his copyright.
*/
#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "final.h"
#include "INTERN.h"
#include "hash.h"
#include "hash.ih"
/* CAA: In the future it might be a good idea to preallocate a region
* of hashents, since allocation overhead on some systems will be as
* large as the structure itself.
*/
/* grab this many hashents at a time (under 1024 for malloc overhead) */
#define HEBLOCKSIZE 1000
/* define the following if you actually want to free the hashents
* You probably don't want to do this with the usual malloc...
*/
/* #define HASH_FREE_ENTRIES */
/* CAA: increased from 600. Each threaded article requires at least
* one hashent, and various newsgroup hashes can easily get large.
*/
/* tunable parameters */
#define RETAIN 1000 /* retain & recycle this many HASHENTs */
static HASHENT* hereuse = NULL;
static int reusables = 0;
HASHTABLE*
hashcreate(size, cmpfunc)
unsigned size; /* a crude guide to size */
int (*cmpfunc) _((char*,int,HASHDATUM));
{
register HASHTABLE* tbl;
/* allocate HASHTABLE and (HASHENT*) array together to reduce the
** number of malloc calls. */
register struct alignalloc {
HASHTABLE ht;
HASHENT* hepa[1]; /* longer than it looks */
}* aap;
if (size < 1) /* size < 1 is nonsense */
size = 1;
aap = (struct alignalloc*)
safemalloc(sizeof *aap + (size-1)*sizeof (HASHENT*));
bzero((char*)aap, sizeof *aap + (size-1)*sizeof (HASHENT*));
tbl = &aap->ht;
tbl->ht_size = size;
tbl->ht_magic = HASHMAG;
tbl->ht_cmp = (cmpfunc == NULL? default_cmp: cmpfunc);
tbl->ht_addr = aap->hepa;
return tbl;
}
/* Free all the memory associated with tbl, erase the pointers to it, and
** invalidate tbl to prevent further use via other pointers to it.
*/
void
hashdestroy(tbl)
register HASHTABLE* tbl;
{
register unsigned idx;
register HASHENT* hp;
register HASHENT* next;
register HASHENT** hepp;
register int tblsize;
if (BADTBL(tbl))
return;
tblsize = tbl->ht_size;
hepp = tbl->ht_addr;
for (idx = 0; idx < tblsize; idx++) {
for (hp = hepp[idx]; hp != NULL; hp = next) {
next = hp->he_next;
hp->he_next = NULL;
hefree(hp);
}
hepp[idx] = NULL;
}
tbl->ht_magic = 0; /* de-certify this table */
tbl->ht_addr = NULL;
free((char*)tbl);
}
void
hashstore(tbl, key, keylen, data)
register HASHTABLE* tbl;
char* key;
int keylen;
HASHDATUM data;
{
register HASHENT* hp;
register HASHENT** nextp;
nextp = hashfind(tbl, key, keylen);
hp = *nextp;
if (hp == NULL) { /* absent; allocate an entry */
hp = healloc();
hp->he_next = NULL;
hp->he_keylen = keylen;
*nextp = hp; /* append to hash chain */
}
hp->he_data = data; /* supersede any old data for this key */
}
void
hashdelete(tbl, key, keylen)
register HASHTABLE* tbl;
char* key;
int keylen;
{
register HASHENT* hp;
register HASHENT** nextp;
nextp = hashfind(tbl, key, keylen);
hp = *nextp;
if (hp == NULL) /* absent */
return;
*nextp = hp->he_next; /* skip this entry */
hp->he_next = NULL;
hp->he_data.dat_ptr = NULL;
hefree(hp);
}
HASHENT** slast_nextp;
int slast_keylen;
HASHDATUM /* data corresponding to key */
hashfetch(tbl, key, keylen)
register HASHTABLE* tbl;
char* key;
int keylen;
{
register HASHENT* hp;
register HASHENT** nextp;
static HASHDATUM errdatum = { NULL, 0 };
nextp = hashfind(tbl, key, keylen);
slast_nextp = nextp;
slast_keylen = keylen;
hp = *nextp;
if (hp == NULL) /* absent */
return errdatum;
return hp->he_data;
}
void
hashstorelast(data)
HASHDATUM data;
{
register HASHENT* hp;
hp = *slast_nextp;
if (hp == NULL) { /* absent; allocate an entry */
hp = healloc();
hp->he_next = NULL;
hp->he_keylen = slast_keylen;
*slast_nextp = hp; /* append to hash chain */
}
hp->he_data = data; /* supersede any old data for this key */
}
/* Visit each entry by calling nodefunc at each, with keylen, data,
** and extra as arguments.
*/
void
hashwalk(tbl, nodefunc, extra)
HASHTABLE* tbl;
register int (*nodefunc) _((int,HASHDATUM*,int));
register int extra;
{
register unsigned idx;
register HASHENT* hp;
register HASHENT* next;
register HASHENT** hepp;
register int tblsize;
if (BADTBL(tbl))
return;
hepp = tbl->ht_addr;
tblsize = tbl->ht_size;
for (idx = 0; idx < tblsize; idx++) {
slast_nextp = &hepp[idx];
for (hp = *slast_nextp; hp != NULL; hp = next) {
next = hp->he_next;
if ((*nodefunc)(hp->he_keylen, &hp->he_data, extra) < 0) {
*slast_nextp = next;
hp->he_next = NULL;
hefree(hp);
}
else
slast_nextp = &hp->he_next;
}
}
}
/* The returned value is the address of the pointer that refers to the
** found object. Said pointer may be NULL if the object was not found;
** if so, this pointer should be updated with the address of the object
** to be inserted, if insertion is desired.
*/
static HASHENT**
hashfind(tbl, key, keylen)
register HASHTABLE* tbl;
char* key;
register int keylen;
{
register HASHENT* hp;
register HASHENT* prevhp = NULL;
register HASHENT** hepp;
register unsigned size;
if (BADTBL(tbl)) {
fputs("Hash table is invalid.",stderr);
finalize(1);
}
size = tbl->ht_size;
hepp = &tbl->ht_addr[hash(key,keylen) % size];
for (hp = *hepp; hp != NULL; prevhp = hp, hp = hp->he_next) {
if (hp->he_keylen == keylen && !(*tbl->ht_cmp)(key, keylen, hp->he_data))
break;
}
/* assert: *(returned value) == hp */
return (prevhp == NULL? hepp: &prevhp->he_next);
}
static unsigned /* not yet taken modulus table size */
hash(key, keylen)
register char* key;
register int keylen;
{
register unsigned hash = 0;
while (keylen--)
hash += *key++;
return hash;
}
static int
default_cmp(key, keylen, data)
char* key;
int keylen;
HASHDATUM data;
{
/* We already know that the lengths are equal, just compare the strings */
return bcmp(key, data.dat_ptr, keylen);
}
static HASHENT*
healloc() /* allocate a hash entry */
{
register HASHENT* hp;
if (hereuse == NULL) {
int i;
/* make a nice big block of hashents to play with */
hp = (HASHENT*)safemalloc(HEBLOCKSIZE * sizeof (HASHENT));
/* set up the pointers within the block */
for (i = 0; i < HEBLOCKSIZE-1; i++)
(hp+i)->he_next = hp + i + 1;
/* The last block is the end of the list */
(hp+i)->he_next = NULL;
hereuse = hp; /* start of list is the first item */
reusables += HEBLOCKSIZE;
}
/* pull the first reusable one off the pile */
hp = hereuse;
hereuse = hereuse->he_next;
hp->he_next = NULL; /* prevent accidents */
reusables--;
return hp;
}
static void
hefree(hp) /* free a hash entry */
register HASHENT* hp;
{
#ifdef HASH_FREE_ENTRIES
if (reusables >= RETAIN) /* compost heap is full? */
free((char*)hp); /* yup, just pitch this one */
else { /* no, just stash for reuse */
++reusables;
hp->he_next = hereuse;
hereuse = hp;
}
#else
/* always add to list */
++reusables;
hp->he_next = hereuse;
hereuse = hp;
#endif
}
/*
* Copyright (c) 1992 Geoffrey Collyer
* All rights reserved.
* Written by Geoffrey Collyer.
*
* This software is not subject to any license of the American Telephone
* and Telegraph Company, the Regents of the University of California, or
* the Free Software Foundation.
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to alter it and redistribute it freely, subject
* to the following restrictions:
*
* 1. The author is not responsible for the consequences of use of this
* software, no matter how awful, even if they arise from flaws in it.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Since few users ever read sources,
* credits must appear in the documentation.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software. Since few users
* ever read sources, credits must appear in the documentation.
*
* 4. This notice may not be removed or altered.
*/
syntax highlighted by Code2HTML, v. 0.9.1