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