26 |
#include "hash.h" |
#include "hash.h" |
27 |
#include "m4private.h" |
#include "m4private.h" |
28 |
|
|
29 |
typedef struct m4_hash_node m4_hash_node; |
typedef struct hash_node hash_node; |
30 |
|
|
31 |
struct m4_hash |
struct m4_hash |
32 |
{ |
{ |
34 |
size_t length; /* number of elements inserted */ |
size_t length; /* number of elements inserted */ |
35 |
m4_hash_hash_func *hash_func; |
m4_hash_hash_func *hash_func; |
36 |
m4_hash_cmp_func *cmp_func; |
m4_hash_cmp_func *cmp_func; |
37 |
m4_hash_node **buckets; |
hash_node **buckets; |
38 |
}; |
}; |
39 |
|
|
40 |
struct m4_hash_node |
struct hash_node |
41 |
{ |
{ |
42 |
m4_hash_node *next; |
hash_node *next; |
43 |
const void *key; |
const void *key; |
44 |
void *value; |
void *value; |
45 |
}; |
}; |
46 |
|
|
47 |
|
|
48 |
|
|
49 |
#define M4_HASH_SIZE(hash) ((hash)->size) |
#define HASH_SIZE(hash) ((hash)->size) |
50 |
#define M4_HASH_LENGTH(hash) ((hash)->length) |
#define HASH_LENGTH(hash) ((hash)->length) |
51 |
#define M4_HASH_BUCKETS(hash) ((hash)->buckets) |
#define HASH_BUCKETS(hash) ((hash)->buckets) |
52 |
#define M4_HASH_HASH_FUNC(hash) ((hash)->hash_func) |
#define HASH_HASH_FUNC(hash) ((hash)->hash_func) |
53 |
#define M4_HASH_CMP_FUNC(hash) ((hash)->cmp_func) |
#define HASH_CMP_FUNC(hash) ((hash)->cmp_func) |
54 |
|
|
55 |
#define M4_HASH_NODE_NEXT(node) ((node)->next) |
#define NODE_NEXT(node) ((node)->next) |
56 |
#define M4_HASH_NODE_KEY(node) ((node)->key) |
#define NODE_KEY(node) ((node)->key) |
57 |
#define M4_HASH_NODE_VAL(node) ((node)->value) |
#define NODE_VALUE(node) ((node)->value) |
58 |
|
|
59 |
/* Helper macros. */ |
/* Helper macros. */ |
60 |
#define M4_HASH_BUCKET_NTH(hash, n) (M4_HASH_BUCKETS (hash)[n]) |
#define BUCKET_NTH(hash, n) (HASH_BUCKETS (hash)[n]) |
61 |
#define M4_HASH_BUCKET_NUM(hash, key) \ |
#define BUCKET_COUNT(hash, key) \ |
62 |
((*M4_HASH_HASH_FUNC(hash))(key) % M4_HASH_SIZE(hash)) |
((*HASH_HASH_FUNC(hash))(key) % HASH_SIZE(hash)) |
63 |
#define M4_HASH_BUCKET_KEY(hash, key) \ |
#define BUCKET_KEY(hash, key) \ |
64 |
(M4_HASH_BUCKET_NTH ((hash), M4_HASH_BUCKET_NUM ((hash), (key)))) |
(BUCKET_NTH ((hash), BUCKET_COUNT ((hash), (key)))) |
65 |
|
|
66 |
|
|
67 |
|
|
68 |
static void m4_hash_bucket_delete (m4_hash *hash, size_t i); |
static void bucket_insert (m4_hash *hash, hash_node *bucket); |
69 |
static void m4_hash_node_delete (m4_hash *hash, |
static void bucket_delete (m4_hash *hash, size_t i); |
70 |
m4_hash_node *node); |
static hash_node * node_new (const void *key, void *value); |
71 |
static m4_hash_node * m4_hash_node_new (const void *key, void *value); |
static void node_insert (m4_hash *hash, hash_node *node); |
72 |
static m4_hash_node * m4_hash_lookup_node (m4_hash *hash, |
static hash_node * node_lookup (m4_hash *hash, const void *key); |
73 |
const void *key); |
static void node_delete (m4_hash *hash, hash_node *node); |
74 |
static void m4_hash_maybe_grow (m4_hash *hash); |
static void maybe_grow (m4_hash *hash); |
|
static void m4_hash_bucket_insert (m4_hash *hash, |
|
|
m4_hash_node *bucket); |
|
|
static void m4_hash_node_insert (m4_hash *hash, |
|
|
m4_hash_node *node); |
|
75 |
|
|
76 |
|
|
77 |
|
|
78 |
static m4_hash_node *m4_hash_node_free_list = 0; |
static hash_node *free_list = 0; |
79 |
|
|
80 |
|
|
81 |
|
|
83 |
SIZE buckets, where HASH_FUNC will be used to generate bucket numbers |
SIZE buckets, where HASH_FUNC will be used to generate bucket numbers |
84 |
and CMP_FUNC will be called to compare keys. */ |
and CMP_FUNC will be called to compare keys. */ |
85 |
m4_hash * |
m4_hash * |
86 |
m4_hash_new (size_t size, m4_hash_hash_func *hash_func, m4_hash_cmp_func *cmp_func) |
m4_hash_new (size_t size, m4_hash_hash_func *hash_func, |
87 |
|
m4_hash_cmp_func *cmp_func) |
88 |
{ |
{ |
89 |
m4_hash *hash; |
m4_hash *hash; |
90 |
|
|
94 |
if (size == 0) |
if (size == 0) |
95 |
size = M4_HASH_DEFAULT_SIZE; |
size = M4_HASH_DEFAULT_SIZE; |
96 |
|
|
97 |
hash = XMALLOC (m4_hash, 1); |
hash = XMALLOC (m4_hash, 1); |
98 |
M4_HASH_SIZE (hash) = size; |
HASH_SIZE (hash) = size; |
99 |
M4_HASH_LENGTH (hash) = 0; |
HASH_LENGTH (hash) = 0; |
100 |
M4_HASH_BUCKETS (hash) = XCALLOC (m4_hash_node *, size); |
HASH_BUCKETS (hash) = XCALLOC (hash_node *, size); |
101 |
M4_HASH_HASH_FUNC (hash) = hash_func; |
HASH_HASH_FUNC (hash) = hash_func; |
102 |
M4_HASH_CMP_FUNC (hash) = cmp_func; |
HASH_CMP_FUNC (hash) = cmp_func; |
103 |
|
|
104 |
return hash; |
return hash; |
105 |
} |
} |
106 |
|
|
107 |
|
m4_hash * |
108 |
|
m4_hash_dup (m4_hash *src, m4_hash_copy_func *copy) |
109 |
|
{ |
110 |
|
m4_hash *dest; |
111 |
|
|
112 |
|
assert (src); |
113 |
|
assert (copy); |
114 |
|
|
115 |
|
dest = m4_hash_new (HASH_SIZE (src), HASH_HASH_FUNC (src), |
116 |
|
HASH_CMP_FUNC (src)); |
117 |
|
|
118 |
|
m4_hash_apply (src, (m4_hash_apply_func *) copy, dest); |
119 |
|
|
120 |
|
return dest; |
121 |
|
} |
122 |
|
|
123 |
/* Recycle each of the nodes in HASH onto the free list, and release |
/* Recycle each of the nodes in HASH onto the free list, and release |
124 |
the rest of the memory used by the table. Memory addressed by |
the rest of the memory used by the table. Memory addressed by |
125 |
the recycled nodes is _NOT_ freed: this needs to be done manually |
the recycled nodes is _NOT_ freed: this needs to be done manually |
131 |
|
|
132 |
assert (hash); |
assert (hash); |
133 |
|
|
134 |
for (i = 0; i < M4_HASH_SIZE (hash); ++i) |
for (i = 0; i < HASH_SIZE (hash); ++i) |
135 |
if (M4_HASH_BUCKET_NTH (hash, i)) |
if (BUCKET_NTH (hash, i)) |
136 |
m4_hash_bucket_delete (hash, i); |
bucket_delete (hash, i); |
137 |
XFREE (M4_HASH_BUCKETS (hash)); |
XFREE (HASH_BUCKETS (hash)); |
138 |
XFREE (hash); |
XFREE (hash); |
139 |
} |
} |
140 |
|
|
141 |
/* Check that the nodes in bucket I have been cleared, and recycle |
/* Check that the nodes in bucket I have been cleared, and recycle |
142 |
each of the nodes in the bucket to the free list. Bucket I must |
each of the nodes in the bucket to the free list. Bucket I must |
143 |
not be empty when this function is called. */ |
not be empty when this function is called. */ |
144 |
void |
static void |
145 |
m4_hash_bucket_delete (m4_hash *hash, size_t i) |
bucket_delete (m4_hash *hash, size_t i) |
146 |
{ |
{ |
147 |
m4_hash_node *node; |
hash_node *node; |
148 |
|
|
149 |
assert (hash); |
assert (hash); |
150 |
assert (M4_HASH_BUCKET_NTH (hash, i)); |
assert (BUCKET_NTH (hash, i)); |
151 |
assert (i < M4_HASH_SIZE (hash)); |
assert (i < HASH_SIZE (hash)); |
152 |
|
|
153 |
for (node = M4_HASH_BUCKET_NTH (hash, i); |
for (node = BUCKET_NTH (hash, i); node->next; node = NODE_NEXT (node)) |
|
node->next; |
|
|
node = M4_HASH_NODE_NEXT (node)) |
|
154 |
{ |
{ |
155 |
assert (M4_HASH_NODE_KEY(node) == 0); |
assert (NODE_KEY(node) == 0); |
156 |
--M4_HASH_LENGTH (hash); |
--HASH_LENGTH (hash); |
157 |
} |
} |
158 |
|
|
159 |
assert (M4_HASH_NODE_KEY(node) == 0); |
assert (NODE_KEY(node) == 0); |
160 |
--M4_HASH_LENGTH (hash); |
--HASH_LENGTH (hash); |
161 |
|
|
162 |
M4_HASH_NODE_NEXT (node) = m4_hash_node_free_list; |
NODE_NEXT (node) = free_list; |
163 |
m4_hash_node_free_list = M4_HASH_BUCKET_NTH (hash, i); |
free_list = BUCKET_NTH (hash, i); |
164 |
M4_HASH_BUCKET_NTH (hash, i) = 0; |
BUCKET_NTH (hash, i) = 0; |
165 |
} |
} |
166 |
|
|
167 |
/* Create and initialise a new node with KEY and VALUE, by reusing a |
/* Create and initialise a new node with KEY and VALUE, by reusing a |
168 |
node from the free list if possible. */ |
node from the free list if possible. */ |
169 |
m4_hash_node * |
static hash_node * |
170 |
m4_hash_node_new (const void *key, void *value) |
node_new (const void *key, void *value) |
171 |
{ |
{ |
172 |
m4_hash_node *node = 0; |
hash_node *node = 0; |
173 |
|
|
174 |
if (m4_hash_node_free_list) |
if (free_list) |
175 |
{ |
{ |
176 |
node = m4_hash_node_free_list; |
node = free_list; |
177 |
m4_hash_node_free_list = M4_HASH_NODE_NEXT (m4_hash_node_free_list); |
free_list = NODE_NEXT (free_list); |
178 |
} |
} |
179 |
else |
else |
180 |
{ |
{ |
181 |
node = XMALLOC (m4_hash_node, 1); |
node = XMALLOC (hash_node, 1); |
182 |
} |
} |
183 |
|
|
184 |
assert (node); |
assert (node); |
185 |
|
|
186 |
M4_HASH_NODE_NEXT (node)= 0; |
NODE_NEXT (node) = 0; |
187 |
M4_HASH_NODE_KEY (node) = key; |
NODE_KEY (node) = key; |
188 |
M4_HASH_NODE_VAL (node) = value; |
NODE_VALUE (node) = value; |
189 |
|
|
190 |
return node; |
return node; |
191 |
} |
} |
192 |
|
|
193 |
/* Check that NODE has been cleared, and recycle it to the free list. */ |
/* Check that NODE has been cleared, and recycle it to the free list. */ |
194 |
void |
static void |
195 |
m4_hash_node_delete (m4_hash *hash, m4_hash_node *node) |
node_delete (m4_hash *hash, hash_node *node) |
196 |
{ |
{ |
197 |
assert (node); |
assert (node); |
198 |
assert (M4_HASH_NODE_KEY(node) == 0); |
assert (NODE_KEY(node) == 0); |
199 |
|
|
200 |
M4_HASH_NODE_NEXT (node) = m4_hash_node_free_list; |
NODE_NEXT (node) = free_list; |
201 |
m4_hash_node_free_list = node; |
free_list = node; |
202 |
|
|
203 |
--M4_HASH_LENGTH (hash); |
--HASH_LENGTH (hash); |
204 |
} |
} |
205 |
|
|
206 |
/* Create a new entry in HASH with KEY and VALUE, making use of |
/* Create a new entry in HASH with KEY and VALUE, making use of |
207 |
nodes in the free list if possible, and potentially growing |
nodes in the free list if possible, and potentially growing |
208 |
the size of the table if node density is too high. */ |
the size of the table if node density is too high. */ |
209 |
void |
const void * |
210 |
m4_hash_insert (m4_hash *hash, const void *key, void *value) |
m4_hash_insert (m4_hash *hash, const void *key, void *value) |
211 |
{ |
{ |
212 |
m4_hash_node *node; |
hash_node *node; |
213 |
|
|
214 |
assert (hash); |
assert (hash); |
215 |
|
|
216 |
node = m4_hash_node_new (key, value); |
node = node_new (key, value); |
217 |
m4_hash_node_insert (hash, node); |
node_insert (hash, node); |
218 |
m4_hash_maybe_grow (hash); |
maybe_grow (hash); |
219 |
|
|
220 |
|
return key; |
221 |
} |
} |
222 |
|
|
223 |
/* Push the unconnected NODE on to the front of the appropriate |
/* Push the unconnected NODE on to the front of the appropriate |
224 |
bucket, effectively preventing retrieval of other nodes with |
bucket, effectively preventing retrieval of other nodes with |
225 |
the same key (where "sameness" is determined by HASH's |
the same key (where "sameness" is determined by HASH's |
226 |
cmp_func). */ |
cmp_func). */ |
227 |
void |
static void |
228 |
m4_hash_node_insert (m4_hash *hash, m4_hash_node *node) |
node_insert (m4_hash *hash, hash_node *node) |
229 |
{ |
{ |
230 |
size_t n; |
size_t n; |
231 |
|
|
232 |
assert (hash); |
assert (hash); |
233 |
assert (node); |
assert (node); |
234 |
assert (M4_HASH_NODE_NEXT (node) == 0); |
assert (NODE_NEXT (node) == 0); |
235 |
|
|
236 |
n = M4_HASH_BUCKET_NUM (hash, M4_HASH_NODE_KEY (node)); |
n = BUCKET_COUNT (hash, NODE_KEY (node)); |
237 |
M4_HASH_NODE_NEXT (node) = M4_HASH_BUCKET_NTH (hash, n); |
NODE_NEXT (node) = BUCKET_NTH (hash, n); |
238 |
M4_HASH_BUCKET_NTH (hash, n) = node; |
BUCKET_NTH (hash, n) = node; |
239 |
|
|
240 |
++M4_HASH_LENGTH (hash); |
++HASH_LENGTH (hash); |
241 |
} |
} |
242 |
|
|
243 |
/* Remove from HASH, the first node with key KEY; comparing keys |
/* Remove from HASH, the first node with key KEY; comparing keys |
251 |
|
|
252 |
assert (hash); |
assert (hash); |
253 |
|
|
254 |
n = M4_HASH_BUCKET_NUM (hash, key); |
n = BUCKET_COUNT (hash, key); |
255 |
|
|
256 |
{ |
{ |
257 |
m4_hash_node *node = 0; |
hash_node *node = 0; |
258 |
|
|
259 |
do |
do |
260 |
{ |
{ |
261 |
m4_hash_node *next = node |
hash_node *next = node ? NODE_NEXT (node) : BUCKET_NTH (hash, n); |
|
? M4_HASH_NODE_NEXT (node) |
|
|
: M4_HASH_BUCKET_NTH (hash, n); |
|
262 |
|
|
263 |
if (next |
if (next && ((*HASH_CMP_FUNC (hash)) (NODE_KEY (next), key) == 0)) |
|
&& ((*M4_HASH_CMP_FUNC (hash))(M4_HASH_NODE_KEY (next), key) == 0)) |
|
264 |
{ |
{ |
265 |
if (node) |
if (node) |
266 |
M4_HASH_NODE_NEXT (node) = M4_HASH_NODE_NEXT (next); |
NODE_NEXT (node) = NODE_NEXT (next); |
267 |
else |
else |
268 |
M4_HASH_BUCKET_NTH (hash, n)= M4_HASH_NODE_NEXT (next); |
BUCKET_NTH (hash, n) = NODE_NEXT (next); |
269 |
|
|
270 |
key = M4_HASH_NODE_KEY (next); |
key = NODE_KEY (next); |
271 |
#ifndef NDEBUG |
#ifndef NDEBUG |
272 |
M4_HASH_NODE_KEY (next) = 0; |
NODE_KEY (next) = 0; |
273 |
#endif |
#endif |
274 |
m4_hash_node_delete (hash, next); |
node_delete (hash, next); |
275 |
break; |
break; |
276 |
} |
} |
277 |
node = next; |
node = next; |
290 |
void ** |
void ** |
291 |
m4_hash_lookup (m4_hash *hash, const void *key) |
m4_hash_lookup (m4_hash *hash, const void *key) |
292 |
{ |
{ |
293 |
m4_hash_node *node; |
hash_node *node; |
294 |
|
|
295 |
assert (hash); |
assert (hash); |
296 |
|
|
297 |
node = m4_hash_lookup_node (hash, key); |
node = node_lookup (hash, key); |
298 |
|
|
299 |
return node ? &M4_HASH_NODE_VAL (node) : 0; |
return node ? &NODE_VALUE (node) : 0; |
300 |
} |
} |
301 |
|
|
302 |
/* Return the first node in HASH that has a matching KEY. */ |
/* Return the first node in HASH that has a matching KEY. */ |
303 |
m4_hash_node * |
static hash_node * |
304 |
m4_hash_lookup_node (m4_hash *hash, const void *key) |
node_lookup (m4_hash *hash, const void *key) |
305 |
{ |
{ |
306 |
m4_hash_node *node; |
hash_node *node; |
307 |
|
|
308 |
assert (hash); |
assert (hash); |
309 |
|
|
310 |
node = M4_HASH_BUCKET_KEY (hash, key); |
node = BUCKET_KEY (hash, key); |
311 |
|
|
312 |
while (node && (*M4_HASH_CMP_FUNC (hash)) (M4_HASH_NODE_KEY (node), key)) |
while (node && (*HASH_CMP_FUNC (hash)) (NODE_KEY (node), key)) |
313 |
node = M4_HASH_NODE_NEXT (node); |
node = NODE_NEXT (node); |
314 |
|
|
315 |
return node; |
return node; |
316 |
} |
} |
317 |
|
|
318 |
/* How many entries are currently contained by HASH. */ |
/* How many entries are currently contained by HASH. */ |
319 |
size_t |
size_t |
320 |
m4_hash_length (m4_hash *hash) |
m4_get_hash_length (m4_hash *hash) |
321 |
{ |
{ |
322 |
assert (hash); |
assert (hash); |
323 |
|
|
324 |
return M4_HASH_LENGTH (hash); |
return HASH_LENGTH (hash); |
325 |
} |
} |
326 |
|
|
327 |
/* Force the number of buckets to be the given value. You probably ought |
/* Force the number of buckets to be the given value. You probably ought |
335 |
void |
void |
336 |
m4_hash_resize (m4_hash *hash, size_t size) |
m4_hash_resize (m4_hash *hash, size_t size) |
337 |
{ |
{ |
338 |
m4_hash_node **original_buckets; |
hash_node **original_buckets; |
339 |
size_t original_size; |
size_t original_size; |
340 |
|
|
341 |
assert (hash); |
assert (hash); |
342 |
|
|
343 |
original_size = M4_HASH_SIZE (hash); |
original_size = HASH_SIZE (hash); |
344 |
original_buckets = M4_HASH_BUCKETS (hash); |
original_buckets = HASH_BUCKETS (hash); |
345 |
|
|
346 |
M4_HASH_SIZE (hash) = size; |
HASH_SIZE (hash) = size; |
347 |
M4_HASH_BUCKETS (hash)= XCALLOC (m4_hash_node *, size); |
HASH_BUCKETS (hash)= XCALLOC (hash_node *, size); |
348 |
|
|
349 |
{ |
{ |
350 |
size_t i; |
size_t i; |
351 |
for (i = 0; i < original_size; ++i) |
for (i = 0; i < original_size; ++i) |
352 |
if (original_buckets[i]) |
if (original_buckets[i]) |
353 |
m4_hash_bucket_insert (hash, original_buckets[i]); |
bucket_insert (hash, original_buckets[i]); |
354 |
} |
} |
355 |
|
|
356 |
XFREE (original_buckets); |
XFREE (original_buckets); |
358 |
|
|
359 |
/* If the node density breaks the threshold, increase the size of |
/* If the node density breaks the threshold, increase the size of |
360 |
HASH and repopulate with the original nodes. */ |
HASH and repopulate with the original nodes. */ |
361 |
void |
static void |
362 |
m4_hash_maybe_grow (m4_hash *hash) |
maybe_grow (m4_hash *hash) |
363 |
{ |
{ |
364 |
float nodes_per_bucket; |
float nodes_per_bucket; |
365 |
|
|
366 |
assert (hash); |
assert (hash); |
367 |
|
|
368 |
nodes_per_bucket = (float) M4_HASH_LENGTH (hash) |
nodes_per_bucket = (float) HASH_LENGTH (hash) / (float) HASH_SIZE (hash); |
|
/ (float) M4_HASH_SIZE (hash); |
|
369 |
|
|
370 |
if (nodes_per_bucket > M4_HASH_MAXIMUM_DENSITY) |
if (nodes_per_bucket > M4_HASH_MAXIMUM_DENSITY) |
371 |
{ |
{ |
372 |
size_t original_size = M4_HASH_SIZE (hash); |
size_t original_size = HASH_SIZE (hash); |
373 |
m4_hash_node **original_buckets = M4_HASH_BUCKETS (hash); |
hash_node **original_buckets = HASH_BUCKETS (hash); |
374 |
|
|
375 |
/* HASH sizes are always 1 less than a power of 2. */ |
/* HASH sizes are always 1 less than a power of 2. */ |
376 |
M4_HASH_SIZE (hash) = (2* (1+ original_size)) -1; |
HASH_SIZE (hash) = (2* (1+ original_size)) -1; |
377 |
M4_HASH_BUCKETS (hash) = XCALLOC (m4_hash_node *, hash->size); |
HASH_BUCKETS (hash) = XCALLOC (hash_node *, hash->size); |
378 |
|
|
379 |
{ |
{ |
380 |
size_t i; |
size_t i; |
381 |
for (i = 0; i < original_size; ++i) |
for (i = 0; i < original_size; ++i) |
382 |
if (original_buckets[i]) |
if (original_buckets[i]) |
383 |
m4_hash_bucket_insert (hash, original_buckets[i]); |
bucket_insert (hash, original_buckets[i]); |
384 |
} |
} |
385 |
|
|
386 |
XFREE (original_buckets); |
XFREE (original_buckets); |
389 |
|
|
390 |
/* Insert each node in BUCKET into HASH. Relative ordering of nodes |
/* Insert each node in BUCKET into HASH. Relative ordering of nodes |
391 |
is not preserved. */ |
is not preserved. */ |
392 |
void |
static void |
393 |
m4_hash_bucket_insert (m4_hash *hash, m4_hash_node *bucket) |
bucket_insert (m4_hash *hash, hash_node *bucket) |
394 |
{ |
{ |
395 |
assert (hash); |
assert (hash); |
396 |
assert (bucket); |
assert (bucket); |
397 |
|
|
398 |
do |
do |
399 |
{ |
{ |
400 |
m4_hash_node *next = M4_HASH_NODE_NEXT (bucket); |
hash_node *next = NODE_NEXT (bucket); |
401 |
|
|
402 |
/* Break link to rest of the bucket before reinserting. */ |
/* Break link to rest of the bucket before reinserting. */ |
403 |
M4_HASH_NODE_NEXT (bucket) = 0; |
NODE_NEXT (bucket) = 0; |
404 |
m4_hash_node_insert (hash, bucket); |
node_insert (hash, bucket); |
405 |
|
|
406 |
bucket = next; |
bucket = next; |
407 |
} |
} |
411 |
void |
void |
412 |
m4_hash_exit (void) |
m4_hash_exit (void) |
413 |
{ |
{ |
414 |
while (m4_hash_node_free_list) |
while (free_list) |
415 |
{ |
{ |
416 |
m4_hash_node *stale = m4_hash_node_free_list; |
hash_node *stale = free_list; |
417 |
m4_hash_node_free_list = M4_HASH_NODE_NEXT (stale); |
free_list = NODE_NEXT (stale); |
418 |
xfree (stale); |
xfree (stale); |
419 |
} |
} |
420 |
} |
} |
424 |
struct m4_hash_iterator |
struct m4_hash_iterator |
425 |
{ |
{ |
426 |
const m4_hash *hash; /* contains the buckets */ |
const m4_hash *hash; /* contains the buckets */ |
427 |
m4_hash_node *place; /* the node we are about to return */ |
hash_node * place; /* the node we are about to return */ |
428 |
m4_hash_node *next; /* the next node, incase PLACE is removed */ |
hash_node * next; /* the next node, incase PLACE is removed */ |
429 |
size_t next_bucket; /* the next bucket index following NEXT */ |
size_t next_bucket; /* the next bucket index following NEXT */ |
430 |
}; |
}; |
431 |
|
|
432 |
#define M4_ITERATOR_HASH(i) ((i)->hash) |
#define ITERATOR_HASH(i) ((i)->hash) |
433 |
#define M4_ITERATOR_PLACE(i) ((i)->place) |
#define ITERATOR_PLACE(i) ((i)->place) |
434 |
#define M4_ITERATOR_NEXT(i) ((i)->next) |
#define ITERATOR_NEXT(i) ((i)->next) |
435 |
#define M4_ITERATOR_NEXT_BUCKET(i) ((i)->next_bucket) |
#define ITERATOR_NEXT_BUCKET(i) ((i)->next_bucket) |
436 |
|
|
437 |
#define M4_ITERATOR_NEXT_NEXT(i) M4_HASH_NODE_NEXT (M4_ITERATOR_PLACE (i)) |
#define ITERATOR_NEXT_NEXT(i) NODE_NEXT (ITERATOR_PLACE (i)) |
438 |
|
|
439 |
m4_hash_iterator * |
m4_hash_iterator * |
440 |
m4_hash_iterator_next (const m4_hash *hash, m4_hash_iterator *place) |
m4_get_hash_iterator_next (const m4_hash *hash, m4_hash_iterator *place) |
441 |
{ |
{ |
442 |
assert (hash); |
assert (hash); |
443 |
assert (!place || (M4_ITERATOR_HASH (place) == hash)); |
assert (!place || (ITERATOR_HASH (place) == hash)); |
444 |
|
|
445 |
/* On the first iteration, allocate an iterator. */ |
/* On the first iteration, allocate an iterator. */ |
446 |
if (!place) |
if (!place) |
447 |
{ |
{ |
448 |
place = XCALLOC (m4_hash_iterator, 1); |
place = XCALLOC (m4_hash_iterator, 1); |
449 |
M4_ITERATOR_HASH (place) = hash; |
ITERATOR_HASH (place) = hash; |
450 |
} |
} |
451 |
|
|
452 |
next: |
next: |
453 |
M4_ITERATOR_PLACE (place) = M4_ITERATOR_NEXT (place); |
ITERATOR_PLACE (place) = ITERATOR_NEXT (place); |
454 |
|
|
455 |
/* If there is another node in the current bucket, select it. */ |
/* If there is another node in the current bucket, select it. */ |
456 |
if (M4_ITERATOR_NEXT (place) && M4_HASH_NODE_NEXT (M4_ITERATOR_NEXT (place))) |
if (ITERATOR_NEXT (place) && NODE_NEXT (ITERATOR_NEXT (place))) |
457 |
{ |
{ |
458 |
M4_ITERATOR_NEXT (place) = M4_HASH_NODE_NEXT (M4_ITERATOR_NEXT (place)); |
ITERATOR_NEXT (place) = NODE_NEXT (ITERATOR_NEXT (place)); |
459 |
} |
} |
460 |
else |
else |
461 |
{ |
{ |
462 |
/* Find the next non-empty bucket. */ |
/* Find the next non-empty bucket. */ |
463 |
while ((M4_ITERATOR_NEXT_BUCKET (place) < M4_HASH_SIZE (hash)) |
while ((ITERATOR_NEXT_BUCKET (place) < HASH_SIZE (hash)) |
464 |
&& (M4_HASH_BUCKET_NTH (hash, M4_ITERATOR_NEXT_BUCKET (place)) == 0)) |
&& (BUCKET_NTH (hash, ITERATOR_NEXT_BUCKET (place)) == 0)) |
465 |
{ |
{ |
466 |
++M4_ITERATOR_NEXT_BUCKET (place); |
++ITERATOR_NEXT_BUCKET (place); |
467 |
} |
} |
468 |
|
|
469 |
/* Select the first node in the new bucket. */ |
/* Select the first node in the new bucket. */ |
470 |
if (M4_ITERATOR_NEXT_BUCKET (place) < M4_HASH_SIZE (hash)) |
if (ITERATOR_NEXT_BUCKET (place) < HASH_SIZE (hash)) |
471 |
{ |
{ |
472 |
M4_ITERATOR_NEXT (place) |
ITERATOR_NEXT (place) |
473 |
= M4_HASH_BUCKET_NTH (hash, M4_ITERATOR_NEXT_BUCKET (place)); |
= BUCKET_NTH (hash, ITERATOR_NEXT_BUCKET (place)); |
474 |
} |
} |
475 |
else |
else |
476 |
M4_ITERATOR_NEXT (place) = 0; |
ITERATOR_NEXT (place) = 0; |
477 |
|
|
478 |
/* Advance the `next' reference. */ |
/* Advance the `next' reference. */ |
479 |
++M4_ITERATOR_NEXT_BUCKET (place); |
++ITERATOR_NEXT_BUCKET (place); |
480 |
} |
} |
481 |
|
|
482 |
/* If there are no more nodes to return, recycle the iterator memory. */ |
/* If there are no more nodes to return, recycle the iterator memory. */ |
483 |
if (! (M4_ITERATOR_PLACE (place) || M4_ITERATOR_NEXT (place))) |
if (! (ITERATOR_PLACE (place) || ITERATOR_NEXT (place))) |
484 |
{ |
{ |
485 |
XFREE (place); |
XFREE (place); |
486 |
return 0; |
return 0; |
488 |
|
|
489 |
/* On the first call we need to put the 1st node in PLACE and |
/* On the first call we need to put the 1st node in PLACE and |
490 |
the 2nd node in NEXT. */ |
the 2nd node in NEXT. */ |
491 |
if (M4_ITERATOR_NEXT (place) && !M4_ITERATOR_PLACE (place)) |
if (ITERATOR_NEXT (place) && !ITERATOR_PLACE (place)) |
492 |
goto next; |
goto next; |
493 |
|
|
494 |
assert (place && M4_ITERATOR_PLACE (place)); |
assert (place && ITERATOR_PLACE (place)); |
495 |
|
|
496 |
return place; |
return place; |
497 |
} |
} |
498 |
|
|
499 |
const void * |
const void * |
500 |
m4_hash_iterator_key (m4_hash_iterator *place) |
m4_get_hash_iterator_key (m4_hash_iterator *place) |
501 |
{ |
{ |
502 |
assert (place); |
assert (place); |
503 |
|
|
504 |
return M4_HASH_NODE_KEY (M4_ITERATOR_PLACE (place)); |
return NODE_KEY (ITERATOR_PLACE (place)); |
505 |
} |
} |
506 |
|
|
507 |
void * |
void * |
508 |
m4_hash_iterator_value (m4_hash_iterator *place) |
m4_get_hash_iterator_value (m4_hash_iterator *place) |
509 |
{ |
{ |
510 |
assert (place); |
assert (place); |
511 |
|
|
512 |
return M4_HASH_NODE_VAL (M4_ITERATOR_PLACE (place)); |
return NODE_VALUE (ITERATOR_PLACE (place)); |
513 |
} |
} |
514 |
|
|
515 |
/* The following function is used for the cases where we want to do |
/* The following function is used for the cases where we want to do |
517 |
traverses the hash table, and calls a specified function FUNC for |
traverses the hash table, and calls a specified function FUNC for |
518 |
each entry in the table. FUNC is called with a pointer to the |
each entry in the table. FUNC is called with a pointer to the |
519 |
entry key, value, and the passed DATA argument. */ |
entry key, value, and the passed DATA argument. */ |
520 |
int |
void * |
521 |
m4_hash_apply (m4_hash *hash, m4_hash_apply_func *func, void *data) |
m4_hash_apply (m4_hash *hash, m4_hash_apply_func *func, void *userdata) |
522 |
{ |
{ |
523 |
int result = 0; |
m4_hash_iterator *place = NULL; |
524 |
m4_hash_iterator *place = NULL; |
void * result = NULL; |
525 |
|
|
526 |
|
assert (hash); |
527 |
assert (func); |
assert (func); |
528 |
|
|
529 |
while ((place = m4_hash_iterator_next (hash, place))) |
while ((place = m4_get_hash_iterator_next (hash, place))) |
530 |
{ |
{ |
531 |
result = (*func) (hash, m4_hash_iterator_key (place), |
result = (*func) (hash, m4_get_hash_iterator_key (place), |
532 |
m4_hash_iterator_value (place), data); |
m4_get_hash_iterator_value (place), userdata); |
533 |
|
|
534 |
if (result != 0) |
if (result != NULL) |
535 |
break; |
break; |
536 |
} |
} |
537 |
|
|