Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
node.c
Go to the documentation of this file.
1
4/*************************************************************************
5 * Copyright (c) 2011 AT&T Intellectual Property
6 * All rights reserved. This program and the accompanying materials
7 * are made available under the terms of the Eclipse Public License v1.0
8 * which accompanies this distribution, and is available at
9 * https://www.eclipse.org/legal/epl-v10.html
10 *
11 * Contributors: Details at https://graphviz.org
12 *************************************************************************/
13
14#include <assert.h>
15#include <cgraph/cghdr.h>
16#include <cgraph/node_set.h>
17#include <stdbool.h>
18#include <stdlib.h>
19#include <util/alloc.h>
20#include <util/unreachable.h>
21
23{
24 Agsubnode_t *sn;
25
26 sn = node_set_find(g->n_id, id);
27 return sn ? sn->node : NULL;
28}
29
30static Agnode_t *agfindnode_by_name(Agraph_t * g, char *name)
31{
32 IDTYPE id;
33
34 if (agmapnametoid(g, AGNODE, name, &id, false))
35 return agfindnode_by_id(g, id);
36 else
37 return NULL;
38}
39
41{
42 Agsubnode_t *sn;
43 sn = dtfirst(g->n_seq);
44 return sn ? sn->node : NULL;
45}
46
48{
49 Agsubnode_t *sn;
50 sn = agsubrep(g, n);
51 if (sn) sn = dtnext(g->n_seq, sn);
52 return sn ? sn->node : NULL;
53}
54
56{
57 Agsubnode_t *sn;
58 sn = dtlast(g->n_seq);
59 return sn ? sn->node : NULL;
60}
61
63{
64 Agsubnode_t *sn;
65 sn = agsubrep(g, n);
66 if (sn) sn = dtprev(g->n_seq, sn);
67 return sn ? sn->node : NULL;
68}
69
70
71/* internal node constructor */
72static Agnode_t *newnode(Agraph_t * g, IDTYPE id, uint64_t seq)
73{
74 assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
75
76 Agnode_t *n = gv_alloc(sizeof(Agnode_t));
77 AGTYPE(n) = AGNODE;
78 AGID(n) = id;
79 AGSEQ(n) = seq & SEQ_MASK;
80 n->root = agroot(g);
81 if (agroot(g)->desc.has_attrs)
82 (void)agbindrec(n, AgDataRecName, sizeof(Agattr_t), false);
83 /* nodeattr_init and method_init will be called later, from the
84 * subgraph where the node was actually created, but first it has
85 * to be installed in all the (sub)graphs up to root. */
86 return n;
87}
88
89static void installnode(Agraph_t * g, Agnode_t * n)
90{
91 Agsubnode_t *sn;
92 size_t osize;
93 (void)osize;
94
95 assert(node_set_size(g->n_id) == (size_t)dtsize(g->n_seq));
96 osize = node_set_size(g->n_id);
97 if (g == agroot(g)) sn = &(n->mainsub);
98 else sn = gv_alloc(sizeof(Agsubnode_t));
99 sn->node = n;
100 node_set_add(g->n_id, sn);
101 dtinsert(g->n_seq, sn);
102 assert(node_set_size(g->n_id) == (size_t)dtsize(g->n_seq));
103 assert(node_set_size(g->n_id) == osize + 1);
104}
105
107{
108 Agraph_t *par;
109 installnode(g, n);
110 if ((par = agparent(g)))
111 installnodetoroot(par, n);
112}
113
114static void initnode(Agraph_t * g, Agnode_t * n)
115{
116 if (agroot(g)->desc.has_attrs)
117 agnodeattr_init(g,n);
118 agmethod_init(g, n);
119}
120
121/* external node constructor - create by id */
122Agnode_t *agidnode(Agraph_t * g, IDTYPE id, int cflag)
123{
124 Agraph_t *root;
125 Agnode_t *n;
126
127 n = agfindnode_by_id(g, id);
128 if (n == NULL && cflag) {
129 root = agroot(g);
130 if ((g != root) && ((n = agfindnode_by_id(root, id)))) /*old */
131 agsubnode(g, n, 1); /* insert locally */
132 else {
133 if (agallocid(g, AGNODE, id)) { /* new */
134 n = newnode(g, id, agnextseq(g, AGNODE));
135 installnodetoroot(g, n);
136 initnode(g, n);
137 } else
138 n = NULL; /* allocid for new node failed */
139 }
140 }
141 /* else return probe result */
142 return n;
143}
144
145Agnode_t *agnode(Agraph_t * g, char *name, int cflag)
146{
147 Agraph_t *root;
148 Agnode_t *n;
149 IDTYPE id;
150
151 root = agroot(g);
152 /* probe for existing node */
153 if (agmapnametoid(g, AGNODE, name, &id, false)) {
154 if ((n = agfindnode_by_id(g, id)))
155 return n;
156
157 /* might already exist globally, but need to insert locally */
158 if (cflag && (g != root) && ((n = agfindnode_by_id(root, id)))) {
159 return agsubnode(g, n, 1);
160 }
161 }
162
163 if (cflag && agmapnametoid(g, AGNODE, name, &id, true)) { /* reserve id */
164 n = newnode(g, id, agnextseq(g, AGNODE));
165 installnodetoroot(g, n);
166 initnode(g, n);
167 assert(agsubrep(g,n));
168 agregister(g, AGNODE, n); /* register in external namespace */
169 return n;
170 }
171
172 return NULL;
173}
174
175/* removes image of node and its edges from graph.
176 caller must ensure n belongs to g. */
177void agdelnodeimage(Agraph_t * g, Agnode_t * n, void *ignored)
178{
179 Agedge_t *e, *f;
180 Agsubnode_t template = {0};
181 template.node = n;
182
183 (void)ignored;
184 for (e = agfstedge(g, n); e; e = f) {
185 f = agnxtedge(g, e, n);
186 agdeledgeimage(g, e, 0);
187 }
188 /* If the following lines are switched, switch the discpline using
189 * free_subnode below.
190 */
192 dtdelete(g->n_seq, &template);
193}
194
196{
197 Agedge_t *e, *f;
198
199 if (!agfindnode_by_id(g, AGID(n)))
200 return FAILURE; /* bad arg */
201 if (g == agroot(g)) {
202 for (e = agfstedge(g, n); e; e = f) {
203 f = agnxtedge(g, e, n);
204 agdeledge(g, e);
205 }
206 if (g->desc.has_attrs)
208 agmethod_delete(g, n);
209 agrecclose((Agobj_t *) n);
210 agfreeid(g, AGNODE, AGID(n));
211 }
212 if (agapply(g, (Agobj_t *)n, (agobjfn_t)agdelnodeimage, NULL, false) == SUCCESS) {
213 if (g == agroot(g))
214 free(n);
215 return SUCCESS;
216 } else
217 return FAILURE;
218}
219
220static void dict_relabel(Agraph_t *ignored, Agnode_t *n, void *arg) {
221 (void)ignored;
222
223 Agraph_t *g;
224 uint64_t new_id;
225
226 g = agraphof(n);
227 new_id = *(uint64_t *) arg;
228 Agsubnode_t *key = agsubrep(g, n);
229 assert(key != NULL && "node being renamed does not exist");
230 node_set_remove(g->n_id, key->node->base.tag.id);
231 AGID(key->node) = new_id;
232 node_set_add(g->n_id, key);
233 /* because all the subgraphs share the same node now, this
234 now requires a separate deletion and insertion phase */
235}
236
237int agrelabel_node(Agnode_t * n, char *newname)
238{
239 Agraph_t *g;
240 IDTYPE new_id;
241
242 g = agroot(agraphof(n));
243 if (agfindnode_by_name(g, newname))
244 return FAILURE;
245 if (agmapnametoid(g, AGNODE, newname, &new_id, true)) {
246 if (agfindnode_by_id(agroot(g), new_id) == NULL) {
247 agfreeid(g, AGNODE, AGID(n));
248 agapply(g, (Agobj_t*)n, (agobjfn_t)dict_relabel, &new_id, false);
249 return SUCCESS;
250 } else {
251 agfreeid(g, AGNODE, new_id); /* couldn't use it after all */
252 }
253 /* obj* is unchanged, so no need to re agregister() */
254 }
255 return FAILURE;
256}
257
258/* lookup or insert <n> in <g> */
259Agnode_t *agsubnode(Agraph_t * g, Agnode_t * n0, int cflag)
260{
261 Agraph_t *par;
262 Agnode_t *n;
263
264 if (g->root != n0->root)
265 return NULL;
266 n = agfindnode_by_id(g, AGID(n0));
267 if (n == NULL && cflag) {
268 if ((par = agparent(g))) {
269 n = agsubnode(par, n0, cflag);
270 installnode(g, n);
271 /* no callback for existing node insertion in subgraph (?) */
272 }
273 /* else impossible that <n> doesn't belong to <g> */
274 }
275 /* else lookup succeeded */
276 return n;
277}
278
284static bool agsubnodeideq(const Agsubnode_t *sn0, IDTYPE id) {
285 return AGID(sn0->node) == id;
286}
287
288static int agsubnodeseqcmpf(void *arg0, void *arg1) {
289 Agsubnode_t *sn0 = arg0;
290 Agsubnode_t *sn1 = arg1;
291
292 if (AGSEQ(sn0->node) < AGSEQ(sn1->node)) return -1;
293 if (AGSEQ(sn0->node) > AGSEQ(sn1->node)) return 1;
294 return 0;
295}
296
297/* free_subnode:
298 * Free Agsubnode_t allocated in installnode. This should
299 * only be done for subgraphs, as the root graph uses the
300 * subnode structure built into the node. This explains the
301 * AGSNMAIN test. Also, note that both the id and the seq
302 * dictionaries use the same subnode object, so only one
303 * should do the deletion.
304 */
305static void free_subnode(void *subnode) {
306 Agsubnode_t *sn = subnode;
307 if (!AGSNMAIN(sn))
308 free(sn);
309}
310
312 .link = offsetof(Agsubnode_t, seq_link), // link offset
313 .freef = free_subnode,
314 .comparf = agsubnodeseqcmpf,
315};
316
317static void agnodesetfinger(Agraph_t * g, Agnode_t * n, void *ignored)
318{
319 Agsubnode_t template = {0};
320 template.node = n;
321 dtsearch(g->n_seq,&template);
322 (void)ignored;
323}
324
325static void agnoderenew(Agraph_t * g, Agnode_t * n, void *ignored)
326{
327 dtrenew(g->n_seq, dtfinger(g->n_seq));
328 (void)n;
329 (void)ignored;
330}
331
333{
334 Agraph_t *g;
335 Agnode_t *n, *nxt;
336
337
338 g = agroot(fst);
339 if (AGSEQ(fst) > AGSEQ(snd)) return SUCCESS;
340
341 /* move snd out of the way somewhere */
342 n = snd;
343 if (agapply (g,(Agobj_t *)n, (agobjfn_t)agnodesetfinger, n, false) != SUCCESS) {
344 return FAILURE;
345 }
346 {
347 uint64_t seq = g->clos->seq[AGNODE] + 2;
348 assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
349 AGSEQ(snd) = seq & SEQ_MASK;
350 }
351 if (agapply(g, (Agobj_t *)n, (agobjfn_t)agnoderenew, n, false) != SUCCESS) {
352 return FAILURE;
353 }
354 n = agprvnode(g,snd);
355 do {
356 nxt = agprvnode(g,n);
357 if (agapply(g, (Agobj_t *)n, (agobjfn_t)agnodesetfinger, n, false) != SUCCESS) {
358 return FAILURE;
359 }
360 uint64_t seq = AGSEQ(n) + 1;
361 assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
362 AGSEQ(n) = seq & SEQ_MASK;
363 if (agapply(g, (Agobj_t *)n, (agobjfn_t)agnoderenew, n, false) != SUCCESS) {
364 return FAILURE;
365 }
366 if (n == fst) break;
367 n = nxt;
368 } while (n);
369 if (agapply(g, (Agobj_t *)snd, (agobjfn_t)agnodesetfinger, n, false) != SUCCESS) {
370 return FAILURE;
371 }
372 assert(AGSEQ(fst) != 0 && "sequence ID overflow");
373 AGSEQ(snd) = (AGSEQ(fst) - 1) & SEQ_MASK;
374 if (agapply(g, (Agobj_t *)snd, (agobjfn_t)agnoderenew, snd, false) != SUCCESS) {
375 return FAILURE;
376 }
377 return SUCCESS;
378}
379
396
398static Agsubnode_t *const TOMBSTONE = (Agsubnode_t *)-1;
399
408static size_t node_set_get_capacity(const node_set_t *self) {
409 assert(self != NULL);
410 return self->slots == NULL ? 0 : 1ul << self->capacity_exp;
411}
412
413node_set_t *node_set_new(void) { return gv_alloc(sizeof(node_set_t)); }
414
423static size_t node_set_hash(IDTYPE id) { return (size_t)id; }
424
426 assert(self != NULL);
427 assert(item != NULL);
428
429 // a watermark ratio at which the set capacity should be expanded
430 static const size_t OCCUPANCY_THRESHOLD_PERCENT = 70;
431
432 // do we need to expand the backing store?
433 size_t capacity = node_set_get_capacity(self);
434 const bool grow = 100 * self->size >= OCCUPANCY_THRESHOLD_PERCENT * capacity;
435
436 if (grow) {
437 const size_t new_c = capacity == 0 ? 10 : self->capacity_exp + 1;
438 Agsubnode_t **new_slots = gv_calloc(1ul << new_c, sizeof(Agsubnode_t *));
439
440 // Construct a new set and copy everything into it. Note we need to rehash
441 // because capacity (and hence modulo wraparound behavior) has changed. This
442 // conveniently flushes out the tombstones too.
443 node_set_t new_self = {.slots = new_slots, .capacity_exp = new_c};
444 for (size_t i = 0; i < capacity; ++i) {
445 // skip empty slots
446 if (self->slots[i] == NULL) {
447 continue;
448 }
449 // skip deleted slots
450 if (self->slots[i] == TOMBSTONE) {
451 continue;
452 }
453 node_set_add(&new_self, self->slots[i]);
454 }
455
456 // replace ourselves with this new set
457 free(self->slots);
458 *self = new_self;
459 }
460
461 // update bounds of what we have seen
462 if (!self->min_initialized || item->node->base.tag.id < self->min) {
463 self->min_initialized = true;
464 self->min = item->node->base.tag.id;
465 }
466 if (item->node->base.tag.id > self->max) {
467 self->max = item->node->base.tag.id;
468 }
469
470 capacity = node_set_get_capacity(self);
471 assert(capacity > self->size);
472
473 const size_t hash = node_set_hash(item->node->base.tag.id);
474
475 for (size_t i = 0; i < capacity; ++i) {
476 const size_t candidate = (hash + i) % capacity;
477
478 // if we found an empty slot or a previously deleted slot, we can insert
479 if (self->slots[candidate] == NULL || self->slots[candidate] == TOMBSTONE) {
480 self->slots[candidate] = item;
481 ++self->size;
482 return;
483 }
484 }
485
486 UNREACHABLE();
487}
488
490 assert(self != NULL);
491
492 // do we know immediately a node of this key has never been inserted?
493 if (self->min_initialized && key < self->min) {
494 return NULL;
495 }
496 if (key > self->max) {
497 return NULL;
498 }
499
500 const size_t hash = node_set_hash(key);
501 const size_t capacity = node_set_get_capacity(self);
502
503 for (size_t i = 0; i < capacity; ++i) {
504 const size_t candidate = (hash + i) % capacity;
505
506 // if we found an empty slot, the sought item does not exist
507 if (self->slots[candidate] == NULL) {
508 return NULL;
509 }
510
511 // if we found a previously deleted slot, skip over it
512 if (self->slots[candidate] == TOMBSTONE) {
513 continue;
514 }
515
516 if (agsubnodeideq(self->slots[candidate], key)) {
517 return self->slots[candidate];
518 }
519 }
520
521 return NULL;
522}
523
525 assert(self != NULL);
526
527 const size_t hash = node_set_hash(item);
528 const size_t capacity = node_set_get_capacity(self);
529
530 for (size_t i = 0; i < capacity; ++i) {
531 const size_t candidate = (hash + i) % capacity;
532
533 // if we found an empty slot, the sought item does not exist
534 if (self->slots[candidate] == NULL) {
535 return;
536 }
537
538 // if we found a previously deleted slot, skip over it
539 if (self->slots[candidate] == TOMBSTONE) {
540 continue;
541 }
542
543 if (agsubnodeideq(self->slots[candidate], item)) {
544 assert(self->size > 0);
545 self->slots[candidate] = TOMBSTONE;
546 --self->size;
547 return;
548 }
549 }
550}
551
552size_t node_set_size(const node_set_t *self) {
553 assert(self != NULL);
554 return self->size;
555}
556
558 assert(self != NULL);
559
560 if (*self != NULL) {
561 free((*self)->slots);
562 }
563
564 free(*self);
565 *self = NULL;
566}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void * gv_alloc(size_t size)
Definition alloc.h:47
int agapply(Agraph_t *g, Agobj_t *obj, agobjfn_t fn, void *arg, int preorder)
Definition apply.c:60
char * AgDataRecName
Definition attr.c:171
#define dtsearch(d, o)
Definition cdt.h:183
CDT_API int dtsize(Dt_t *)
Definition dtsize.c:12
#define dtfinger(d)
Definition cdt.h:177
#define dtinsert(d, o)
Definition cdt.h:185
#define dtprev(d, o)
Definition cdt.h:182
CDT_API void * dtrenew(Dt_t *, void *)
Definition dtrenew.c:9
#define dtlast(d)
Definition cdt.h:181
#define dtdelete(d, o)
Definition cdt.h:186
#define dtnext(d, o)
Definition cdt.h:180
#define dtfirst(d)
Definition cdt.h:179
cgraph.h additions
void agfreeid(Agraph_t *g, int objtype, IDTYPE id)
Definition id.c:146
void agrecclose(Agobj_t *obj)
Definition rec.c:227
int agmapnametoid(Agraph_t *g, int objtype, char *str, IDTYPE *result, bool createflag)
Definition id.c:112
void agdeledgeimage(Agraph_t *g, Agedge_t *edge, void *ignored)
Definition edge.c:304
int agallocid(Agraph_t *g, int objtype, IDTYPE request)
Definition id.c:141
uint64_t agnextseq(Agraph_t *g, int objtype)
Definition graph.c:164
#define FAILURE
Definition cghdr.h:45
void agregister(Agraph_t *g, int objtype, void *obj)
Definition id.c:185
#define SUCCESS
Definition cghdr.h:44
@ SEQ_MASK
Definition cghdr.h:77
static Agnode_t * agfindnode_by_name(Agraph_t *g, char *name)
Definition node.c:30
static Agsubnode_t *const TOMBSTONE
a sentinel, marking a set slot from which an element has been deleted
Definition node.c:398
static int agsubnodeseqcmpf(void *arg0, void *arg1)
Definition node.c:288
Agsubnode_t * node_set_find(node_set_t *self, IDTYPE key)
Definition node.c:489
static size_t node_set_hash(IDTYPE id)
Definition node.c:423
Agnode_t * agfindnode_by_id(Agraph_t *g, IDTYPE id)
Definition node.c:22
void agdelnodeimage(Agraph_t *g, Agnode_t *n, void *ignored)
Definition node.c:177
Dtdisc_t Ag_subnode_seq_disc
Definition node.c:311
static void installnode(Agraph_t *g, Agnode_t *n)
Definition node.c:89
static void installnodetoroot(Agraph_t *g, Agnode_t *n)
Definition node.c:106
static void initnode(Agraph_t *g, Agnode_t *n)
Definition node.c:114
static void dict_relabel(Agraph_t *ignored, Agnode_t *n, void *arg)
Definition node.c:220
void node_set_free(node_set_t **self)
Definition node.c:557
static void agnoderenew(Agraph_t *g, Agnode_t *n, void *ignored)
Definition node.c:325
size_t node_set_size(const node_set_t *self)
Definition node.c:552
static bool agsubnodeideq(const Agsubnode_t *sn0, IDTYPE id)
Definition node.c:284
static Agnode_t * newnode(Agraph_t *g, IDTYPE id, uint64_t seq)
Definition node.c:72
node_set_t * node_set_new(void)
Definition node.c:413
void node_set_add(node_set_t *self, Agsubnode_t *item)
Definition node.c:425
void node_set_remove(node_set_t *self, IDTYPE item)
Definition node.c:524
static size_t node_set_get_capacity(const node_set_t *self)
Definition node.c:408
static void free_subnode(void *subnode)
Definition node.c:305
static void agnodesetfinger(Agraph_t *g, Agnode_t *n, void *ignored)
Definition node.c:317
#define hash
Definition dthdr.h:13
void free(void *)
node NULL
Definition grammar.y:163
void agnodeattr_init(Agraph_t *g, Agnode_t *n)
Definition attr.c:401
void agnodeattr_delete(Agnode_t *n)
Definition attr.c:410
void agmethod_delete(Agraph_t *g, void *obj)
Definition obj.c:138
void agmethod_init(Agraph_t *g, void *obj)
Definition obj.c:78
int agdeledge(Agraph_t *g, Agedge_t *arg_e)
Definition edge.c:334
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:94
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:85
#define AGSNMAIN(sn)
Definition cgraph.h:908
void(* agobjfn_t)(Agraph_t *g, Agobj_t *obj, void *arg)
Definition cgraph.h:369
Agnode_t * agnode(Agraph_t *g, char *name, int cflag)
Definition node.c:145
int agnodebefore(Agnode_t *fst, Agnode_t *snd)
Definition node.c:332
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agprvnode(Agraph_t *g, Agnode_t *n)
Definition node.c:62
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n0, int cflag)
Definition node.c:259
Agnode_t * agidnode(Agraph_t *g, IDTYPE id, int cflag)
Definition node.c:122
Agnode_t * aglstnode(Agraph_t *g)
Definition node.c:55
int agrelabel_node(Agnode_t *n, char *newname)
Definition node.c:237
int agdelnode(Agraph_t *g, Agnode_t *n)
removes a node from a graph or subgraph.
Definition node.c:195
Agsubnode_t * agsubrep(Agraph_t *g, Agnode_t *n)
Definition edge.c:145
Agraph_t * agraphof(void *obj)
Definition obj.c:185
#define AGID(obj)
returns the unique integer ID associated with the object
Definition cgraph.h:221
uint64_t IDTYPE
unique per main graph ID
Definition cgraph.h:73
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
Agraph_t * agroot(void *obj)
Definition obj.c:168
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGNODE
Definition cgraph.h:207
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:89
Agraph_t * agparent(Agraph_t *g)
Definition subg.c:91
static uint64_t id
Definition gv2gml.c:42
unordered set of Agsubnode_t *
string attribute container
Definition cgraph.h:629
uint64_t seq[3]
Definition cgraph.h:415
unsigned has_attrs
Definition cgraph.h:290
Agsubnode_t mainsub
Definition cgraph.h:262
Agraph_t * root
Definition cgraph.h:261
Agobj_t base
Definition cgraph.h:260
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
Agtag_t tag
access with AGTAG
Definition cgraph.h:211
graph or subgraph
Definition cgraph.h:425
struct graphviz_node_set * n_id
the node set indexed by ID
Definition cgraph.h:431
Agraph_t * root
subgraphs - ancestors
Definition cgraph.h:434
Dict_t * n_seq
the node set in sequence
Definition cgraph.h:430
Agclos_t * clos
shared resources
Definition cgraph.h:435
Agdesc_t desc
Definition cgraph.h:427
This is the node struct allocated per graph (or subgraph).
Definition cgraph.h:251
Agnode_t * node
Definition cgraph.h:254
IDTYPE id
Definition cgraph.h:203
int link
Definition cdt.h:87
bool min_initialized
Definition node.c:392
size_t capacity_exp
logâ‚‚ size of slots
Definition node.c:383
size_t size
number of elements in the set
Definition node.c:382
Agsubnode_t ** slots
backing store for elements
Definition node.c:381
Definition utils.c:747
#define UNREACHABLE()
Definition unreachable.h:30