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