2 * Copyright (c) 2012 Sean Bartell
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @addtogroup bithenge
34 * Compound transforms.
38 #include <bithenge/compound.h>
39 #include <bithenge/expression.h>
40 #include <bithenge/transform.h>
41 #include <bithenge/tree.h>
46 /***************** compose_transform *****************/
49 bithenge_transform_t base
;
50 bithenge_transform_t
**xforms
;
52 } compose_transform_t
;
54 static bithenge_transform_t
*compose_as_transform(compose_transform_t
*xform
)
59 static compose_transform_t
*transform_as_compose(bithenge_transform_t
*xform
)
61 return (compose_transform_t
*)xform
;
64 static errno_t
compose_apply(bithenge_transform_t
*base
, bithenge_scope_t
*scope
,
65 bithenge_node_t
*in
, bithenge_node_t
**out
)
68 compose_transform_t
*self
= transform_as_compose(base
);
69 bithenge_node_inc_ref(in
);
71 /* i ranges from (self->num - 1) to 0 inside the loop. */
72 for (size_t i
= self
->num
; i
--; ) {
74 rc
= bithenge_transform_apply(self
->xforms
[i
], scope
, in
,
76 bithenge_node_dec_ref(in
);
86 static errno_t
compose_prefix_length(bithenge_transform_t
*base
,
87 bithenge_scope_t
*scope
, bithenge_blob_t
*blob
, aoff64_t
*out
)
89 compose_transform_t
*self
= transform_as_compose(base
);
90 return bithenge_transform_prefix_length(self
->xforms
[self
->num
- 1],
94 static void compose_destroy(bithenge_transform_t
*base
)
96 compose_transform_t
*self
= transform_as_compose(base
);
97 for (size_t i
= 0; i
< self
->num
; i
++)
98 bithenge_transform_dec_ref(self
->xforms
[i
]);
103 static const bithenge_transform_ops_t compose_transform_ops
= {
104 .apply
= compose_apply
,
105 .prefix_length
= compose_prefix_length
,
106 .destroy
= compose_destroy
,
109 /** Create a composition of multiple transforms. When the result is applied to a
110 * node, each transform is applied in turn, with the last transform applied
111 * first. @a xforms may contain any number of transforms or no transforms at
112 * all. This function takes ownership of @a xforms and the references therein.
113 * @param[out] out Holds the result.
114 * @param[in] xforms The transforms to apply.
115 * @param num The number of transforms.
116 * @return EOK on success or an error code from errno.h. */
117 errno_t
bithenge_new_composed_transform(bithenge_transform_t
**out
,
118 bithenge_transform_t
**xforms
, size_t num
)
122 } else if (num
== 1) {
129 compose_transform_t
*self
= malloc(sizeof(*self
));
134 rc
= bithenge_init_transform(compose_as_transform(self
),
135 &compose_transform_ops
, 0);
138 self
->xforms
= xforms
;
140 *out
= compose_as_transform(self
);
143 for (size_t i
= 0; i
< num
; i
++)
144 bithenge_transform_dec_ref(xforms
[i
]);
152 /***************** if_transform *****************/
155 bithenge_transform_t base
;
156 bithenge_expression_t
*expr
;
157 bithenge_transform_t
*true_xform
, *false_xform
;
160 static inline bithenge_transform_t
*if_as_transform(if_transform_t
*self
)
165 static inline if_transform_t
*transform_as_if(bithenge_transform_t
*base
)
167 return (if_transform_t
*)base
;
170 static errno_t
if_transform_choose(if_transform_t
*self
, bithenge_scope_t
*scope
,
173 bithenge_node_t
*cond_node
;
174 errno_t rc
= bithenge_expression_evaluate(self
->expr
, scope
, &cond_node
);
177 if (bithenge_node_type(cond_node
) != BITHENGE_NODE_BOOLEAN
) {
178 bithenge_node_dec_ref(cond_node
);
181 *out
= bithenge_boolean_node_value(cond_node
);
182 bithenge_node_dec_ref(cond_node
);
186 static errno_t
if_transform_apply(bithenge_transform_t
*base
,
187 bithenge_scope_t
*scope
, bithenge_node_t
*in
, bithenge_node_t
**out
)
189 if_transform_t
*self
= transform_as_if(base
);
191 errno_t rc
= if_transform_choose(self
, scope
, &cond
);
194 return bithenge_transform_apply(
195 cond
? self
->true_xform
: self
->false_xform
, scope
, in
, out
);
198 static errno_t
if_transform_prefix_length(bithenge_transform_t
*base
,
199 bithenge_scope_t
*scope
, bithenge_blob_t
*in
, aoff64_t
*out
)
201 if_transform_t
*self
= transform_as_if(base
);
203 errno_t rc
= if_transform_choose(self
, scope
, &cond
);
206 return bithenge_transform_prefix_length(
207 cond
? self
->true_xform
: self
->false_xform
, scope
, in
, out
);
210 static void if_transform_destroy(bithenge_transform_t
*base
)
212 if_transform_t
*self
= transform_as_if(base
);
213 bithenge_expression_dec_ref(self
->expr
);
214 bithenge_transform_dec_ref(self
->true_xform
);
215 bithenge_transform_dec_ref(self
->false_xform
);
219 static const bithenge_transform_ops_t if_transform_ops
= {
220 .apply
= if_transform_apply
,
221 .prefix_length
= if_transform_prefix_length
,
222 .destroy
= if_transform_destroy
,
225 /** Create a transform that applies either of two transforms depending on a
226 * boolean expression. Takes references to @a expr, @a true_xform, and
228 * @param[out] out Holds the new transform.
229 * @param expr The boolean expression to evaluate.
230 * @param true_xform The transform to apply if the expression is true.
231 * @param false_xform The transform to apply if the expression is false.
232 * @return EOK on success or an error code from errno.h. */
233 errno_t
bithenge_if_transform(bithenge_transform_t
**out
,
234 bithenge_expression_t
*expr
, bithenge_transform_t
*true_xform
,
235 bithenge_transform_t
*false_xform
)
238 if_transform_t
*self
= malloc(sizeof(*self
));
244 rc
= bithenge_init_transform(if_as_transform(self
), &if_transform_ops
,
250 self
->true_xform
= true_xform
;
251 self
->false_xform
= false_xform
;
252 *out
= if_as_transform(self
);
257 bithenge_expression_dec_ref(expr
);
258 bithenge_transform_dec_ref(true_xform
);
259 bithenge_transform_dec_ref(false_xform
);
265 /***************** partial_transform *****************/
268 bithenge_transform_t base
;
269 bithenge_transform_t
*xform
;
270 } partial_transform_t
;
272 static inline bithenge_transform_t
*partial_as_transform(
273 partial_transform_t
*self
)
278 static inline partial_transform_t
*transform_as_partial(
279 bithenge_transform_t
*base
)
281 return (partial_transform_t
*)base
;
284 static errno_t
partial_transform_apply(bithenge_transform_t
*base
,
285 bithenge_scope_t
*scope
, bithenge_node_t
*in
, bithenge_node_t
**out
)
287 partial_transform_t
*self
= transform_as_partial(base
);
288 if (bithenge_node_type(in
) != BITHENGE_NODE_BLOB
)
290 return bithenge_transform_prefix_apply(self
->xform
, scope
,
291 bithenge_node_as_blob(in
), out
, NULL
);
294 static void partial_transform_destroy(bithenge_transform_t
*base
)
296 partial_transform_t
*self
= transform_as_partial(base
);
297 bithenge_transform_dec_ref(self
->xform
);
301 static const bithenge_transform_ops_t partial_transform_ops
= {
302 .apply
= partial_transform_apply
,
303 .destroy
= partial_transform_destroy
,
306 /** Create a transform that doesn't require its subtransform to use the whole
307 * input. Takes a reference to @a xform.
308 * @param[out] out Holds the new transform.
309 * @param xform The subtransform to apply.
310 * @return EOK on success or an error code from errno.h. */
311 errno_t
bithenge_partial_transform(bithenge_transform_t
**out
,
312 bithenge_transform_t
*xform
)
315 partial_transform_t
*self
= malloc(sizeof(*self
));
321 rc
= bithenge_init_transform(partial_as_transform(self
),
322 &partial_transform_ops
, 0);
327 *out
= partial_as_transform(self
);
332 bithenge_transform_dec_ref(xform
);