xenocara/app/xrdb-cpp/hash.c
matthieu a2223c7302 Import a copy of ucpp, lightweight cpp for use by xrdb so that it
doesn't depend on the 'comp'  set. ok espie@ deraadt@
2014-07-12 14:25:39 +00:00

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);
}