1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2013 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * David Korn <dgkorn@gmail.com> *
19 * Phong Vo <phongvo@gmail.com> *
20 * *
21 ***********************************************************************/
22 #include "config_ast.h" // IWYU pragma: keep
23
24 #include <stddef.h>
25 #include <string.h>
26
27 #include "ast_assert.h"
28 #include "cdt.h"
29 #include "cdtlib.h"
30
31 /* Ordered set/multiset
32 ** dt: dictionary being searched
33 ** obj: the object to look for.
34 ** type: search type.
35 **
36 ** Written by Kiem-Phong Vo, phongvo@gmail.com (5/25/96)
37 */
38
39 typedef struct _dttree_s {
40 Dtdata_t data;
41 Dtlink_t *root; /* tree root */
42 } Dttree_t;
43
44 #ifdef _BLD_DEBUG
dttreeprint(Dt_t * dt,Dtlink_t * here,int lev,char * (* objprintf)(void *))45 int dttreeprint(Dt_t *dt, Dtlink_t *here, int lev, char *(*objprintf)(void *)) {
46 int k, rv;
47 char *obj, *endb, buf[1024];
48 Dtdisc_t *disc = dt->disc;
49 Dttree_t *tree = (Dttree_t *)dt->data;
50
51 if (!here && !(here = tree->root)) return -1;
52
53 endb = buf; /* indentation */
54 for (k = 0; k < lev; ++k) {
55 *endb++ = ' ';
56 *endb++ = ' ';
57 }
58
59 *endb++ = '(';
60 obj = (*objprintf)(_DTOBJ(disc, here));
61 k = strlen(obj);
62 memcpy(endb, obj, k);
63 endb += k;
64 *endb++ = ')';
65 *endb++ = ':';
66 *endb++ = ' ';
67
68 *endb++ = '<';
69 if (here->_left)
70 obj = (*objprintf)(_DTOBJ(disc, here->_left));
71 else
72 obj = "NIL";
73 k = strlen(obj);
74 memcpy(endb, obj, k);
75 endb += k;
76 *endb++ = '>';
77 *endb++ = ' ';
78
79 *endb++ = '<';
80 if (here->_rght)
81 obj = (*objprintf)(_DTOBJ(disc, here->_rght));
82 else
83 obj = "NIL";
84 k = strlen(obj);
85 memcpy(endb, obj, k);
86 endb += k;
87 *endb++ = '>';
88 *endb++ = '\n';
89 write(2, buf, endb - buf);
90
91 if (here->_left) dttreeprint(dt, here->_left, lev + 1, objprintf);
92 if (here->_rght) dttreeprint(dt, here->_rght, lev + 1, objprintf);
93
94 return 0;
95 }
96 #endif
97
98 /* terminal object: DT_FIRST|DT_LAST */
tfirstlast(Dt_t * dt,int type)99 void *tfirstlast(Dt_t *dt, int type) {
100 Dtlink_t *t, *root;
101 Dtdisc_t *disc = dt->disc;
102 Dttree_t *tree = (Dttree_t *)dt->data;
103
104 if (!(root = tree->root)) return NULL;
105
106 if (type & DT_LAST) {
107 while ((t = root->_rght)) LROTATE(root, t);
108 } else /* type&DT_FIRST */
109 {
110 while ((t = root->_left)) RROTATE(root, t);
111 }
112 tree->root = root;
113
114 return _DTOBJ(disc, root);
115 }
116
117 /* DT_CLEAR */
dttree_clear(Dt_t * dt)118 static_fn void *dttree_clear(Dt_t *dt) {
119 Dtlink_t *root, *t;
120 Dtdisc_t *disc = dt->disc;
121 Dttree_t *tree = (Dttree_t *)dt->data;
122
123 root = tree->root;
124 tree->root = NULL;
125 tree->data.size = 0;
126
127 if (root && (disc->link < 0 || disc->freef)) {
128 do {
129 while ((t = root->_left)) RROTATE(root, t);
130 t = root->_rght;
131 _dtfree(dt, root, DT_DELETE);
132 } while ((root = t));
133 }
134
135 return NULL;
136 }
137
dttree_list(Dt_t * dt,Dtlink_t * list,int type)138 static_fn void *dttree_list(Dt_t *dt, Dtlink_t *list, int type) {
139 void *obj;
140 Dtlink_t *last, *r, *t;
141 Dttree_t *tree = (Dttree_t *)dt->data;
142 Dtdisc_t *disc = dt->disc;
143
144 if (type & (DT_FLATTEN | DT_EXTRACT)) {
145 if ((list = tree->root)) {
146 while ((t = list->_left)) { /* make smallest object root */
147 RROTATE(list, t);
148 }
149 for (r = (last = list)->_rght; r; r = (last = r)->_rght) {
150 while ((t = r->_left)) { /* no left children */
151 RROTATE(r, t);
152 }
153 last->_rght = r;
154 }
155 }
156
157 if (type & DT_FLATTEN) {
158 tree->root = list;
159 } else {
160 tree->root = NULL;
161 dt->data->size = 0;
162 }
163 } else /* if(type&DT_RESTORE) */
164 {
165 dt->data->size = 0;
166 for (r = list; r; r = t) {
167 t = r->_rght;
168 obj = _DTOBJ(disc, r);
169 if ((*dt->meth->searchf)(dt, (void *)r, DT_RELINK) == obj) dt->data->size += 1;
170 }
171 }
172
173 return (void *)list;
174 }
175
dttree_size(Dtlink_t * root,ssize_t lev,Dtstat_t * st)176 static_fn ssize_t dttree_size(Dtlink_t *root, ssize_t lev, Dtstat_t *st) {
177 ssize_t size, z;
178
179 if (!root) { /* nothing to do */
180 return 0;
181 }
182
183 if (lev >= DT_MAXRECURSE) { /* avoid blowing the stack */
184 return -1;
185 }
186
187 if (st) {
188 st->mlev = lev > st->mlev ? lev : st->mlev;
189 if (lev < DT_MAXSIZE) {
190 st->msize = lev > st->msize ? lev : st->msize;
191 st->lsize[lev] += 1; /* count #objects per level */
192 }
193 }
194
195 size = 1;
196 z = dttree_size(root->_left, lev + 1, st);
197 if (z < 0) return -1;
198
199 size += z;
200 z = dttree_size(root->_rght, lev + 1, st);
201 if (z < 0) return -1;
202
203 return size + z;
204 }
205
dttree_stat(Dt_t * dt,Dtstat_t * st)206 static_fn void *dttree_stat(Dt_t *dt, Dtstat_t *st) {
207 ssize_t size;
208 Dttree_t *tree = (Dttree_t *)dt->data;
209
210 if (!st) return (void *)dt->data->size;
211 memset(st, 0, sizeof(Dtstat_t));
212 size = dttree_size(tree->root, 0, st);
213 assert((dt->data->type & DT_SHARE) || size == dt->data->size);
214 st->meth = dt->meth->type;
215 st->size = size;
216 st->space = sizeof(Dttree_t) + (dt->disc->link >= 0 ? 0 : size * sizeof(Dthold_t));
217 return (void *)size;
218 }
219
220 /* make a list into a balanced tree */
dttree_balance(Dtlink_t * list,ssize_t size)221 static_fn Dtlink_t *dttree_balance(Dtlink_t *list, ssize_t size) {
222 ssize_t n;
223 Dtlink_t *l, *mid;
224
225 if (size <= 2) return list;
226
227 for (l = list, n = size / 2 - 1; n > 0; n -= 1) l = l->_rght;
228
229 mid = l->_rght;
230 l->_rght = NULL;
231 mid->_left = dttree_balance(list, (n = size / 2));
232 mid->_rght = dttree_balance(mid->_rght, size - (n + 1));
233 return mid;
234 }
235
dttree_optimize(Dt_t * dt)236 static_fn void dttree_optimize(Dt_t *dt) {
237 ssize_t size;
238 Dtlink_t *l, *list;
239 Dttree_t *tree = (Dttree_t *)dt->data;
240
241 if ((list = (Dtlink_t *)dttree_list(dt, NULL, DT_FLATTEN))) {
242 for (size = 0, l = list; l; l = l->_rght) size += 1;
243 tree->root = dttree_balance(list, size);
244 }
245 }
246
dttree_root(Dt_t * dt,Dtlink_t * list,Dtlink_t * link,void * obj,int type)247 static_fn Dtlink_t *dttree_root(Dt_t *dt, Dtlink_t *list, Dtlink_t *link, void *obj, int type) {
248 Dtlink_t *root, *last, *t, *r, *l;
249 void *key, *o, *k;
250 Dtdisc_t *disc = dt->disc;
251
252 key = _DTKEY(disc, obj); /* key of object */
253
254 if (type & (DT_ATMOST | DT_ATLEAST)) /* find the left-most or right-most element */
255 {
256 list->_left = link->_rght;
257 list->_rght = link->_left;
258 if (type & DT_ATMOST) {
259 while ((l = list->_left)) {
260 while ((r = l->_rght)) { /* get the max elt of left subtree */
261 LROTATE(l, r);
262 }
263 list->_left = l;
264
265 o = _DTOBJ(disc, l);
266 k = _DTKEY(disc, o);
267 if (_DTCMP(dt, key, k, disc) != 0) {
268 break;
269 } else {
270 RROTATE(list, l);
271 }
272 }
273 } else {
274 while ((r = list->_rght)) {
275 while ((l = r->_left)) { /* get the min elt of right subtree */
276 RROTATE(r, l);
277 }
278 list->_rght = r;
279
280 o = _DTOBJ(disc, r);
281 k = _DTKEY(disc, o);
282 if (_DTCMP(dt, key, k, disc) != 0) {
283 break;
284 } else {
285 LROTATE(list, r);
286 }
287 }
288 }
289 link->_rght = list->_left;
290 link->_left = list->_rght;
291 return list;
292 }
293
294 last = list;
295 list->_left = list->_rght = NULL;
296 root = NULL;
297
298 while (!root && (t = link->_rght)) /* link->_rght is the left subtree <= obj */
299 {
300 while ((r = t->_rght)) { /* make t the maximum element */
301 LROTATE(t, r);
302 }
303
304 o = _DTOBJ(disc, t);
305 k = _DTKEY(disc, o);
306 if (_DTCMP(dt, key, k, disc) != 0) {
307 link->_rght = t; /* no more of this group in subtree */
308 break;
309 } else if ((type & (DT_REMOVE | DT_NEXT | DT_PREV)) && o == obj) {
310 link->_rght = t->_left; /* found the exact object */
311 root = t;
312 } else /* add t to equal list in an order-preserving manner */
313 {
314 link->_rght = t->_left;
315 t->_left = t->_rght = NULL;
316 if (type & DT_NEXT) {
317 last->_left = t;
318 last = t;
319 } else {
320 t->_rght = list;
321 list = t;
322 }
323 }
324 }
325
326 while (!root && (t = link->_left)) /* link->_left is the right subtree >= obj */
327 {
328 while ((l = t->_left)) { /* make t the minimum element */
329 RROTATE(t, l);
330 }
331
332 o = _DTOBJ(disc, t);
333 k = _DTKEY(disc, o);
334 if (_DTCMP(dt, key, k, disc) != 0) {
335 link->_left = t; /* no more of this group in subtree */
336 break;
337 } else if ((type & (DT_REMOVE | DT_NEXT | DT_PREV)) && o == obj) {
338 link->_left = t->_rght; /* found the exact object */
339 root = t;
340 } else /* add t to equal list in an order-preserving manner */
341 {
342 link->_left = t->_rght;
343 t->_left = t->_rght = NULL;
344 if (type & DT_NEXT) {
345 t->_left = list;
346 list = t;
347 } else {
348 last->_rght = t;
349 last = t;
350 }
351 }
352 }
353
354 if (!root) /* always set a non-trivial root */
355 {
356 root = list;
357 if (type & DT_NEXT) {
358 list = list->_left;
359 } else {
360 list = list->_rght;
361 }
362 }
363
364 if (list) /* add the rest of the equal-list to the proper subtree */
365 {
366 if (type & DT_NEXT) {
367 last->_left = link->_rght;
368 link->_rght = list;
369 } else {
370 last->_rght = link->_left;
371 link->_left = list;
372 }
373 }
374
375 return root;
376 }
377
dttree(Dt_t * dt,void * obj,int type)378 static_fn void *dttree(Dt_t *dt, void *obj, int type) {
379 int cmp;
380 void *o, *k, *key;
381 Dtlink_t *root, *t, *l, *r, *me, link;
382 Dtlink_t **fngr = NULL;
383 Dtdisc_t *disc = dt->disc;
384 Dttree_t *tree = (Dttree_t *)dt->data;
385
386 if (!(type & DT_OPERATIONS)) return NULL;
387
388 DTSETLOCK(dt);
389
390 if (type & (DT_FIRST | DT_LAST)) {
391 DTRETURN(obj, tfirstlast(dt, type));
392 } else if (type & (DT_EXTRACT | DT_RESTORE | DT_FLATTEN)) {
393 DTRETURN(obj, dttree_list(dt, (Dtlink_t *)obj, type));
394 } else if (type & DT_CLEAR) {
395 DTRETURN(obj, dttree_clear(dt));
396 } else if (type & DT_STAT) {
397 dttree_optimize(dt); /* balance tree to avoid deep recursion */
398 DTRETURN(obj, dttree_stat(dt, (Dtstat_t *)obj));
399 } else if (type & DT_START) {
400 if (!(fngr = (Dtlink_t **)(*dt->memoryf)(dt, NULL, sizeof(Dtlink_t *), disc))) {
401 DTRETURN(obj, NULL);
402 }
403 if (!obj) {
404 if (!(obj = tfirstlast(dt, DT_FIRST))) {
405 (void)(*dt->memoryf)(dt, (void *)fngr, 0, disc);
406 DTRETURN(obj, NULL);
407 } else {
408 *fngr = tree->root;
409 DTRETURN(obj, (void *)fngr);
410 }
411 }
412 /* else: fall through to search for obj */
413 } else if (type & DT_STEP) {
414 if (!(fngr = (Dtlink_t **)obj) || !(l = *fngr)) DTRETURN(obj, NULL);
415 obj = _DTOBJ(disc, l);
416 *fngr = NULL;
417 /* fall through to search for obj */
418 } else if (type & DT_STOP) {
419 if (obj) { /* free allocated memory for finger */
420 (void)(*dt->memoryf)(dt, obj, 0, disc);
421 }
422 DTRETURN(obj, NULL);
423 }
424
425 if (!obj) { /* from here on, an object prototype is required */
426 DTRETURN(obj, NULL);
427 }
428
429 if (type & DT_RELINK) /* relinking objects after some processing */
430 {
431 me = (Dtlink_t *)obj;
432 obj = _DTOBJ(disc, me);
433 key = _DTKEY(disc, obj);
434 } else {
435 me = NULL;
436 if (type & DT_MATCH) /* no prototype object given, just the key */
437 {
438 key = obj;
439 obj = NULL;
440 } else {
441 key = _DTKEY(disc, obj); /* get key from prototype object */
442 }
443 }
444
445 memset(&link, 0, sizeof(link));
446 l = r = &link; /* link._rght(_left) will be LEFT(RIGHT) tree after splaying */
447 if ((root = tree->root) && _DTOBJ(disc, root) != obj) /* splay-search for a matching object */
448 {
449 while (1) {
450 o = _DTOBJ(disc, root);
451 k = _DTKEY(disc, o);
452 if ((cmp = _DTCMP(dt, key, k, disc)) == 0) {
453 break;
454 } else if (cmp < 0) {
455 if ((t = root->_left)) {
456 o = _DTOBJ(disc, t);
457 k = _DTKEY(disc, o);
458 if ((cmp = _DTCMP(dt, key, k, disc)) < 0) {
459 rrotate(root, t);
460 rlink(r, t);
461 if (!(root = t->_left)) break;
462 } else if (cmp == 0) {
463 rlink(r, root);
464 root = t;
465 break;
466 } else /* if(cmp > 0) */
467 {
468 llink(l, t);
469 rlink(r, root);
470 if (!(root = t->_rght)) break;
471 }
472 } else {
473 rlink(r, root);
474 root = NULL;
475 break;
476 }
477 } else /* if(cmp > 0) */
478 {
479 if ((t = root->_rght)) {
480 o = _DTOBJ(disc, t);
481 k = _DTKEY(disc, o);
482 if ((cmp = _DTCMP(dt, key, k, disc)) > 0) {
483 lrotate(root, t);
484 llink(l, t);
485 if (!(root = t->_rght)) break;
486 } else if (cmp == 0) {
487 llink(l, root);
488 root = t;
489 break;
490 } else /* if(cmp < 0) */
491 {
492 rlink(r, t);
493 llink(l, root);
494 if (!(root = t->_left)) break;
495 }
496 } else {
497 llink(l, root);
498 root = NULL;
499 break;
500 }
501 }
502 }
503 }
504 l->_rght = root ? root->_left : NULL;
505 r->_left = root ? root->_rght : NULL;
506
507 if (root) {
508 if (dt->meth->type & DT_OBAG) /* may need to reset root to the right object */
509 {
510 if ((type & (DT_ATLEAST | DT_ATMOST)) ||
511 ((type & (DT_NEXT | DT_PREV | DT_REMOVE)) && _DTOBJ(disc, root) != obj)) {
512 // Coverity CID 253782 says there is a theoretical path that can reach this point
513 // with `obj` set to NULL. Which is a problem because dttree_root() dereferences it.
514 assert(obj);
515 root = dttree_root(dt, root, &link, obj, type);
516 }
517 }
518
519 if (type & (DT_START | DT_SEARCH | DT_MATCH | DT_ATMOST | DT_ATLEAST)) {
520 has_root: /* reconstitute the tree */
521 root->_left = link._rght;
522 root->_rght = link._left;
523 tree->root = root;
524
525 if (type & DT_START) { // walk is now well-defined
526 assert(fngr);
527 *fngr = root;
528 DTRETURN(obj, (void *)fngr);
529 } else if (type & DT_STEP) { // return obj and set fngr to next
530 assert(fngr);
531 *fngr = root;
532 goto dt_return;
533 } else {
534 DTRETURN(obj, _DTOBJ(disc, root));
535 }
536 } else if (type & (DT_NEXT | DT_STEP)) {
537 root->_left = link._rght;
538 root->_rght = NULL;
539 link._rght = root;
540 dt_next:
541 if ((root = link._left)) {
542 while ((t = root->_left)) RROTATE(root, t);
543 link._left = root->_rght;
544 goto has_root;
545 } else {
546 goto no_root;
547 }
548 } else if (type & DT_PREV) {
549 root->_rght = link._left;
550 root->_left = NULL;
551 link._left = root;
552 dt_prev:
553 if ((root = link._rght)) {
554 while ((t = root->_rght)) LROTATE(root, t);
555 link._rght = root->_left;
556 goto has_root;
557 } else {
558 goto no_root;
559 }
560 } else if (type & (DT_DELETE | DT_DETACH)) {
561 dt_delete: /* remove an object from the dictionary */
562 obj = _DTOBJ(disc, root);
563 _dtfree(dt, root, type);
564 dt->data->size -= 1;
565 goto no_root;
566 } else if (type & DT_REMOVE) /* remove a particular object */
567 {
568 if (_DTOBJ(disc, root) == obj) {
569 goto dt_delete;
570 } else {
571 root->_left = link._rght;
572 root->_rght = link._left;
573 tree->root = root;
574 DTRETURN(obj, NULL);
575 }
576 } else if (type & (DT_INSERT | DT_APPEND | DT_ATTACH)) {
577 if (dt->meth->type & DT_OSET) {
578 type |= DT_MATCH; /* for announcement */
579 goto has_root;
580 } else /* if(dt->meth->type&DT_OBAG) */
581 {
582 root->_left = NULL;
583 root->_rght = link._left;
584 link._left = root;
585 goto dt_insert;
586 }
587 } else if (type & DT_INSTALL) { /* remove old object before insert new one */
588 o = _DTOBJ(disc, root);
589 _dtfree(dt, root, DT_DELETE);
590 DTANNOUNCE(dt, o, DT_DELETE);
591 goto dt_insert;
592 } else if (type & DT_RELINK) /* a duplicate */
593 {
594 if (dt->meth->type & DT_OSET) { /* remove object */
595 o = _DTOBJ(disc, me);
596 _dtfree(dt, me, DT_DELETE);
597 DTANNOUNCE(dt, o, DT_DELETE);
598 } else {
599 me->_left = NULL;
600 me->_rght = link._left;
601 link._left = me;
602 }
603 goto has_root;
604 }
605 } else /* no matching object, tree has been split to LEFT&RIGHT subtrees */
606 {
607 if (type & (DT_START | DT_SEARCH | DT_MATCH)) {
608 no_root:
609 if (!(l = link._rght)) { /* no LEFT subtree */
610 tree->root = link._left; /* tree is RIGHT tree */
611 } else {
612 while ((t = l->_rght)) /* maximize root of LEFT tree */
613 {
614 if (t->_rght) {
615 LLSHIFT(l, t);
616 } else {
617 LROTATE(l, t);
618 }
619 }
620 l->_rght = link._left; /* hook RIGHT tree to LEFT root */
621 tree->root = l; /* LEFT tree is now the entire tree */
622 }
623
624 if (type & (DT_DELETE | DT_DETACH | DT_REMOVE | DT_STEP)) {
625 goto dt_return;
626 } else {
627 if (type & DT_START) { /* cannot start a walk from nowhere */
628 (void)(*dt->memoryf)(dt, (void *)fngr, 0, disc);
629 }
630 DTRETURN(obj, NULL);
631 }
632 } else if (type & (DT_NEXT | DT_ATLEAST)) {
633 goto dt_next;
634 } else if (type & (DT_PREV | DT_ATMOST)) {
635 goto dt_prev;
636 } else if (type & (DT_DELETE | DT_DETACH | DT_REMOVE)) {
637 obj = NULL;
638 goto no_root;
639 } else if (type & (DT_INSERT | DT_APPEND | DT_ATTACH | DT_INSTALL)) {
640 dt_insert:
641 if (!(root = _dtmake(dt, obj, type))) {
642 obj = NULL;
643 goto no_root;
644 } else {
645 dt->data->size += 1;
646 goto has_root;
647 }
648 } else if (type & DT_RELINK) {
649 root = me;
650 goto has_root;
651 }
652 }
653 DTRETURN(obj, NULL);
654
655 dt_return:
656 DTANNOUNCE(dt, obj, type);
657 DTCLRLOCK(dt);
658 return obj;
659 }
660
dttree_event(Dt_t * dt,int event,void * arg)661 static_fn int dttree_event(Dt_t *dt, int event, void *arg) {
662 UNUSED(arg);
663 Dttree_t *tree = (Dttree_t *)dt->data;
664
665 if (event == DT_OPEN) {
666 if (tree) { /* already initialized */
667 return 0;
668 }
669 if (!(tree = (Dttree_t *)(*dt->memoryf)(dt, 0, sizeof(Dttree_t), dt->disc))) {
670 DTERROR(dt, "Error in allocating a tree data structure");
671 return -1;
672 }
673 memset(tree, 0, sizeof(Dttree_t));
674 dt->data = (Dtdata_t *)tree;
675 return 1;
676 } else if (event == DT_CLOSE) {
677 if (!tree) return 0;
678 if (tree->root) (void)dttree_clear(dt);
679 (void)(*dt->memoryf)(dt, (void *)tree, 0, dt->disc);
680 dt->data = NULL;
681 return 0;
682 } else if (event == DT_OPTIMIZE) { // balance the search tree
683 dttree_optimize(dt);
684 return 0;
685 }
686 return 0;
687 }
688
689 /* make this method available */
690 static Dtmethod_t _Dtoset = {
691 .searchf = dttree, .type = DT_OSET, .eventf = dttree_event, .name = "Dtoset"};
692 static Dtmethod_t _Dtobag = {
693 .searchf = dttree, .type = DT_OBAG, .eventf = dttree_event, .name = "Dtobag"};
694 Dtmethod_t *Dtoset = &_Dtoset;
695 Dtmethod_t *Dtobag = &_Dtobag;
696