/* $Id: htable.c,v 1.1 2001/07/13 18:28:45 sandro Exp $ */ /* * Copyright (c) 1997-2001 Sandro Sigala. 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. * * 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. */ #ifdef TEST #undef NDEBUG #endif #include #include #include #include "config.h" #include "htable.h" #define HTABLE_DEFAULT_SIZE 127 typedef struct hbucket_s *hbucket; struct hbucket_s { hpair pair; hbucket next; }; struct htable_s { unsigned long size; hbucket *table; hfunc_t hfunc; }; /* * Default hashing function. */ static unsigned long htable_hash(const char *p, unsigned long size) { unsigned long hash = 0, g; while (*p != '\0') { hash = (hash << 4) + *p++; if ((g = hash & 0xF0000000) != 0) hash ^= g >> 24; hash &= ~g; } return hash % size; } static hbucket build_bucket(const char *key) { hbucket ptr; ptr = (hbucket)xmalloc(sizeof(*ptr)); memset(ptr, 0, sizeof(*ptr)); ptr->pair.key = xstrdup(key); return ptr; } static void free_bucket(hbucket ptr) { if (ptr->pair.key != NULL) free(ptr->pair.key); free(ptr); } htable htable_new(void) { return htable_new_custom(HTABLE_DEFAULT_SIZE); } htable htable_new_custom(unsigned long size) { htable ptr; ptr = (htable)xmalloc(sizeof(*ptr)); ptr->size = size; ptr->table = (hbucket *)xmalloc(ptr->size * sizeof(hbucket)); memset(ptr->table, 0, ptr->size * sizeof(hbucket)); ptr->hfunc = htable_hash; return ptr; } void htable_delete(htable ht) { hbucket bucket, next; unsigned int i; for (i = 0; i < ht->size; i++) { bucket = ht->table[i]; while (bucket != NULL) { next = bucket->next; free_bucket(bucket); bucket = next; } } free(ht->table); free(ht); } void htable_set_hash_func(htable ht, hfunc_t hfunc) { ht->hfunc = hfunc; } int htable_store_key(htable ht, const char *key) { unsigned long hash; if (htable_exists(ht, key)) return -1; hash = ht->hfunc(key, ht->size); if (ht->table[hash] == NULL) ht->table[hash] = build_bucket(key); else { hbucket bucket = build_bucket(key); bucket->next = ht->table[hash]; ht->table[hash] = bucket; } return 0; } int htable_store_data(htable ht, const char *key, void *data) { hbucket bucket; unsigned long hash; hash = ht->hfunc(key, ht->size); for (bucket = ht->table[hash]; bucket != NULL; bucket = bucket->next) if (!strcmp(bucket->pair.key, key)) { bucket->pair.data = data; return 0; } return -1; } int htable_store(htable ht, const char *key, void *data) { if (htable_exists(ht, key)) htable_remove(ht, key); htable_store_key(ht, key); htable_store_data(ht, key, data); return 0; } int htable_exists(htable ht, const char *key) { hbucket bucket; unsigned long hash; hash = ht->hfunc(key, ht->size); for (bucket = ht->table[hash]; bucket != NULL; bucket = bucket->next) if (!strcmp(bucket->pair.key, key)) return 1; return 0; } void *htable_fetch(htable ht, const char *key) { hbucket bucket; unsigned long hash; hash = ht->hfunc(key, ht->size); for (bucket = ht->table[hash]; bucket != NULL; bucket = bucket->next) if (!strcmp(bucket->pair.key, key)) return bucket->pair.data; return NULL; } int htable_remove(htable ht, const char *key) { hbucket bucket, last = NULL; unsigned long hash; hash = ht->hfunc(key, ht->size); for (bucket = ht->table[hash]; bucket != NULL; bucket = bucket->next) { if (!strcmp(bucket->pair.key, key)) { if (bucket == ht->table[hash]) ht->table[hash] = bucket->next; else last->next = bucket->next; free_bucket(bucket); return 0; } last = bucket; } return -1; } void htable_dump(htable ht, FILE *fout) { hbucket bucket; unsigned int i; int nelements = 0, dups = 0; fprintf(fout, "Hash table dump:\n"); for (i = 0; i < ht->size; ++i) { bucket = ht->table[i]; if (bucket != NULL) { fprintf(fout, "table[%d] = {\n", i); for (; bucket != NULL; bucket = bucket->next) { ++nelements; if (bucket != ht->table[i]) ++dups; fprintf(fout, "\ttable[\"%s\"] = %p\n", bucket->pair.key, bucket->pair.data); } fprintf(fout, "}\n"); } } fprintf(fout, "Table size: %ld\n", ht->size); fprintf(fout, "Number of elements: %d\n", nelements); fprintf(fout, "Duplicated hash values: %d\n", dups); fprintf(fout, "Performance: %.2f%%\n", (1-(float)dups/nelements)*100); } alist htable_list(htable ht) { alist al; hbucket bucket; unsigned int i; al = alist_new(); for (i = 0; i < ht->size; ++i) if ((bucket = ht->table[i]) != NULL) for (; bucket != NULL; bucket = bucket->next) alist_append(al, &bucket->pair); return al; } #ifdef TEST int main(void) { htable ht = htable_new(); assert(htable_store_key(ht, "hello") == 0); assert(htable_store_key(ht, "world") == 0); assert(htable_store_key(ht, "bar") == 0); assert(htable_store_key(ht, "hello") != 0); assert(htable_exists(ht, "hello")); assert(htable_exists(ht, "world")); assert(htable_remove(ht, "world") == 0); assert(!htable_exists(ht, "world")); assert(htable_store_key(ht, "world") == 0); assert(htable_store_data(ht, "hello", "hello data") == 0); assert(htable_store_data(ht, "baz", "baz data") != 0); assert(!htable_exists(ht, "baz")); assert(htable_store(ht, "foo", "foo data") == 0); assert(htable_store_key(ht, "var1") == 0); assert(htable_store_key(ht, "var2") == 0); assert(htable_fetch(ht, "foo") != NULL && !strcmp(htable_fetch(ht, "foo"), "foo data")); assert(htable_fetch(ht, "hello") != NULL && !strcmp(htable_fetch(ht, "hello"), "hello data")); htable_dump(ht, stdout); htable_delete(ht); printf("htable test successful.\n"); return 0; } #endif /* TEST */