1 | /*
|
---|
2 | * Copyright (c) 2012 Sean Bartell
|
---|
3 | * All rights reserved.
|
---|
4 | *
|
---|
5 | * Redistribution and use in source and binary forms, with or without
|
---|
6 | * modification, are permitted provided that the following conditions
|
---|
7 | * are met:
|
---|
8 | *
|
---|
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.
|
---|
16 | *
|
---|
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.
|
---|
27 | */
|
---|
28 |
|
---|
29 | /** @addtogroup bithenge
|
---|
30 | * @{
|
---|
31 | */
|
---|
32 | /**
|
---|
33 | * @file
|
---|
34 | * Compound transforms.
|
---|
35 | */
|
---|
36 |
|
---|
37 | #include <stdlib.h>
|
---|
38 | #include <bithenge/compound.h>
|
---|
39 | #include <bithenge/expression.h>
|
---|
40 | #include <bithenge/os.h>
|
---|
41 | #include <bithenge/transform.h>
|
---|
42 | #include <bithenge/tree.h>
|
---|
43 |
|
---|
44 |
|
---|
45 |
|
---|
46 | /***************** compose_transform *****************/
|
---|
47 |
|
---|
48 | typedef struct {
|
---|
49 | bithenge_transform_t base;
|
---|
50 | bithenge_transform_t **xforms;
|
---|
51 | size_t num;
|
---|
52 | } compose_transform_t;
|
---|
53 |
|
---|
54 | static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
|
---|
55 | {
|
---|
56 | return &xform->base;
|
---|
57 | }
|
---|
58 |
|
---|
59 | static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
|
---|
60 | {
|
---|
61 | return (compose_transform_t *)xform;
|
---|
62 | }
|
---|
63 |
|
---|
64 | static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
|
---|
65 | bithenge_node_t *in, bithenge_node_t **out)
|
---|
66 | {
|
---|
67 | int rc;
|
---|
68 | compose_transform_t *self = transform_as_compose(base);
|
---|
69 | bithenge_node_inc_ref(in);
|
---|
70 |
|
---|
71 | /* i ranges from (self->num - 1) to 0 inside the loop. */
|
---|
72 | for (size_t i = self->num; i--; ) {
|
---|
73 | bithenge_node_t *tmp;
|
---|
74 | rc = bithenge_transform_apply(self->xforms[i], scope, in,
|
---|
75 | &tmp);
|
---|
76 | bithenge_node_dec_ref(in);
|
---|
77 | if (rc != EOK)
|
---|
78 | return rc;
|
---|
79 | in = tmp;
|
---|
80 | }
|
---|
81 |
|
---|
82 | *out = in;
|
---|
83 | return rc;
|
---|
84 | }
|
---|
85 |
|
---|
86 | static int compose_prefix_length(bithenge_transform_t *base,
|
---|
87 | bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
|
---|
88 | {
|
---|
89 | compose_transform_t *self = transform_as_compose(base);
|
---|
90 | return bithenge_transform_prefix_length(self->xforms[self->num - 1],
|
---|
91 | scope, blob, out);
|
---|
92 | }
|
---|
93 |
|
---|
94 | static void compose_destroy(bithenge_transform_t *base)
|
---|
95 | {
|
---|
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]);
|
---|
99 | free(self->xforms);
|
---|
100 | free(self);
|
---|
101 | }
|
---|
102 |
|
---|
103 | static const bithenge_transform_ops_t compose_transform_ops = {
|
---|
104 | .apply = compose_apply,
|
---|
105 | .prefix_length = compose_prefix_length,
|
---|
106 | .destroy = compose_destroy,
|
---|
107 | };
|
---|
108 |
|
---|
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 | int bithenge_new_composed_transform(bithenge_transform_t **out,
|
---|
118 | bithenge_transform_t **xforms, size_t num)
|
---|
119 | {
|
---|
120 | if (num == 0) {
|
---|
121 | /* TODO: optimize */
|
---|
122 | } else if (num == 1) {
|
---|
123 | *out = xforms[0];
|
---|
124 | free(xforms);
|
---|
125 | return EOK;
|
---|
126 | }
|
---|
127 |
|
---|
128 | int rc;
|
---|
129 | compose_transform_t *self = malloc(sizeof(*self));
|
---|
130 | if (!self) {
|
---|
131 | rc = ENOMEM;
|
---|
132 | goto error;
|
---|
133 | }
|
---|
134 | rc = bithenge_init_transform(compose_as_transform(self),
|
---|
135 | &compose_transform_ops, 0);
|
---|
136 | if (rc != EOK)
|
---|
137 | goto error;
|
---|
138 | self->xforms = xforms;
|
---|
139 | self->num = num;
|
---|
140 | *out = compose_as_transform(self);
|
---|
141 | return EOK;
|
---|
142 | error:
|
---|
143 | for (size_t i = 0; i < num; i++)
|
---|
144 | bithenge_transform_dec_ref(xforms[i]);
|
---|
145 | free(xforms);
|
---|
146 | free(self);
|
---|
147 | return rc;
|
---|
148 | }
|
---|
149 |
|
---|
150 |
|
---|
151 |
|
---|
152 | /***************** if_transform *****************/
|
---|
153 |
|
---|
154 | typedef struct {
|
---|
155 | bithenge_transform_t base;
|
---|
156 | bithenge_expression_t *expr;
|
---|
157 | bithenge_transform_t *true_xform, *false_xform;
|
---|
158 | } if_transform_t;
|
---|
159 |
|
---|
160 | static inline bithenge_transform_t *if_as_transform(if_transform_t *self)
|
---|
161 | {
|
---|
162 | return &self->base;
|
---|
163 | }
|
---|
164 |
|
---|
165 | static inline if_transform_t *transform_as_if(bithenge_transform_t *base)
|
---|
166 | {
|
---|
167 | return (if_transform_t *)base;
|
---|
168 | }
|
---|
169 |
|
---|
170 | static int if_transform_choose(if_transform_t *self, bithenge_scope_t *scope,
|
---|
171 | bool *out)
|
---|
172 | {
|
---|
173 | bithenge_node_t *cond_node;
|
---|
174 | int rc = bithenge_expression_evaluate(self->expr, scope, &cond_node);
|
---|
175 | if (rc != EOK)
|
---|
176 | return rc;
|
---|
177 | if (bithenge_node_type(cond_node) != BITHENGE_NODE_BOOLEAN) {
|
---|
178 | bithenge_node_dec_ref(cond_node);
|
---|
179 | return EINVAL;
|
---|
180 | }
|
---|
181 | *out = bithenge_boolean_node_value(cond_node);
|
---|
182 | bithenge_node_dec_ref(cond_node);
|
---|
183 | return EOK;
|
---|
184 | }
|
---|
185 |
|
---|
186 | static int if_transform_apply(bithenge_transform_t *base,
|
---|
187 | bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
|
---|
188 | {
|
---|
189 | if_transform_t *self = transform_as_if(base);
|
---|
190 | bool cond;
|
---|
191 | int rc = if_transform_choose(self, scope, &cond);
|
---|
192 | if (rc != EOK)
|
---|
193 | return rc;
|
---|
194 | return bithenge_transform_apply(
|
---|
195 | cond ? self->true_xform : self->false_xform, scope, in, out);
|
---|
196 | }
|
---|
197 |
|
---|
198 | static int if_transform_prefix_length(bithenge_transform_t *base,
|
---|
199 | bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
|
---|
200 | {
|
---|
201 | if_transform_t *self = transform_as_if(base);
|
---|
202 | bool cond;
|
---|
203 | int rc = if_transform_choose(self, scope, &cond);
|
---|
204 | if (rc != EOK)
|
---|
205 | return rc;
|
---|
206 | return bithenge_transform_prefix_length(
|
---|
207 | cond ? self->true_xform : self->false_xform, scope, in, out);
|
---|
208 | }
|
---|
209 |
|
---|
210 | static void if_transform_destroy(bithenge_transform_t *base)
|
---|
211 | {
|
---|
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);
|
---|
216 | free(self);
|
---|
217 | }
|
---|
218 |
|
---|
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,
|
---|
223 | };
|
---|
224 |
|
---|
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
|
---|
227 | * @a false_xform.
|
---|
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 | int bithenge_if_transform(bithenge_transform_t **out,
|
---|
234 | bithenge_expression_t *expr, bithenge_transform_t *true_xform,
|
---|
235 | bithenge_transform_t *false_xform)
|
---|
236 | {
|
---|
237 | int rc;
|
---|
238 | if_transform_t *self = malloc(sizeof(*self));
|
---|
239 | if (!self) {
|
---|
240 | rc = ENOMEM;
|
---|
241 | goto error;
|
---|
242 | }
|
---|
243 |
|
---|
244 | rc = bithenge_init_transform(if_as_transform(self), &if_transform_ops,
|
---|
245 | 0);
|
---|
246 | if (rc != EOK)
|
---|
247 | goto error;
|
---|
248 |
|
---|
249 | self->expr = expr;
|
---|
250 | self->true_xform = true_xform;
|
---|
251 | self->false_xform = false_xform;
|
---|
252 | *out = if_as_transform(self);
|
---|
253 | return EOK;
|
---|
254 |
|
---|
255 | error:
|
---|
256 | free(self);
|
---|
257 | bithenge_expression_dec_ref(expr);
|
---|
258 | bithenge_transform_dec_ref(true_xform);
|
---|
259 | bithenge_transform_dec_ref(false_xform);
|
---|
260 | return rc;
|
---|
261 | }
|
---|
262 |
|
---|
263 |
|
---|
264 |
|
---|
265 | /***************** partial_transform *****************/
|
---|
266 |
|
---|
267 | typedef struct {
|
---|
268 | bithenge_transform_t base;
|
---|
269 | bithenge_transform_t *xform;
|
---|
270 | } partial_transform_t;
|
---|
271 |
|
---|
272 | static inline bithenge_transform_t *partial_as_transform(
|
---|
273 | partial_transform_t *self)
|
---|
274 | {
|
---|
275 | return &self->base;
|
---|
276 | }
|
---|
277 |
|
---|
278 | static inline partial_transform_t *transform_as_partial(
|
---|
279 | bithenge_transform_t *base)
|
---|
280 | {
|
---|
281 | return (partial_transform_t *)base;
|
---|
282 | }
|
---|
283 |
|
---|
284 | static int partial_transform_apply(bithenge_transform_t *base,
|
---|
285 | bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
|
---|
286 | {
|
---|
287 | partial_transform_t *self = transform_as_partial(base);
|
---|
288 | if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
|
---|
289 | return EINVAL;
|
---|
290 | return bithenge_transform_prefix_apply(self->xform, scope,
|
---|
291 | bithenge_node_as_blob(in), out, NULL);
|
---|
292 | }
|
---|
293 |
|
---|
294 | static void partial_transform_destroy(bithenge_transform_t *base)
|
---|
295 | {
|
---|
296 | partial_transform_t *self = transform_as_partial(base);
|
---|
297 | bithenge_transform_dec_ref(self->xform);
|
---|
298 | free(self);
|
---|
299 | }
|
---|
300 |
|
---|
301 | static const bithenge_transform_ops_t partial_transform_ops = {
|
---|
302 | .apply = partial_transform_apply,
|
---|
303 | .destroy = partial_transform_destroy,
|
---|
304 | };
|
---|
305 |
|
---|
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 | int bithenge_partial_transform(bithenge_transform_t **out,
|
---|
312 | bithenge_transform_t *xform)
|
---|
313 | {
|
---|
314 | int rc;
|
---|
315 | partial_transform_t *self = malloc(sizeof(*self));
|
---|
316 | if (!self) {
|
---|
317 | rc = ENOMEM;
|
---|
318 | goto error;
|
---|
319 | }
|
---|
320 |
|
---|
321 | rc = bithenge_init_transform(partial_as_transform(self),
|
---|
322 | &partial_transform_ops, 0);
|
---|
323 | if (rc != EOK)
|
---|
324 | goto error;
|
---|
325 |
|
---|
326 | self->xform = xform;
|
---|
327 | *out = partial_as_transform(self);
|
---|
328 | return EOK;
|
---|
329 |
|
---|
330 | error:
|
---|
331 | free(self);
|
---|
332 | bithenge_transform_dec_ref(xform);
|
---|
333 | return rc;
|
---|
334 | }
|
---|
335 |
|
---|
336 | /** @}
|
---|
337 | */
|
---|