blob: 057fa2e22e969fa0df734c200cf4376b357137ff [file] [log] [blame]
/* $Id: hash.c,v 1.2 2003/09/01 14:24:08 fredette Exp $ */
/* libtme/hash.c - hash table support: */
/*
* Copyright (c) 2003 Matt Fredette
* All rights reserved.
*
* 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 Matt Fredette.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <tme/common.h>
_TME_RCSID("$Id: hash.c,v 1.2 2003/09/01 14:24:08 fredette Exp $");
/* includes: */
#include <tme/hash.h>
/* our hash table size array: */
static unsigned long _tme_hash_sizes[] = {
2,
3,
5,
7,
11,
17,
37,
83,
281,
421,
631,
947,
2131,
7193,
10789,
16183,
81929,
414763,
933217,
10629917,
35875969,
80720929,
};
/* this allocates and returns a new hash: */
tme_hash_t
tme_hash_new(tme_hash_func_t hash_func,
tme_compare_func_t compare_func,
tme_hash_data_t value_null)
{
tme_hash_t hash;
hash = tme_new0(struct _tme_hash, 1);
hash->_tme_hash_size = _tme_hash_sizes[0];
hash->_tme_hash_table =
tme_new0(struct _tme_hash_bucket *,
hash->_tme_hash_size);
hash->_tme_hash_count = 0;
hash->_tme_hash_hash = hash_func;
hash->_tme_hash_compare = compare_func;
hash->_tme_hash_null = value_null;
return (hash);
}
/* this destroys a hash: */
void
tme_hash_destroy(tme_hash_t hash)
{
struct _tme_hash_bucket *bucket, *bucket_next;
unsigned long bucket_i;
/* free all of the buckets in the hash table: */
for (bucket_i = 0;
bucket_i < hash->_tme_hash_size;
bucket_i++) {
for (bucket = hash->_tme_hash_table[bucket_i];
bucket != NULL;
bucket = bucket_next) {
bucket_next = bucket->_tme_hash_bucket_next;
tme_free(bucket);
}
}
tme_free(hash->_tme_hash_table);
tme_free(hash);
}
/* this does an internal lookup in a hash table: */
static struct _tme_hash_bucket *
_tme_hash_lookup_internal(tme_hash_t hash,
tme_hash_data_t key,
struct _tme_hash_bucket ***__bucket)
{
unsigned long bucket_i;
struct _tme_hash_bucket **_bucket, *bucket;
/* hash the key: */
bucket_i = (*hash->_tme_hash_hash)(key) % hash->_tme_hash_size;
/* walk the chain of buckets: */
for (_bucket = hash->_tme_hash_table + bucket_i;
(bucket = *_bucket) != NULL;
_bucket = &bucket->_tme_hash_bucket_next) {
/* compare the key in this bucket with the lookup key.
if it succeeds, return the bucket: */
if ((*hash->_tme_hash_compare)(key, bucket->_tme_hash_bucket_key)) {
if (__bucket != NULL) {
*__bucket = _bucket;
}
return (bucket);
}
}
/* the lookup failed. return where the bucket might be inserted: */
if (__bucket != NULL) {
*__bucket = _bucket;
}
return (NULL);
}
/* this inserts a value into a hash table: */
void
tme_hash_insert(tme_hash_t hash,
tme_hash_data_t key,
tme_hash_data_t value)
{
struct _tme_hash_bucket *bucket, *bucket_next, **_bucket;
struct _tme_hash hash_new;
int size_i;
unsigned long bucket_i;
/* if this key is not already present in the hash table: */
bucket = _tme_hash_lookup_internal(hash, key, &_bucket);
if (bucket == NULL) {
/* if we need to resize this hash table: */
if ((hash->_tme_hash_count * 2) > hash->_tme_hash_size) {
/* make a copy of the top of the hash: */
hash_new = *hash;
/* set the new size of the hash: */
hash_new._tme_hash_size = hash->_tme_hash_count * 2;
for (size_i = 0;
_tme_hash_sizes[size_i] < hash_new._tme_hash_size;
size_i++) {
if (size_i + 1 == TME_ARRAY_ELS(_tme_hash_sizes)) {
abort();
}
}
hash_new._tme_hash_size = _tme_hash_sizes[size_i];
/* allocate the new hash table: */
hash_new._tme_hash_table =
tme_new0(struct _tme_hash_bucket *,
hash_new._tme_hash_size);
/* move everything from the old hash table into the new: */
for (bucket_i = 0;
bucket_i < hash->_tme_hash_size;
bucket_i++) {
for (bucket = hash->_tme_hash_table[bucket_i];
bucket != NULL;
bucket = bucket_next) {
bucket_next = bucket->_tme_hash_bucket_next;
_tme_hash_lookup_internal(&hash_new,
bucket->_tme_hash_bucket_key,
&_bucket);
bucket->_tme_hash_bucket_next = *_bucket;
*_bucket = bucket;
}
}
/* free the old hash table: */
tme_free(hash->_tme_hash_table);
/* set the new top of the hash: */
*hash = hash_new;
/* do the internal lookup again: */
_tme_hash_lookup_internal(hash, key, &_bucket);
}
/* create the new bucket and link it in: */
bucket = tme_new(struct _tme_hash_bucket, 1);
bucket->_tme_hash_bucket_next = *_bucket;
*_bucket = bucket;
/* increment the number of keys in the hash: */
hash->_tme_hash_count++;
}
/* set the key and value in the bucket: */
bucket->_tme_hash_bucket_key = key;
bucket->_tme_hash_bucket_value = value;
}
/* this looks up a key in the hash: */
tme_hash_data_t
tme_hash_lookup(tme_hash_t hash,
tme_hash_data_t key)
{
struct _tme_hash_bucket *bucket;
bucket = _tme_hash_lookup_internal(hash, key, NULL);
return (bucket != NULL
? bucket->_tme_hash_bucket_value
: hash->_tme_hash_null);
}
/* this removes a key in the hash: */
void
tme_hash_remove(tme_hash_t hash,
tme_hash_data_t key)
{
struct _tme_hash_bucket *bucket, **_bucket;
bucket = _tme_hash_lookup_internal(hash, key, &_bucket);
if (bucket != NULL) {
*_bucket = bucket->_tme_hash_bucket_next;
tme_free(bucket);
hash->_tme_hash_count--;
}
}
/* this calls a function for each key and value in the hash: */
void
tme_hash_foreach(tme_hash_t hash,
tme_foreach_func_t func,
void *private)
{
struct _tme_hash_bucket *bucket;
unsigned long bucket_i;
/* walk all of the buckets in the hash table: */
for (bucket_i = 0;
bucket_i < hash->_tme_hash_size;
bucket_i++) {
for (bucket = hash->_tme_hash_table[bucket_i];
bucket != NULL;
bucket = bucket->_tme_hash_bucket_next) {
(*func)(bucket->_tme_hash_bucket_key,
bucket->_tme_hash_bucket_value,
private);
}
}
}
/* this calls a function for each key and value in the hash.
if the function returns TRUE the entry is removed: */
unsigned long
tme_hash_foreach_remove(tme_hash_t hash,
tme_foreach_remove_func_t func,
void *private)
{
struct _tme_hash_bucket **_bucket, *bucket;
unsigned long bucket_i, count;
/* walk all of the buckets in the hash table: */
count = 0;
for (bucket_i = 0;
bucket_i < hash->_tme_hash_size;
bucket_i++) {
for (_bucket = &hash->_tme_hash_table[bucket_i];
(bucket = *_bucket) != NULL; ) {
if ((*func)(bucket->_tme_hash_bucket_key,
bucket->_tme_hash_bucket_value,
private)) {
*_bucket = bucket->_tme_hash_bucket_next;
tme_free(bucket);
hash->_tme_hash_count--;
count++;
}
else {
_bucket = &bucket->_tme_hash_bucket_next;
}
}
}
return (count);
}
/* this hashes a direct value: */
unsigned long
tme_direct_hash(tme_hash_data_t key)
{
return ((unsigned long) key);
}
/* this compares two direct values: */
int
tme_direct_compare(tme_hash_data_t key0,
tme_hash_data_t key1)
{
return (key0 == key1);
}
/* this hashes a string value: */
unsigned long
tme_string_hash(tme_hash_data_t key)
{
/* this is cribbed from the Dragon book: */
const char *p;
char c;
unsigned long h, g;
p = (const char *) key;
h = 0;
for (; (c = *(p++)) != '\0'; ) {
h = (h << 4) + c;
g = (h & 0xf0000000);
if (g) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
return (h);
}
/* this compares two string values: */
int
tme_string_compare(tme_hash_data_t key0,
tme_hash_data_t key1)
{
return (!strcmp((const char *) key0, (const char *) key1));
}