1 /* 2 * Copyright 2003-2004 Brandon Long 3 * All Rights Reserved. 4 * 5 * ClearSilver Templating System 6 * 7 * This code is made available under the terms of the ClearSilver License. 8 * http://www.clearsilver.net/license.hdf 9 * 10 */ 11 12 #include "cs_config.h" 13 14 #include <stdlib.h> 15 #include <string.h> 16 17 #include "neo_misc.h" 18 #include "neo_err.h" 19 #include "neo_hash.h" 20 21 static NEOERR *_hash_resize(NE_HASH *hash); 22 static NE_HASHNODE **_hash_lookup_node (NE_HASH *hash, void *key, UINT32 *hashv); 23 24 NEOERR *ne_hash_init (NE_HASH **hash, NE_HASH_FUNC hash_func, NE_COMP_FUNC comp_func) 25 { 26 NE_HASH *my_hash = NULL; 27 28 my_hash = (NE_HASH *) calloc(1, sizeof(NE_HASH)); 29 if (my_hash == NULL) 30 return nerr_raise(NERR_NOMEM, "Unable to allocate memory for NE_HASH"); 31 32 my_hash->size = 256; 33 my_hash->num = 0; 34 my_hash->hash_func = hash_func; 35 my_hash->comp_func = comp_func; 36 37 my_hash->nodes = (NE_HASHNODE **) calloc (my_hash->size, sizeof(NE_HASHNODE *)); 38 if (my_hash->nodes == NULL) 39 { 40 free(my_hash); 41 return nerr_raise(NERR_NOMEM, "Unable to allocate memory for NE_HASHNODES"); 42 } 43 44 *hash = my_hash; 45 46 return STATUS_OK; 47 } 48 49 void ne_hash_destroy (NE_HASH **hash) 50 { 51 NE_HASH *my_hash; 52 NE_HASHNODE *node, *next; 53 int x; 54 55 if (hash == NULL || *hash == NULL) 56 return; 57 58 my_hash = *hash; 59 60 for (x = 0; x < my_hash->size; x++) 61 { 62 node = my_hash->nodes[x]; 63 while (node) 64 { 65 next = node->next; 66 free(node); 67 node = next; 68 } 69 } 70 free(my_hash->nodes); 71 my_hash->nodes = NULL; 72 free(my_hash); 73 *hash = NULL; 74 } 75 76 NEOERR *ne_hash_insert(NE_HASH *hash, void *key, void *value) 77 { 78 UINT32 hashv; 79 NE_HASHNODE **node; 80 81 node = _hash_lookup_node(hash, key, &hashv); 82 83 if (*node) 84 { 85 (*node)->value = value; 86 } 87 else 88 { 89 *node = (NE_HASHNODE *) malloc(sizeof(NE_HASHNODE)); 90 if (node == NULL) 91 return nerr_raise(NERR_NOMEM, "Unable to allocate NE_HASHNODE"); 92 93 (*node)->hashv = hashv; 94 (*node)->key = key; 95 (*node)->value = value; 96 (*node)->next = NULL; 97 } 98 hash->num++; 99 100 return _hash_resize(hash); 101 } 102 103 void *ne_hash_lookup(NE_HASH *hash, void *key) 104 { 105 NE_HASHNODE *node; 106 107 node = *_hash_lookup_node(hash, key, NULL); 108 109 return (node) ? node->value : NULL; 110 } 111 112 void *ne_hash_remove(NE_HASH *hash, void *key) 113 { 114 NE_HASHNODE **node, *remove; 115 void *value = NULL; 116 117 node = _hash_lookup_node(hash, key, NULL); 118 if (*node) 119 { 120 remove = *node; 121 *node = remove->next; 122 value = remove->value; 123 free(remove); 124 hash->num--; 125 } 126 return value; 127 } 128 129 int ne_hash_has_key(NE_HASH *hash, void *key) 130 { 131 NE_HASHNODE *node; 132 133 node = *_hash_lookup_node(hash, key, NULL); 134 135 if (node) return 1; 136 return 0; 137 } 138 139 void *ne_hash_next(NE_HASH *hash, void **key) 140 { 141 NE_HASHNODE **node = 0; 142 UINT32 hashv, bucket; 143 144 if (*key) 145 { 146 node = _hash_lookup_node(hash, key, NULL); 147 148 if (*node) 149 { 150 bucket = (*node)->hashv & (hash->size - 1); 151 } 152 else 153 { 154 hashv = hash->hash_func(*key); 155 bucket = hashv & (hash->size - 1); 156 } 157 } 158 else 159 { 160 bucket = 0; 161 } 162 163 if (*node) 164 { 165 if ((*node)->next) 166 { 167 *key = (*node)->next->key; 168 return (*node)->next->value; 169 } 170 bucket++; 171 } 172 173 while (bucket < hash->size) 174 { 175 if (hash->nodes[bucket]) 176 { 177 *key = hash->nodes[bucket]->key; 178 return hash->nodes[bucket]->value; 179 } 180 bucket++; 181 } 182 183 return NULL; 184 } 185 186 static NE_HASHNODE **_hash_lookup_node (NE_HASH *hash, void *key, UINT32 *o_hashv) 187 { 188 UINT32 hashv, bucket; 189 NE_HASHNODE **node; 190 191 hashv = hash->hash_func(key); 192 if (o_hashv) *o_hashv = hashv; 193 bucket = hashv & (hash->size - 1); 194 /* ne_warn("Lookup %s %d %d", key, hashv, bucket); */ 195 196 node = &(hash->nodes[bucket]); 197 198 if (hash->comp_func) 199 { 200 while (*node && !(hash->comp_func((*node)->key, key))) 201 node = &(*node)->next; 202 } 203 else 204 { 205 /* No comp_func means we're doing pointer comparisons */ 206 while (*node && (*node)->key != key) 207 node = &(*node)->next; 208 } 209 210 /* ne_warn("Node %x", node); */ 211 return node; 212 } 213 214 /* Ok, we're doing some weirdness here... */ 215 static NEOERR *_hash_resize(NE_HASH *hash) 216 { 217 NE_HASHNODE **new_nodes; 218 NE_HASHNODE *entry, *prev; 219 int x, next_bucket; 220 int orig_size = hash->size; 221 UINT32 hash_mask; 222 223 if (hash->size > hash->num) 224 return STATUS_OK; 225 226 /* We always double in size */ 227 new_nodes = (NE_HASHNODE **) realloc (hash->nodes, (hash->size*2) * sizeof(NE_HASHNODE)); 228 if (new_nodes == NULL) 229 return nerr_raise(NERR_NOMEM, "Unable to allocate memory to resize NE_HASH"); 230 231 hash->nodes = new_nodes; 232 orig_size = hash->size; 233 hash->size = hash->size*2; 234 235 /* Initialize new parts */ 236 for (x = orig_size; x < hash->size; x++) 237 { 238 hash->nodes[x] = NULL; 239 } 240 241 hash_mask = hash->size - 1; 242 243 for (x = 0; x < orig_size; x++) 244 { 245 prev = NULL; 246 next_bucket = x + orig_size; 247 for (entry = hash->nodes[x]; 248 entry; 249 entry = prev ? prev->next : hash->nodes[x]) 250 { 251 if ((entry->hashv & hash_mask) != x) 252 { 253 if (prev) 254 { 255 prev->next = entry->next; 256 } 257 else 258 { 259 hash->nodes[x] = entry->next; 260 } 261 entry->next = hash->nodes[next_bucket]; 262 hash->nodes[next_bucket] = entry; 263 } 264 else 265 { 266 prev = entry; 267 } 268 } 269 } 270 271 return STATUS_OK; 272 } 273 274 int ne_hash_str_comp(const void *a, const void *b) 275 { 276 return !strcmp((const char *)a, (const char *)b); 277 } 278 279 UINT32 ne_hash_str_hash(const void *a) 280 { 281 return ne_crc((unsigned char *)a, strlen((const char *)a)); 282 } 283 284 int ne_hash_int_comp(const void *a, const void *b) 285 { 286 if (a == b) return 1; 287 return 0; 288 } 289 290 UINT32 ne_hash_int_hash(const void *a) 291 { 292 return (UINT32)(long)(a); 293 } 294