2002-10-29 07:03:13 +03:00
|
|
|
/* Generic hash table
|
|
|
|
**
|
2002-07-09 16:24:59 +04:00
|
|
|
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
|
|
|
|
** Distributed under the terms of the NewOS License.
|
|
|
|
*/
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
#include <KernelExport.h>
|
2002-10-30 02:07:06 +03:00
|
|
|
#include <malloc.h>
|
2002-07-09 16:24:59 +04:00
|
|
|
#include <debug.h>
|
|
|
|
#include <Errors.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <khash.h>
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
#define TRACE_HASH 0
|
|
|
|
#if TRACE_HASH
|
|
|
|
# define TRACE(x) dprintf x
|
|
|
|
#else
|
|
|
|
# define TRACE(x) ;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// ToDo: the hashtable is not expanded when necessary (no load factor, no nothing)
|
|
|
|
// Could try to use pools instead of malloc() for the elements - might be
|
|
|
|
// faster than the current approach.
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
struct hash_table {
|
2002-11-29 11:30:20 +03:00
|
|
|
struct hash_element **table;
|
|
|
|
int next_ptr_offset;
|
|
|
|
uint32 table_size;
|
|
|
|
int num_elements;
|
|
|
|
int flags;
|
|
|
|
int (*compare_func)(void *e, const void *key);
|
|
|
|
uint32 (*hash_func)(void *e, const void *key, uint32 range);
|
2002-07-09 16:24:59 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
// XXX gross hack
|
|
|
|
#define NEXT_ADDR(t, e) ((void *)(((unsigned long)(e)) + (t)->next_ptr_offset))
|
|
|
|
#define NEXT(t, e) ((void *)(*(unsigned long *)NEXT_ADDR(t, e)))
|
|
|
|
#define PUT_IN_NEXT(t, e, val) (*(unsigned long *)NEXT_ADDR(t, e) = (long)(val))
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
static inline void *
|
|
|
|
next_element(hash_table *table, void *element)
|
|
|
|
{
|
|
|
|
// ToDo: should we use this instead of the NEXT() macro?
|
|
|
|
return (void *)(*(unsigned long *)NEXT_ADDR(table, element));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct hash_table *
|
|
|
|
hash_init(uint32 table_size, int next_ptr_offset,
|
2002-07-09 16:24:59 +04:00
|
|
|
int compare_func(void *e, const void *key),
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 hash_func(void *e, const void *key, uint32 range))
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct hash_table *t;
|
|
|
|
unsigned int i;
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
if (compare_func == NULL || hash_func == NULL) {
|
|
|
|
dprintf("hash_init() called with NULL function pointer\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
t = (struct hash_table *)malloc(sizeof(struct hash_table));
|
2002-10-29 07:03:13 +03:00
|
|
|
if (t == NULL)
|
2002-07-09 16:24:59 +04:00
|
|
|
return NULL;
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
t->table = (struct hash_element **)malloc(sizeof(void *) * table_size);
|
2002-11-28 05:25:04 +03:00
|
|
|
if (t->table == NULL) {
|
|
|
|
free(t);
|
|
|
|
return NULL;
|
|
|
|
}
|
2002-11-29 11:30:20 +03:00
|
|
|
|
|
|
|
for (i = 0; i < table_size; i++)
|
2002-07-09 16:24:59 +04:00
|
|
|
t->table[i] = NULL;
|
2002-11-29 11:30:20 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
t->table_size = table_size;
|
|
|
|
t->next_ptr_offset = next_ptr_offset;
|
|
|
|
t->flags = 0;
|
2002-11-29 11:30:20 +03:00
|
|
|
t->num_elements = 0;
|
2002-07-09 16:24:59 +04:00
|
|
|
t->compare_func = compare_func;
|
|
|
|
t->hash_func = hash_func;
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
TRACE(("hash_init: created table %p, next_ptr_offset %d, compare_func %p, hash_func %p\n",
|
|
|
|
t, next_ptr_offset, compare_func, hash_func));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
int
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_uninit(struct hash_table *table)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
ASSERT(table->num_elements == 0);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
free(table->table);
|
|
|
|
free(table);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
status_t
|
|
|
|
hash_insert(struct hash_table *table, void *element)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 hash;
|
|
|
|
|
|
|
|
ASSERT(table != NULL && element != NULL);
|
|
|
|
TRACE(("hash_insert: table 0x%x, element 0x%x\n", table, element));
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
hash = table->hash_func(element, NULL, table->table_size);
|
|
|
|
PUT_IN_NEXT(table, element, table->table[hash]);
|
|
|
|
table->table[hash] = (struct hash_element *)element;
|
|
|
|
table->num_elements++;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
// ToDo: resize hash table if it's grown too much!
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
status_t
|
|
|
|
hash_remove(struct hash_table *table, void *_element)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 hash = table->hash_func(_element, NULL, table->table_size);
|
|
|
|
void *element, *lastElement = NULL;
|
|
|
|
|
|
|
|
for (element = table->table[hash]; element != NULL; lastElement = element, element = NEXT(table, element)) {
|
|
|
|
if (element == _element) {
|
|
|
|
if (lastElement != NULL) {
|
|
|
|
// connect the previous entry with the next one
|
|
|
|
PUT_IN_NEXT(table, lastElement, NEXT(table, element));
|
|
|
|
} else
|
|
|
|
table->table[hash] = (struct hash_element *)NEXT(table, element);
|
|
|
|
table->num_elements--;
|
|
|
|
|
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-07-12 02:21:56 +04:00
|
|
|
return B_ERROR;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
void *
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_find(struct hash_table *table, void *searchedElement)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 hash = table->hash_func(searchedElement, NULL, table->table_size);
|
|
|
|
void *element;
|
|
|
|
|
|
|
|
for (element = table->table[hash]; element != NULL; element = NEXT(table, element)) {
|
|
|
|
if (element == searchedElement)
|
|
|
|
return element;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
void *
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_lookup(struct hash_table *table, const void *key)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 hash = table->hash_func(NULL, key, table->table_size);
|
|
|
|
void *element;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
for (element = table->table[hash]; element != NULL; element = NEXT(table, element)) {
|
|
|
|
if (table->compare_func(element, key) == 0)
|
|
|
|
return element;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
struct hash_iterator *
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_open(struct hash_table *table, struct hash_iterator *iterator)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
if (iterator == NULL) {
|
|
|
|
iterator = (struct hash_iterator *)malloc(sizeof(struct hash_iterator));
|
|
|
|
if (iterator == NULL)
|
2002-07-09 16:24:59 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_rewind(table, iterator);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
return iterator;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
void
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_close(struct hash_table *table, struct hash_iterator *iterator, bool freeIterator)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-10-29 07:03:13 +03:00
|
|
|
if (freeIterator)
|
2002-11-29 11:30:20 +03:00
|
|
|
free(iterator);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
void
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_rewind(struct hash_table *table, struct hash_iterator *iterator)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
iterator->current = NULL;
|
|
|
|
iterator->bucket = -1;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
|
|
|
void *
|
2002-11-29 11:30:20 +03:00
|
|
|
hash_next(struct hash_table *table, struct hash_iterator *iterator)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 index;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
restart:
|
2002-11-29 11:30:20 +03:00
|
|
|
if (iterator->current == NULL) {
|
|
|
|
// get next bucket
|
|
|
|
for (index = (uint32)(iterator->bucket + 1); index < table->table_size; index++) {
|
|
|
|
if (table->table[index]) {
|
|
|
|
iterator->bucket = index;
|
|
|
|
iterator->current = table->table[index];
|
2002-07-09 16:24:59 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2002-11-29 11:30:20 +03:00
|
|
|
iterator->current = NEXT(table, iterator->current);
|
|
|
|
if (!iterator->current)
|
2002-07-09 16:24:59 +04:00
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
return iterator->current;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32
|
|
|
|
hash_hash_string(const char *string)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2002-11-29 11:30:20 +03:00
|
|
|
uint32 hash = 0;
|
|
|
|
char c;
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
// we assume hash to be at least 32 bits
|
2002-11-29 11:30:20 +03:00
|
|
|
while ((c = *string++) != 0) {
|
2002-07-09 16:24:59 +04:00
|
|
|
hash ^= hash >> 28;
|
|
|
|
hash <<= 4;
|
2002-11-29 11:30:20 +03:00
|
|
|
hash ^= c;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
2002-10-29 07:03:13 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
return hash;
|
|
|
|
}
|
2002-07-19 05:27:22 +04:00
|
|
|
|