6 static struct graphpin
*get_last_pin(struct graphnode
*node
)
9 for (pin
=node
->pins
; pin
&& pin
->next
; pin
=pin
->next
);
13 err_t
graphnode_add_pin(struct graphnode
*node
, struct graphpin
**pin_out
)
15 // TODO: allocate node + pins in one block?
16 struct graphpin
*pin
= malloc(sizeof *pin
), *pin_prev
;
19 return make_error(ENOMEM
, node
, "memory full: allocating graph node pin");
21 memset(pin
, 0, sizeof *pin
);
25 pin_prev
= get_last_pin(node
);
36 err_t
graphnode_create(struct graphnode
**node_out
, const struct graphnode_functab
*functab
, size_t extra_bytes
)
38 struct graphnode
*node
;
39 node
= malloc(sizeof *node
+ extra_bytes
);
41 return make_error(ENOMEM
, NULL
, "memory full: allocating graph node");
43 memset(node
, 0, sizeof *node
);
44 node
->functab
= functab
;
45 node
->extra
= node
->extra_data
;
50 void graphnode_free(struct graphnode
*node
)
52 struct graphpin
*pin
, *pin_next
;
53 struct graphedge
*edge
;
60 // disconnect it (free the edge)
64 free(edge
); // XXX: may need more freeing than this in the future
73 bool graphnode_is_source(struct graphnode
*node
)
76 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
77 if (pin
->dir
!= DIR_OUT
){
84 bool graphnode_all_inputs_connected(struct graphnode
*node
)
87 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
88 if (pin
->dir
== DIR_IN
&& !pin
->edge
){
95 err_t
graph_create(struct graph
*graph
)
97 graph
->node_first
= graph
->node_last
= NULL
;
101 void graph_destroy(struct graph
*graph
)
103 struct graphnode
*node
= graph
->node_first
, *node_next
;
105 node_next
= node
->next
;
106 graphnode_free(node
);
109 graph
->node_first
= graph
->node_last
= NULL
;
112 err_t
graph_add_node(struct graph
*graph
, struct graphnode
*node
)
114 node
->prev
= graph
->node_last
;
116 *(graph
->node_last
? &graph
->node_last
->next
: &graph
->node_first
) = node
;
117 graph
->node_last
= node
;
121 err_t
graph_remove_node(struct graph
*graph
, struct graphnode
*node
)
123 *(node
->prev
? &node
->prev
->next
: &graph
->node_first
) = node
->next
;
124 *(node
->next
? &node
->next
->prev
: &graph
->node_last
) = node
->prev
;
128 static err_t
do_connect(struct graph
*graph
, struct graphpin
*a
, struct graphpin
*b
, const struct aformat
*af
)
130 struct graphedge
*edge
= malloc(sizeof *edge
);
134 return make_error(ENOMEM
, graph
, "memory full: allocating edge");
137 buffer_init(&edge
->buf
);
138 edge
->buf
.format
= *af
;
140 if (a
->node
->functab
->set_buffer
){
141 err
= a
->node
->functab
->set_buffer(a
->node
, a
, &edge
->buf
);
148 if (b
->node
->functab
->set_buffer
){
149 err
= b
->node
->functab
->set_buffer(b
->node
, b
, &edge
->buf
);
158 a
->edge
= b
->edge
= edge
;
162 err_t
graph_connect(struct graph
*graph
, struct graphpin
*a
, struct graphpin
*b
)
167 // check a and b direction
168 if (!(a
->dir
== DIR_OUT
&& b
->dir
== DIR_IN
)){
170 return make_error(EPINDIR
, graph
,
171 "incorrect pin directions while trying to connect");
173 // if a is a source node, it will be able to provide us with a media type
174 // if a is not a source node, all its inputs must be connected
175 // otherwise we won't know what output format to use.
176 if (graphnode_all_inputs_connected(a
->node
)){
177 // get media type from source
178 err
= a
->node
->functab
->get_output_format(a
->node
, a
, &af
);
182 // check media type with b node
183 if (b
->node
->functab
->is_acceptable_input_format
184 && !b
->node
->functab
->is_acceptable_input_format(b
->node
, b
, &af
)){
185 return make_error(ENO_AGREEABLE_FORMAT
, graph
, "cannot agree on a common media format");
187 // actually do the connection
188 return do_connect(graph
, a
, b
, &af
);
190 // there are unconnected inputs, so we can't connect
191 // (because we don't know what output format to use
192 // without an input format)
193 return make_error(EUNCONNECTED
, graph
,
194 "unconnected input pins: cannot determine media type");
199 // count the number of unprocessed incoming edges of a node
200 static int count_incoming(struct graphnode
*node
)
202 struct graphpin
*pin
;
204 for (pin
=node
->pins
; pin
; pin
=pin
->next
){
205 if (pin
->dir
== DIR_IN
&& pin
->edge
&& !pin
->edge
->processed
){
212 err_t
graph_sort(struct graph
*graph
)
216 struct stack_item
*prev
;
217 struct graphnode
*node
;
218 } *S
= NULL
, *new, *old
;
220 struct graphnode
*n
, *m
, *prev_L
, **next_ptr_ptr
;
221 struct graphpin
*pin
;
222 struct graphedge
*edge
;
224 // find all source nodes
225 for (n
=graph
->node_first
; n
; n
=n
->next
){
226 if (graphnode_is_source(n
)){
228 new = malloc(sizeof *new); // TODO: use alloca or something?
232 return make_error(ENOMEM
, NULL
, "memory full: allocating stack item");
240 // while S is not empty
241 next_ptr_ptr
= &graph
->sorted_nodes
;
244 // remove a node n from S
249 // insert n into L (the sorted list)
250 n
->sorted_prev
= prev_L
;
252 next_ptr_ptr
= &n
->sorted_next
;
254 // for each node m with an edge from n to m
255 for (pin
=n
->pins
; pin
; pin
=pin
->next
){
256 if (pin
->dir
!= DIR_OUT
257 || !pin
->edge
// pin isn't connected (ignore it)
258 || pin
->edge
->processed
// edge already 'removed' by algorithm
264 // remove edge from graph (we actually want to keep it, so we just
265 // mark it, and not do any real removal)
266 edge
->processed
= true;
267 // does it have any other incoming edges?
268 if (count_incoming(m
) == 0){
271 new = malloc(sizeof *new);
275 return make_error(ENOMEM
, NULL
, "memory full: allocating stack item");
283 // set the next pointer of the last node in the sequence to NULL
284 // this finishes off the linked list.
285 *next_ptr_ptr
= NULL
;
287 // TODO: if the graph still has unprocessed edges, then the graph
288 // has at least one cycle. Maybe we should do something about that.
293 err_t
graph_run(struct graph
*graph
)
295 struct graphnode
*node
;
298 for (node
=graph
->sorted_nodes
; node
; node
=node
->sorted_next
){
299 if (node
->functab
->run
){
300 err
= node
->functab
->run(node
);