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