source: mainline/uspace/app/bithenge/transform.c@ 84e8a70

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 84e8a70 was 84e8a70, checked in by Sean Bartell <wingedtachikoma@…>, 13 years ago

Bithenge: fix struct_transform and add parameter test script

  • Property mode set to 100644
File size: 22.6 KB
Line 
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 * Transforms.
35 */
36
37#include <assert.h>
38#include <errno.h>
39#include <stdlib.h>
40#include "blob.h"
41#include "transform.h"
42
43/** Initialize a new transform.
44 * @param[out] self Transform to initialize.
45 * @param[in] ops Operations provided by the transform.
46 * @param num_params The number of parameters required. If this is nonzero, the
47 * transform will get its own context with parameters, probably provided by a
48 * param_wrapper. If this is zero, the existing outer context will be used with
49 * whatever parameters it has, so they can be passed to any param_wrappers
50 * within.
51 * @return EOK or an error code from errno.h. */
52int bithenge_init_transform(bithenge_transform_t *self,
53 const bithenge_transform_ops_t *ops, int num_params)
54{
55 assert(ops);
56 assert(ops->apply);
57 assert(ops->destroy);
58 self->ops = ops;
59 self->refs = 1;
60 self->num_params = num_params;
61 return EOK;
62}
63
64static void transform_indestructible(bithenge_transform_t *self)
65{
66 assert(false);
67}
68
69typedef struct {
70 bithenge_transform_t base;
71 bithenge_transform_t *transform;
72} param_transform_t;
73
74static inline param_transform_t *transform_as_param(
75 bithenge_transform_t *base)
76{
77 return (param_transform_t *)base;
78}
79
80static inline bithenge_transform_t *param_as_transform(
81 param_transform_t *self)
82{
83 return &self->base;
84}
85
86static int param_transform_apply(bithenge_transform_t *base,
87 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
88{
89 param_transform_t *self = transform_as_param(base);
90 return bithenge_transform_apply(self->transform, scope, in, out);
91}
92
93static int param_transform_prefix_length(bithenge_transform_t *base,
94 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
95{
96 param_transform_t *self = transform_as_param(base);
97 return bithenge_transform_prefix_length(self->transform, scope, in,
98 out);
99}
100
101static void param_transform_destroy(bithenge_transform_t *base)
102{
103 param_transform_t *self = transform_as_param(base);
104 bithenge_transform_dec_ref(self->transform);
105 free(self);
106}
107
108static const bithenge_transform_ops_t param_transform_ops = {
109 .apply = param_transform_apply,
110 .prefix_length = param_transform_prefix_length,
111 .destroy = param_transform_destroy,
112};
113
114/** Create a wrapper transform with a different number of parameters. Takes a
115 * reference to @a transform, which it will use for all operations.
116 * @param[out] out Holds the created transform.
117 * @param transform The transform to wrap.
118 * @param num_params The number of parameters to require.
119 * @return EOK on success or an error code from errno.h. */
120int bithenge_new_param_transform(bithenge_transform_t **out,
121 bithenge_transform_t *transform, int num_params)
122{
123 assert(transform);
124 assert(bithenge_transform_num_params(transform) == 0);
125 assert(num_params != 0);
126
127 int rc;
128 param_transform_t *self = malloc(sizeof(*self));
129 if (!self) {
130 rc = ENOMEM;
131 goto error;
132 }
133 rc = bithenge_init_transform(param_as_transform(self),
134 &param_transform_ops, num_params);
135 if (rc != EOK)
136 goto error;
137 self->transform = transform;
138 *out = param_as_transform(self);
139 return EOK;
140error:
141 bithenge_transform_dec_ref(transform);
142 free(self);
143 return rc;
144}
145
146static int ascii_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
147 bithenge_node_t *in, bithenge_node_t **out)
148{
149 int rc;
150 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
151 return EINVAL;
152 bithenge_blob_t *blob = bithenge_node_as_blob(in);
153 aoff64_t size;
154 rc = bithenge_blob_size(blob, &size);
155 if (rc != EOK)
156 return rc;
157
158 char *buffer = malloc(size + 1);
159 if (!buffer)
160 return ENOMEM;
161 aoff64_t size_read = size;
162 rc = bithenge_blob_read(blob, 0, buffer, &size_read);
163 if (rc != EOK) {
164 free(buffer);
165 return rc;
166 }
167 if (size_read != size) {
168 free(buffer);
169 return EINVAL;
170 }
171 buffer[size] = '\0';
172
173 /* TODO: what if the OS encoding is incompatible with ASCII? */
174 return bithenge_new_string_node(out, buffer, true);
175}
176
177static const bithenge_transform_ops_t ascii_ops = {
178 .apply = ascii_apply,
179 .destroy = transform_indestructible,
180};
181
182/** The ASCII text transform. */
183bithenge_transform_t bithenge_ascii_transform = {
184 &ascii_ops, 1, 0
185};
186
187static int known_length_apply(bithenge_transform_t *self,
188 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
189{
190 bithenge_node_t *length_node;
191 int rc = bithenge_scope_get_param(scope, 0, &length_node);
192 if (rc != EOK)
193 return rc;
194 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
195 bithenge_node_dec_ref(length_node);
196 return EINVAL;
197 }
198 bithenge_int_t length = bithenge_integer_node_value(length_node);
199 bithenge_node_dec_ref(length_node);
200
201 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
202 return EINVAL;
203 aoff64_t size;
204 rc = bithenge_blob_size(bithenge_node_as_blob(in), &size);
205 if (rc != EOK)
206 return rc;
207 if (length != (bithenge_int_t)size)
208 return EINVAL;
209
210 bithenge_node_inc_ref(in);
211 *out = in;
212 return EOK;
213}
214
215static int known_length_prefix_length(bithenge_transform_t *self,
216 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
217{
218 bithenge_node_t *length_node;
219 int rc = bithenge_scope_get_param(scope, 0, &length_node);
220 if (rc != EOK)
221 return rc;
222 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
223 bithenge_node_dec_ref(length_node);
224 return EINVAL;
225 }
226 bithenge_int_t length = bithenge_integer_node_value(length_node);
227 bithenge_node_dec_ref(length_node);
228
229 *out = (aoff64_t)length;
230 return EOK;
231}
232
233static const bithenge_transform_ops_t known_length_ops = {
234 .apply = known_length_apply,
235 .prefix_length = known_length_prefix_length,
236 .destroy = transform_indestructible,
237};
238
239/** Pass through a blob, but require its length to equal the first argument. */
240bithenge_transform_t bithenge_known_length_transform = {
241 &known_length_ops, 1, 1
242};
243
244static int prefix_length_1(bithenge_transform_t *self, bithenge_scope_t *scope,
245 bithenge_blob_t *blob, aoff64_t *out)
246{
247 *out = 1;
248 return EOK;
249}
250
251static int prefix_length_2(bithenge_transform_t *self, bithenge_scope_t *scope,
252 bithenge_blob_t *blob, aoff64_t *out)
253{
254 *out = 2;
255 return EOK;
256}
257
258static int prefix_length_4(bithenge_transform_t *self, bithenge_scope_t *scope,
259 bithenge_blob_t *blob, aoff64_t *out)
260{
261 *out = 4;
262 return EOK;
263}
264
265static int prefix_length_8(bithenge_transform_t *self, bithenge_scope_t *scope,
266 bithenge_blob_t *blob, aoff64_t *out)
267{
268 *out = 8;
269 return EOK;
270}
271
272#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
273 static int NAME##_apply(bithenge_transform_t *self, \
274 bithenge_scope_t *scope, bithenge_node_t *in, \
275 bithenge_node_t **out) \
276 { \
277 int rc; \
278 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
279 return EINVAL; \
280 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
281 \
282 /* Read too many bytes; success means the blob is too long. */ \
283 TYPE val[2]; \
284 aoff64_t size = sizeof(val[0]) + 1; \
285 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
286 if (rc != EOK) \
287 return rc; \
288 if (size != sizeof(val[0])) \
289 return EINVAL; \
290 \
291 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
292 } \
293 \
294 static const bithenge_transform_ops_t NAME##_ops = { \
295 .apply = NAME##_apply, \
296 .prefix_length = PREFIX_LENGTH_FUNC, \
297 .destroy = transform_indestructible, \
298 }; \
299 \
300 bithenge_transform_t bithenge_##NAME##_transform = { \
301 &NAME##_ops, 1, 0 \
302 }
303
304MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
305MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
306MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
307MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
308MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
309MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
310MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
311
312static int zero_terminated_apply(bithenge_transform_t *self,
313 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
314{
315 int rc;
316 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
317 return EINVAL;
318 bithenge_blob_t *blob = bithenge_node_as_blob(in);
319 aoff64_t size;
320 rc = bithenge_blob_size(blob, &size);
321 if (rc != EOK)
322 return rc;
323 if (size < 1)
324 return EINVAL;
325 char ch;
326 aoff64_t size_read = 1;
327 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
328 if (rc != EOK)
329 return rc;
330 if (size_read != 1 || ch != '\0')
331 return EINVAL;
332 bithenge_blob_inc_ref(blob);
333 return bithenge_new_subblob(out, blob, 0, size - 1);
334}
335
336static int zero_terminated_prefix_length(bithenge_transform_t *self,
337 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
338{
339 int rc;
340 char buffer[4096];
341 aoff64_t offset = 0, size_read = sizeof(buffer);
342 do {
343 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
344 if (rc != EOK)
345 return rc;
346 char *found = memchr(buffer, '\0', size_read);
347 if (found) {
348 *out = found - buffer + offset + 1;
349 return EOK;
350 }
351 offset += size_read;
352 } while (size_read == sizeof(buffer));
353 return EINVAL;
354}
355
356static const bithenge_transform_ops_t zero_terminated_ops = {
357 .apply = zero_terminated_apply,
358 .prefix_length = zero_terminated_prefix_length,
359 .destroy = transform_indestructible,
360};
361
362/** The zero-terminated data transform. */
363bithenge_transform_t bithenge_zero_terminated_transform = {
364 &zero_terminated_ops, 1, 0
365};
366
367static bithenge_named_transform_t primitive_transforms[] = {
368 {"ascii", &bithenge_ascii_transform},
369 {"known_length", &bithenge_known_length_transform},
370 {"uint8", &bithenge_uint8_transform},
371 {"uint16le", &bithenge_uint16le_transform},
372 {"uint16be", &bithenge_uint16be_transform},
373 {"uint32le", &bithenge_uint32le_transform},
374 {"uint32be", &bithenge_uint32be_transform},
375 {"uint64le", &bithenge_uint64le_transform},
376 {"uint64be", &bithenge_uint64be_transform},
377 {"zero_terminated", &bithenge_zero_terminated_transform},
378 {NULL, NULL}
379};
380
381/** An array of named built-in transforms. */
382bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
383
384typedef struct {
385 bithenge_node_t base;
386 struct struct_transform *transform;
387 bithenge_scope_t scope;
388 bithenge_blob_t *blob;
389 bool prefix;
390} struct_node_t;
391
392typedef struct struct_transform {
393 bithenge_transform_t base;
394 bithenge_named_transform_t *subtransforms;
395} struct_transform_t;
396
397static bithenge_node_t *struct_as_node(struct_node_t *node)
398{
399 return &node->base;
400}
401
402static struct_node_t *node_as_struct(bithenge_node_t *node)
403{
404 return (struct_node_t *)node;
405}
406
407static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
408{
409 return &xform->base;
410}
411
412static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
413{
414 return (struct_transform_t *)xform;
415}
416
417static int struct_node_for_one(const char *name,
418 bithenge_transform_t *subxform, bithenge_scope_t *scope,
419 bithenge_blob_t **blob, bithenge_for_each_func_t func, void *data)
420{
421 int rc;
422 bithenge_node_t *subxform_result = NULL;
423
424 aoff64_t sub_size;
425 rc = bithenge_transform_prefix_length(subxform, scope, *blob,
426 &sub_size);
427 if (rc != EOK)
428 goto error;
429
430 bithenge_node_t *subblob_node;
431 bithenge_blob_inc_ref(*blob);
432 rc = bithenge_new_subblob(&subblob_node, *blob, 0, sub_size);
433 if (rc != EOK)
434 goto error;
435
436 rc = bithenge_transform_apply(subxform, scope, subblob_node,
437 &subxform_result);
438 bithenge_node_dec_ref(subblob_node);
439 if (rc != EOK)
440 goto error;
441
442 if (name) {
443 bithenge_node_t *name_node;
444 rc = bithenge_new_string_node(&name_node, name, false);
445 if (rc != EOK)
446 goto error;
447 rc = func(name_node, subxform_result, data);
448 subxform_result = NULL;
449 if (rc != EOK)
450 goto error;
451 } else {
452 if (bithenge_node_type(subxform_result) !=
453 BITHENGE_NODE_INTERNAL) {
454 rc = EINVAL;
455 goto error;
456 }
457 rc = bithenge_node_for_each(subxform_result, func, data);
458 if (rc != EOK)
459 goto error;
460 }
461
462 bithenge_node_t *blob_node;
463 rc = bithenge_new_offset_blob(&blob_node, *blob, sub_size);
464 *blob = NULL;
465 if (rc != EOK)
466 goto error;
467 *blob = bithenge_node_as_blob(blob_node);
468
469error:
470 bithenge_node_dec_ref(subxform_result);
471 return rc;
472}
473
474static int struct_node_for_each(bithenge_node_t *base,
475 bithenge_for_each_func_t func, void *data)
476{
477 int rc = EOK;
478 struct_node_t *struct_node = node_as_struct(base);
479 bithenge_named_transform_t *subxforms =
480 struct_node->transform->subtransforms;
481
482 bithenge_node_t *blob_node = NULL;
483 bithenge_blob_t *blob = NULL;
484 bithenge_blob_inc_ref(struct_node->blob);
485 rc = bithenge_new_offset_blob(&blob_node, struct_node->blob, 0);
486 if (rc != EOK) {
487 blob = NULL;
488 goto error;
489 }
490 blob = bithenge_node_as_blob(blob_node);
491
492 for (size_t i = 0; subxforms[i].transform; i++) {
493 rc = struct_node_for_one(subxforms[i].name,
494 subxforms[i].transform, &struct_node->scope, &blob, func,
495 data);
496 if (rc != EOK)
497 goto error;
498 }
499
500 if (!struct_node->prefix) {
501 aoff64_t remaining;
502 rc = bithenge_blob_size(blob, &remaining);
503 if (rc != EOK)
504 goto error;
505 if (remaining != 0) {
506 rc = EINVAL;
507 goto error;
508 }
509 }
510
511error:
512 bithenge_blob_dec_ref(blob);
513 return rc;
514}
515
516static void struct_node_destroy(bithenge_node_t *base)
517{
518 struct_node_t *node = node_as_struct(base);
519
520 /* We didn't inc_ref for the scope in struct_transform_make_node, so
521 * make sure it doesn't try to dec_ref. */
522 node->scope.current_node = NULL;
523 bithenge_scope_destroy(&node->scope);
524
525 bithenge_transform_dec_ref(struct_as_transform(node->transform));
526 bithenge_blob_dec_ref(node->blob);
527 free(node);
528}
529
530static const bithenge_internal_node_ops_t struct_node_ops = {
531 .for_each = struct_node_for_each,
532 .destroy = struct_node_destroy,
533};
534
535static int struct_transform_make_node(struct_transform_t *self,
536 bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
537 bool prefix)
538{
539 struct_node_t *node = malloc(sizeof(*node));
540 if (!node)
541 return ENOMEM;
542 bithenge_scope_init(&node->scope);
543 int rc = bithenge_scope_copy(&node->scope, scope);
544 if (rc != EOK) {
545 free(node);
546 return rc;
547 }
548 rc = bithenge_init_internal_node(struct_as_node(node),
549 &struct_node_ops);
550 if (rc != EOK) {
551 bithenge_scope_destroy(&node->scope);
552 free(node);
553 return rc;
554 }
555 bithenge_transform_inc_ref(struct_as_transform(self));
556 bithenge_blob_inc_ref(blob);
557 node->transform = self;
558 node->blob = blob;
559 node->prefix = prefix;
560 *out = struct_as_node(node);
561
562 /* We should inc_ref(*out) here, but that would make a cycle. Instead,
563 * we leave it 1 too low, so that when the only remaining use of *out
564 * is the scope, *out will be destroyed. Also see the comment in
565 * struct_node_destroy. */
566 bithenge_scope_set_current_node(&node->scope, *out);
567
568 return EOK;
569}
570
571static int struct_transform_apply(bithenge_transform_t *base,
572 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
573{
574 struct_transform_t *self = transform_as_struct(base);
575 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
576 return EINVAL;
577 return struct_transform_make_node(self, out, scope,
578 bithenge_node_as_blob(in), false);
579}
580
581static int struct_transform_prefix_length(bithenge_transform_t *base,
582 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
583{
584 struct_transform_t *self = transform_as_struct(base);
585 bithenge_node_t *struct_node;
586 int rc = struct_transform_make_node(self, &struct_node, scope, blob,
587 true);
588 if (rc != EOK)
589 return rc;
590 bithenge_scope_t *inner = &node_as_struct(struct_node)->scope;
591
592 bithenge_node_t *node;
593 bithenge_blob_inc_ref(blob);
594 rc = bithenge_new_offset_blob(&node, blob, 0);
595 blob = NULL;
596 if (rc != EOK)
597 goto error;
598 blob = bithenge_node_as_blob(node);
599 *out = 0;
600 for (size_t i = 0; self->subtransforms[i].transform; i++) {
601 bithenge_transform_t *subxform =
602 self->subtransforms[i].transform;
603 aoff64_t sub_size;
604 rc = bithenge_transform_prefix_length(subxform, inner, blob,
605 &sub_size);
606 if (rc != EOK)
607 goto error;
608 *out += sub_size;
609 rc = bithenge_new_offset_blob(&node, blob, sub_size);
610 blob = NULL;
611 if (rc != EOK)
612 goto error;
613 blob = bithenge_node_as_blob(node);
614 }
615
616error:
617 bithenge_node_dec_ref(struct_node);
618 bithenge_blob_dec_ref(blob);
619 return rc;
620}
621
622static void free_subtransforms(bithenge_named_transform_t *subtransforms)
623{
624 for (size_t i = 0; subtransforms[i].transform; i++) {
625 free((void *)subtransforms[i].name);
626 bithenge_transform_dec_ref(subtransforms[i].transform);
627 }
628 free(subtransforms);
629}
630
631static void struct_transform_destroy(bithenge_transform_t *base)
632{
633 struct_transform_t *self = transform_as_struct(base);
634 free_subtransforms(self->subtransforms);
635 free(self);
636}
637
638static bithenge_transform_ops_t struct_transform_ops = {
639 .apply = struct_transform_apply,
640 .prefix_length = struct_transform_prefix_length,
641 .destroy = struct_transform_destroy,
642};
643
644/** Create a struct transform. The transform will apply its subtransforms
645 * sequentially to a blob to create an internal node. Each result is either
646 * given a key from @a subtransforms or, if the name is NULL, the result's keys
647 * and values are merged into the struct transform's result. This function
648 * takes ownership of @a subtransforms and the names and references therein.
649 * @param[out] out Stores the created transform.
650 * @param subtransforms The subtransforms and field names.
651 * @return EOK on success or an error code from errno.h. */
652int bithenge_new_struct(bithenge_transform_t **out,
653 bithenge_named_transform_t *subtransforms)
654{
655 int rc;
656 struct_transform_t *self = malloc(sizeof(*self));
657 if (!self) {
658 rc = ENOMEM;
659 goto error;
660 }
661 rc = bithenge_init_transform(struct_as_transform(self),
662 &struct_transform_ops, 0);
663 if (rc != EOK)
664 goto error;
665 self->subtransforms = subtransforms;
666 *out = struct_as_transform(self);
667 return EOK;
668error:
669 free_subtransforms(subtransforms);
670 free(self);
671 return rc;
672}
673
674typedef struct {
675 bithenge_transform_t base;
676 bithenge_transform_t **xforms;
677 size_t num;
678} compose_transform_t;
679
680static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
681{
682 return &xform->base;
683}
684
685static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
686{
687 return (compose_transform_t *)xform;
688}
689
690static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
691 bithenge_node_t *in, bithenge_node_t **out)
692{
693 int rc;
694 compose_transform_t *self = transform_as_compose(base);
695 bithenge_node_inc_ref(in);
696
697 /* i ranges from (self->num - 1) to 0 inside the loop. */
698 for (size_t i = self->num; i--; ) {
699 bithenge_node_t *tmp;
700 rc = bithenge_transform_apply(self->xforms[i], scope, in,
701 &tmp);
702 bithenge_node_dec_ref(in);
703 if (rc != EOK)
704 return rc;
705 in = tmp;
706 }
707
708 *out = in;
709 return rc;
710}
711
712static int compose_prefix_length(bithenge_transform_t *base,
713 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
714{
715 compose_transform_t *self = transform_as_compose(base);
716 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
717 scope, blob, out);
718}
719
720static void compose_destroy(bithenge_transform_t *base)
721{
722 compose_transform_t *self = transform_as_compose(base);
723 for (size_t i = 0; i < self->num; i++)
724 bithenge_transform_dec_ref(self->xforms[i]);
725 free(self->xforms);
726 free(self);
727}
728
729static const bithenge_transform_ops_t compose_transform_ops = {
730 .apply = compose_apply,
731 .prefix_length = compose_prefix_length,
732 .destroy = compose_destroy,
733};
734
735/** Create a composition of multiple transforms. When the result is applied to a
736 * node, each transform is applied in turn, with the last transform applied
737 * first. @a xforms may contain any number of transforms or no transforms at
738 * all. This function takes ownership of @a xforms and the references therein.
739 * @param[out] out Holds the result.
740 * @param[in] xforms The transforms to apply.
741 * @param num The number of transforms.
742 * @return EOK on success or an error code from errno.h. */
743int bithenge_new_composed_transform(bithenge_transform_t **out,
744 bithenge_transform_t **xforms, size_t num)
745{
746 if (num == 0) {
747 /* TODO: optimize */
748 } else if (num == 1) {
749 *out = xforms[0];
750 free(xforms);
751 return EOK;
752 }
753
754 int rc;
755 compose_transform_t *self = malloc(sizeof(*self));
756 if (!self) {
757 rc = ENOMEM;
758 goto error;
759 }
760 rc = bithenge_init_transform(compose_as_transform(self),
761 &compose_transform_ops, 0);
762 if (rc != EOK)
763 goto error;
764 self->xforms = xforms;
765 self->num = num;
766 *out = compose_as_transform(self);
767 return EOK;
768error:
769 for (size_t i = 0; i < num; i++)
770 bithenge_transform_dec_ref(xforms[i]);
771 free(xforms);
772 free(self);
773 return rc;
774}
775
776/** @}
777 */
Note: See TracBrowser for help on using the repository browser.