• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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