|
|
1.1 ! root 1: /* $Id: hash.c,v 1.2 2003/09/01 14:24:08 fredette Exp $ */ ! 2: ! 3: /* libtme/hash.c - hash table support: */ ! 4: ! 5: /* ! 6: * Copyright (c) 2003 Matt Fredette ! 7: * All rights reserved. ! 8: * ! 9: * Redistribution and use in source and binary forms, with or without ! 10: * modification, are permitted provided that the following conditions ! 11: * are met: ! 12: * 1. Redistributions of source code must retain the above copyright ! 13: * notice, this list of conditions and the following disclaimer. ! 14: * 2. Redistributions in binary form must reproduce the above copyright ! 15: * notice, this list of conditions and the following disclaimer in the ! 16: * documentation and/or other materials provided with the distribution. ! 17: * 3. All advertising materials mentioning features or use of this software ! 18: * must display the following acknowledgement: ! 19: * This product includes software developed by Matt Fredette. ! 20: * 4. The name of the author may not be used to endorse or promote products ! 21: * derived from this software without specific prior written permission. ! 22: * ! 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ! 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ! 25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ! 26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, ! 27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ! 28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ! 29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ! 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ! 31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ! 32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ! 33: * POSSIBILITY OF SUCH DAMAGE. ! 34: */ ! 35: ! 36: #include <tme/common.h> ! 37: _TME_RCSID("$Id: hash.c,v 1.2 2003/09/01 14:24:08 fredette Exp $"); ! 38: ! 39: /* includes: */ ! 40: #include <tme/hash.h> ! 41: ! 42: /* our hash table size array: */ ! 43: static unsigned long _tme_hash_sizes[] = { ! 44: 2, ! 45: 3, ! 46: 5, ! 47: 7, ! 48: 11, ! 49: 17, ! 50: 37, ! 51: 83, ! 52: 281, ! 53: 421, ! 54: 631, ! 55: 947, ! 56: 2131, ! 57: 7193, ! 58: 10789, ! 59: 16183, ! 60: 81929, ! 61: 414763, ! 62: 933217, ! 63: 10629917, ! 64: 35875969, ! 65: 80720929, ! 66: }; ! 67: ! 68: /* this allocates and returns a new hash: */ ! 69: tme_hash_t ! 70: tme_hash_new(tme_hash_func_t hash_func, ! 71: tme_compare_func_t compare_func, ! 72: tme_hash_data_t value_null) ! 73: { ! 74: tme_hash_t hash; ! 75: ! 76: hash = tme_new0(struct _tme_hash, 1); ! 77: hash->_tme_hash_size = _tme_hash_sizes[0]; ! 78: hash->_tme_hash_table = ! 79: tme_new0(struct _tme_hash_bucket *, ! 80: hash->_tme_hash_size); ! 81: hash->_tme_hash_count = 0; ! 82: hash->_tme_hash_hash = hash_func; ! 83: hash->_tme_hash_compare = compare_func; ! 84: hash->_tme_hash_null = value_null; ! 85: return (hash); ! 86: } ! 87: ! 88: /* this destroys a hash: */ ! 89: void ! 90: tme_hash_destroy(tme_hash_t hash) ! 91: { ! 92: struct _tme_hash_bucket *bucket, *bucket_next; ! 93: unsigned long bucket_i; ! 94: ! 95: /* free all of the buckets in the hash table: */ ! 96: for (bucket_i = 0; ! 97: bucket_i < hash->_tme_hash_size; ! 98: bucket_i++) { ! 99: for (bucket = hash->_tme_hash_table[bucket_i]; ! 100: bucket != NULL; ! 101: bucket = bucket_next) { ! 102: bucket_next = bucket->_tme_hash_bucket_next; ! 103: tme_free(bucket); ! 104: } ! 105: } ! 106: tme_free(hash->_tme_hash_table); ! 107: tme_free(hash); ! 108: } ! 109: ! 110: /* this does an internal lookup in a hash table: */ ! 111: static struct _tme_hash_bucket * ! 112: _tme_hash_lookup_internal(tme_hash_t hash, ! 113: tme_hash_data_t key, ! 114: struct _tme_hash_bucket ***__bucket) ! 115: { ! 116: unsigned long bucket_i; ! 117: struct _tme_hash_bucket **_bucket, *bucket; ! 118: ! 119: /* hash the key: */ ! 120: bucket_i = (*hash->_tme_hash_hash)(key) % hash->_tme_hash_size; ! 121: ! 122: /* walk the chain of buckets: */ ! 123: for (_bucket = hash->_tme_hash_table + bucket_i; ! 124: (bucket = *_bucket) != NULL; ! 125: _bucket = &bucket->_tme_hash_bucket_next) { ! 126: ! 127: /* compare the key in this bucket with the lookup key. ! 128: if it succeeds, return the bucket: */ ! 129: if ((*hash->_tme_hash_compare)(key, bucket->_tme_hash_bucket_key)) { ! 130: if (__bucket != NULL) { ! 131: *__bucket = _bucket; ! 132: } ! 133: return (bucket); ! 134: } ! 135: } ! 136: ! 137: /* the lookup failed. return where the bucket might be inserted: */ ! 138: if (__bucket != NULL) { ! 139: *__bucket = _bucket; ! 140: } ! 141: return (NULL); ! 142: } ! 143: ! 144: /* this inserts a value into a hash table: */ ! 145: void ! 146: tme_hash_insert(tme_hash_t hash, ! 147: tme_hash_data_t key, ! 148: tme_hash_data_t value) ! 149: { ! 150: struct _tme_hash_bucket *bucket, *bucket_next, **_bucket; ! 151: struct _tme_hash hash_new; ! 152: int size_i; ! 153: unsigned long bucket_i; ! 154: ! 155: /* if this key is not already present in the hash table: */ ! 156: bucket = _tme_hash_lookup_internal(hash, key, &_bucket); ! 157: if (bucket == NULL) { ! 158: ! 159: /* if we need to resize this hash table: */ ! 160: if ((hash->_tme_hash_count * 2) > hash->_tme_hash_size) { ! 161: ! 162: /* make a copy of the top of the hash: */ ! 163: hash_new = *hash; ! 164: ! 165: /* set the new size of the hash: */ ! 166: hash_new._tme_hash_size = hash->_tme_hash_count * 2; ! 167: for (size_i = 0; ! 168: _tme_hash_sizes[size_i] < hash_new._tme_hash_size; ! 169: size_i++) { ! 170: if (size_i + 1 == TME_ARRAY_ELS(_tme_hash_sizes)) { ! 171: abort(); ! 172: } ! 173: } ! 174: hash_new._tme_hash_size = _tme_hash_sizes[size_i]; ! 175: ! 176: /* allocate the new hash table: */ ! 177: hash_new._tme_hash_table = ! 178: tme_new0(struct _tme_hash_bucket *, ! 179: hash_new._tme_hash_size); ! 180: ! 181: /* move everything from the old hash table into the new: */ ! 182: for (bucket_i = 0; ! 183: bucket_i < hash->_tme_hash_size; ! 184: bucket_i++) { ! 185: for (bucket = hash->_tme_hash_table[bucket_i]; ! 186: bucket != NULL; ! 187: bucket = bucket_next) { ! 188: bucket_next = bucket->_tme_hash_bucket_next; ! 189: _tme_hash_lookup_internal(&hash_new, ! 190: bucket->_tme_hash_bucket_key, ! 191: &_bucket); ! 192: bucket->_tme_hash_bucket_next = *_bucket; ! 193: *_bucket = bucket; ! 194: } ! 195: } ! 196: ! 197: /* free the old hash table: */ ! 198: tme_free(hash->_tme_hash_table); ! 199: ! 200: /* set the new top of the hash: */ ! 201: *hash = hash_new; ! 202: ! 203: /* do the internal lookup again: */ ! 204: _tme_hash_lookup_internal(hash, key, &_bucket); ! 205: } ! 206: ! 207: /* create the new bucket and link it in: */ ! 208: bucket = tme_new(struct _tme_hash_bucket, 1); ! 209: bucket->_tme_hash_bucket_next = *_bucket; ! 210: *_bucket = bucket; ! 211: ! 212: /* increment the number of keys in the hash: */ ! 213: hash->_tme_hash_count++; ! 214: } ! 215: ! 216: /* set the key and value in the bucket: */ ! 217: bucket->_tme_hash_bucket_key = key; ! 218: bucket->_tme_hash_bucket_value = value; ! 219: } ! 220: ! 221: /* this looks up a key in the hash: */ ! 222: tme_hash_data_t ! 223: tme_hash_lookup(tme_hash_t hash, ! 224: tme_hash_data_t key) ! 225: { ! 226: struct _tme_hash_bucket *bucket; ! 227: ! 228: bucket = _tme_hash_lookup_internal(hash, key, NULL); ! 229: return (bucket != NULL ! 230: ? bucket->_tme_hash_bucket_value ! 231: : hash->_tme_hash_null); ! 232: } ! 233: ! 234: /* this removes a key in the hash: */ ! 235: void ! 236: tme_hash_remove(tme_hash_t hash, ! 237: tme_hash_data_t key) ! 238: { ! 239: struct _tme_hash_bucket *bucket, **_bucket; ! 240: ! 241: bucket = _tme_hash_lookup_internal(hash, key, &_bucket); ! 242: if (bucket != NULL) { ! 243: *_bucket = bucket->_tme_hash_bucket_next; ! 244: tme_free(bucket); ! 245: hash->_tme_hash_count--; ! 246: } ! 247: } ! 248: ! 249: /* this calls a function for each key and value in the hash: */ ! 250: void ! 251: tme_hash_foreach(tme_hash_t hash, ! 252: tme_foreach_func_t func, ! 253: void *private) ! 254: { ! 255: struct _tme_hash_bucket *bucket; ! 256: unsigned long bucket_i; ! 257: ! 258: /* walk all of the buckets in the hash table: */ ! 259: for (bucket_i = 0; ! 260: bucket_i < hash->_tme_hash_size; ! 261: bucket_i++) { ! 262: for (bucket = hash->_tme_hash_table[bucket_i]; ! 263: bucket != NULL; ! 264: bucket = bucket->_tme_hash_bucket_next) { ! 265: (*func)(bucket->_tme_hash_bucket_key, ! 266: bucket->_tme_hash_bucket_value, ! 267: private); ! 268: } ! 269: } ! 270: } ! 271: ! 272: /* this calls a function for each key and value in the hash. ! 273: if the function returns TRUE the entry is removed: */ ! 274: unsigned long ! 275: tme_hash_foreach_remove(tme_hash_t hash, ! 276: tme_foreach_remove_func_t func, ! 277: void *private) ! 278: { ! 279: struct _tme_hash_bucket **_bucket, *bucket; ! 280: unsigned long bucket_i, count; ! 281: ! 282: /* walk all of the buckets in the hash table: */ ! 283: count = 0; ! 284: for (bucket_i = 0; ! 285: bucket_i < hash->_tme_hash_size; ! 286: bucket_i++) { ! 287: for (_bucket = &hash->_tme_hash_table[bucket_i]; ! 288: (bucket = *_bucket) != NULL; ) { ! 289: if ((*func)(bucket->_tme_hash_bucket_key, ! 290: bucket->_tme_hash_bucket_value, ! 291: private)) { ! 292: *_bucket = bucket->_tme_hash_bucket_next; ! 293: tme_free(bucket); ! 294: hash->_tme_hash_count--; ! 295: count++; ! 296: } ! 297: else { ! 298: _bucket = &bucket->_tme_hash_bucket_next; ! 299: } ! 300: } ! 301: } ! 302: return (count); ! 303: } ! 304: ! 305: /* this hashes a direct value: */ ! 306: unsigned long ! 307: tme_direct_hash(tme_hash_data_t key) ! 308: { ! 309: return ((unsigned long) key); ! 310: } ! 311: ! 312: /* this compares two direct values: */ ! 313: int ! 314: tme_direct_compare(tme_hash_data_t key0, ! 315: tme_hash_data_t key1) ! 316: { ! 317: return (key0 == key1); ! 318: } ! 319: ! 320: /* this hashes a string value: */ ! 321: unsigned long ! 322: tme_string_hash(tme_hash_data_t key) ! 323: { ! 324: /* this is cribbed from the Dragon book: */ ! 325: const char *p; ! 326: char c; ! 327: unsigned long h, g; ! 328: ! 329: p = (const char *) key; ! 330: h = 0; ! 331: ! 332: for (; (c = *(p++)) != '\0'; ) { ! 333: h = (h << 4) + c; ! 334: g = (h & 0xf0000000); ! 335: if (g) { ! 336: h = h ^ (g >> 24); ! 337: h = h ^ g; ! 338: } ! 339: } ! 340: ! 341: return (h); ! 342: } ! 343: ! 344: /* this compares two string values: */ ! 345: int ! 346: tme_string_compare(tme_hash_data_t key0, ! 347: tme_hash_data_t key1) ! 348: { ! 349: return (!strcmp((const char *) key0, (const char *) key1)); ! 350: } ! 351:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.