aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2016-08-25 16:06:58 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2016-08-25 16:06:58 +0300
commitc2f02c56a38a3e6d50b8ca2081db9d2de658b8ed (patch)
treea6c80fd81a6b5aad899d8fb0e3e41ece650eed9b /src
parent102d1b9c1a94548dfa0c498845c77933db6a7738 (diff)
downloadgrecs-c2f02c56a38a3e6d50b8ca2081db9d2de658b8ed.tar.gz
grecs-c2f02c56a38a3e6d50b8ca2081db9d2de658b8ed.tar.bz2
symtabs: allow to modify the list during iteration over it.
* include/grecs/list.h (grecs_list_remove): New proto. * include/grecs/symtab.h (grecs_symtab_count_entries): Rename to grecs_symtab_count. * src/list.c (grecs_list_remove): New function. (grecs_list_remove_tail, grecs_list_clear) (grecs_list_locate,grecs_list_index): Treat NULL list as empty list. * src/symtab.c: Defer table modifications during iteration (symtab_defer_type): New enum. (grecs_symtab)<elcount,itr_level,defer_list>: New members. (symtab_defer_op): New static function. (grecs_symtab_remove): When called during iteration, add the entry to the defer_del deferment list, unless it is already in defer_add, in which case remove it from there. Update elcount. (grecs_symtab_lookup_or_install): Defer addition when iterating. Update elcount. (grecs_symtab_clear): Reset elcount to 0. (grecs_symtab_create): Initialize elcount. (grecs_symtab_foreach): Process deferred modifications.
Diffstat (limited to 'src')
-rw-r--r--src/list.c80
-rw-r--r--src/symtab.c97
2 files changed, 140 insertions, 37 deletions
diff --git a/src/list.c b/src/list.c
index b2d9f13..afd1b5d 100644
--- a/src/list.c
+++ b/src/list.c
@@ -86,6 +86,46 @@ grecs_list_remove_entry(struct grecs_list *lp, struct grecs_list_entry *ent)
86 lp->count--; 86 lp->count--;
87} 87}
88 88
89void *
90grecs_list_remove_tail(struct grecs_list *lp)
91{
92 void *data;
93 struct grecs_list_entry *ep;
94
95 if (!lp || !lp->tail)
96 return NULL;
97 ep = lp->tail;
98 data = lp->tail->data;
99 grecs_list_remove_entry(lp, ep);
100 return data;
101}
102
103static int
104_ptrcmp(const void *a, const void *b)
105{
106 return a != b;
107}
108
109int
110grecs_list_remove(struct grecs_list *lp, void *data)
111{
112 struct grecs_list_entry *ep;
113 int (*cmp)(const void *, const void *);
114
115
116 if (!lp)
117 return 1;
118
119 cmp = lp->cmp ? lp->cmp : _ptrcmp;
120 for (ep = lp->head; ep; ep = ep->next) {
121 if (cmp(ep->data, data) == 0) {
122 grecs_list_remove_entry(lp, ep);
123 return 0;
124 }
125 }
126 return 1;
127}
128
89void 129void
90grecs_list_append(struct grecs_list *lp, void *val) 130grecs_list_append(struct grecs_list *lp, void *val)
91{ 131{
@@ -123,7 +163,11 @@ void *
123grecs_list_pop(struct grecs_list *lp) 163grecs_list_pop(struct grecs_list *lp)
124{ 164{
125 void *data; 165 void *data;
126 struct grecs_list_entry *ep = lp->head; 166 struct grecs_list_entry *ep;
167
168 if (!lp)
169 return NULL;
170 ep = lp->head;
127 if (ep) { 171 if (ep) {
128 data = ep->data; 172 data = ep->data;
129 grecs_list_remove_entry(lp, ep); 173 grecs_list_remove_entry(lp, ep);
@@ -132,25 +176,14 @@ grecs_list_pop(struct grecs_list *lp)
132 return data; 176 return data;
133} 177}
134 178
135void *
136grecs_list_remove_tail(struct grecs_list *lp)
137{
138 void *data;
139 struct grecs_list_entry *ep;
140
141 if (!lp->tail)
142 return NULL;
143 ep = lp->tail;
144 data = lp->tail->data;
145 grecs_list_remove_entry(lp, ep);
146 return data;
147}
148
149void 179void
150grecs_list_clear(struct grecs_list *lp) 180grecs_list_clear(struct grecs_list *lp)
151{ 181{
152 struct grecs_list_entry *ep = lp->head; 182 struct grecs_list_entry *ep;
153 183
184 if (!lp)
185 return;
186 ep = lp->head;
154 while (ep) { 187 while (ep) {
155 struct grecs_list_entry *next = ep->next; 188 struct grecs_list_entry *next = ep->next;
156 if (lp->free_entry) 189 if (lp->free_entry)
@@ -171,17 +204,16 @@ grecs_list_free(struct grecs_list *lp)
171 } 204 }
172} 205}
173 206
174static int
175_ptrcmp(const void *a, const void *b)
176{
177 return a != b;
178}
179
180void * 207void *
181grecs_list_locate(struct grecs_list *lp, void *data) 208grecs_list_locate(struct grecs_list *lp, void *data)
182{ 209{
183 struct grecs_list_entry *ep; 210 struct grecs_list_entry *ep;
184 int (*cmp)(const void *, const void *) = lp->cmp ? lp->cmp : _ptrcmp; 211 int (*cmp)(const void *, const void *);
212
213 if (!lp)
214 return NULL;
215
216 cmp = lp->cmp ? lp->cmp : _ptrcmp;
185 217
186 for (ep = lp->head; ep; ep = ep->next) { 218 for (ep = lp->head; ep; ep = ep->next) {
187 if (cmp(ep->data, data) == 0) 219 if (cmp(ep->data, data) == 0)
@@ -195,6 +227,8 @@ grecs_list_index(struct grecs_list *lp, size_t idx)
195{ 227{
196 struct grecs_list_entry *ep; 228 struct grecs_list_entry *ep;
197 229
230 if (!lp)
231 return NULL;
198 for (ep = lp->head; ep && idx; ep = ep->next, idx--) 232 for (ep = lp->head; ep && idx; ep = ep->next, idx--)
199 ; 233 ;
200 return ep ? ep->data : NULL; 234 return ep ? ep->data : NULL;
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. */
34static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]); 34static unsigned int max_rehash = sizeof(hash_size) / sizeof(hash_size[0]);
35 35
36enum symtab_defer_type {
37 defer_add,
38 defer_del
39};
40
36struct grecs_symtab { 41struct 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
48static void 68static void
@@ -73,6 +93,16 @@ syment_alloc(struct grecs_symtab *st, void *key)
73 return ent; 93 return ent;
74} 94}
75 95
96static void
97symtab_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
77unsigned 107unsigned
78grecs_hash_string(const char *name, unsigned long hashsize) 108grecs_hash_string(const char *name, unsigned long hashsize)
@@ -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;