Graphviz 13.0.0~dev.20241220.2304
Loading...
Searching...
No Matches
rec.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 <cgraph/cghdr.h>
17#include <stdbool.h>
18#include <stdlib.h>
19#include <util/streq.h>
20#include <util/alloc.h>
21#include <util/unreachable.h>
22
23/*
24 * run time records
25 */
26
27static void set_data(Agobj_t * obj, Agrec_t * data, bool mtflock)
28{
29 Agedge_t *e;
30
31 obj->data = data;
32 obj->tag.mtflock = mtflock;
33 if (AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE) {
34 e = agopp((Agedge_t *) obj);
35 AGDATA(e) = data;
36 e->base.tag.mtflock = mtflock;
37 }
38}
39
40/* find record in circular list and do optional move-to-front */
41Agrec_t *aggetrec(void *obj, const char *name, int mtf)
42{
43 Agobj_t *hdr;
44 Agrec_t *d, *first;
45
46 hdr = obj;
47 first = d = hdr->data;
48 while (d && !streq(name, d->name)) {
49 d = d->next;
50 if (d == first)
51 return NULL;
52 }
53 if (!d)
54 return NULL;
55
56 if (hdr->tag.mtflock) {
57 if (mtf && hdr->data != d)
58 agerrorf("move to front lock inconsistency");
59 } else if (d != first || mtf != 0) {
60 set_data(hdr, d, mtf != 0); /* Always optimize */
61 }
62 return d;
63}
64
65/* insert the record in data list of this object (only) */
66static void objputrec(Agobj_t * obj, void *arg)
67{
68 Agrec_t *firstrec, *newrec;
69
70 newrec = arg;
71 firstrec = obj->data;
72 if (firstrec == NULL)
73 newrec->next = newrec; /* 0 elts */
74 else {
75 if (firstrec->next == firstrec) {
76 firstrec->next = newrec; /* 1 elt */
77 newrec->next = firstrec;
78 } else {
79 newrec->next = firstrec->next;
80 firstrec->next = newrec;
81 }
82 }
83 if (!obj->tag.mtflock)
84 set_data(obj, newrec, false);
85}
86
87/* attach a new record of the given size to the object.
88 */
89void *agbindrec(void *arg_obj, const char *recname, unsigned int recsize,
90 int move_to_front)
91{
92 Agraph_t *g;
93 Agobj_t *obj;
94
95 obj = arg_obj;
96 g = agraphof(obj);
97 Agrec_t *rec = aggetrec(obj, recname, 0);
98 if (rec == NULL && recsize > 0) {
99 rec = gv_calloc(recsize, sizeof(char));
100 rec->name = agstrdup(g, recname);
101 objputrec(obj, rec);
102 }
103 if (move_to_front)
104 aggetrec(arg_obj, recname, 1);
105 return rec;
106}
107
108
109/* if obj points to rec, move its data pointer. break any mtf lock(?) */
110static void objdelrec(Agraph_t * g, Agobj_t * obj, void *arg_rec)
111{
112 (void)g;
113 Agrec_t *rec = arg_rec, *newrec;
114 if (obj->data == rec) {
115 if (rec->next == rec)
116 newrec = NULL;
117 else
118 newrec = rec->next;
119 set_data(obj, newrec, false);
120 }
121}
122
123/* delete a record from a circular data list */
124static void listdelrec(Agobj_t * obj, Agrec_t * rec)
125{
126 Agrec_t *prev;
127
128 prev = obj->data;
129 while (prev->next != rec) {
130 prev = prev->next;
131 assert(prev != obj->data);
132 }
133 /* following is a harmless no-op if the list is trivial */
134 prev->next = rec->next;
135}
136
137int agdelrec(void *arg_obj, const char *name)
138{
139 Agobj_t *obj = arg_obj;
140 Agraph_t *g = agraphof(obj);
141 Agrec_t *rec = aggetrec(obj, name, 0);
142 if (!rec)
143 return FAILURE;
144
145 listdelrec(obj, rec); /* zap it from the circular list */
146 switch (obj->tag.objtype) { /* refresh any stale pointers */
147 case AGRAPH:
148 objdelrec(g, obj, rec);
149 break;
150 case AGNODE:
151 case AGINEDGE:
152 case AGOUTEDGE:
153 agapply(agroot(g), obj, objdelrec, rec, false);
154 break;
155 default:
156 UNREACHABLE();
157 }
158 agstrfree(g, rec->name);
159 free(rec);
160
161 return SUCCESS;
162}
163
164static void simple_delrec(Agraph_t * g, Agobj_t * obj, void *rec_name)
165{
166 (void)g;
167 agdelrec(obj, rec_name);
168}
169
170void aginit(Agraph_t * g, int kind, const char *rec_name, int arg_rec_size,
171 int mtf) {
172 Agnode_t *n;
173 Agedge_t *e;
174 Agraph_t *s;
175 unsigned int rec_size;
176 bool recur = arg_rec_size < 0; /* if recursive on subgraphs */
177
178 rec_size = (unsigned int) abs(arg_rec_size);
179 switch (kind) {
180 case AGRAPH:
181 agbindrec(g, rec_name, rec_size, mtf);
182 if (recur)
183 for (s = agfstsubg(g); s; s = agnxtsubg(s))
184 aginit(s,kind,rec_name,arg_rec_size,mtf);
185 break;
186 case AGNODE:
187 case AGOUTEDGE:
188 case AGINEDGE:
189 for (n = agfstnode(g); n; n = agnxtnode(g, n))
190 if (kind == AGNODE)
191 agbindrec(n, rec_name, rec_size, mtf);
192 else {
193 for (e = agfstout(g, n); e; e = agnxtout(g, e))
194 agbindrec(e, rec_name, rec_size, mtf);
195 }
196 break;
197 default:
198 break;
199 }
200}
201
202void agclean(Agraph_t * g, int kind, char *rec_name)
203{
204 Agnode_t *n;
205 Agedge_t *e;
206
207 switch (kind) {
208 case AGRAPH:
209 agapply(g, (Agobj_t *)g, simple_delrec, rec_name, true);
210 break;
211 case AGNODE:
212 case AGOUTEDGE:
213 case AGINEDGE:
214 for (n = agfstnode(g); n; n = agnxtnode(g, n))
215 if (kind == AGNODE)
216 agdelrec(n, rec_name);
217 else {
218 for (e = agfstout(g, n); e; e = agnxtout(g, e))
219 agdelrec(e, rec_name);
220 }
221 break;
222 default:
223 break;
224 }
225}
226
228{
229 Agraph_t *g;
230 Agrec_t *rec, *nrec;
231
232 g = agraphof(obj);
233 if ((rec = obj->data)) {
234 do {
235 nrec = rec->next;
236 agstrfree(g, rec->name);
237 free(rec);
238 rec = nrec;
239 } while (rec != obj->data);
240 }
241 obj->data = NULL;
242}
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
int agapply(Agraph_t *g, Agobj_t *obj, agobjfn_t fn, void *arg, int preorder)
Definition apply.c:60
cgraph.h additions
#define FAILURE
Definition cghdr.h:45
#define SUCCESS
Definition cghdr.h:44
void free(void *)
node NULL
Definition grammar.y:163
#define agopp(e)
opposite edge: flip Agedgepair_s.out ⇄ Agedgepair_s.in/*#end#*‍/
Definition cgraph.h:882
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:24
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:39
void agerrorf(const char *fmt,...)
Definition agerror.c:165
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:47
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:40
Agraph_t * agraphof(void *obj)
Definition obj.c:185
#define AGDATA(obj)
returns Agrec_t
Definition cgraph.h:227
#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
@ AGOUTEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGINEDGE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
Agrec_t * aggetrec(void *obj, const char *name, int mtf)
find record in circular list and do optional move-to-front and lock
Definition rec.c:41
void aginit(Agraph_t *g, int kind, const char *rec_name, int arg_rec_size, int mtf)
attach new records to objects of specified kind
Definition rec.c:170
void * agbindrec(void *arg_obj, const char *recname, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:89
void agclean(Agraph_t *g, int kind, char *rec_name)
calls agdelrec for all objects of the same class in an entire graph
Definition rec.c:202
int agdelrec(void *arg_obj, const char *name)
deletes a named record from one object
Definition rec.c:137
int agstrfree(Agraph_t *, const char *)
Definition refstr.c:139
char * agstrdup(Agraph_t *, const char *)
returns a pointer to a reference-counted copy of the argument string, creating one if necessary
Definition refstr.c:131
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:78
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:83
$2 u p prev
Definition htmlparse.y:297
void agrecclose(Agobj_t *obj)
Definition rec.c:227
static void objdelrec(Agraph_t *g, Agobj_t *obj, void *arg_rec)
Definition rec.c:110
static void listdelrec(Agobj_t *obj, Agrec_t *rec)
Definition rec.c:124
static void simple_delrec(Agraph_t *g, Agobj_t *obj, void *rec_name)
Definition rec.c:164
static void objputrec(Agobj_t *obj, void *arg)
Definition rec.c:66
static void set_data(Agobj_t *obj, Agrec_t *data, bool mtflock)
Definition rec.c:27
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
Agobj_t base
Definition cgraph.h:269
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
Agrec_t * data
stores programmer-defined data, access with AGDATA
Definition cgraph.h:212
Agtag_t tag
access with AGTAG
Definition cgraph.h:211
graph or subgraph
Definition cgraph.h:425
implementation of Agrec_t
Definition cgraph.h:172
Agrec_t * next
circular linked list of records
Definition cgraph.h:174
char * name
Definition cgraph.h:173
unsigned objtype
access with AGTYPE
Definition cgraph.h:199
unsigned mtflock
move-to-front lock, guards Agobj_s::data
Definition cgraph.h:200
Definition legal.c:50
Definition grammar.c:93
#define UNREACHABLE()
Definition unreachable.h:30