a2223c7302
doesn't depend on the 'comp' set. ok espie@ deraadt@
330 lines
7.7 KiB
C
330 lines
7.7 KiB
C
/*
|
|
* Generic hash table routines.
|
|
* (c) Thomas Pornin 1998, 1999, 2000
|
|
*
|
|
* 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.
|
|
* 4. The name of the authors may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT 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 AUTHORS 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.
|
|
*
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "hash.h"
|
|
#include "mem.h"
|
|
#include "tune.h"
|
|
|
|
/*
|
|
* hash_string() is a sample hash function for strings
|
|
*/
|
|
int hash_string(char *s)
|
|
{
|
|
#ifdef FAST_HASH
|
|
unsigned h = 0, g;
|
|
|
|
while (*s) {
|
|
h = (h << 4) + *(unsigned char *)(s ++);
|
|
if ((g = h & 0xF000U) != 0) h ^= (g >> 12);
|
|
h &= ~g;
|
|
}
|
|
return (h ^ (h >> 9)) & 127U;
|
|
#else
|
|
unsigned char h = 0;
|
|
|
|
for (; *s; s ++) h ^= (unsigned char)(*s);
|
|
return ((int)h);
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* struct hash_item is the basic data type to internally handle hash tables
|
|
*/
|
|
struct hash_item {
|
|
void *data;
|
|
struct hash_item *next;
|
|
};
|
|
|
|
/*
|
|
* This function adds an entry to the struct hash_item list
|
|
*/
|
|
static struct hash_item *add_entry(struct hash_item *blist, void *data)
|
|
{
|
|
struct hash_item *t = getmem(sizeof(struct hash_item));
|
|
|
|
t->data = data;
|
|
t->next = blist;
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
* This function finds a struct hash_item in a list, using the
|
|
* comparison function provided as cmpdata (*cmpdata() returns
|
|
* non-zero if the two parameters are to be considered identical).
|
|
*
|
|
* It returns 0 if the item is not found.
|
|
*/
|
|
static struct hash_item *get_entry(struct hash_item *blist, void *data,
|
|
int (*cmpdata)(void *, void *))
|
|
{
|
|
while (blist) {
|
|
if ((*cmpdata)(data, blist->data)) return blist;
|
|
blist = blist->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function acts like get_entry but deletes the found item, using
|
|
* the provided function deldata(); it returns 0 if the given data was
|
|
* not found.
|
|
*/
|
|
static struct hash_item *del_entry(struct hash_item *blist, void *data,
|
|
int (*cmpdata)(void *, void *), void (*deldata)(void *))
|
|
{
|
|
struct hash_item *prev = 0, *save = blist;
|
|
|
|
while (blist) {
|
|
if ((*cmpdata)(data, blist->data)) {
|
|
if (deldata) (*deldata)(blist->data);
|
|
if (prev) prev->next = blist->next;
|
|
if (save == blist) save = blist->next;
|
|
freemem(blist);
|
|
return save;
|
|
}
|
|
prev = blist;
|
|
blist = blist->next;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function creates a new hashtable, with the hashing and comparison
|
|
* functions given as parameters
|
|
*/
|
|
struct HT *newHT(int n, int (*cmpdata)(void *, void *), int (*hash)(void *),
|
|
void (*deldata)(void *))
|
|
{
|
|
struct HT *t = getmem(sizeof(struct HT));
|
|
int i;
|
|
|
|
t->lists = getmem(n * sizeof(struct hash_item *));
|
|
for (i = 0; i < n; i ++) t->lists[i] = 0;
|
|
t->nb_lists = n;
|
|
t->cmpdata = cmpdata;
|
|
t->hash = hash;
|
|
t->deldata = deldata;
|
|
return t;
|
|
}
|
|
|
|
/*
|
|
* This function adds a new entry in the hashtable ht; it returns 0
|
|
* on success, or a pointer to the already present item otherwise.
|
|
*/
|
|
void *putHT(struct HT *ht, void *data)
|
|
{
|
|
int h;
|
|
struct hash_item *d;
|
|
|
|
h = ((*(ht->hash))(data));
|
|
#ifndef FAST_HASH
|
|
h %= ht->nb_lists;
|
|
#endif
|
|
if ((d = get_entry(ht->lists[h], data, ht->cmpdata)))
|
|
return d->data;
|
|
ht->lists[h] = add_entry(ht->lists[h], data);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function adds a new entry in the hashtable ht, even if an equal
|
|
* entry is already there. Exercise caution !
|
|
* The new entry will "hide" the old one, which means that the new will be
|
|
* found upon lookup/delete, not the old one.
|
|
*/
|
|
void *forceputHT(struct HT *ht, void *data)
|
|
{
|
|
int h;
|
|
|
|
h = ((*(ht->hash))(data));
|
|
#ifndef FAST_HASH
|
|
h %= ht->nb_lists;
|
|
#endif
|
|
ht->lists[h] = add_entry(ht->lists[h], data);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* This function finds the entry corresponding to *data in the
|
|
* hashtable ht (using the comparison function given as argument
|
|
* to newHT)
|
|
*/
|
|
void *getHT(struct HT *ht, void *data)
|
|
{
|
|
int h;
|
|
struct hash_item *t;
|
|
|
|
h = ((*(ht->hash))(data));
|
|
#ifndef FAST_HASH
|
|
h %= ht->nb_lists;
|
|
#endif
|
|
if ((t = get_entry(ht->lists[h], data, ht->cmpdata)) == 0)
|
|
return 0;
|
|
return (t->data);
|
|
}
|
|
|
|
/*
|
|
* This function finds and delete the entry corresponding to *data
|
|
* in the hashtable ht (using the comparison function given as
|
|
* argument to newHT).
|
|
*/
|
|
|
|
int delHT(struct HT *ht, void *data)
|
|
{
|
|
int h;
|
|
|
|
h = ((*(ht->hash))(data));
|
|
#ifndef FAST_HASH
|
|
h %= ht->nb_lists;
|
|
#endif
|
|
ht->lists[h] = del_entry(ht->lists[h], data, ht->cmpdata, ht->deldata);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* This function completely eradicates from memory a given hash table,
|
|
* releasing all objects
|
|
*/
|
|
void killHT(struct HT *ht)
|
|
{
|
|
int i;
|
|
struct hash_item *t, *n;
|
|
void (*dd)(void *) = ht->deldata;
|
|
|
|
for (i = 0; i < ht->nb_lists; i ++) for (t = ht->lists[i]; t;) {
|
|
n = t->next;
|
|
if (dd) (*dd)(t->data);
|
|
freemem(t);
|
|
t = n;
|
|
}
|
|
freemem(ht->lists);
|
|
freemem(ht);
|
|
}
|
|
|
|
/*
|
|
* This function stores a backup of the hash table, for context stacking.
|
|
*/
|
|
void saveHT(struct HT *ht, void **buffer)
|
|
{
|
|
struct hash_item **b = (struct hash_item **)buffer;
|
|
|
|
mmv(b, ht->lists, ht->nb_lists * sizeof(struct hash_item *));
|
|
}
|
|
|
|
/*
|
|
* This function restores the saved state of the hash table.
|
|
* Do NOT use if some of the entries that were present before the backup
|
|
* have been removed (even temporarily).
|
|
*/
|
|
void restoreHT(struct HT *ht, void **buffer)
|
|
{
|
|
struct hash_item **b = (struct hash_item **)buffer;
|
|
int i;
|
|
|
|
for (i = 0; i < ht->nb_lists; i ++) {
|
|
struct hash_item *t = ht->lists[i], *n;
|
|
|
|
while (t != b[i]) {
|
|
n = t->next;
|
|
(*(ht->deldata))(t->data);
|
|
freemem(t);
|
|
t = n;
|
|
}
|
|
ht->lists[i] = b[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This function is evil. It inserts a new item in a saved hash table,
|
|
* tweaking the save buffer and the hash table in order to keep things
|
|
* stable. There are no checks.
|
|
*/
|
|
void tweakHT(struct HT *ht, void **buffer, void *data)
|
|
{
|
|
int h;
|
|
struct hash_item *d, *e;
|
|
|
|
h = ((*(ht->hash))(data));
|
|
#ifndef FAST_HASH
|
|
h %= ht->nb_lists;
|
|
#endif
|
|
for (d = ht->lists[h]; d != buffer[h]; d = d->next);
|
|
d = add_entry(buffer[h], data);
|
|
if (buffer[h] == ht->lists[h]) {
|
|
buffer[h] = ht->lists[h] = d;
|
|
return;
|
|
}
|
|
for (e = ht->lists[h]; e->next != buffer[h]; e = e->next);
|
|
e->next = d;
|
|
buffer[h] = d;
|
|
}
|
|
|
|
/*
|
|
* This function scans the whole table and calls the given function on
|
|
* each entry.
|
|
*/
|
|
void scanHT(struct HT *ht, void (*action)(void *))
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ht->nb_lists; i ++) {
|
|
struct hash_item *t = ht->lists[i];
|
|
|
|
while (t) {
|
|
(*action)(t->data);
|
|
t = t->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The two following fonctions are generic for storing structures
|
|
* uniquely identified by their name, which must be the first
|
|
* field of the structure.
|
|
*/
|
|
int hash_struct(void *m)
|
|
{
|
|
char *n = *(char **)m;
|
|
|
|
#ifdef FAST_HASH
|
|
return hash_string(n);
|
|
#else
|
|
return hash_string(n) & 127;
|
|
#endif
|
|
}
|
|
|
|
int cmp_struct(void *m1, void *m2)
|
|
{
|
|
char *n1 = *(char **)m1, *n2 = *(char **)m2;
|
|
|
|
return !strcmp(n1, n2);
|
|
}
|