diff options
Diffstat (limited to 'src/symtab.c')
-rw-r--r-- | src/symtab.c | 113 |
1 files changed, 91 insertions, 22 deletions
diff --git a/src/symtab.c b/src/symtab.c index 540ea0f..3647a81 100644 --- a/src/symtab.c +++ b/src/symtab.c | |||
@@ -33,16 +33,36 @@ static unsigned int hash_size[] = { | |||
33 | /* |max_rehash| keeps the number of entries in |hash_size| table. */ | 33 | /* |max_rehash| keeps the number of entries in |hash_size| table. */ |
34 | static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]); | 34 | static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]); |
35 | 35 | ||
36 | enum symtab_defer_type { | ||
37 | defer_add, | ||
38 | defer_del | ||
39 | }; | ||
40 | |||
36 | struct grecs_symtab { | 41 | struct grecs_symtab { |
37 | int flags; | 42 | int flags; |
38 | unsigned int hash_num; /* Index to hash_size table */ | 43 | unsigned int hash_num; /* Index to hash_size table */ |
39 | size_t elsize; /* Size of an element */ | 44 | size_t elsize; /* Size of an element */ |
40 | struct grecs_syment **tab; | 45 | size_t elcount; /* Number of elements in use */ |
41 | unsigned (*hash_fun)(void *, unsigned long hash_num); | 46 | struct grecs_syment **tab; /* Table of element pointers */ |
47 | |||
48 | /* Functions that can be overridden when creating the symtab: */ | ||
49 | unsigned (*hash_fun)(void *, unsigned long); | ||
42 | int (*cmp_fun)(const void *, const void *); | 50 | int (*cmp_fun)(const void *, const void *); |
43 | int (*copy_fun)(void *, void *); | 51 | int (*copy_fun)(void *, void *); |
44 | void *(*syment_alloc_fun)(size_t size); | 52 | void *(*syment_alloc_fun)(size_t); |
45 | void (*syment_free_fun) (void *); | 53 | void (*syment_free_fun) (void *); |
54 | |||
55 | /* Support for deferred operations. When an insertion or deletion | ||
56 | is requested while the symtab is being iterated over, this operation | ||
57 | is deferred until the iteration is finished. */ | ||
58 | |||
59 | /* Iteration level. Gets increased upon entry to grecs_symtab_foreach, | ||
60 | and decreased right before leaving it. */ | ||
61 | unsigned int itr_level; | ||
62 | /* Two deferment lists keep track of the deferred additions and | ||
63 | removals: | ||
64 | */ | ||
65 | grecs_list_ptr_t defer_list[2]; | ||
46 | }; | 66 | }; |
47 | 67 | ||
48 | static void | 68 | static void |
@@ -72,7 +92,17 @@ syment_alloc(struct grecs_symtab *st, void *key) | |||
72 | } | 92 | } |
73 | return ent; | 93 | return ent; |
74 | } | 94 | } |
75 | 95 | ||
96 | static void | ||
97 | symtab_defer_op(struct grecs_symtab *st, enum symtab_defer_type type, | ||
98 | struct grecs_syment *ent) | ||
99 | { | ||
100 | if (!st->defer_list[type]) { | ||
101 | st->defer_list[type] = grecs_list_create(); | ||
102 | st->defer_list[type]->cmp = st->cmp_fun; | ||
103 | } | ||
104 | grecs_list_append(st->defer_list[type], ent); | ||
105 | } | ||
76 | 106 | ||
77 | unsigned | 107 | unsigned |
78 | grecs_hash_string(const char *name, unsigned long hashsize) | 108 | grecs_hash_string(const char *name, unsigned long hashsize) |
@@ -101,8 +131,8 @@ grecs_hash_string_ci(const char *name, unsigned long hashsize) | |||
101 | static unsigned | 131 | static unsigned |
102 | def_hash(void *data, unsigned long hashsize) | 132 | def_hash(void *data, unsigned long hashsize) |
103 | { | 133 | { |
104 | struct grecs_syment *sym = data; | 134 | struct grecs_syment *sym = data; |
105 | return grecs_hash_string(sym->name, hashsize); | 135 | return grecs_hash_string(sym->name, hashsize); |
106 | } | 136 | } |
107 | 137 | ||
108 | static int | 138 | static int |
@@ -218,6 +248,11 @@ grecs_symtab_remove(struct grecs_symtab *st, void *elt) | |||
218 | unsigned int pos, i, j, r; | 248 | unsigned int pos, i, j, r; |
219 | struct grecs_syment *entry; | 249 | struct grecs_syment *entry; |
220 | 250 | ||
251 | if (st->itr_level) { | ||
252 | if (grecs_list_remove(st->defer_list[defer_add], elt) == 0) | ||
253 | return 0; | ||
254 | } | ||
255 | |||
221 | pos = st->hash_fun(elt, hash_size[st->hash_num]); | 256 | pos = st->hash_fun(elt, hash_size[st->hash_num]); |
222 | for (i = pos; (entry = st->tab[i]);) { | 257 | for (i = pos; (entry = st->tab[i]);) { |
223 | if (st->cmp_fun(entry, elt) == 0) | 258 | if (st->cmp_fun(entry, elt) == 0) |
@@ -231,7 +266,14 @@ grecs_symtab_remove(struct grecs_symtab *st, void *elt) | |||
231 | if (!entry) | 266 | if (!entry) |
232 | return ENOENT; | 267 | return ENOENT; |
233 | 268 | ||
269 | if (st->itr_level) { | ||
270 | symtab_defer_op(st, defer_del, entry); | ||
271 | //FIXME: mark the entry as deleted | ||
272 | return 0; | ||
273 | } | ||
274 | |||
234 | syment_free(st, entry); | 275 | syment_free(st, entry); |
276 | st->elcount--; | ||
235 | 277 | ||
236 | for (;;) { | 278 | for (;;) { |
237 | st->tab[i] = NULL; | 279 | st->tab[i] = NULL; |
@@ -312,10 +354,20 @@ grecs_symtab_lookup_or_install(struct grecs_symtab *st, void *key, | |||
312 | errno = ENOMEM; | 354 | errno = ENOMEM; |
313 | return NULL; | 355 | return NULL; |
314 | } | 356 | } |
315 | st->tab[i] = ent; | 357 | if (st->itr_level) { |
358 | symtab_defer_op(st, defer_add, ent); | ||
359 | } else { | ||
360 | st->tab[i] = ent; | ||
361 | st->elcount++; | ||
362 | } | ||
316 | return ent; | 363 | return ent; |
317 | } else | 364 | } else |
318 | return st->tab[i]; | 365 | return st->tab[i]; |
366 | } else if (rc == ENOENT && st->itr_level) { | ||
367 | void *ptr = grecs_list_locate(st->defer_list[defer_add], key); | ||
368 | if (ptr) | ||
369 | return ptr; | ||
370 | rc = ENOENT; | ||
319 | } | 371 | } |
320 | errno = rc; | 372 | errno = rc; |
321 | return NULL; | 373 | return NULL; |
@@ -337,6 +389,7 @@ grecs_symtab_clear(struct grecs_symtab *st) | |||
337 | st->tab[i] = NULL; | 389 | st->tab[i] = NULL; |
338 | } | 390 | } |
339 | } | 391 | } |
392 | st->elcount = 0; | ||
340 | } | 393 | } |
341 | 394 | ||
342 | struct grecs_symtab * | 395 | struct grecs_symtab * |
@@ -350,6 +403,7 @@ grecs_symtab_create(size_t elsize, | |||
350 | if (st) { | 403 | if (st) { |
351 | memset(st, 0, sizeof(*st)); | 404 | memset(st, 0, sizeof(*st)); |
352 | st->elsize = elsize; | 405 | st->elsize = elsize; |
406 | st->elcount = 0; | ||
353 | st->hash_fun = hash_fun ? hash_fun : def_hash; | 407 | st->hash_fun = hash_fun ? hash_fun : def_hash; |
354 | st->cmp_fun = cmp_fun ? cmp_fun : def_cmp; | 408 | st->cmp_fun = cmp_fun ? cmp_fun : def_cmp; |
355 | st->copy_fun = copy_fun ? copy_fun : def_copy; | 409 | st->copy_fun = copy_fun ? copy_fun : def_copy; |
@@ -381,39 +435,54 @@ grecs_symtab_free(struct grecs_symtab *st) | |||
381 | { | 435 | { |
382 | if (st) { | 436 | if (st) { |
383 | grecs_symtab_clear(st); | 437 | grecs_symtab_clear(st); |
438 | grecs_list_free(st->defer_list[defer_add]); | ||
439 | grecs_list_free(st->defer_list[defer_del]); | ||
384 | free(st->tab); | 440 | free(st->tab); |
385 | free(st); | 441 | free(st); |
386 | } | 442 | } |
387 | } | 443 | } |
388 | 444 | ||
389 | size_t | 445 | size_t |
390 | grecs_symtab_count_entries(struct grecs_symtab *st) | 446 | grecs_symtab_count(struct grecs_symtab *st) |
391 | { | 447 | { |
392 | unsigned i; | 448 | return st->elcount; |
393 | size_t count = 0; | ||
394 | |||
395 | for (i = 0; i < hash_size[st->hash_num]; i++) | ||
396 | if (st->tab[i]) | ||
397 | count++; | ||
398 | return count; | ||
399 | } | 449 | } |
400 | 450 | ||
401 | int | 451 | int |
402 | grecs_symtab_enumerate(struct grecs_symtab *st, grecs_symtab_enumerator_t fun, | 452 | grecs_symtab_foreach(struct grecs_symtab *st, grecs_symtab_enumerator_t fun, |
403 | void *data) | 453 | void *data) |
404 | { | 454 | { |
405 | unsigned i; | 455 | unsigned i; |
406 | 456 | struct grecs_syment *ep; | |
457 | |||
407 | if (!st) | 458 | if (!st) |
408 | return 0; | 459 | return 0; |
460 | |||
461 | ++st->itr_level; | ||
462 | |||
409 | for (i = 0; i < hash_size[st->hash_num]; i++) { | 463 | for (i = 0; i < hash_size[st->hash_num]; i++) { |
410 | struct grecs_syment *ep = st->tab[i]; | 464 | ep = st->tab[i]; |
411 | if (ep) { | 465 | if (ep && !grecs_list_locate(st->defer_list[defer_del], ep)) { |
412 | int rc = fun(ep, data); | 466 | int rc = fun(ep, data); |
413 | if (rc) | 467 | if (rc) |
414 | return rc; | 468 | return rc; |
415 | } | 469 | } |
416 | } | 470 | } |
471 | |||
472 | if (--st->itr_level == 0) { | ||
473 | while ((ep = grecs_list_pop(st->defer_list[defer_del])) != NULL) | ||
474 | grecs_symtab_remove(st, ep); | ||
475 | |||
476 | while ((ep = grecs_list_pop(st->defer_list[defer_add])) != NULL) { | ||
477 | int install = 1; | ||
478 | if (grecs_symtab_get_index(&i, st, ep, | ||
479 | &install) == 0) { | ||
480 | st->tab[i] = ep; | ||
481 | st->elcount++; | ||
482 | } | ||
483 | } | ||
484 | } | ||
485 | |||
417 | return 0; | 486 | return 0; |
418 | } | 487 | } |
419 | 488 | ||