From f113c5978e5fb4939eca5f0bc1e3d5a45cb0a332 Mon Sep 17 00:00:00 2001 From: Daniel Veillard Date: Wed, 30 Nov 2005 13:36:58 +0000 Subject: [PATCH] oops, forgot hash.[ch], Daniel --- src/hash.c | 460 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/hash.h | 71 +++++++++ 2 files changed, 531 insertions(+) create mode 100644 src/hash.c create mode 100644 src/hash.h diff --git a/src/hash.c b/src/hash.c new file mode 100644 index 0000000000..3673b60d2f --- /dev/null +++ b/src/hash.c @@ -0,0 +1,460 @@ +/* + * hash.c: chained hash tables + * + * Reference: Your favorite introductory book on algorithms + * + * Copyright (C) 2000 Bjorn Reese and Daniel Veillard. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND + * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER. + * + * Author: breese@users.sourceforge.net + */ + +#define IN_LIBXML + +#include +#include "hash.h" + +#define MAX_HASH_LEN 8 + +/* #define DEBUG_GROW */ + +/* + * A single entry in the hash table + */ +typedef struct _xenHashEntry xenHashEntry; +typedef xenHashEntry *xenHashEntryPtr; +struct _xenHashEntry { + struct _xenHashEntry *next; + char *name; + void *payload; + int valid; +}; + +/* + * The entire hash table + */ +struct _xenHashTable { + struct _xenHashEntry *table; + int size; + int nbElems; +}; + +/* + * xenHashComputeKey: + * Calculate the hash key + */ +static unsigned long +xenHashComputeKey(xenHashTablePtr table, const char *name) { + unsigned long value = 0L; + char ch; + + if (name != NULL) { + value += 30 * (*name); + while ((ch = *name++) != 0) { + value = value ^ ((value << 5) + (value >> 3) + (unsigned long)ch); + } + } + return (value % table->size); +} + +/** + * xenHashCreate: + * @size: the size of the hash table + * + * Create a new xenHashTablePtr. + * + * Returns the newly created object, or NULL if an error occured. + */ +xenHashTablePtr +xenHashCreate(int size) { + xenHashTablePtr table; + + if (size <= 0) + size = 256; + + table = malloc(sizeof(xenHashTable)); + if (table) { + table->size = size; + table->nbElems = 0; + table->table = malloc(size * sizeof(xenHashEntry)); + if (table->table) { + memset(table->table, 0, size * sizeof(xenHashEntry)); + return(table); + } + free(table); + } + return(NULL); +} + +/** + * xenHashGrow: + * @table: the hash table + * @size: the new size of the hash table + * + * resize the hash table + * + * Returns 0 in case of success, -1 in case of failure + */ +static int +xenHashGrow(xenHashTablePtr table, int size) { + unsigned long key; + int oldsize, i; + xenHashEntryPtr iter, next; + struct _xenHashEntry *oldtable; +#ifdef DEBUG_GROW + unsigned long nbElem = 0; +#endif + + if (table == NULL) + return(-1); + if (size < 8) + return(-1); + if (size > 8 * 2048) + return(-1); + + oldsize = table->size; + oldtable = table->table; + if (oldtable == NULL) + return(-1); + + table->table = malloc(size * sizeof(xenHashEntry)); + if (table->table == NULL) { + table->table = oldtable; + return(-1); + } + memset(table->table, 0, size * sizeof(xenHashEntry)); + table->size = size; + + /* If the two loops are merged, there would be situations where + a new entry needs to allocated and data copied into it from + the main table. So instead, we run through the array twice, first + copying all the elements in the main array (where we can't get + conflicts) and then the rest, so we only free (and don't allocate) + */ + for (i = 0; i < oldsize; i++) { + if (oldtable[i].valid == 0) + continue; + key = xenHashComputeKey(table, oldtable[i].name); + memcpy(&(table->table[key]), &(oldtable[i]), sizeof(xenHashEntry)); + table->table[key].next = NULL; + } + + for (i = 0; i < oldsize; i++) { + iter = oldtable[i].next; + while (iter) { + next = iter->next; + + /* + * put back the entry in the new table + */ + + key = xenHashComputeKey(table, iter->name); + if (table->table[key].valid == 0) { + memcpy(&(table->table[key]), iter, sizeof(xenHashEntry)); + table->table[key].next = NULL; + free(iter); + } else { + iter->next = table->table[key].next; + table->table[key].next = iter; + } + +#ifdef DEBUG_GROW + nbElem++; +#endif + + iter = next; + } + } + + free(oldtable); + +#ifdef DEBUG_GROW + xmlGenericError(xmlGenericErrorContext, + "xenHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem); +#endif + + return(0); +} + +/** + * xenHashFree: + * @table: the hash table + * @f: the deallocator function for items in the hash + * + * Free the hash @table and its contents. The userdata is + * deallocated with @f if provided. + */ +void +xenHashFree(xenHashTablePtr table, xenHashDeallocator f) { + int i; + xenHashEntryPtr iter; + xenHashEntryPtr next; + int inside_table = 0; + int nbElems; + + if (table == NULL) + return; + if (table->table) { + nbElems = table->nbElems; + for(i = 0; (i < table->size) && (nbElems > 0); i++) { + iter = &(table->table[i]); + if (iter->valid == 0) + continue; + inside_table = 1; + while (iter) { + next = iter->next; + if ((f != NULL) && (iter->payload != NULL)) + f(iter->payload, iter->name); + if (iter->name) + free(iter->name); + iter->payload = NULL; + if (!inside_table) + free(iter); + nbElems--; + inside_table = 0; + iter = next; + } + inside_table = 0; + } + free(table->table); + } + free(table); +} + +/** + * xenHashAddEntry3: + * @table: the hash table + * @name: the name of the userdata + * @userdata: a pointer to the userdata + * + * Add the @userdata to the hash @table. This can later be retrieved + * by using @name. Duplicate entries generate errors. + * + * Returns 0 the addition succeeded and -1 in case of error. + */ +int +xenHashAddEntry(xenHashTablePtr table, const char *name, + void *userdata) { + unsigned long key, len = 0; + xenHashEntryPtr entry; + xenHashEntryPtr insert; + + if ((table == NULL) || (name == NULL)) + return(-1); + + /* + * Check for duplicate and insertion location. + */ + key = xenHashComputeKey(table, name); + if (table->table[key].valid == 0) { + insert = NULL; + } else { + for (insert = &(table->table[key]); insert->next != NULL; + insert = insert->next) { + if (!strcmp(insert->name, name)) + return(-1); + len++; + } + if (!strcmp(insert->name, name)) + return(-1); + } + + if (insert == NULL) { + entry = &(table->table[key]); + } else { + entry = malloc(sizeof(xenHashEntry)); + if (entry == NULL) + return(-1); + } + + entry->name = strdup(name); + entry->payload = userdata; + entry->next = NULL; + entry->valid = 1; + + + if (insert != NULL) + insert->next = entry; + + table->nbElems++; + + if (len > MAX_HASH_LEN) + xenHashGrow(table, MAX_HASH_LEN * table->size); + + return(0); +} + +/** + * xenHashUpdateEntry: + * @table: the hash table + * @name: the name of the userdata + * @userdata: a pointer to the userdata + * @f: the deallocator function for replaced item (if any) + * + * Add the @userdata to the hash @table. This can later be retrieved + * by using @name. Existing entry for this tuple + * will be removed and freed with @f if found. + * + * Returns 0 the addition succeeded and -1 in case of error. + */ +int +xenHashUpdateEntry(xenHashTablePtr table, const char *name, + void *userdata, xenHashDeallocator f) { + unsigned long key; + xenHashEntryPtr entry; + xenHashEntryPtr insert; + + if ((table == NULL) || name == NULL) + return(-1); + + /* + * Check for duplicate and insertion location. + */ + key = xenHashComputeKey(table, name); + if (table->table[key].valid == 0) { + insert = NULL; + } else { + for (insert = &(table->table[key]); insert->next != NULL; + insert = insert->next) { + if (!strcmp(insert->name, name)) { + if (f) + f(insert->payload, insert->name); + insert->payload = userdata; + return(0); + } + } + if (!strcmp(insert->name, name)) { + if (f) + f(insert->payload, insert->name); + insert->payload = userdata; + return(0); + } + } + + if (insert == NULL) { + entry = &(table->table[key]); + } else { + entry = malloc(sizeof(xenHashEntry)); + if (entry == NULL) + return(-1); + } + + entry->name = strdup(name); + entry->payload = userdata; + entry->next = NULL; + entry->valid = 1; + table->nbElems++; + + + if (insert != NULL) { + insert->next = entry; + } + return(0); +} + +/** + * xenHashLookup: + * @table: the hash table + * @name: the name of the userdata + * + * Find the userdata specified by the (@name, @name2, @name3) tuple. + * + * Returns the a pointer to the userdata + */ +void * +xenHashLookup(xenHashTablePtr table, const char *name) { + unsigned long key; + xenHashEntryPtr entry; + + if (table == NULL) + return(NULL); + if (name == NULL) + return(NULL); + key = xenHashComputeKey(table, name); + if (table->table[key].valid == 0) + return(NULL); + for (entry = &(table->table[key]); entry != NULL; entry = entry->next) { + if (!strcmp(entry->name, name)) + return(entry->payload); + } + return(NULL); +} + +/** + * xenHashSize: + * @table: the hash table + * + * Query the number of elements installed in the hash @table. + * + * Returns the number of elements in the hash table or + * -1 in case of error + */ +int +xenHashSize(xenHashTablePtr table) { + if (table == NULL) + return(-1); + return(table->nbElems); +} + +/** + * xenHashRemoveEntry: + * @table: the hash table + * @name: the name of the userdata + * @f: the deallocator function for removed item (if any) + * + * Find the userdata specified by the @name and remove + * it from the hash @table. Existing userdata for this tuple will be removed + * and freed with @f. + * + * Returns 0 if the removal succeeded and -1 in case of error or not found. + */ +int +xenHashRemoveEntry(xenHashTablePtr table, const char *name, + xenHashDeallocator f) { + unsigned long key; + xenHashEntryPtr entry; + xenHashEntryPtr prev = NULL; + + if (table == NULL || name == NULL) + return(-1); + + key = xenHashComputeKey(table, name); + if (table->table[key].valid == 0) { + return(-1); + } else { + for (entry = &(table->table[key]); entry != NULL; entry = entry->next) { + if (!strcmp(entry->name, name)) { + if ((f != NULL) && (entry->payload != NULL)) + f(entry->payload, entry->name); + entry->payload = NULL; + if(entry->name) + free(entry->name); + if(prev) { + prev->next = entry->next; + free(entry); + } else { + if (entry->next == NULL) { + entry->valid = 0; + } else { + entry = entry->next; + memcpy(&(table->table[key]), entry, sizeof(xenHashEntry)); + free(entry); + } + } + table->nbElems--; + return(0); + } + prev = entry; + } + return(-1); + } +} + diff --git a/src/hash.h b/src/hash.h new file mode 100644 index 0000000000..6ebb571c22 --- /dev/null +++ b/src/hash.h @@ -0,0 +1,71 @@ +/* + * Summary: Chained hash tables + * Description: This module implements the hash table support used in + * various places in the library. + * + * Copy: See Copyright for the status of this software. + * + * Author: Bjorn Reese + */ + +#ifndef __XEN_HASH_H__ +#define __XEN_HASH_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * The hash table. + */ +typedef struct _xenHashTable xenHashTable; +typedef xenHashTable *xenHashTablePtr; + +/* + * function types: + */ +/** + * xenHashDeallocator: + * @payload: the data in the hash + * @name: the name associated + * + * Callback to free data from a hash. + */ +typedef void (*xenHashDeallocator)(void *payload, char *name); + +/* + * Constructor and destructor. + */ +xenHashTablePtr xenHashCreate (int size); +void + xenHashFree (xenHashTablePtr table, + xenHashDeallocator f); +int xenHashSize (xenHashTablePtr table); + +/* + * Add a new entry to the hash table. + */ +int xenHashAddEntry (xenHashTablePtr table, + const char *name, + void *userdata); +int xenHashUpdateEntry(xenHashTablePtr table, + const char *name, + void *userdata, + xenHashDeallocator f); + +/* + * Remove an entry from the hash table. + */ +int xenHashRemoveEntry(xenHashTablePtr table, + const char *name, + xenHashDeallocator f); +/* + * Retrieve the userdata. + */ +void * xenHashLookup (xenHashTablePtr table, + const char *name); + +#ifdef __cplusplus +} +#endif +#endif /* ! __XEN_HASH_H__ */ -- GitLab