File: | workdir/unxlngi6.pro/CustomTarget/ucpp/source/nhash.c |
Location: | line 275, column 2 |
Description: | Dereference of undefined pointer value |
1 | /* | |||
2 | * Mixed hash table / binary tree code. | |||
3 | * (c) Thomas Pornin 2002 | |||
4 | * | |||
5 | * Redistribution and use in source and binary forms, with or without | |||
6 | * modification, are permitted provided that the following conditions | |||
7 | * are met: | |||
8 | * 1. Redistributions of source code must retain the above copyright | |||
9 | * notice, this list of conditions and the following disclaimer. | |||
10 | * 2. Redistributions in binary form must reproduce the above copyright | |||
11 | * notice, this list of conditions and the following disclaimer in the | |||
12 | * documentation and/or other materials provided with the distribution. | |||
13 | * 4. The name of the authors may not be used to endorse or promote | |||
14 | * products derived from this software without specific prior written | |||
15 | * permission. | |||
16 | * | |||
17 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |||
18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||
19 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE | |||
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |||
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT | |||
23 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |||
24 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |||
25 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |||
26 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |||
27 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
28 | * | |||
29 | */ | |||
30 | ||||
31 | #include <stddef.h> | |||
32 | #include <string.h> | |||
33 | #include <limits.h> | |||
34 | #include "nhash.h" | |||
35 | #include "mem.h" | |||
36 | ||||
37 | /* | |||
38 | * Hash a string into an `unsigned' value. This function is derived | |||
39 | * from the hash function used in the ELF binary object file format | |||
40 | * hash tables. The result size is a 32-bit number if the `unsigned' | |||
41 | * type is big enough to hold 32-bit arbitrary numbers, a 16-bit number | |||
42 | * otherwise. | |||
43 | */ | |||
44 | static unsigned hash_string(char *name) | |||
45 | { | |||
46 | unsigned h = 0; | |||
47 | ||||
48 | for (h = 0; *name; name ++) { | |||
49 | unsigned g; | |||
50 | ||||
51 | h = (h << 4) + *(unsigned char *)name; | |||
52 | #if UINT_MAX(2147483647 *2U +1U) >= 0xffffffffU | |||
53 | g = h & 0xF0000000U; | |||
54 | h ^= (g >> 24); | |||
55 | #else | |||
56 | g = h & 0xF000U; | |||
57 | h ^= (g >> 12); | |||
58 | #endif | |||
59 | h &= ~g; | |||
60 | } | |||
61 | return h; | |||
62 | } | |||
63 | ||||
64 | /* | |||
65 | * Each item in the table is a structure beginning with a `hash_item_header' | |||
66 | * structure. Those headers define binary trees such that all left-descendants | |||
67 | * (respectively right-descendants) of a given tree node have an associated | |||
68 | * hash value strictly smaller (respectively greater) than the hash value | |||
69 | * associated with this node. | |||
70 | * | |||
71 | * The `ident' field points to an array of char. The `sizeof(unsigned)' | |||
72 | * first `char' contain a copy of an `unsigned' value which is the hashed | |||
73 | * string, except the least significant bit. When this bit is set to 0, | |||
74 | * the node contains the unique item using that hash value. If the bit | |||
75 | * is set to 1, then there are several items with that hash value. | |||
76 | * | |||
77 | * When several items share the same hash value, they are linked together | |||
78 | * in a linked list by their `left' field. The node contains no data; | |||
79 | * it is a "fake item". | |||
80 | * | |||
81 | * The `char' following the hash value encode the item name for true items. | |||
82 | * For fake items, they contain the pointer to the first true item of the | |||
83 | * corresponding link list (suitably aligned). | |||
84 | * | |||
85 | * There are HTT_NUM_TREES trees; the items are sorted among trees by the | |||
86 | * lest significant bits of their hash value. | |||
87 | */ | |||
88 | ||||
89 | static void internal_init(HTT *htt, void (*deldata)(void *), int reduced) | |||
90 | { | |||
91 | htt->deldata = deldata; | |||
92 | if (reduced) { | |||
93 | HTT2 *htt2 = (HTT2 *)htt; | |||
94 | ||||
95 | htt2->tree[0] = htt2->tree[1] = NULL((void*)0); | |||
96 | } else { | |||
97 | unsigned u; | |||
98 | ||||
99 | for (u = 0; u < HTT_NUM_TREES128; u ++) htt->tree[u] = NULL((void*)0); | |||
100 | } | |||
101 | } | |||
102 | ||||
103 | /* see nhash.h */ | |||
104 | void HTT_init(HTT *htt, void (*deldata)(void *)) | |||
105 | { | |||
106 | internal_init(htt, deldata, 0); | |||
107 | } | |||
108 | ||||
109 | /* see nhash.h */ | |||
110 | void HTT2_init(HTT2 *htt, void (*deldata)(void *)) | |||
111 | { | |||
112 | internal_init((HTT *)htt, deldata, 1); | |||
113 | } | |||
114 | ||||
115 | #define PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *))) (sizeof(hash_item_header *) * \ | |||
116 | ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / \ | |||
117 | sizeof(hash_item_header *))) | |||
118 | ||||
119 | #define TREE(u)(*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) : htt-> tree + ((u) & (128 - 1)))) (*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) \ | |||
120 | : htt->tree + ((u) & (HTT_NUM_TREES128 - 1)))) | |||
121 | ||||
122 | /* | |||
123 | * Find a node for the given hash value. If `father' is not NULL, fill | |||
124 | * `*father' with a pointer to the node's father. | |||
125 | * If the return value is NULL, then no existing node was found; if `*father' | |||
126 | * is also NULL, the tree is empty. If the return value is not NULL but | |||
127 | * `*father' is NULL, then the found node is the tree root. | |||
128 | * | |||
129 | * If `father' is not NULL, then `*leftson' is filled with 1 if the node | |||
130 | * was looked for as the father left son, 0 otherwise. | |||
131 | */ | |||
132 | static hash_item_header *find_node(HTT *htt, unsigned u, | |||
133 | hash_item_header **father, int *leftson, int reduced) | |||
134 | { | |||
135 | hash_item_header *node = TREE(u)(*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) : htt-> tree + ((u) & (128 - 1)))); | |||
136 | hash_item_header *nodef = NULL((void*)0); | |||
137 | int ls; | |||
138 | ||||
139 | u &= ~1U; | |||
140 | while (node != NULL((void*)0)) { | |||
141 | unsigned v = *(unsigned *)(node->ident); | |||
142 | unsigned w = v & ~1U; | |||
143 | ||||
144 | if (u == w) break; | |||
145 | nodef = node; | |||
146 | if (u < w) { | |||
147 | node = node->left; | |||
148 | ls = 1; | |||
149 | } else { | |||
150 | node = node->right; | |||
151 | ls = 0; | |||
152 | } | |||
153 | } | |||
154 | if (father != NULL((void*)0)) { | |||
155 | *father = nodef; | |||
156 | *leftson = ls; | |||
157 | } | |||
158 | return node; | |||
159 | } | |||
160 | ||||
161 | static void *internal_get(HTT *htt, char *name, int reduced) | |||
162 | { | |||
163 | unsigned u = hash_string(name), v; | |||
164 | hash_item_header *node = find_node(htt, u, NULL((void*)0), NULL((void*)0), reduced); | |||
165 | ||||
166 | if (node == NULL((void*)0)) return NULL((void*)0); | |||
167 | v = *(unsigned *)(node->ident); | |||
168 | if ((v & 1U) == 0) { | |||
169 | return (strcmp(HASH_ITEM_NAME(node)(((hash_item_header *)(node))->ident + sizeof(unsigned)), name) == 0) ? node : NULL((void*)0); | |||
170 | } | |||
171 | node = *(hash_item_header **)(node->ident + PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *)))); | |||
172 | while (node != NULL((void*)0)) { | |||
173 | if (strcmp(HASH_ITEM_NAME(node)(((hash_item_header *)(node))->ident + sizeof(unsigned)), name) == 0) return node; | |||
174 | node = node->left; | |||
175 | } | |||
176 | return NULL((void*)0); | |||
177 | } | |||
178 | ||||
179 | /* see nhash.h */ | |||
180 | void *HTT_get(HTT *htt, char *name) | |||
181 | { | |||
182 | return internal_get(htt, name, 0); | |||
183 | } | |||
184 | ||||
185 | /* see nhash.h */ | |||
186 | void *HTT2_get(HTT2 *htt, char *name) | |||
187 | { | |||
188 | return internal_get((HTT *)htt, name, 1); | |||
189 | } | |||
190 | ||||
191 | /* | |||
192 | * Make an item identifier from its name and its hash value. | |||
193 | */ | |||
194 | static char *make_ident(char *name, unsigned u) | |||
195 | { | |||
196 | size_t n = strlen(name) + 1; | |||
197 | char *ident = getmemmalloc(n + sizeof(unsigned)); | |||
198 | ||||
199 | *(unsigned *)ident = u & ~1U; | |||
200 | memcpy(ident + sizeof(unsigned), name, n); | |||
201 | return ident; | |||
202 | } | |||
203 | ||||
204 | /* | |||
205 | * Make an identifier for a fake item, pointing to a true item. | |||
206 | */ | |||
207 | static char *make_fake_ident(unsigned u, hash_item_header *next) | |||
208 | { | |||
209 | char *ident = getmemmalloc(PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *))) + sizeof(hash_item_header *)); | |||
210 | ||||
211 | *(unsigned *)ident = u | 1U; | |||
212 | *(hash_item_header **)(ident + PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *)))) = next; | |||
213 | return ident; | |||
214 | } | |||
215 | ||||
216 | /* | |||
217 | * Adding an item is straightforward: | |||
218 | * 1. look for its emplacement | |||
219 | * 2. if no node is found, use the item as a new node and link it to the tree | |||
220 | * 3. if a node is found: | |||
221 | * 3.1. if the node is real, check for name inequality, then create a | |||
222 | * fake node and assemble the two-element linked list | |||
223 | * 3.2. if the node is fake, look for the name in the list; if not found, | |||
224 | * add the node at the list end | |||
225 | */ | |||
226 | static void *internal_put(HTT *htt, void *item, char *name, int reduced) | |||
227 | { | |||
228 | unsigned u = hash_string(name), v; | |||
229 | int ls; | |||
230 | hash_item_header *father; | |||
231 | hash_item_header *node = find_node(htt, u, &father, &ls, reduced); | |||
232 | hash_item_header *itemg = item, *pnode; | |||
233 | ||||
234 | if (node == NULL((void*)0)) { | |||
235 | itemg->left = itemg->right = NULL((void*)0); | |||
236 | itemg->ident = make_ident(name, u); | |||
237 | if (father == NULL((void*)0)) { | |||
238 | TREE(u)(*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) : htt-> tree + ((u) & (128 - 1)))) = itemg; | |||
239 | } else if (ls) { | |||
240 | father->left = itemg; | |||
241 | } else { | |||
242 | father->right = itemg; | |||
243 | } | |||
244 | return NULL((void*)0); | |||
245 | } | |||
246 | v = *(unsigned *)(node->ident); | |||
247 | if ((v & 1U) == 0) { | |||
248 | if (strcmp(HASH_ITEM_NAME(node)(((hash_item_header *)(node))->ident + sizeof(unsigned)), name) == 0) | |||
249 | return node; | |||
250 | pnode = getmemmalloc(sizeof *pnode); | |||
251 | pnode->left = node->left; | |||
252 | pnode->right = node->right; | |||
253 | pnode->ident = make_fake_ident(u, node); | |||
254 | node->left = itemg; | |||
255 | node->right = NULL((void*)0); | |||
256 | itemg->left = itemg->right = NULL((void*)0); | |||
257 | itemg->ident = make_ident(name, u); | |||
258 | if (father == NULL((void*)0)) { | |||
259 | TREE(u)(*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) : htt-> tree + ((u) & (128 - 1)))) = pnode; | |||
260 | } else if (ls) { | |||
261 | father->left = pnode; | |||
262 | } else { | |||
263 | father->right = pnode; | |||
264 | } | |||
265 | return NULL((void*)0); | |||
266 | } | |||
267 | node = *(hash_item_header **)(node->ident + PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *)))); | |||
268 | while (node != NULL((void*)0)) { | |||
269 | if (strcmp(HASH_ITEM_NAME(node)(((hash_item_header *)(node))->ident + sizeof(unsigned)), name) == 0) return node; | |||
270 | pnode = node; | |||
271 | node = node->left; | |||
272 | } | |||
273 | itemg->left = itemg->right = NULL((void*)0); | |||
274 | itemg->ident = make_ident(name, u); | |||
275 | pnode->left = itemg; | |||
| ||||
276 | return NULL((void*)0); | |||
277 | } | |||
278 | ||||
279 | /* see nhash.h */ | |||
280 | void *HTT_put(HTT *htt, void *item, char *name) | |||
281 | { | |||
282 | return internal_put(htt, item, name, 0); | |||
283 | } | |||
284 | ||||
285 | /* see nhash.h */ | |||
286 | void *HTT2_put(HTT2 *htt, void *item, char *name) | |||
287 | { | |||
288 | return internal_put((HTT *)htt, item, name, 1); | |||
| ||||
289 | } | |||
290 | ||||
291 | /* | |||
292 | * A fake node subnode list has shrunk to one item only; make the | |||
293 | * node real again. | |||
294 | * fnode the fake node | |||
295 | * node the last remaining node | |||
296 | * father the fake node father (NULL if the fake node is root) | |||
297 | * leftson 1 if the fake node is a left son, 0 otehrwise | |||
298 | * u the hash value for this node | |||
299 | */ | |||
300 | static void shrink_node(HTT *htt, hash_item_header *fnode, | |||
301 | hash_item_header *node, hash_item_header *father, int leftson, | |||
302 | unsigned u, int reduced) | |||
303 | { | |||
304 | node->left = fnode->left; | |||
305 | node->right = fnode->right; | |||
306 | if (father == NULL((void*)0)) { | |||
307 | TREE(u)(*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) : htt-> tree + ((u) & (128 - 1)))) = node; | |||
308 | } else if (leftson) { | |||
309 | father->left = node; | |||
310 | } else { | |||
311 | father->right = node; | |||
312 | } | |||
313 | freememfree(fnode->ident); | |||
314 | freememfree(fnode); | |||
315 | } | |||
316 | ||||
317 | /* | |||
318 | * Deletion algorithm: | |||
319 | * 1. look for the node; if not found, exit | |||
320 | * 2. if the node is real: | |||
321 | * 2.1. check for equality; exit otherwise | |||
322 | * 2.2. delete the node | |||
323 | * 2.3. promote the leftest of right descendants or rightest of left | |||
324 | * descendants | |||
325 | * 3. if the node is fake: | |||
326 | * 3.1. check the list items for equality; exit otherwise | |||
327 | * 3.2. delete the correct item | |||
328 | * 3.3. if there remains only one item, supress the fake node | |||
329 | */ | |||
330 | static int internal_del(HTT *htt, char *name, int reduced) | |||
331 | { | |||
332 | unsigned u = hash_string(name), v; | |||
333 | int ls; | |||
334 | hash_item_header *father; | |||
335 | hash_item_header *node = find_node(htt, u, &father, &ls, reduced); | |||
336 | hash_item_header *pnode, *fnode, *znode; | |||
337 | char *tmp; | |||
338 | ||||
339 | if (node == NULL((void*)0)) return 0; | |||
340 | v = *(unsigned *)(node->ident); | |||
341 | if ((v & 1U) != 0) { | |||
342 | fnode = node; | |||
343 | node = znode = *(hash_item_header **)(node->ident + PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *)))); | |||
344 | pnode = NULL((void*)0); | |||
345 | while (node != NULL((void*)0)) { | |||
346 | if (strcmp(HASH_ITEM_NAME(node)(((hash_item_header *)(node))->ident + sizeof(unsigned)), name) == 0) break; | |||
347 | pnode = node; | |||
348 | node = node->left; | |||
349 | } | |||
350 | if (node == NULL((void*)0)) return 0; | |||
351 | if (pnode == NULL((void*)0)) { | |||
352 | /* | |||
353 | * We supress the first item in the list. | |||
354 | */ | |||
355 | *(hash_item_header **)(fnode->ident + PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *)))) = | |||
356 | node->left; | |||
357 | if (node->left->left == NULL((void*)0)) { | |||
358 | shrink_node(htt, fnode, node->left, | |||
359 | father, ls, u, reduced); | |||
360 | } | |||
361 | } else { | |||
362 | pnode->left = node->left; | |||
363 | if (pnode->left == NULL((void*)0) && znode == pnode) { | |||
364 | shrink_node(htt, fnode, pnode, | |||
365 | father, ls, u, reduced); | |||
366 | } | |||
367 | } | |||
368 | } else { | |||
369 | if (strcmp(HASH_ITEM_NAME(node)(((hash_item_header *)(node))->ident + sizeof(unsigned)), name) != 0) return 0; | |||
370 | if (node->left != NULL((void*)0)) { | |||
371 | for (znode = node, pnode = node->left; pnode->right; | |||
372 | znode = pnode, pnode = pnode->right); | |||
373 | if (znode != node) { | |||
374 | znode->right = pnode->left; | |||
375 | pnode->left = node->left; | |||
376 | } | |||
377 | pnode->right = node->right; | |||
378 | } else if (node->right != NULL((void*)0)) { | |||
379 | for (znode = node, pnode = node->right; pnode->left; | |||
380 | znode = pnode, pnode = pnode->left); | |||
381 | if (znode != node) { | |||
382 | znode->left = pnode->right; | |||
383 | pnode->right = node->right; | |||
384 | } | |||
385 | pnode->left = node->left; | |||
386 | } else pnode = NULL((void*)0); | |||
387 | if (father == NULL((void*)0)) { | |||
388 | TREE(u)(*(reduced ? ((HTT2 *)htt)->tree + ((u) & 1) : htt-> tree + ((u) & (128 - 1)))) = pnode; | |||
389 | } else if (ls) { | |||
390 | father->left = pnode; | |||
391 | } else { | |||
392 | father->right = pnode; | |||
393 | } | |||
394 | } | |||
395 | tmp = node->ident; | |||
396 | htt->deldata(node); | |||
397 | freememfree(tmp); | |||
398 | return 1; | |||
399 | } | |||
400 | ||||
401 | /* see nhash.h */ | |||
402 | int HTT_del(HTT *htt, char *name) | |||
403 | { | |||
404 | return internal_del(htt, name, 0); | |||
405 | } | |||
406 | ||||
407 | /* see nhash.h */ | |||
408 | int HTT2_del(HTT2 *htt, char *name) | |||
409 | { | |||
410 | return internal_del((HTT *)htt, name, 1); | |||
411 | } | |||
412 | ||||
413 | /* | |||
414 | * Apply `action()' on all nodes of the tree whose root is given as | |||
415 | * parameter `node'. If `wipe' is non-zero, the nodes are removed | |||
416 | * from memory. | |||
417 | */ | |||
418 | static void scan_node(hash_item_header *node, void (*action)(void *), int wipe) | |||
419 | { | |||
420 | unsigned v; | |||
421 | ||||
422 | if (node == NULL((void*)0)) return; | |||
423 | scan_node(node->left, action, wipe); | |||
424 | scan_node(node->right, action, wipe); | |||
425 | v = *(unsigned *)(node->ident); | |||
426 | if ((v & 1U) != 0) { | |||
427 | hash_item_header *pnode, *nnode; | |||
428 | ||||
429 | for (pnode = *(hash_item_header **)(node->ident + PTR_SHIFT(sizeof(hash_item_header *) * ((sizeof(unsigned) + sizeof(hash_item_header *) - 1) / sizeof(hash_item_header *)))); | |||
430 | pnode != NULL((void*)0); pnode = nnode) { | |||
431 | char *tmp = pnode->ident; | |||
432 | ||||
433 | nnode = pnode->left; | |||
434 | action(pnode); | |||
435 | if (wipe) freememfree(tmp); | |||
436 | } | |||
437 | if (wipe) { | |||
438 | freememfree(node->ident); | |||
439 | freememfree(node); | |||
440 | } | |||
441 | } else { | |||
442 | char *tmp = node->ident; | |||
443 | ||||
444 | action(node); | |||
445 | if (wipe) freememfree(tmp); | |||
446 | } | |||
447 | } | |||
448 | ||||
449 | /* see nhash.h */ | |||
450 | void HTT_scan(HTT *htt, void (*action)(void *)) | |||
451 | { | |||
452 | unsigned u; | |||
453 | ||||
454 | for (u = 0; u < HTT_NUM_TREES128; u ++) { | |||
455 | scan_node(htt->tree[u], action, 0); | |||
456 | } | |||
457 | } | |||
458 | ||||
459 | /* see nhash.h */ | |||
460 | void HTT2_scan(HTT2 *htt, void (*action)(void *)) | |||
461 | { | |||
462 | scan_node(htt->tree[0], action, 0); | |||
463 | scan_node(htt->tree[1], action, 0); | |||
464 | } | |||
465 | ||||
466 | /* see nhash.h */ | |||
467 | void HTT_kill(HTT *htt) | |||
468 | { | |||
469 | unsigned u; | |||
470 | ||||
471 | for (u = 0; u < HTT_NUM_TREES128; u ++) { | |||
472 | scan_node(htt->tree[u], htt->deldata, 1); | |||
473 | } | |||
474 | } | |||
475 | ||||
476 | /* see nhash.h */ | |||
477 | void HTT2_kill(HTT2 *htt) | |||
478 | { | |||
479 | scan_node(htt->tree[0], htt->deldata, 1); | |||
480 | scan_node(htt->tree[1], htt->deldata, 1); | |||
481 | } |