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