2005-02-01 10:50:37 +00:00
|
|
|
/*-
|
2002-04-13 10:57:56 +00:00
|
|
|
* Copyright (c) 1988, 1989, 1990, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
1995-01-23 21:03:17 +00:00
|
|
|
* Copyright (c) 1988, 1989 by Adam de Boor
|
1994-05-27 12:33:43 +00:00
|
|
|
* Copyright (c) 1989 by Berkeley Softworks
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This code is derived from software contributed to Berkeley by
|
|
|
|
* Adam de Boor.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by the University of
|
|
|
|
* California, Berkeley and its contributors.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
2000-07-09 02:54:54 +00:00
|
|
|
*
|
|
|
|
* @(#)hash.c 8.1 (Berkeley) 6/6/93
|
1994-05-27 12:33:43 +00:00
|
|
|
*/
|
|
|
|
|
2000-07-09 02:54:54 +00:00
|
|
|
#include <sys/cdefs.h>
|
2002-04-13 10:17:18 +00:00
|
|
|
__FBSDID("$FreeBSD$");
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
/* hash.c --
|
|
|
|
*
|
|
|
|
* This module contains routines to manipulate a hash table.
|
|
|
|
* See hash.h for a definition of the structure of the hash
|
|
|
|
* table. Hash tables grow automatically as the amount of
|
|
|
|
* information increases.
|
|
|
|
*/
|
2005-02-01 10:50:37 +00:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2001-07-26 11:02:39 +00:00
|
|
|
#include <unistd.h>
|
2005-02-01 10:50:37 +00:00
|
|
|
|
1994-05-27 12:33:43 +00:00
|
|
|
#include "hash.h"
|
2005-02-01 10:50:37 +00:00
|
|
|
#include "sprite.h"
|
|
|
|
#include "util.h"
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Forward references to local procedures that are used before they're
|
|
|
|
* defined:
|
|
|
|
*/
|
2002-03-22 01:33:25 +00:00
|
|
|
static void RebuildTable(Hash_Table *);
|
1994-05-27 12:33:43 +00:00
|
|
|
|
1995-05-30 06:41:30 +00:00
|
|
|
/*
|
1994-05-27 12:33:43 +00:00
|
|
|
* The following defines the ratio of # entries to # buckets
|
|
|
|
* at which we rebuild the table to make it larger.
|
|
|
|
*/
|
|
|
|
|
2002-09-17 21:29:06 +00:00
|
|
|
#define rebuildLimit 8
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
1995-05-30 06:41:30 +00:00
|
|
|
*
|
1994-05-27 12:33:43 +00:00
|
|
|
* Hash_InitTable --
|
2004-11-30 17:46:29 +00:00
|
|
|
*
|
2002-10-09 03:42:10 +00:00
|
|
|
* Set up the hash table t with a given number of buckets, or a
|
|
|
|
* reasonable default if the number requested is less than or
|
|
|
|
* equal to zero. Hash tables will grow in size as needed.
|
1994-05-27 12:33:43 +00:00
|
|
|
*
|
|
|
|
*
|
1995-05-30 06:41:30 +00:00
|
|
|
* Results:
|
1994-05-27 12:33:43 +00:00
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* Memory is allocated for the initial bucket area.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2002-10-09 03:42:10 +00:00
|
|
|
Hash_InitTable(Hash_Table *t, int numBuckets)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
int i;
|
|
|
|
struct Hash_Entry **hp;
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
/*
|
1995-05-30 06:41:30 +00:00
|
|
|
* Round up the size to a power of two.
|
1994-05-27 12:33:43 +00:00
|
|
|
*/
|
|
|
|
if (numBuckets <= 0)
|
|
|
|
i = 16;
|
|
|
|
else {
|
|
|
|
for (i = 2; i < numBuckets; i <<= 1)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
t->numEntries = 0;
|
|
|
|
t->size = i;
|
|
|
|
t->mask = i - 1;
|
2004-12-01 10:29:20 +00:00
|
|
|
t->bucketPtr = hp = emalloc(sizeof(*hp) * i);
|
1994-05-27 12:33:43 +00:00
|
|
|
while (--i >= 0)
|
|
|
|
*hp++ = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Hash_DeleteTable --
|
|
|
|
*
|
|
|
|
* This routine removes everything from a hash table
|
|
|
|
* and frees up the memory space it occupied (except for
|
|
|
|
* the space in the Hash_Table structure).
|
|
|
|
*
|
1995-05-30 06:41:30 +00:00
|
|
|
* Results:
|
1994-05-27 12:33:43 +00:00
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* Lots of memory is freed up.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2002-10-09 03:42:10 +00:00
|
|
|
Hash_DeleteTable(Hash_Table *t)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
struct Hash_Entry **hp, *h, *nexth = NULL;
|
|
|
|
int i;
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
for (hp = t->bucketPtr, i = t->size; --i >= 0;) {
|
|
|
|
for (h = *hp++; h != NULL; h = nexth) {
|
|
|
|
nexth = h->next;
|
2004-12-01 10:29:20 +00:00
|
|
|
free(h);
|
1994-05-27 12:33:43 +00:00
|
|
|
}
|
|
|
|
}
|
2004-12-01 10:29:20 +00:00
|
|
|
free(t->bucketPtr);
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set up the hash table to cause memory faults on any future access
|
1995-05-30 06:41:30 +00:00
|
|
|
* attempts until re-initialization.
|
1994-05-27 12:33:43 +00:00
|
|
|
*/
|
|
|
|
t->bucketPtr = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Hash_FindEntry --
|
|
|
|
*
|
|
|
|
* Searches a hash table for an entry corresponding to key.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* The return value is a pointer to the entry for key,
|
|
|
|
* if key was present in the table. If key was not
|
|
|
|
* present, NULL is returned.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Hash_Entry *
|
2004-12-06 08:56:30 +00:00
|
|
|
Hash_FindEntry(const Hash_Table *t, const char *key)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
Hash_Entry *e;
|
|
|
|
unsigned h;
|
2004-12-06 08:56:30 +00:00
|
|
|
const char *p;
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
for (h = 0, p = key; *p;)
|
|
|
|
h = (h << 5) - h + *p++;
|
|
|
|
p = key;
|
|
|
|
for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next)
|
|
|
|
if (e->namehash == h && strcmp(e->name, p) == 0)
|
|
|
|
return (e);
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Hash_CreateEntry --
|
|
|
|
*
|
|
|
|
* Searches a hash table for an entry corresponding to
|
|
|
|
* key. If no entry is found, then one is created.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* The return value is a pointer to the entry. If *newPtr
|
|
|
|
* isn't NULL, then *newPtr is filled in with TRUE if a
|
|
|
|
* new entry was created, and FALSE if an entry already existed
|
|
|
|
* with the given key.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* Memory may be allocated, and the hash buckets may be modified.
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Hash_Entry *
|
2004-12-06 08:56:30 +00:00
|
|
|
Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
Hash_Entry *e;
|
2002-10-09 03:42:10 +00:00
|
|
|
unsigned int h;
|
2004-12-06 08:56:30 +00:00
|
|
|
const char *p;
|
1994-05-27 12:33:43 +00:00
|
|
|
int keylen;
|
|
|
|
struct Hash_Entry **hp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hash the key. As a side effect, save the length (strlen) of the
|
|
|
|
* key in case we need to create the entry.
|
|
|
|
*/
|
|
|
|
for (h = 0, p = key; *p;)
|
|
|
|
h = (h << 5) - h + *p++;
|
|
|
|
keylen = p - key;
|
|
|
|
p = key;
|
|
|
|
for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) {
|
|
|
|
if (e->namehash == h && strcmp(e->name, p) == 0) {
|
|
|
|
if (newPtr != NULL)
|
|
|
|
*newPtr = FALSE;
|
|
|
|
return (e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The desired entry isn't there. Before allocating a new entry,
|
|
|
|
* expand the table if necessary (and this changes the resulting
|
1995-05-30 06:41:30 +00:00
|
|
|
* bucket chain).
|
1994-05-27 12:33:43 +00:00
|
|
|
*/
|
|
|
|
if (t->numEntries >= rebuildLimit * t->size)
|
|
|
|
RebuildTable(t);
|
2004-12-01 10:29:20 +00:00
|
|
|
e = emalloc(sizeof(*e) + keylen);
|
1994-05-27 12:33:43 +00:00
|
|
|
hp = &t->bucketPtr[h & t->mask];
|
|
|
|
e->next = *hp;
|
|
|
|
*hp = e;
|
|
|
|
e->clientData = NULL;
|
|
|
|
e->namehash = h;
|
2004-11-30 17:46:29 +00:00
|
|
|
strcpy(e->name, p);
|
1994-05-27 12:33:43 +00:00
|
|
|
t->numEntries++;
|
|
|
|
|
|
|
|
if (newPtr != NULL)
|
|
|
|
*newPtr = TRUE;
|
|
|
|
return (e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Hash_DeleteEntry --
|
|
|
|
*
|
|
|
|
* Delete the given hash table entry and free memory associated with
|
|
|
|
* it.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* Hash chain that entry lives in is modified and memory is freed.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
void
|
2002-10-09 03:42:10 +00:00
|
|
|
Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
Hash_Entry **hp, *p;
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
if (e == NULL)
|
|
|
|
return;
|
|
|
|
for (hp = &t->bucketPtr[e->namehash & t->mask];
|
|
|
|
(p = *hp) != NULL; hp = &p->next) {
|
|
|
|
if (p == e) {
|
|
|
|
*hp = p->next;
|
2004-12-01 10:29:20 +00:00
|
|
|
free(p);
|
1994-05-27 12:33:43 +00:00
|
|
|
t->numEntries--;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2004-11-30 17:46:29 +00:00
|
|
|
write(STDERR_FILENO, "bad call to Hash_DeleteEntry\n", 29);
|
1994-05-27 12:33:43 +00:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Hash_EnumFirst --
|
|
|
|
* This procedure sets things up for a complete search
|
|
|
|
* of all entries recorded in the hash table.
|
|
|
|
*
|
1995-05-30 06:41:30 +00:00
|
|
|
* Results:
|
1994-05-27 12:33:43 +00:00
|
|
|
* The return value is the address of the first entry in
|
|
|
|
* the hash table, or NULL if the table is empty.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* The information in searchPtr is initialized so that successive
|
|
|
|
* calls to Hash_Next will return successive HashEntry's
|
|
|
|
* from the table.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Hash_Entry *
|
2004-12-06 15:20:12 +00:00
|
|
|
Hash_EnumFirst(const Hash_Table *t, Hash_Search *searchPtr)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2004-11-30 17:46:29 +00:00
|
|
|
|
1994-05-27 12:33:43 +00:00
|
|
|
searchPtr->tablePtr = t;
|
|
|
|
searchPtr->nextIndex = 0;
|
|
|
|
searchPtr->hashEntryPtr = NULL;
|
2004-11-30 17:46:29 +00:00
|
|
|
return (Hash_EnumNext(searchPtr));
|
1994-05-27 12:33:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Hash_EnumNext --
|
|
|
|
* This procedure returns successive entries in the hash table.
|
|
|
|
*
|
|
|
|
* Results:
|
|
|
|
* The return value is a pointer to the next HashEntry
|
|
|
|
* in the table, or NULL when the end of the table is
|
|
|
|
* reached.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* The information in searchPtr is modified to advance to the
|
|
|
|
* next entry.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Hash_Entry *
|
2002-10-09 03:42:10 +00:00
|
|
|
Hash_EnumNext(Hash_Search *searchPtr)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
Hash_Entry *e;
|
2004-12-06 15:20:12 +00:00
|
|
|
const Hash_Table *t = searchPtr->tablePtr;
|
1994-05-27 12:33:43 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The hashEntryPtr field points to the most recently returned
|
2000-12-02 18:58:01 +00:00
|
|
|
* entry, or is NULL if we are starting up. If not NULL, we have
|
1994-05-27 12:33:43 +00:00
|
|
|
* to start at the next one in the chain.
|
|
|
|
*/
|
|
|
|
e = searchPtr->hashEntryPtr;
|
|
|
|
if (e != NULL)
|
|
|
|
e = e->next;
|
|
|
|
/*
|
|
|
|
* If the chain ran out, or if we are starting up, we need to
|
|
|
|
* find the next nonempty chain.
|
|
|
|
*/
|
|
|
|
while (e == NULL) {
|
|
|
|
if (searchPtr->nextIndex >= t->size)
|
|
|
|
return (NULL);
|
|
|
|
e = t->bucketPtr[searchPtr->nextIndex++];
|
|
|
|
}
|
|
|
|
searchPtr->hashEntryPtr = e;
|
|
|
|
return (e);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*
|
|
|
|
* RebuildTable --
|
|
|
|
* This local routine makes a new hash table that
|
|
|
|
* is larger than the old one.
|
|
|
|
*
|
1995-05-30 06:41:30 +00:00
|
|
|
* Results:
|
1994-05-27 12:33:43 +00:00
|
|
|
* None.
|
|
|
|
*
|
|
|
|
* Side Effects:
|
|
|
|
* The entire hash table is moved, so any bucket numbers
|
|
|
|
* from the old table are invalid.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------
|
|
|
|
*/
|
|
|
|
static void
|
2002-10-09 03:42:10 +00:00
|
|
|
RebuildTable(Hash_Table *t)
|
1994-05-27 12:33:43 +00:00
|
|
|
{
|
2002-04-13 10:05:30 +00:00
|
|
|
Hash_Entry *e, *next = NULL, **hp, **xp;
|
|
|
|
int i, mask;
|
|
|
|
Hash_Entry **oldhp;
|
1994-05-27 12:33:43 +00:00
|
|
|
int oldsize;
|
|
|
|
|
|
|
|
oldhp = t->bucketPtr;
|
|
|
|
oldsize = i = t->size;
|
|
|
|
i <<= 1;
|
|
|
|
t->size = i;
|
|
|
|
t->mask = mask = i - 1;
|
2004-12-01 10:29:20 +00:00
|
|
|
t->bucketPtr = hp = emalloc(sizeof(*hp) * i);
|
1994-05-27 12:33:43 +00:00
|
|
|
while (--i >= 0)
|
|
|
|
*hp++ = NULL;
|
|
|
|
for (hp = oldhp, i = oldsize; --i >= 0;) {
|
|
|
|
for (e = *hp++; e != NULL; e = next) {
|
|
|
|
next = e->next;
|
|
|
|
xp = &t->bucketPtr[e->namehash & mask];
|
|
|
|
e->next = *xp;
|
|
|
|
*xp = e;
|
|
|
|
}
|
|
|
|
}
|
2004-12-01 10:29:20 +00:00
|
|
|
free(oldhp);
|
1994-05-27 12:33:43 +00:00
|
|
|
}
|