/* * A Z-Machine * Copyright (C) 2000 Andrew Hunter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ /* * Hash table */ #include #include #include #include "hash.h" /* #define DEBUG */ /* Power of 2 */ #define NUM_BUCKETS 8 /* Number of collisions before a hash becomes 'unhappy' */ #define UNHAPPY_THRESHOLD 4 struct bucket { char *key; int keylen; void *data; int count; struct bucket *next; }; hash hash_create(void) { struct hash *hash; int x; hash = malloc(sizeof(struct hash)); hash->n_buckets = NUM_BUCKETS; hash->bucket = malloc(sizeof(struct bucket *)*NUM_BUCKETS); hash->unhappy = 0; for (x=0; xbucket[x] = NULL; } #ifdef DEBUG printf_debug("*** Hash - created new hash (%i elements)\n", hash->n_buckets); #endif return hash; } /* Blantantly nicked from the comp.compression FAQ... */ static unsigned long crc32_table[256] = { 0, 0, 0, 0 }; #define CRC32_POLY 0x04c11db7 /* AUTODIN II, Ethernet, & FDDI */ static void init_crc32() { int i, j; unsigned long c; for (i = 0; i < 256; ++i) { for (c = i << 24, j = 8; j > 0; --j) c = c & 0x80000000 ? (c << 1) ^ CRC32_POLY : (c << 1); crc32_table[i] = c & 0xffffffff; } } unsigned long hash_hash(unsigned char *buf, int len) { unsigned char *p; unsigned long crc; if (!crc32_table[1]) /* if not already done, */ init_crc32(); /* build table */ crc = 0xffffffff; /* preload shift register, per CRC-32 spec */ for (p = buf; len > 0; ++p, --len) crc = ((crc << 8) ^ crc32_table[(crc >> 24) ^ *p]) & 0xffffffff; return ~crc & 0xffffffff; /* transmit complement, per CRC-32 spec */ } static struct bucket *hash_lookup(hash hash, unsigned char* key, int keylen, unsigned long value) { struct bucket *next; struct bucket *match; next = hash->bucket[value]; match = NULL; while (next != NULL && match == NULL) { if (next->keylen == keylen) { if (memcmp(next->key, key, keylen) == 0) match = next; } next = next->next; } return match; } void hash_store_happy(hash hash, unsigned char *key, int keylen, void *data) { hash_store(hash, key, keylen, data); if (hash->unhappy) { int new_size; new_size = hash->n_buckets*2; if (new_size<64) new_size *= 2; #ifdef DEBUG printf_debug("*** Hash - unhappy hash, resizing to %i\n", new_size); #endif hash_resize(hash, new_size); } } void hash_store(hash hash, unsigned char *key, int len, void *data) { unsigned long value; struct bucket *bucket; value = hash_hash(key, len)&(hash->n_buckets-1); bucket = hash_lookup(hash, key, len, value); if (bucket == NULL) { bucket = malloc(sizeof(struct bucket)); #ifdef DEBUG printf_debug("*** Hash - storing new value in bucket 0x%x\n", value); #endif bucket->key = malloc(len+1); bucket->keylen = len; memcpy(bucket->key, key, len); bucket->next = hash->bucket[value]; hash->bucket[value] = bucket; if (bucket->next != NULL) { bucket->count = bucket->next->count + 1; if (bucket->count > UNHAPPY_THRESHOLD) hash->unhappy = 1; } else bucket->count = 0; } #ifdef DEBUG else { printf_debug("*** Hash - replacing value in bucket 0x%x\n", key, value); } #endif bucket->data = data; } void hash_free(hash hash) { int x; for (x=0; xn_buckets; x++) { struct bucket *next; next = hash->bucket[x]; while (next != NULL) { struct bucket *last; last = next; next = next->next; free(last); } } free(hash); } void hash_iterate(hash hash, int (*func)(unsigned char *key, int keylen, void *data, void *arg), void *arg) { int x; int res; res = 0; for (x=0; xn_buckets && res == 0; x++) { struct bucket *next; next = hash->bucket[x]; while (next != NULL && res == 0) { res = (func)(next->key, next->keylen, next->data, arg); next = next->next; } } } void *hash_get(hash hash, unsigned char* key, int len) { unsigned long value; struct bucket *bucket; value = hash_hash(key, len)&(hash->n_buckets-1); bucket = hash_lookup(hash, key, len, value); if (bucket != NULL) return bucket->data; return NULL; } static int resize_func(unsigned char *key, int keylen, void *data, void *new_hash) { if (data != NULL) hash_store((hash) new_hash, key, keylen, data); return 0; } void hash_resize(hash hsh, int n_buckets) { hash new_hash; if (n_buckets > 32768) { /* We'll max out here for the time being */ return; } new_hash = hash_create(); #ifdef DEBUG printf_debug("*** Hash - resizing to %i\n", n_buckets); #endif free(new_hash->bucket); *new_hash = *hsh; hsh->n_buckets = n_buckets; hsh->bucket = calloc(n_buckets, sizeof(struct bucket *)); hsh->unhappy = 0; hash_iterate(new_hash, resize_func, hsh); hash_free(new_hash); }