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
ne_hash_init(NE_HASH ** hash,NE_HASH_FUNC hash_func,NE_COMP_FUNC comp_func)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
ne_hash_destroy(NE_HASH ** hash)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
ne_hash_insert(NE_HASH * hash,void * key,void * value)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
ne_hash_lookup(NE_HASH * hash,void * key)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
ne_hash_remove(NE_HASH * hash,void * key)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
ne_hash_has_key(NE_HASH * hash,void * key)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
ne_hash_next(NE_HASH * hash,void ** key)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
_hash_lookup_node(NE_HASH * hash,void * key,UINT32 * o_hashv)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... */
_hash_resize(NE_HASH * hash)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
ne_hash_str_comp(const void * a,const void * b)274 int ne_hash_str_comp(const void *a, const void *b)
275 {
276 return !strcmp((const char *)a, (const char *)b);
277 }
278
ne_hash_str_hash(const void * a)279 UINT32 ne_hash_str_hash(const void *a)
280 {
281 return ne_crc((unsigned char *)a, strlen((const char *)a));
282 }
283
ne_hash_int_comp(const void * a,const void * b)284 int ne_hash_int_comp(const void *a, const void *b)
285 {
286 if (a == b) return 1;
287 return 0;
288 }
289
ne_hash_int_hash(const void * a)290 UINT32 ne_hash_int_hash(const void *a)
291 {
292 return (UINT32)(long)(a);
293 }
294