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

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

Bithenge: move struct code to sequence.c; factor out non-struct-specific code

  • 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 || ops->prefix_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
69/** Apply a transform. Takes ownership of nothing.
70 * @memberof bithenge_transform_t
71 * @param self The transform.
72 * @param scope The scope.
73 * @param in The input tree.
74 * @param[out] out Where the output tree will be stored.
75 * @return EOK on success or an error code from errno.h. */
76int bithenge_transform_apply(bithenge_transform_t *self,
77 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
78{
79 assert(self);
80 assert(self->ops);
81 if (self->ops->apply)
82 return self->ops->apply(self, scope, in, out);
83
84 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
85 return EINVAL;
86 aoff64_t self_size, whole_size;
87 int rc = bithenge_transform_prefix_apply(self, scope,
88 bithenge_node_as_blob(in), out, &self_size);
89 if (rc != EOK)
90 return rc;
91 rc = bithenge_blob_size(bithenge_node_as_blob(in), &whole_size);
92 if (rc == EOK && whole_size != self_size)
93 rc = EINVAL;
94 if (rc != EOK) {
95 bithenge_node_dec_ref(*out);
96 return rc;
97 }
98 return EOK;
99}
100
101/** Find the length of the prefix of a blob this transform can use as input. In
102 * other words, figure out how many bytes this transform will use up. This
103 * method is optional and can return an error, but it must succeed for struct
104 * subtransforms. Takes ownership of nothing.
105 * @memberof bithenge_transform_t
106 * @param self The transform.
107 * @param scope The scope.
108 * @param blob The blob.
109 * @param[out] out Where the prefix length will be stored.
110 * @return EOK on success, ENOTSUP if not supported, or another error code from
111 * errno.h. */
112int bithenge_transform_prefix_length(bithenge_transform_t *self,
113 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
114{
115 assert(self);
116 assert(self->ops);
117 if (self->ops->prefix_length)
118 return self->ops->prefix_length(self, scope, blob, out);
119 if (!self->ops->prefix_apply)
120 return ENOTSUP;
121
122 bithenge_node_t *node;
123 int rc = bithenge_transform_prefix_apply(self, scope, blob, &node,
124 out);
125 if (rc != EOK)
126 return rc;
127 bithenge_node_dec_ref(node);
128 return EOK;
129}
130
131/** Apply this transform to a prefix of a blob. In other words, feed as much of
132 * the blob into this transform as possible. Takes ownership of nothing.
133 * @memberof bithenge_transform_t
134 * @param self The transform.
135 * @param scope The scope.
136 * @param blob The blob.
137 * @param[out] out_node Holds the result of applying this transform to the
138 * prefix.
139 * @param[out] out_size Holds the size of the prefix.
140 * @return EOK on success, ENOTSUP if not supported, or another error code from
141 * errno.h. */
142int bithenge_transform_prefix_apply(bithenge_transform_t *self,
143 bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
144 aoff64_t *out_size)
145{
146 assert(self);
147 assert(self->ops);
148 if (self->ops->prefix_apply)
149 return self->ops->prefix_apply(self, scope, blob, out_node,
150 out_size);
151 if (!self->ops->prefix_length)
152 return ENOTSUP;
153
154 int rc = bithenge_transform_prefix_length(self, scope, blob, out_size);
155 if (rc != EOK)
156 return rc;
157 bithenge_node_t *prefix_blob;
158 bithenge_blob_inc_ref(blob);
159 rc = bithenge_new_subblob(&prefix_blob, blob, 0, *out_size);
160 if (rc != EOK)
161 return rc;
162 rc = bithenge_transform_apply(self, scope, prefix_blob, out_node);
163 bithenge_node_dec_ref(prefix_blob);
164 return rc;
165}
166
167/** Initialize a transform scope. It must be destroyed with @a
168 * bithenge_scope_destroy after it is used.
169 * @param[out] scope The scope to initialize. */
170void bithenge_scope_init(bithenge_scope_t *scope)
171{
172 scope->num_params = 0;
173 scope->params = NULL;
174 scope->current_node = NULL;
175}
176
177/** Destroy a transform scope.
178 * @param scope The scope to destroy.
179 * @return EOK on success or an error code from errno.h. */
180void bithenge_scope_destroy(bithenge_scope_t *scope)
181{
182 bithenge_node_dec_ref(scope->current_node);
183 for (int i = 0; i < scope->num_params; i++)
184 bithenge_node_dec_ref(scope->params[i]);
185 free(scope->params);
186}
187
188/** Copy a scope.
189 * @param[out] out The scope to fill in; must have been initialized with @a
190 * bithenge_scope_init.
191 * @param scope The scope to copy.
192 * @return EOK on success or an error code from errno.h. */
193int bithenge_scope_copy(bithenge_scope_t *out, bithenge_scope_t *scope)
194{
195 out->params = malloc(sizeof(*out->params) * scope->num_params);
196 if (!out->params)
197 return ENOMEM;
198 memcpy(out->params, scope->params, sizeof(*out->params) *
199 scope->num_params);
200 out->num_params = scope->num_params;
201 for (int i = 0; i < out->num_params; i++)
202 bithenge_node_inc_ref(out->params[i]);
203 out->current_node = scope->current_node;
204 if (out->current_node)
205 bithenge_node_inc_ref(out->current_node);
206 return EOK;
207}
208
209/** Set the current node being created. Takes a reference to @a node.
210 * @param scope The scope to set the current node in.
211 * @param node The current node being created, or NULL.
212 * @return EOK on success or an error code from errno.h. */
213void bithenge_scope_set_current_node(bithenge_scope_t *scope,
214 bithenge_node_t *node)
215{
216 bithenge_node_dec_ref(scope->current_node);
217 scope->current_node = node;
218}
219
220/** Get the current node being created, which may be NULL.
221 * @param scope The scope to get the current node from.
222 * @return The node being created, or NULL. */
223bithenge_node_t *bithenge_scope_get_current_node(bithenge_scope_t *scope)
224{
225 if (scope->current_node)
226 bithenge_node_inc_ref(scope->current_node);
227 return scope->current_node;
228}
229
230/** Allocate parameters. The parameters must then be set with @a
231 * bithenge_scope_set_param. This must not be called on a scope that already
232 * has parameters.
233 * @param scope The scope in which to allocate parameters.
234 * @param num_params The number of parameters to allocate.
235 * @return EOK on success or an error code from errno.h. */
236int bithenge_scope_alloc_params(bithenge_scope_t *scope, int num_params)
237{
238 scope->params = malloc(sizeof(*scope->params) * num_params);
239 if (!scope->params)
240 return ENOMEM;
241 scope->num_params = num_params;
242 for (int i = 0; i < num_params; i++)
243 scope->params[i] = NULL;
244 return EOK;
245}
246
247/** Set a parameter. Takes a reference to @a value. Note that range checking is
248 * not done in release builds.
249 * @param scope The scope in which to allocate parameters.
250 * @param i The index of the parameter to set.
251 * @param value The value to store in the parameter.
252 * @return EOK on success or an error code from errno.h. */
253int bithenge_scope_set_param( bithenge_scope_t *scope, int i,
254 bithenge_node_t *node)
255{
256 assert(scope);
257 assert(i >= 0 && i < scope->num_params);
258 scope->params[i] = node;
259 return EOK;
260}
261
262/** Get a parameter. Note that range checking is not done in release builds.
263 * @param scope The scope to get the parameter from.
264 * @param i The index of the parameter to set.
265 * @param[out] out Stores a new reference to the parameter.
266 * @return EOK on success or an error code from errno.h. */
267int bithenge_scope_get_param(bithenge_scope_t *scope, int i,
268 bithenge_node_t **out)
269{
270 assert(scope);
271 assert(i >= 0 && i < scope->num_params);
272 *out = scope->params[i];
273 bithenge_node_inc_ref(*out);
274 return EOK;
275}
276
277typedef struct {
278 bithenge_transform_t base;
279 bithenge_transform_t *transform;
280} scope_transform_t;
281
282static inline scope_transform_t *transform_as_param(
283 bithenge_transform_t *base)
284{
285 return (scope_transform_t *)base;
286}
287
288static inline bithenge_transform_t *param_as_transform(
289 scope_transform_t *self)
290{
291 return &self->base;
292}
293
294static int scope_transform_apply(bithenge_transform_t *base,
295 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
296{
297 scope_transform_t *self = transform_as_param(base);
298 bithenge_scope_t inner_scope;
299 bithenge_scope_init(&inner_scope);
300 int rc = bithenge_scope_copy(&inner_scope, scope);
301 if (rc != EOK)
302 goto error;
303 bithenge_scope_set_current_node(&inner_scope, NULL);
304 rc = bithenge_transform_apply(self->transform, scope, in, out);
305error:
306 bithenge_scope_destroy(&inner_scope);
307 return rc;
308}
309
310static int scope_transform_prefix_length(bithenge_transform_t *base,
311 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
312{
313 scope_transform_t *self = transform_as_param(base);
314 return bithenge_transform_prefix_length(self->transform, scope, in,
315 out);
316}
317
318static void scope_transform_destroy(bithenge_transform_t *base)
319{
320 scope_transform_t *self = transform_as_param(base);
321 bithenge_transform_dec_ref(self->transform);
322 free(self);
323}
324
325static const bithenge_transform_ops_t scope_transform_ops = {
326 .apply = scope_transform_apply,
327 .prefix_length = scope_transform_prefix_length,
328 .destroy = scope_transform_destroy,
329};
330
331/** Create a wrapper transform that creates a new outer scope. This ensures
332 * nothing from the transform's users is passed in, other than parameters. The
333 * wrapper may have a different value for num_params. Takes a reference to
334 * @a transform, which it will use for all operations.
335 * @param[out] out Holds the created transform.
336 * @param transform The transform to wrap.
337 * @param num_params The number of parameters to require, which may be 0.
338 * @return EOK on success or an error code from errno.h. */
339int bithenge_new_scope_transform(bithenge_transform_t **out,
340 bithenge_transform_t *transform, int num_params)
341{
342 assert(transform);
343 assert(bithenge_transform_num_params(transform) == 0);
344
345 int rc;
346 scope_transform_t *self = malloc(sizeof(*self));
347 if (!self) {
348 rc = ENOMEM;
349 goto error;
350 }
351 rc = bithenge_init_transform(param_as_transform(self),
352 &scope_transform_ops, num_params);
353 if (rc != EOK)
354 goto error;
355 self->transform = transform;
356 *out = param_as_transform(self);
357 return EOK;
358error:
359 bithenge_transform_dec_ref(transform);
360 free(self);
361 return rc;
362}
363
364static int ascii_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
365 bithenge_node_t *in, bithenge_node_t **out)
366{
367 int rc;
368 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
369 return EINVAL;
370 bithenge_blob_t *blob = bithenge_node_as_blob(in);
371 aoff64_t size;
372 rc = bithenge_blob_size(blob, &size);
373 if (rc != EOK)
374 return rc;
375
376 char *buffer = malloc(size + 1);
377 if (!buffer)
378 return ENOMEM;
379 aoff64_t size_read = size;
380 rc = bithenge_blob_read(blob, 0, buffer, &size_read);
381 if (rc != EOK) {
382 free(buffer);
383 return rc;
384 }
385 if (size_read != size) {
386 free(buffer);
387 return EINVAL;
388 }
389 buffer[size] = '\0';
390
391 /* TODO: what if the OS encoding is incompatible with ASCII? */
392 return bithenge_new_string_node(out, buffer, true);
393}
394
395static const bithenge_transform_ops_t ascii_ops = {
396 .apply = ascii_apply,
397 .destroy = transform_indestructible,
398};
399
400/** The ASCII text transform. */
401bithenge_transform_t bithenge_ascii_transform = {
402 &ascii_ops, 1, 0
403};
404
405static int invalid_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
406 bithenge_node_t *in, bithenge_node_t **out)
407{
408 return EINVAL;
409}
410
411static const bithenge_transform_ops_t invalid_ops = {
412 .apply = invalid_apply,
413 .destroy = transform_indestructible,
414};
415
416/** A transform that always raises an error. */
417bithenge_transform_t bithenge_invalid_transform = {
418 &invalid_ops, 1, 0
419};
420
421static int known_length_apply(bithenge_transform_t *self,
422 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
423{
424 bithenge_node_t *length_node;
425 int rc = bithenge_scope_get_param(scope, 0, &length_node);
426 if (rc != EOK)
427 return rc;
428 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
429 bithenge_node_dec_ref(length_node);
430 return EINVAL;
431 }
432 bithenge_int_t length = bithenge_integer_node_value(length_node);
433 bithenge_node_dec_ref(length_node);
434
435 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
436 return EINVAL;
437 aoff64_t size;
438 rc = bithenge_blob_size(bithenge_node_as_blob(in), &size);
439 if (rc != EOK)
440 return rc;
441 if (length != (bithenge_int_t)size)
442 return EINVAL;
443
444 bithenge_node_inc_ref(in);
445 *out = in;
446 return EOK;
447}
448
449static int known_length_prefix_length(bithenge_transform_t *self,
450 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
451{
452 bithenge_node_t *length_node;
453 int rc = bithenge_scope_get_param(scope, 0, &length_node);
454 if (rc != EOK)
455 return rc;
456 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
457 bithenge_node_dec_ref(length_node);
458 return EINVAL;
459 }
460 bithenge_int_t length = bithenge_integer_node_value(length_node);
461 bithenge_node_dec_ref(length_node);
462
463 *out = (aoff64_t)length;
464 return EOK;
465}
466
467static const bithenge_transform_ops_t known_length_ops = {
468 .apply = known_length_apply,
469 .prefix_length = known_length_prefix_length,
470 .destroy = transform_indestructible,
471};
472
473/** Pass through a blob, but require its length to equal the first argument. */
474bithenge_transform_t bithenge_known_length_transform = {
475 &known_length_ops, 1, 1
476};
477
478static int prefix_length_1(bithenge_transform_t *self, bithenge_scope_t *scope,
479 bithenge_blob_t *blob, aoff64_t *out)
480{
481 *out = 1;
482 return EOK;
483}
484
485static int prefix_length_2(bithenge_transform_t *self, bithenge_scope_t *scope,
486 bithenge_blob_t *blob, aoff64_t *out)
487{
488 *out = 2;
489 return EOK;
490}
491
492static int prefix_length_4(bithenge_transform_t *self, bithenge_scope_t *scope,
493 bithenge_blob_t *blob, aoff64_t *out)
494{
495 *out = 4;
496 return EOK;
497}
498
499static int prefix_length_8(bithenge_transform_t *self, bithenge_scope_t *scope,
500 bithenge_blob_t *blob, aoff64_t *out)
501{
502 *out = 8;
503 return EOK;
504}
505
506#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
507 static int NAME##_apply(bithenge_transform_t *self, \
508 bithenge_scope_t *scope, bithenge_node_t *in, \
509 bithenge_node_t **out) \
510 { \
511 int rc; \
512 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
513 return EINVAL; \
514 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
515 \
516 /* Read too many bytes; success means the blob is too long. */ \
517 TYPE val[2]; \
518 aoff64_t size = sizeof(val[0]) + 1; \
519 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
520 if (rc != EOK) \
521 return rc; \
522 if (size != sizeof(val[0])) \
523 return EINVAL; \
524 \
525 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
526 } \
527 \
528 static const bithenge_transform_ops_t NAME##_ops = { \
529 .apply = NAME##_apply, \
530 .prefix_length = PREFIX_LENGTH_FUNC, \
531 .destroy = transform_indestructible, \
532 }; \
533 \
534 bithenge_transform_t bithenge_##NAME##_transform = { \
535 &NAME##_ops, 1, 0 \
536 }
537
538MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
539MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
540MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
541MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
542MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
543MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
544MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
545
546static int zero_terminated_apply(bithenge_transform_t *self,
547 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
548{
549 int rc;
550 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
551 return EINVAL;
552 bithenge_blob_t *blob = bithenge_node_as_blob(in);
553 aoff64_t size;
554 rc = bithenge_blob_size(blob, &size);
555 if (rc != EOK)
556 return rc;
557 if (size < 1)
558 return EINVAL;
559 char ch;
560 aoff64_t size_read = 1;
561 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
562 if (rc != EOK)
563 return rc;
564 if (size_read != 1 || ch != '\0')
565 return EINVAL;
566 bithenge_blob_inc_ref(blob);
567 return bithenge_new_subblob(out, blob, 0, size - 1);
568}
569
570static int zero_terminated_prefix_length(bithenge_transform_t *self,
571 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
572{
573 int rc;
574 char buffer[4096];
575 aoff64_t offset = 0, size_read = sizeof(buffer);
576 do {
577 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
578 if (rc != EOK)
579 return rc;
580 char *found = memchr(buffer, '\0', size_read);
581 if (found) {
582 *out = found - buffer + offset + 1;
583 return EOK;
584 }
585 offset += size_read;
586 } while (size_read == sizeof(buffer));
587 return EINVAL;
588}
589
590static const bithenge_transform_ops_t zero_terminated_ops = {
591 .apply = zero_terminated_apply,
592 .prefix_length = zero_terminated_prefix_length,
593 .destroy = transform_indestructible,
594};
595
596/** The zero-terminated data transform. */
597bithenge_transform_t bithenge_zero_terminated_transform = {
598 &zero_terminated_ops, 1, 0
599};
600
601static bithenge_named_transform_t primitive_transforms[] = {
602 {"ascii", &bithenge_ascii_transform},
603 {"known_length", &bithenge_known_length_transform},
604 {"uint8", &bithenge_uint8_transform},
605 {"uint16le", &bithenge_uint16le_transform},
606 {"uint16be", &bithenge_uint16be_transform},
607 {"uint32le", &bithenge_uint32le_transform},
608 {"uint32be", &bithenge_uint32be_transform},
609 {"uint64le", &bithenge_uint64le_transform},
610 {"uint64be", &bithenge_uint64be_transform},
611 {"zero_terminated", &bithenge_zero_terminated_transform},
612 {NULL, NULL}
613};
614
615/** An array of named built-in transforms. */
616bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
617
618typedef struct {
619 bithenge_transform_t base;
620 bithenge_transform_t **xforms;
621 size_t num;
622} compose_transform_t;
623
624static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
625{
626 return &xform->base;
627}
628
629static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
630{
631 return (compose_transform_t *)xform;
632}
633
634static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
635 bithenge_node_t *in, bithenge_node_t **out)
636{
637 int rc;
638 compose_transform_t *self = transform_as_compose(base);
639 bithenge_node_inc_ref(in);
640
641 /* i ranges from (self->num - 1) to 0 inside the loop. */
642 for (size_t i = self->num; i--; ) {
643 bithenge_node_t *tmp;
644 rc = bithenge_transform_apply(self->xforms[i], scope, in,
645 &tmp);
646 bithenge_node_dec_ref(in);
647 if (rc != EOK)
648 return rc;
649 in = tmp;
650 }
651
652 *out = in;
653 return rc;
654}
655
656static int compose_prefix_length(bithenge_transform_t *base,
657 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
658{
659 compose_transform_t *self = transform_as_compose(base);
660 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
661 scope, blob, out);
662}
663
664static void compose_destroy(bithenge_transform_t *base)
665{
666 compose_transform_t *self = transform_as_compose(base);
667 for (size_t i = 0; i < self->num; i++)
668 bithenge_transform_dec_ref(self->xforms[i]);
669 free(self->xforms);
670 free(self);
671}
672
673static const bithenge_transform_ops_t compose_transform_ops = {
674 .apply = compose_apply,
675 .prefix_length = compose_prefix_length,
676 .destroy = compose_destroy,
677};
678
679/** Create a composition of multiple transforms. When the result is applied to a
680 * node, each transform is applied in turn, with the last transform applied
681 * first. @a xforms may contain any number of transforms or no transforms at
682 * all. This function takes ownership of @a xforms and the references therein.
683 * @param[out] out Holds the result.
684 * @param[in] xforms The transforms to apply.
685 * @param num The number of transforms.
686 * @return EOK on success or an error code from errno.h. */
687int bithenge_new_composed_transform(bithenge_transform_t **out,
688 bithenge_transform_t **xforms, size_t num)
689{
690 if (num == 0) {
691 /* TODO: optimize */
692 } else if (num == 1) {
693 *out = xforms[0];
694 free(xforms);
695 return EOK;
696 }
697
698 int rc;
699 compose_transform_t *self = malloc(sizeof(*self));
700 if (!self) {
701 rc = ENOMEM;
702 goto error;
703 }
704 rc = bithenge_init_transform(compose_as_transform(self),
705 &compose_transform_ops, 0);
706 if (rc != EOK)
707 goto error;
708 self->xforms = xforms;
709 self->num = num;
710 *out = compose_as_transform(self);
711 return EOK;
712error:
713 for (size_t i = 0; i < num; i++)
714 bithenge_transform_dec_ref(xforms[i]);
715 free(xforms);
716 free(self);
717 return rc;
718}
719
720/** @}
721 */
Note: See TracBrowser for help on using the repository browser.