hash.c revision f14f4646
1/* 2 * Copyright (c) 2007,2008 Paulo Cesar Pereira de Andrade 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 * 23 * Author: Paulo Cesar Pereira de Andrade 24 */ 25 26#include "util.h" 27#include <stdlib.h> 28#include <string.h> 29 30/* 31 * This is a very simplified and adapted version of the hash tables I am 32 * using in a personal project. It was added to try to have a single hash 33 * table implementation in xedit. The lisp (for user code) version was not 34 * converted, but the following hastables have been converted: 35 * ispell.c - list of replace and ignore words 36 * hook.c - list of auto replace words 37 * internal lisp data structures: 38 * atoms 39 * strings 40 * packages 41 * opaque types 42 * also, all code traversing hash tables is now using 43 * hash_iter_first() and hash_iter_next() 44 * conversion isn't as good as I originally wanted, code is using hash_check 45 * instead of hash_get, but this is due to the code not having a basic 46 * { void *data; int length; } object to store string like objects 47 * 48 * Also, this hash table implementation was added mainly for the tags 49 * support. 50 */ 51 52/* 53 * Prototypes 54 */ 55static int hash_equal(hash_table *hash, hash_key *left, hash_key *right); 56static unsigned int hash_data(char *value, unsigned int length); 57static unsigned int hash_value(hash_key *key); 58 59 60/* 61 * Implementation 62 */ 63static int 64hash_equal(hash_table *hash, hash_key *left, hash_key *right) 65{ 66 if (left->length == right->length) { 67 if (left == right) 68 return (1); 69 if (hash->compare) 70 return (hash->compare(left, right)); 71 return (memcmp(left->value, right->value, left->length) == 0); 72 } 73 74 return (0); 75} 76 77static unsigned int 78hash_data(char *value, unsigned int length) 79{ 80 char *ptr; 81 unsigned int i, key; 82 83 for (i = key = 0, ptr = value; i < length; i++) 84 key = (key << (key & 1)) ^ ptr[i]; 85 86 return (key); 87} 88 89static unsigned int 90hash_value(hash_key *key) 91{ 92 return (hash_data(key->value, key->length)); 93} 94 95hash_table * 96hash_new(unsigned int length, hash_compare compare) 97{ 98 hash_table *hash; 99 100 hash = calloc(1, sizeof(hash_table)); 101 if (hash) { 102 hash->entries = calloc(length, sizeof(hash_entry *)); 103 if (hash->entries) { 104 hash->length = length; 105 hash->compare = compare; 106 hash->iter.offset = -1; 107 } 108 else { 109 free(hash); 110 hash = (hash_table *)0; 111 } 112 } 113 114 return (hash); 115} 116 117hash_entry * 118hash_put(hash_table *hash, hash_entry *entry) 119{ 120 unsigned int key; 121 hash_entry *prev, *ptr; 122 123 /* Offset in hash table vector for this entry */ 124 key = hash_value(entry->key) % hash->length; 125 126 /* We hope this is nil for most calls */ 127 ptr = hash->entries[key]; 128 129 /* Check if clashed with another entry */ 130 for (prev = ptr; ptr; prev = ptr, ptr = ptr->next) { 131 /* Replace current entry */ 132 if (hash_equal(hash, entry->key, ptr->key)) { 133 /* If not trying to readd same value */ 134 if (entry != ptr) { 135 if (ptr == prev) 136 hash->entries[key] = entry; 137 else 138 prev->next = entry; 139 entry->next = ptr->next; 140 /* Finished */ 141 } 142 else 143 ptr = (hash_entry *)0; 144 goto hash_put_done; 145 } 146 } 147 148 /* Add new entry */ 149 if (prev == (hash_entry *)0) 150 /* If no entry in offset */ 151 hash->entries[key] = entry; 152 else 153 /* Add to end of clashing list */ 154 prev->next = entry; 155 entry->next = (hash_entry *)0; 156 157 /* Increase sum of entries counter*/ 158 ++hash->count; 159 160hash_put_done: 161 /* ptr will be nil if no entry was replaced, of tried to add 162 * again an entry already in the hash table */ 163 return (ptr); 164} 165 166hash_entry * 167hash_get(hash_table *hash, hash_key *name) 168{ 169 unsigned int key; 170 hash_entry *entry; 171 172 key = hash_value(name) % hash->length; 173 for (entry = hash->entries[key]; entry; entry = entry->next) { 174 if (hash_equal(hash, name, entry->key)) { 175 176 return (entry); 177 } 178 } 179 180 return ((hash_entry *)0); 181} 182 183hash_entry * 184hash_check(hash_table *hash, char *name, unsigned int length) 185{ 186 unsigned int key; 187 hash_entry *entry; 188 189 key = hash_data(name, length) % hash->length; 190 for (entry = hash->entries[key]; entry; entry = entry->next) { 191 if (length == entry->key->length && 192 memcmp(name, entry->key->value, length) == 0) { 193 194 return (entry); 195 } 196 } 197 198 return ((hash_entry *)0); 199} 200 201hash_entry * 202hash_rem_no_free(hash_table *hash, hash_entry *entry) 203{ 204 unsigned int key; 205 hash_entry *ptr, *prev; 206 207 key = hash_value(entry->key) % hash->length; 208 for (ptr = prev = hash->entries[key]; ptr; prev = ptr, ptr = ptr->next) { 209 if (ptr == entry) { 210 --hash->count; 211 if (ptr == prev) 212 hash->entries[key] = ptr->next; 213 else 214 prev->next = ptr->next; 215 break; 216 } 217 } 218 219 if (ptr && ptr == hash->iter.entry) 220 hash->iter.entry = ptr->next; 221 222 /* If entry wasn't in hash table ptr will be nil */ 223 return (ptr); 224} 225 226void 227hash_rem(hash_table *hash, hash_entry *entry) 228{ 229 entry = hash_rem_no_free(hash, entry); 230 if (entry) { 231 free(entry->key->value); 232 free(entry->key); 233 free(entry); 234 } 235} 236 237void 238hash_rehash(hash_table *hash, unsigned int length) 239{ 240 unsigned int i, key; 241 hash_entry *entry, *next, **entries; 242 243 entries = (hash_entry **)calloc(length, sizeof(hash_entry *)); 244 if (entries) { 245 /* Populate the new table, note that clashes are now in reverse order */ 246 for (i = 0; i < hash->length; i++) { 247 for (entry = hash->entries[i]; entry; entry = next) { 248 next = entry->next; 249 key = hash_value(entry->key) % length; 250 entry->next = entries[key]; 251 entries[key] = entry; 252 } 253 } 254 255 /* Finish updating hash table */ 256 free(hash->entries); 257 hash->entries = entries; 258 hash->length = length; 259 } 260 hash->iter.offset = -1; 261} 262 263hash_entry * 264hash_iter_first(hash_table *hash) 265{ 266 hash->iter.offset = 0; 267 hash->iter.entry = (hash_entry *)0; 268 269 return (hash_iter_next(hash)); 270} 271 272hash_entry * 273hash_iter_next(hash_table *hash) 274{ 275 if (hash->iter.offset >= 0) { 276 if (hash->iter.entry) { 277 if ((hash->iter.entry = hash->iter.entry->next)) 278 return (hash->iter.entry); 279 ++hash->iter.offset; 280 } 281 for (; hash->iter.offset < hash->length; hash->iter.offset++) { 282 if ((hash->iter.entry = hash->entries[hash->iter.offset])) 283 return (hash->iter.entry); 284 } 285 hash->iter.entry = (hash_entry *)0; 286 hash->iter.offset = -1; 287 } 288 289 return ((hash_entry *)0); 290} 291 292void 293hash_clr(hash_table *hash) 294{ 295 unsigned int i; 296 hash_entry *entry, *next; 297 298 /* Extra data should be free'd with the iterator */ 299 for (i = 0; i < hash->length; i++) { 300 entry = hash->entries[i]; 301 if (entry) { 302 for (next = entry; entry; entry = next) { 303 next = entry->next; 304 free(entry->key->value); 305 free(entry->key); 306 free(entry); 307 } 308 hash->entries[i] = (hash_entry *)0; 309 } 310 } 311 312 hash->count = 0; 313 hash->iter.offset = -1; 314} 315 316void 317hash_del(hash_table *hash) 318{ 319 hash_clr(hash); 320 free(hash->entries); 321 free(hash); 322} 323