Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
edge.c
Go to the documentation of this file.
1
6/*************************************************************************
7 * Copyright (c) 2011 AT&T Intellectual Property
8 * All rights reserved. This program and the accompanying materials
9 * are made available under the terms of the Eclipse Public License v1.0
10 * which accompanies this distribution, and is available at
11 * https://www.eclipse.org/legal/epl-v10.html
12 *
13 * Contributors: Details at https://graphviz.org
14 *************************************************************************/
15
16#include <assert.h>
17#include <cgraph/cghdr.h>
18#include <cgraph/node_set.h>
19#include <stdbool.h>
20#include <stdlib.h>
21#include <util/alloc.h>
22
23/* return first outedge of <n> */
25{
26 Agsubnode_t *sn;
27 Agedge_t *e = NULL;
28
29 sn = agsubrep(g, n);
30 if (sn) {
31 dtrestore(g->e_seq, sn->out_seq);
32 e = dtfirst(g->e_seq);
33 sn->out_seq = dtextract(g->e_seq);
34 }
35 return e;
36}
37
38/* return outedge that follows <e> of <n> */
40{
41 Agnode_t *n;
42 Agsubnode_t *sn;
43 Agedge_t *f = NULL;
44
45 n = AGTAIL(e);
46 sn = agsubrep(g, n);
47 if (sn) {
48 dtrestore(g->e_seq, sn->out_seq);
49 f = dtnext(g->e_seq, e);
50 sn->out_seq = dtextract(g->e_seq);
51 }
52 return f;
53}
54
56{
57 Agsubnode_t *sn;
58 Agedge_t *e = NULL;
59
60 sn = agsubrep(g, n);
61 if (sn) {
62 dtrestore(g->e_seq, sn->in_seq);
63 e = dtfirst(g->e_seq);
64 sn->in_seq = dtextract(g->e_seq);
65 }
66 return e;
67}
68
70{
71 Agnode_t *n;
72 Agsubnode_t *sn;
73 Agedge_t *f = NULL;
74
75 n = AGHEAD(e);
76 sn = agsubrep(g, n);
77 if (sn) {
78 dtrestore(g->e_seq, sn->in_seq);
79 f = dtnext(g->e_seq, e);
80 sn->in_seq = dtextract(g->e_seq);
81 }
82 return f;
83}
84
86{
87 Agedge_t *rv;
88 rv = agfstout(g, n);
89 if (rv == NULL)
90 rv = agfstin(g, n);
91 return rv;
92}
93
95{
96 Agedge_t *rv;
97
98 if (AGTYPE(e) == AGOUTEDGE) {
99 rv = agnxtout(g, e);
100 if (rv == NULL) {
101 do {
102 rv = !rv ? agfstin(g, n) : agnxtin(g,rv);
103 } while (rv && rv->node == n);
104 }
105 } else {
106 do {
107 rv = agnxtin(g, e); /* so that we only see each edge once, */
108 e = rv;
109 } while (rv && rv->node == n); /* ignore loops as in-edges */
110 }
111 return rv;
112}
113
114/* internal edge set lookup */
116 Agtag_t key)
117{
118 Agedge_t *e, template;
119 Agsubnode_t *sn;
120
121 if (t == NULL || h == NULL)
122 return NULL;
123 template.base.tag = key;
124 template.node = t; /* guess that fan-in < fan-out */
125 sn = agsubrep(g, h);
126 if (!sn) e = 0;
127 else {
128 dtrestore(g->e_id, sn->in_id);
129 e = dtsearch(g->e_id, &template);
130 sn->in_id = dtextract(g->e_id);
131 }
132 return e;
133}
134
136 IDTYPE id)
137{
138 Agtag_t tag = {0};
139
140 tag.objtype = AGEDGE;
141 tag.id = id;
142 return agfindedge_by_key(g, t, h, tag);
143}
144
146{
147 return g == n->root ? &n->mainsub : node_set_find(g->n_id, n->base.tag.id);
148}
149
150static void ins(Dict_t * d, Dtlink_t ** set, Agedge_t * e)
151{
152 dtrestore(d, *set);
153 dtinsert(d, e);
154 *set = dtextract(d);
155}
156
157static void del(Dict_t * d, Dtlink_t ** set, Agedge_t * e)
158{
159 void *x;
160 (void)x;
161 dtrestore(d, *set);
162 x = dtdelete(d, e);
163 assert(x);
164 *set = dtextract(d);
165}
166
167static void installedge(Agraph_t * g, Agedge_t * e)
168{
169 Agnode_t *t, *h;
170 Agedge_t *out, *in;
171 Agsubnode_t *sn;
172
173 out = AGMKOUT(e);
174 in = AGMKIN(e);
175 t = agtail(e);
176 h = aghead(e);
177 while (g) {
178 if (agfindedge_by_key(g, t, h, AGTAG(e))) break;
179 sn = agsubrep(g, t);
180 ins(g->e_seq, &sn->out_seq, out);
181 ins(g->e_id, &sn->out_id, out);
182 sn = agsubrep(g, h);
183 ins(g->e_seq, &sn->in_seq, in);
184 ins(g->e_id, &sn->in_id, in);
185 g = agparent(g);
186 }
187}
188
189static void subedge(Agraph_t * g, Agedge_t * e)
190{
191 installedge(g, e);
192 /* might an init method call be needed here? */
193}
194
196 IDTYPE id)
197{
198 Agedge_t *in, *out;
199
200 (void)agsubnode(g, t, 1);
201 (void)agsubnode(g, h, 1);
202 Agedgepair_t *e2 = gv_alloc(sizeof(Agedgepair_t));
203 in = &(e2->in);
204 out = &(e2->out);
205 uint64_t seq = agnextseq(g, AGEDGE);
206 assert((seq & SEQ_MASK) == seq && "sequence ID overflow");
207 AGTYPE(in) = AGINEDGE;
209 AGID(in) = AGID(out) = id;
210 AGSEQ(in) = AGSEQ(out) = seq & SEQ_MASK;
211 in->node = t;
212 out->node = h;
213
214 installedge(g, out);
215 if (g->desc.has_attrs) {
216 (void)agbindrec(out, AgDataRecName, sizeof(Agattr_t), false);
218 }
219 agmethod_init(g, out);
220 return out;
221}
222
223/* edge creation predicate */
224static bool ok_to_make_edge(Agraph_t *g, Agnode_t *t, Agnode_t *h) {
225 Agtag_t key = {0};
226
227 /* protect against self, multi-edges in strict graphs */
228 if (agisstrict(g)) {
229 key.objtype = 0; /* wild card */
230 if (agfindedge_by_key(g, t, h, key))
231 return false;
232 }
233 if (g->desc.no_loop && (t == h)) /* simple graphs */
234 return false;
235 return true;
236}
237
239 IDTYPE id, int cflag)
240{
241 Agraph_t *root;
242 Agedge_t *e;
243
244 e = agfindedge_by_id(g, t, h, id);
245 if (e == NULL && agisundirected(g))
246 e = agfindedge_by_id(g, h, t, id);
247 if (e == NULL && cflag && ok_to_make_edge(g, t, h)) {
248 root = agroot(g);
249 if (g != root && ((e = agfindedge_by_id(root, t, h, id)))) {
250 subedge(g, e); /* old */
251 } else {
252 if (agallocid(g, AGEDGE, id)) {
253 e = newedge(g, t, h, id); /* new */
254 }
255 }
256 }
257 return e;
258}
259
260Agedge_t *agedge(Agraph_t * g, Agnode_t * t, Agnode_t * h, char *name,
261 int cflag)
262{
263 Agedge_t *e;
264 IDTYPE my_id;
265
266 int have_id = agmapnametoid(g, AGEDGE, name, &my_id, false);
267 if (have_id || (name == NULL && (!cflag || agisstrict(g)))) {
268 /* probe for pre-existing edge */
269 Agtag_t key = {0};
270 if (have_id) {
271 key.id = my_id;
272 key.objtype = AGEDGE;
273 } else {
274 key.id = key.objtype = 0;
275 }
276
277 /* might already exist locally */
278 e = agfindedge_by_key(g, t, h, key);
279 if (e == NULL && agisundirected(g))
280 e = agfindedge_by_key(g, h, t, key);
281 if (e)
282 return e;
283 if (cflag) {
284 e = agfindedge_by_key(agroot(g), t, h, key);
285 if (e == NULL && agisundirected(g))
286 e = agfindedge_by_key(agroot(g), h, t, key);
287 if (e) {
288 subedge(g,e);
289 return e;
290 }
291 }
292 }
293
294 if (cflag && ok_to_make_edge(g, t, h)
295 && agmapnametoid(g, AGEDGE, name, &my_id, true)) { /* reserve id */
296 e = newedge(g, t, h, my_id);
297 agregister(g, AGEDGE, e); /* register new object in external namespace */
298 }
299 else
300 e = NULL;
301 return e;
302}
303
304void agdeledgeimage(Agraph_t * g, Agedge_t * e, void *ignored)
305{
306 Agedge_t *in, *out;
307 Agnode_t *t, *h;
308 Agsubnode_t *sn;
309
310 (void)ignored;
311 if (AGTYPE(e) == AGINEDGE) {
312 in = e;
313 out = AGIN2OUT(e);
314 } else {
315 out = e;
316 in = AGOUT2IN(e);
317 }
318 t = in->node;
319 h = out->node;
320 sn = agsubrep(g, t);
321 del(g->e_seq, &sn->out_seq, out);
322 del(g->e_id, &sn->out_id, out);
323 sn = agsubrep(g, h);
324 del(g->e_seq, &sn->in_seq, in);
325 del(g->e_id, &sn->in_id, in);
326#ifdef DEBUG
327 for (e = agfstin(g,h); e; e = agnxtin(g,e))
328 assert(e != in);
329 for (e = agfstout(g,t); e; e = agnxtout(g,e))
330 assert(e != out);
331#endif
332}
333
335{
336 e = AGMKOUT(e);
337 if (agfindedge_by_key(g, agtail(e), aghead(e), AGTAG(e)) == NULL)
338 return FAILURE;
339
340 if (g == agroot(g)) {
341 if (g->desc.has_attrs)
343 agmethod_delete(g, e);
344 agrecclose((Agobj_t *) e);
345 agfreeid(g, AGEDGE, AGID(e));
346 }
347 if (agapply(g, (Agobj_t *)e, (agobjfn_t)agdeledgeimage, NULL, false) == SUCCESS) {
348 if (g == agroot(g))
349 free(e);
350 return SUCCESS;
351 } else
352 return FAILURE;
353}
354
355Agedge_t *agsubedge(Agraph_t * g, Agedge_t * e, int cflag)
356{
357 Agnode_t *t;
358 Agedge_t *rv;
359
360 rv = NULL;
361 t = agsubnode(g, AGTAIL(e), cflag);
362 if (t != NULL || cflag) {
363 Agnode_t *const h = agsubnode(g, AGHEAD(e), cflag);
364 if (t != NULL && h != NULL) {
365 rv = agfindedge_by_key(g, t, h, AGTAG(e));
366 if (cflag && rv == NULL) {
367 installedge(g, e);
368 rv = e;
369 }
370 if (rv && (AGTYPE(rv) != AGTYPE(e)))
371 rv = AGOPP(rv);
372 }
373 }
374 return rv;
375}
376
377/* edge comparison. AGTYPE(e) == 0 means ID is a wildcard. */
378static int agedgeidcmpf(void *arg_e0, void *arg_e1) {
379 Agedge_t *e0 = arg_e0;
380 Agedge_t *e1 = arg_e1;
381
382 if (AGID(e0->node) < AGID(e1->node)) return -1;
383 if (AGID(e0->node) > AGID(e1->node)) return 1;
384 /* same node */
385 if (AGTYPE(e0) != 0 && AGTYPE(e1) != 0) {
386 if (AGID(e0) < AGID(e1)) return -1;
387 if (AGID(e0) > AGID(e1)) return 1;
388 }
389 return 0;
390}
391
392/* edge comparison. for ordered traversal. */
393static int agedgeseqcmpf(void *arg_e0, void *arg_e1) {
394 Agedge_t *e0 = arg_e0;
395 Agedge_t *e1 = arg_e1;
396 assert(arg_e0 && arg_e1);
397
398 if (e0->node != e1->node) {
399 if (AGSEQ(e0->node) < AGSEQ(e1->node)) return -1;
400 if (AGSEQ(e0->node) > AGSEQ(e1->node)) return 1;
401 }
402 else {
403 if (AGSEQ(e0) < AGSEQ(e1)) return -1;
404 if (AGSEQ(e0) > AGSEQ(e1)) return 1;
405 }
406 return 0;
407}
408
409/* indexing for ordered traversal */
411 .link = offsetof(Agedge_t, seq_link), // use internal links
412 .comparf = agedgeseqcmpf,
413};
414
416 .link = -1, // use external holder objects
417 .comparf = agedgeseqcmpf,
418};
419
420/* indexing for random search */
422 .link = offsetof(Agedge_t, id_link), // use internal links
423 .comparf = agedgeidcmpf,
424};
425
427 .link = -1, // use external holder objects
428 .comparf = agedgeidcmpf,
429};
430
431/* expose macros as functions for ease of debugging
432and to expose them to foreign languages without C preprocessor. */
433#ifdef ageqedge
434#undef ageqedge
435#endif
436CGRAPH_API int ageqedge(Agedge_t *e, Agedge_t *f);
437CGRAPH_API int ageqedge(Agedge_t * e, Agedge_t * f)
438{
439 return AGEQEDGE(e, f);
440}
441
442#ifdef agmkout
443#undef agmkout
444#endif
445CGRAPH_API Agedge_t *agmkout(Agedge_t *e);
446CGRAPH_API Agedge_t *agmkout(Agedge_t * e)
447{
448 return AGMKOUT(e);
449}
450
451#ifdef agmkin
452#undef agmkin
453#endif
454CGRAPH_API Agedge_t *agmkin(Agedge_t *e);
455CGRAPH_API Agedge_t *agmkin(Agedge_t * e)
456{
457 return AGMKIN(e);
458}
459
460#ifdef agtail
461#undef agtail
462#endif
463CGRAPH_API Agnode_t *agtail(Agedge_t *e);
464CGRAPH_API Agnode_t *agtail(Agedge_t * e)
465{
466 return AGTAIL(e);
467}
468
469#ifdef aghead
470#undef aghead
471#endif
472CGRAPH_API Agnode_t *aghead(Agedge_t *e);
473CGRAPH_API Agnode_t *aghead(Agedge_t * e)
474{
475 return AGHEAD(e);
476}
477
478#ifdef agopp
479#undef agopp
480#endif
481CGRAPH_API Agedge_t *agopp(Agedge_t *e);
482CGRAPH_API Agedge_t *agopp(Agedge_t * e)
483{
484 return AGOPP(e);
485}
static void out(agerrlevel_t level, const char *fmt, va_list args)
Report messages using a user-supplied or default write function.
Definition agerror.c:84
Memory allocation wrappers that exit on failure.
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
CDT_API Dtlink_t * dtextract(Dt_t *)
Definition dtextract.c:9
#define dtsearch(d, o)
Definition cdt.h:183
#define dtinsert(d, o)
Definition cdt.h:185
#define dtdelete(d, o)
Definition cdt.h:186
CDT_API int dtrestore(Dt_t *, Dtlink_t *)
Definition dtrestore.c:11
#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
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
Agsubnode_t * node_set_find(node_set_t *self, IDTYPE key)
Definition node.c:489
static bool ok_to_make_edge(Agraph_t *g, Agnode_t *t, Agnode_t *h)
Definition edge.c:224
static int agedgeseqcmpf(void *arg_e0, void *arg_e1)
Definition edge.c:393
static Agedge_t * agfindedge_by_id(Agraph_t *g, Agnode_t *t, Agnode_t *h, IDTYPE id)
Definition edge.c:135
static Agedge_t * newedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, IDTYPE id)
Definition edge.c:195
void agdeledgeimage(Agraph_t *g, Agedge_t *e, void *ignored)
Definition edge.c:304
Agedge_t * agmkout(Agedge_t *e)
Definition edge.c:446
Agedge_t * agmkin(Agedge_t *e)
Definition edge.c:455
static void subedge(Agraph_t *g, Agedge_t *e)
Definition edge.c:189
Dtdisc_t Ag_subedge_seq_disc
Definition edge.c:415
static Agedge_t * agfindedge_by_key(Agraph_t *g, Agnode_t *t, Agnode_t *h, Agtag_t key)
Definition edge.c:115
static int agedgeidcmpf(void *arg_e0, void *arg_e1)
Definition edge.c:378
static void ins(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:150
Dtdisc_t Ag_mainedge_seq_disc
Definition edge.c:410
static void del(Dict_t *d, Dtlink_t **set, Agedge_t *e)
Definition edge.c:157
Dtdisc_t Ag_subedge_id_disc
Definition edge.c:426
Dtdisc_t Ag_mainedge_id_disc
Definition edge.c:421
static void installedge(Agraph_t *g, Agedge_t *e)
Definition edge.c:167
void free(void *)
node NULL
Definition grammar.y:163
void agedgeattr_init(Agraph_t *g, Agedge_t *e)
Definition attr.c:420
void agedgeattr_delete(Agedge_t *e)
Definition attr.c:429
void agmethod_delete(Agraph_t *g, void *obj)
Definition obj.c:138
void agmethod_init(Agraph_t *g, void *obj)
Definition obj.c:78
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int cflag)
Definition edge.c:260
Agedge_t * agidedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, IDTYPE id, int cflag)
Definition edge.c:238
#define agopp(e)
opposite edge: flip Agedgepair_s.out ⇄ Agedgepair_s.in/*#end#*‍/
Definition cgraph.h:882
#define AGMKOUT(e)
Definition cgraph.h:874
Agedge_t * agsubedge(Agraph_t *g, Agedge_t *e, int cflag)
Definition edge.c:355
#define AGIN2OUT(inedge)
Agedgepair_s.in -> Agedgepair_s.out.
Definition cgraph.h:869
int agdeledge(Agraph_t *g, Agedge_t *e)
Definition edge.c:334
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:69
#define AGEQEDGE(e, f)
Definition cgraph.h:878
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:24
#define ageqedge(e, f)
edges are equal
Definition cgraph.h:885
#define AGMKIN(e)
Definition cgraph.h:875
#define agtail(e)
Definition cgraph.h:880
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:94
#define aghead(e)
Definition cgraph.h:881
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:85
#define AGTAIL(e)
Definition cgraph.h:876
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:55
#define AGOPP(e)
Definition cgraph.h:873
#define AGHEAD(e)
Definition cgraph.h:877
#define AGOUT2IN(outedge)
Agedgepair_s.out -> Agedgepair_s.in/*#end#*‍/.
Definition cgraph.h:870
void(* agobjfn_t)(Agraph_t *g, Agobj_t *obj, void *arg)
Definition cgraph.h:369
int agisstrict(Agraph_t *g)
Definition graph.c:200
int agisundirected(Agraph_t *g)
Definition graph.c:195
Agnode_t * agsubnode(Agraph_t *g, Agnode_t *n, int createflag)
Definition node.c:259
Agsubnode_t * agsubrep(Agraph_t *g, Agnode_t *n)
Definition edge.c:145
#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
#define AGTAG(obj)
Definition cgraph.h:215
Agraph_t * agroot(void *obj)
Definition obj.c:168
#define AGSEQ(obj)
Definition cgraph.h:225
@ AGOUTEDGE
Definition cgraph.h:207
@ AGEDGE
Definition cgraph.h:207
@ AGINEDGE
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
unsigned has_attrs
Definition cgraph.h:290
unsigned no_loop
Definition cgraph.h:287
Agnode_t * node
Definition cgraph.h:272
Agedge_t in
Definition cgraph.h:276
Agedge_t out
Definition cgraph.h:276
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
Dict_t * e_seq
Definition cgraph.h:432
Agdesc_t desc
Definition cgraph.h:427
Dict_t * e_id
holders for edge sets
Definition cgraph.h:432
This is the node struct allocated per graph (or subgraph).
Definition cgraph.h:251
Dtlink_t * out_seq
Definition cgraph.h:256
Dtlink_t * out_id
Definition cgraph.h:255
Dtlink_t * in_id
Definition cgraph.h:255
Dtlink_t * in_seq
Definition cgraph.h:256
tag in Agobj_s for graphs, nodes, and edges.
Definition cgraph.h:197
unsigned objtype
access with AGTYPE
Definition cgraph.h:199
IDTYPE id
Definition cgraph.h:203
Definition cdt.h:100
int link
Definition cdt.h:87