source: mainline/uspace/app/bithenge/transform.c@ f85ca3f

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

Bithenge: add expressions that use the current node being created

  • Property mode set to 100644
File size: 21.7 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} struct_node_t;
390
391typedef struct struct_transform {
392 bithenge_transform_t base;
393 bithenge_named_transform_t *subtransforms;
394} struct_transform_t;
395
396static bithenge_node_t *struct_as_node(struct_node_t *node)
397{
398 return &node->base;
399}
400
401static struct_node_t *node_as_struct(bithenge_node_t *node)
402{
403 return (struct_node_t *)node;
404}
405
406static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
407{
408 return &xform->base;
409}
410
411static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
412{
413 return (struct_transform_t *)xform;
414}
415
416static int struct_node_for_one(const char *name,
417 bithenge_transform_t *subxform, bithenge_scope_t *scope,
418 bithenge_blob_t **blob, bithenge_for_each_func_t func, void *data)
419{
420 int rc;
421 bithenge_node_t *subxform_result = NULL;
422
423 aoff64_t sub_size;
424 rc = bithenge_transform_prefix_length(subxform, scope, *blob,
425 &sub_size);
426 if (rc != EOK)
427 goto error;
428
429 bithenge_node_t *subblob_node;
430 bithenge_blob_inc_ref(*blob);
431 rc = bithenge_new_subblob(&subblob_node, *blob, 0, sub_size);
432 if (rc != EOK)
433 goto error;
434
435 rc = bithenge_transform_apply(subxform, scope, subblob_node,
436 &subxform_result);
437 bithenge_node_dec_ref(subblob_node);
438 if (rc != EOK)
439 goto error;
440
441 if (name) {
442 bithenge_node_t *name_node;
443 rc = bithenge_new_string_node(&name_node, name, false);
444 if (rc != EOK)
445 goto error;
446 rc = func(name_node, subxform_result, data);
447 subxform_result = NULL;
448 if (rc != EOK)
449 goto error;
450 } else {
451 if (bithenge_node_type(subxform_result) !=
452 BITHENGE_NODE_INTERNAL) {
453 rc = EINVAL;
454 goto error;
455 }
456 rc = bithenge_node_for_each(subxform_result, func, data);
457 if (rc != EOK)
458 goto error;
459 }
460
461 bithenge_node_t *blob_node;
462 rc = bithenge_new_offset_blob(&blob_node, *blob, sub_size);
463 *blob = NULL;
464 if (rc != EOK)
465 goto error;
466 *blob = bithenge_node_as_blob(blob_node);
467
468error:
469 bithenge_node_dec_ref(subxform_result);
470 return rc;
471}
472
473static int struct_node_for_each(bithenge_node_t *base,
474 bithenge_for_each_func_t func, void *data)
475{
476 int rc = EOK;
477 struct_node_t *struct_node = node_as_struct(base);
478 bithenge_named_transform_t *subxforms =
479 struct_node->transform->subtransforms;
480
481 bithenge_node_t *blob_node = NULL;
482 bithenge_blob_t *blob = NULL;
483 bithenge_blob_inc_ref(struct_node->blob);
484 rc = bithenge_new_offset_blob(&blob_node, struct_node->blob, 0);
485 if (rc != EOK) {
486 blob = NULL;
487 goto error;
488 }
489 blob = bithenge_node_as_blob(blob_node);
490
491 for (size_t i = 0; subxforms[i].transform; i++) {
492 rc = struct_node_for_one(subxforms[i].name,
493 subxforms[i].transform, &struct_node->scope, &blob, func,
494 data);
495 if (rc != EOK)
496 goto error;
497 }
498
499 aoff64_t remaining;
500 rc = bithenge_blob_size(blob, &remaining);
501 if (rc != EOK)
502 goto error;
503 if (remaining != 0) {
504 rc = EINVAL;
505 goto error;
506 }
507
508error:
509 bithenge_blob_dec_ref(blob);
510 return rc;
511}
512
513static void struct_node_destroy(bithenge_node_t *base)
514{
515 struct_node_t *node = node_as_struct(base);
516 bithenge_transform_dec_ref(struct_as_transform(node->transform));
517 bithenge_blob_dec_ref(node->blob);
518 bithenge_scope_destroy(&node->scope);
519 free(node);
520}
521
522static const bithenge_internal_node_ops_t struct_node_ops = {
523 .for_each = struct_node_for_each,
524 .destroy = struct_node_destroy,
525};
526
527static int struct_transform_apply(bithenge_transform_t *base,
528 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
529{
530 struct_transform_t *self = transform_as_struct(base);
531 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
532 return EINVAL;
533 struct_node_t *node = malloc(sizeof(*node));
534 if (!node)
535 return ENOMEM;
536 bithenge_scope_init(&node->scope);
537 int rc = bithenge_scope_copy(&node->scope, scope);
538 if (rc != EOK) {
539 free(node);
540 return rc;
541 }
542 rc = bithenge_init_internal_node(struct_as_node(node),
543 &struct_node_ops);
544 if (rc != EOK) {
545 bithenge_scope_destroy(&node->scope);
546 free(node);
547 return rc;
548 }
549 bithenge_transform_inc_ref(base);
550 node->transform = self;
551 bithenge_node_inc_ref(in);
552 node->blob = bithenge_node_as_blob(in);
553 *out = struct_as_node(node);
554 bithenge_node_inc_ref(*out);
555 bithenge_scope_set_current_node(&node->scope, *out);
556 return EOK;
557}
558
559static int struct_transform_prefix_length(bithenge_transform_t *base,
560 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
561{
562 struct_transform_t *self = transform_as_struct(base);
563 int rc = EOK;
564 bithenge_node_t *node;
565 bithenge_blob_inc_ref(blob);
566 rc = bithenge_new_offset_blob(&node, blob, 0);
567 blob = NULL;
568 if (rc != EOK)
569 goto error;
570 blob = bithenge_node_as_blob(node);
571 *out = 0;
572 for (size_t i = 0; self->subtransforms[i].transform; i++) {
573 bithenge_transform_t *subxform =
574 self->subtransforms[i].transform;
575 aoff64_t sub_size;
576 rc = bithenge_transform_prefix_length(subxform, scope, blob,
577 &sub_size);
578 if (rc != EOK)
579 goto error;
580 *out += sub_size;
581 rc = bithenge_new_offset_blob(&node, blob, sub_size);
582 blob = NULL;
583 if (rc != EOK)
584 goto error;
585 blob = bithenge_node_as_blob(node);
586 }
587error:
588 bithenge_blob_dec_ref(blob);
589 return EOK;
590}
591
592static void free_subtransforms(bithenge_named_transform_t *subtransforms)
593{
594 for (size_t i = 0; subtransforms[i].transform; i++) {
595 free((void *)subtransforms[i].name);
596 bithenge_transform_dec_ref(subtransforms[i].transform);
597 }
598 free(subtransforms);
599}
600
601static void struct_transform_destroy(bithenge_transform_t *base)
602{
603 struct_transform_t *self = transform_as_struct(base);
604 free_subtransforms(self->subtransforms);
605 free(self);
606}
607
608static bithenge_transform_ops_t struct_transform_ops = {
609 .apply = struct_transform_apply,
610 .prefix_length = struct_transform_prefix_length,
611 .destroy = struct_transform_destroy,
612};
613
614/** Create a struct transform. The transform will apply its subtransforms
615 * sequentially to a blob to create an internal node. Each result is either
616 * given a key from @a subtransforms or, if the name is NULL, the result's keys
617 * and values are merged into the struct transform's result. This function
618 * takes ownership of @a subtransforms and the names and references therein.
619 * @param[out] out Stores the created transform.
620 * @param subtransforms The subtransforms and field names.
621 * @return EOK on success or an error code from errno.h. */
622int bithenge_new_struct(bithenge_transform_t **out,
623 bithenge_named_transform_t *subtransforms)
624{
625 int rc;
626 struct_transform_t *self = malloc(sizeof(*self));
627 if (!self) {
628 rc = ENOMEM;
629 goto error;
630 }
631 rc = bithenge_init_transform(struct_as_transform(self),
632 &struct_transform_ops, 0);
633 if (rc != EOK)
634 goto error;
635 self->subtransforms = subtransforms;
636 *out = struct_as_transform(self);
637 return EOK;
638error:
639 free_subtransforms(subtransforms);
640 free(self);
641 return rc;
642}
643
644typedef struct {
645 bithenge_transform_t base;
646 bithenge_transform_t **xforms;
647 size_t num;
648} compose_transform_t;
649
650static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
651{
652 return &xform->base;
653}
654
655static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
656{
657 return (compose_transform_t *)xform;
658}
659
660static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
661 bithenge_node_t *in, bithenge_node_t **out)
662{
663 int rc;
664 compose_transform_t *self = transform_as_compose(base);
665 bithenge_node_inc_ref(in);
666
667 /* i ranges from (self->num - 1) to 0 inside the loop. */
668 for (size_t i = self->num; i--; ) {
669 bithenge_node_t *tmp;
670 rc = bithenge_transform_apply(self->xforms[i], scope, in,
671 &tmp);
672 bithenge_node_dec_ref(in);
673 if (rc != EOK)
674 return rc;
675 in = tmp;
676 }
677
678 *out = in;
679 return rc;
680}
681
682static int compose_prefix_length(bithenge_transform_t *base,
683 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
684{
685 compose_transform_t *self = transform_as_compose(base);
686 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
687 scope, blob, out);
688}
689
690static void compose_destroy(bithenge_transform_t *base)
691{
692 compose_transform_t *self = transform_as_compose(base);
693 for (size_t i = 0; i < self->num; i++)
694 bithenge_transform_dec_ref(self->xforms[i]);
695 free(self->xforms);
696 free(self);
697}
698
699static const bithenge_transform_ops_t compose_transform_ops = {
700 .apply = compose_apply,
701 .prefix_length = compose_prefix_length,
702 .destroy = compose_destroy,
703};
704
705/** Create a composition of multiple transforms. When the result is applied to a
706 * node, each transform is applied in turn, with the last transform applied
707 * first. @a xforms may contain any number of transforms or no transforms at
708 * all. This function takes ownership of @a xforms and the references therein.
709 * @param[out] out Holds the result.
710 * @param[in] xforms The transforms to apply.
711 * @param num The number of transforms.
712 * @return EOK on success or an error code from errno.h. */
713int bithenge_new_composed_transform(bithenge_transform_t **out,
714 bithenge_transform_t **xforms, size_t num)
715{
716 if (num == 0) {
717 /* TODO: optimize */
718 } else if (num == 1) {
719 *out = xforms[0];
720 free(xforms);
721 return EOK;
722 }
723
724 int rc;
725 compose_transform_t *self = malloc(sizeof(*self));
726 if (!self) {
727 rc = ENOMEM;
728 goto error;
729 }
730 rc = bithenge_init_transform(compose_as_transform(self),
731 &compose_transform_ops, 0);
732 if (rc != EOK)
733 goto error;
734 self->xforms = xforms;
735 self->num = num;
736 *out = compose_as_transform(self);
737 return EOK;
738error:
739 for (size_t i = 0; i < num; i++)
740 bithenge_transform_dec_ref(xforms[i]);
741 free(xforms);
742 free(self);
743 return rc;
744}
745
746/** @}
747 */
Note: See TracBrowser for help on using the repository browser.