source: mainline/uspace/app/bithenge/transform.c@ 23db8aa

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

Bithenge: add switch transforms and sugar

  • Property mode set to 100644
File size: 24.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 invalid_apply(bithenge_transform_t *self, bithenge_scope_t *scope,
188 bithenge_node_t *in, bithenge_node_t **out)
189{
190 return EINVAL;
191}
192
193static const bithenge_transform_ops_t invalid_ops = {
194 .apply = invalid_apply,
195 .destroy = transform_indestructible,
196};
197
198/** A transform that always raises an error. */
199bithenge_transform_t bithenge_invalid_transform = {
200 &invalid_ops, 1, 0
201};
202
203static int known_length_apply(bithenge_transform_t *self,
204 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
205{
206 bithenge_node_t *length_node;
207 int rc = bithenge_scope_get_param(scope, 0, &length_node);
208 if (rc != EOK)
209 return rc;
210 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
211 bithenge_node_dec_ref(length_node);
212 return EINVAL;
213 }
214 bithenge_int_t length = bithenge_integer_node_value(length_node);
215 bithenge_node_dec_ref(length_node);
216
217 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
218 return EINVAL;
219 aoff64_t size;
220 rc = bithenge_blob_size(bithenge_node_as_blob(in), &size);
221 if (rc != EOK)
222 return rc;
223 if (length != (bithenge_int_t)size)
224 return EINVAL;
225
226 bithenge_node_inc_ref(in);
227 *out = in;
228 return EOK;
229}
230
231static int known_length_prefix_length(bithenge_transform_t *self,
232 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
233{
234 bithenge_node_t *length_node;
235 int rc = bithenge_scope_get_param(scope, 0, &length_node);
236 if (rc != EOK)
237 return rc;
238 if (bithenge_node_type(length_node) != BITHENGE_NODE_INTEGER) {
239 bithenge_node_dec_ref(length_node);
240 return EINVAL;
241 }
242 bithenge_int_t length = bithenge_integer_node_value(length_node);
243 bithenge_node_dec_ref(length_node);
244
245 *out = (aoff64_t)length;
246 return EOK;
247}
248
249static const bithenge_transform_ops_t known_length_ops = {
250 .apply = known_length_apply,
251 .prefix_length = known_length_prefix_length,
252 .destroy = transform_indestructible,
253};
254
255/** Pass through a blob, but require its length to equal the first argument. */
256bithenge_transform_t bithenge_known_length_transform = {
257 &known_length_ops, 1, 1
258};
259
260static int prefix_length_1(bithenge_transform_t *self, bithenge_scope_t *scope,
261 bithenge_blob_t *blob, aoff64_t *out)
262{
263 *out = 1;
264 return EOK;
265}
266
267static int prefix_length_2(bithenge_transform_t *self, bithenge_scope_t *scope,
268 bithenge_blob_t *blob, aoff64_t *out)
269{
270 *out = 2;
271 return EOK;
272}
273
274static int prefix_length_4(bithenge_transform_t *self, bithenge_scope_t *scope,
275 bithenge_blob_t *blob, aoff64_t *out)
276{
277 *out = 4;
278 return EOK;
279}
280
281static int prefix_length_8(bithenge_transform_t *self, bithenge_scope_t *scope,
282 bithenge_blob_t *blob, aoff64_t *out)
283{
284 *out = 8;
285 return EOK;
286}
287
288#define MAKE_UINT_TRANSFORM(NAME, TYPE, ENDIAN, PREFIX_LENGTH_FUNC) \
289 static int NAME##_apply(bithenge_transform_t *self, \
290 bithenge_scope_t *scope, bithenge_node_t *in, \
291 bithenge_node_t **out) \
292 { \
293 int rc; \
294 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB) \
295 return EINVAL; \
296 bithenge_blob_t *blob = bithenge_node_as_blob(in); \
297 \
298 /* Read too many bytes; success means the blob is too long. */ \
299 TYPE val[2]; \
300 aoff64_t size = sizeof(val[0]) + 1; \
301 rc = bithenge_blob_read(blob, 0, (char *)val, &size); \
302 if (rc != EOK) \
303 return rc; \
304 if (size != sizeof(val[0])) \
305 return EINVAL; \
306 \
307 return bithenge_new_integer_node(out, ENDIAN(val[0])); \
308 } \
309 \
310 static const bithenge_transform_ops_t NAME##_ops = { \
311 .apply = NAME##_apply, \
312 .prefix_length = PREFIX_LENGTH_FUNC, \
313 .destroy = transform_indestructible, \
314 }; \
315 \
316 bithenge_transform_t bithenge_##NAME##_transform = { \
317 &NAME##_ops, 1, 0 \
318 }
319
320MAKE_UINT_TRANSFORM(uint8 , uint8_t , , prefix_length_1);
321MAKE_UINT_TRANSFORM(uint16le, uint16_t, uint16_t_le2host, prefix_length_2);
322MAKE_UINT_TRANSFORM(uint16be, uint16_t, uint16_t_be2host, prefix_length_2);
323MAKE_UINT_TRANSFORM(uint32le, uint32_t, uint32_t_le2host, prefix_length_4);
324MAKE_UINT_TRANSFORM(uint32be, uint32_t, uint32_t_be2host, prefix_length_4);
325MAKE_UINT_TRANSFORM(uint64le, uint64_t, uint32_t_le2host, prefix_length_8);
326MAKE_UINT_TRANSFORM(uint64be, uint64_t, uint32_t_be2host, prefix_length_8);
327
328static int zero_terminated_apply(bithenge_transform_t *self,
329 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
330{
331 int rc;
332 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
333 return EINVAL;
334 bithenge_blob_t *blob = bithenge_node_as_blob(in);
335 aoff64_t size;
336 rc = bithenge_blob_size(blob, &size);
337 if (rc != EOK)
338 return rc;
339 if (size < 1)
340 return EINVAL;
341 char ch;
342 aoff64_t size_read = 1;
343 rc = bithenge_blob_read(blob, size - 1, &ch, &size_read);
344 if (rc != EOK)
345 return rc;
346 if (size_read != 1 || ch != '\0')
347 return EINVAL;
348 bithenge_blob_inc_ref(blob);
349 return bithenge_new_subblob(out, blob, 0, size - 1);
350}
351
352static int zero_terminated_prefix_length(bithenge_transform_t *self,
353 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
354{
355 int rc;
356 char buffer[4096];
357 aoff64_t offset = 0, size_read = sizeof(buffer);
358 do {
359 rc = bithenge_blob_read(blob, offset, buffer, &size_read);
360 if (rc != EOK)
361 return rc;
362 char *found = memchr(buffer, '\0', size_read);
363 if (found) {
364 *out = found - buffer + offset + 1;
365 return EOK;
366 }
367 offset += size_read;
368 } while (size_read == sizeof(buffer));
369 return EINVAL;
370}
371
372static const bithenge_transform_ops_t zero_terminated_ops = {
373 .apply = zero_terminated_apply,
374 .prefix_length = zero_terminated_prefix_length,
375 .destroy = transform_indestructible,
376};
377
378/** The zero-terminated data transform. */
379bithenge_transform_t bithenge_zero_terminated_transform = {
380 &zero_terminated_ops, 1, 0
381};
382
383static bithenge_named_transform_t primitive_transforms[] = {
384 {"ascii", &bithenge_ascii_transform},
385 {"known_length", &bithenge_known_length_transform},
386 {"uint8", &bithenge_uint8_transform},
387 {"uint16le", &bithenge_uint16le_transform},
388 {"uint16be", &bithenge_uint16be_transform},
389 {"uint32le", &bithenge_uint32le_transform},
390 {"uint32be", &bithenge_uint32be_transform},
391 {"uint64le", &bithenge_uint64le_transform},
392 {"uint64be", &bithenge_uint64be_transform},
393 {"zero_terminated", &bithenge_zero_terminated_transform},
394 {NULL, NULL}
395};
396
397/** An array of named built-in transforms. */
398bithenge_named_transform_t *bithenge_primitive_transforms = primitive_transforms;
399
400typedef struct {
401 bithenge_node_t base;
402 bithenge_scope_t scope;
403 struct struct_transform *transform;
404 bithenge_blob_t *blob;
405 aoff64_t *ends;
406 size_t num_ends;
407 bool prefix;
408} struct_node_t;
409
410typedef struct struct_transform {
411 bithenge_transform_t base;
412 bithenge_named_transform_t *subtransforms;
413 size_t num_subtransforms;
414} struct_transform_t;
415
416static bithenge_node_t *struct_as_node(struct_node_t *node)
417{
418 return &node->base;
419}
420
421static struct_node_t *node_as_struct(bithenge_node_t *node)
422{
423 return (struct_node_t *)node;
424}
425
426static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
427{
428 return &xform->base;
429}
430
431static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
432{
433 return (struct_transform_t *)xform;
434}
435
436static int struct_node_field_offset(struct_node_t *self, aoff64_t *out,
437 size_t index)
438{
439 if (index == 0) {
440 *out = 0;
441 return EOK;
442 }
443 index--;
444 aoff64_t prev_offset =
445 self->num_ends ? self->ends[self->num_ends - 1] : 0;
446 for (; self->num_ends <= index; self->num_ends++) {
447 bithenge_node_t *subblob_node;
448 bithenge_blob_inc_ref(self->blob);
449 int rc = bithenge_new_offset_blob(&subblob_node, self->blob,
450 prev_offset);
451 if (rc != EOK)
452 return rc;
453
454 bithenge_blob_t *subblob = bithenge_node_as_blob(subblob_node);
455 aoff64_t field_size;
456 rc = bithenge_transform_prefix_length(
457 self->transform->subtransforms[self->num_ends].transform,
458 &self->scope, subblob, &field_size);
459 bithenge_node_dec_ref(subblob_node);
460 if (rc != EOK)
461 return rc;
462
463 prev_offset = self->ends[self->num_ends] =
464 prev_offset + field_size;
465 }
466 *out = self->ends[index];
467 return EOK;
468}
469
470static int struct_node_subtransform(struct_node_t *self, bithenge_node_t **out,
471 size_t index)
472{
473 aoff64_t start_pos, end_pos;
474 int rc = struct_node_field_offset(self, &start_pos, index);
475 if (rc != EOK)
476 return rc;
477 rc = struct_node_field_offset(self, &end_pos, index + 1);
478 if (rc != EOK)
479 return rc;
480
481 bithenge_node_t *blob_node;
482 bithenge_blob_inc_ref(self->blob);
483 rc = bithenge_new_subblob(&blob_node, self->blob, start_pos,
484 end_pos - start_pos);
485 if (rc != EOK)
486 return rc;
487
488 rc = bithenge_transform_apply(
489 self->transform->subtransforms[index].transform, &self->scope,
490 blob_node, out);
491 bithenge_node_dec_ref(blob_node);
492 if (rc != EOK)
493 return rc;
494
495 return EOK;
496}
497
498static int struct_node_for_each(bithenge_node_t *base,
499 bithenge_for_each_func_t func, void *data)
500{
501 int rc = EOK;
502 struct_node_t *self = node_as_struct(base);
503 bithenge_named_transform_t *subxforms =
504 self->transform->subtransforms;
505
506 for (size_t i = 0; subxforms[i].transform; i++) {
507 bithenge_node_t *subxform_result;
508 rc = struct_node_subtransform(self, &subxform_result, i);
509 if (rc != EOK)
510 return rc;
511
512 if (subxforms[i].name) {
513 bithenge_node_t *name_node;
514 rc = bithenge_new_string_node(&name_node,
515 subxforms[i].name, false);
516 if (rc == EOK) {
517 rc = func(name_node, subxform_result, data);
518 subxform_result = NULL;
519 }
520 } else {
521 if (bithenge_node_type(subxform_result) !=
522 BITHENGE_NODE_INTERNAL) {
523 rc = EINVAL;
524 } else {
525 rc = bithenge_node_for_each(subxform_result,
526 func, data);
527 }
528 }
529 bithenge_node_dec_ref(subxform_result);
530 if (rc != EOK)
531 return rc;
532 }
533
534 if (!self->prefix) {
535 aoff64_t blob_size, end_pos;
536 rc = bithenge_blob_size(self->blob, &blob_size);
537 if (rc != EOK)
538 return rc;
539 rc = struct_node_field_offset(self, &end_pos,
540 self->transform->num_subtransforms);
541 if (rc != EOK)
542 return rc;
543 if (blob_size != end_pos) {
544 rc = EINVAL;
545 return rc;
546 }
547 }
548
549 return rc;
550}
551
552static int struct_node_get(bithenge_node_t *base, bithenge_node_t *key,
553 bithenge_node_t **out)
554{
555 struct_node_t *self = node_as_struct(base);
556
557 if (bithenge_node_type(key) != BITHENGE_NODE_STRING) {
558 bithenge_node_dec_ref(key);
559 return ENOENT;
560 }
561 const char *name = bithenge_string_node_value(key);
562
563 for (size_t i = 0; self->transform->subtransforms[i].transform; i++) {
564 if (self->transform->subtransforms[i].name
565 && !str_cmp(name, self->transform->subtransforms[i].name)) {
566 bithenge_node_dec_ref(key);
567 return struct_node_subtransform(self, out, i);
568 }
569 }
570
571 for (size_t i = 0; self->transform->subtransforms[i].transform; i++) {
572 if (self->transform->subtransforms[i].name)
573 continue;
574 bithenge_node_t *subxform_result;
575 int rc = struct_node_subtransform(self, &subxform_result, i);
576 if (rc != EOK) {
577 bithenge_node_dec_ref(key);
578 return rc;
579 }
580 if (bithenge_node_type(subxform_result) !=
581 BITHENGE_NODE_INTERNAL) {
582 bithenge_node_dec_ref(subxform_result);
583 bithenge_node_dec_ref(key);
584 return EINVAL;
585 }
586 bithenge_node_inc_ref(key);
587 rc = bithenge_node_get(subxform_result, key, out);
588 bithenge_node_dec_ref(subxform_result);
589 if (rc != ENOENT) {
590 bithenge_node_dec_ref(key);
591 return rc;
592 }
593 }
594
595 bithenge_node_dec_ref(key);
596 return ENOENT;
597}
598
599static void struct_node_destroy(bithenge_node_t *base)
600{
601 struct_node_t *node = node_as_struct(base);
602
603 /* We didn't inc_ref for the scope in struct_transform_make_node, so
604 * make sure it doesn't try to dec_ref. */
605 node->scope.current_node = NULL;
606 bithenge_scope_destroy(&node->scope);
607
608 bithenge_transform_dec_ref(struct_as_transform(node->transform));
609 bithenge_blob_dec_ref(node->blob);
610 free(node->ends);
611 free(node);
612}
613
614static const bithenge_internal_node_ops_t struct_node_ops = {
615 .for_each = struct_node_for_each,
616 .get = struct_node_get,
617 .destroy = struct_node_destroy,
618};
619
620static int struct_transform_make_node(struct_transform_t *self,
621 bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
622 bool prefix)
623{
624 struct_node_t *node = malloc(sizeof(*node));
625 if (!node)
626 return ENOMEM;
627
628 bithenge_scope_init(&node->scope);
629 int rc = bithenge_scope_copy(&node->scope, scope);
630 if (rc != EOK) {
631 free(node);
632 return rc;
633 }
634
635 node->ends = malloc(sizeof(*node->ends) * self->num_subtransforms);
636 if (!node->ends) {
637 bithenge_scope_destroy(&node->scope);
638 free(node);
639 return ENOMEM;
640 }
641
642 rc = bithenge_init_internal_node(struct_as_node(node),
643 &struct_node_ops);
644 if (rc != EOK) {
645 bithenge_scope_destroy(&node->scope);
646 free(node->ends);
647 free(node);
648 return rc;
649 }
650
651 bithenge_transform_inc_ref(struct_as_transform(self));
652 bithenge_blob_inc_ref(blob);
653 node->transform = self;
654 node->blob = blob;
655 node->prefix = prefix;
656 node->num_ends = 0;
657 *out = struct_as_node(node);
658
659 /* We should inc_ref(*out) here, but that would make a cycle. Instead,
660 * we leave it 1 too low, so that when the only remaining use of *out
661 * is the scope, *out will be destroyed. Also see the comment in
662 * struct_node_destroy. */
663 bithenge_scope_set_current_node(&node->scope, *out);
664
665 return EOK;
666}
667
668static int struct_transform_apply(bithenge_transform_t *base,
669 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
670{
671 struct_transform_t *self = transform_as_struct(base);
672 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
673 return EINVAL;
674 return struct_transform_make_node(self, out, scope,
675 bithenge_node_as_blob(in), false);
676}
677
678static int struct_transform_prefix_length(bithenge_transform_t *base,
679 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
680{
681 struct_transform_t *self = transform_as_struct(base);
682 bithenge_node_t *struct_node;
683 int rc = struct_transform_make_node(self, &struct_node, scope, blob,
684 true);
685 if (rc != EOK)
686 return rc;
687
688 rc = struct_node_field_offset(node_as_struct(struct_node), out,
689 self->num_subtransforms);
690 bithenge_node_dec_ref(struct_node);
691 return rc;
692}
693
694static void free_subtransforms(bithenge_named_transform_t *subtransforms)
695{
696 for (size_t i = 0; subtransforms[i].transform; i++) {
697 free((void *)subtransforms[i].name);
698 bithenge_transform_dec_ref(subtransforms[i].transform);
699 }
700 free(subtransforms);
701}
702
703static void struct_transform_destroy(bithenge_transform_t *base)
704{
705 struct_transform_t *self = transform_as_struct(base);
706 free_subtransforms(self->subtransforms);
707 free(self);
708}
709
710static bithenge_transform_ops_t struct_transform_ops = {
711 .apply = struct_transform_apply,
712 .prefix_length = struct_transform_prefix_length,
713 .destroy = struct_transform_destroy,
714};
715
716/** Create a struct transform. The transform will apply its subtransforms
717 * sequentially to a blob to create an internal node. Each result is either
718 * given a key from @a subtransforms or, if the name is NULL, the result's keys
719 * and values are merged into the struct transform's result. This function
720 * takes ownership of @a subtransforms and the names and references therein.
721 * @param[out] out Stores the created transform.
722 * @param subtransforms The subtransforms and field names.
723 * @return EOK on success or an error code from errno.h. */
724int bithenge_new_struct(bithenge_transform_t **out,
725 bithenge_named_transform_t *subtransforms)
726{
727 int rc;
728 struct_transform_t *self = malloc(sizeof(*self));
729 if (!self) {
730 rc = ENOMEM;
731 goto error;
732 }
733 rc = bithenge_init_transform(struct_as_transform(self),
734 &struct_transform_ops, 0);
735 if (rc != EOK)
736 goto error;
737 self->subtransforms = subtransforms;
738 self->num_subtransforms = 0;
739 for (self->num_subtransforms = 0;
740 subtransforms[self->num_subtransforms].transform;
741 self->num_subtransforms++);
742 *out = struct_as_transform(self);
743 return EOK;
744error:
745 free_subtransforms(subtransforms);
746 free(self);
747 return rc;
748}
749
750typedef struct {
751 bithenge_transform_t base;
752 bithenge_transform_t **xforms;
753 size_t num;
754} compose_transform_t;
755
756static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
757{
758 return &xform->base;
759}
760
761static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
762{
763 return (compose_transform_t *)xform;
764}
765
766static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
767 bithenge_node_t *in, bithenge_node_t **out)
768{
769 int rc;
770 compose_transform_t *self = transform_as_compose(base);
771 bithenge_node_inc_ref(in);
772
773 /* i ranges from (self->num - 1) to 0 inside the loop. */
774 for (size_t i = self->num; i--; ) {
775 bithenge_node_t *tmp;
776 rc = bithenge_transform_apply(self->xforms[i], scope, in,
777 &tmp);
778 bithenge_node_dec_ref(in);
779 if (rc != EOK)
780 return rc;
781 in = tmp;
782 }
783
784 *out = in;
785 return rc;
786}
787
788static int compose_prefix_length(bithenge_transform_t *base,
789 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
790{
791 compose_transform_t *self = transform_as_compose(base);
792 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
793 scope, blob, out);
794}
795
796static void compose_destroy(bithenge_transform_t *base)
797{
798 compose_transform_t *self = transform_as_compose(base);
799 for (size_t i = 0; i < self->num; i++)
800 bithenge_transform_dec_ref(self->xforms[i]);
801 free(self->xforms);
802 free(self);
803}
804
805static const bithenge_transform_ops_t compose_transform_ops = {
806 .apply = compose_apply,
807 .prefix_length = compose_prefix_length,
808 .destroy = compose_destroy,
809};
810
811/** Create a composition of multiple transforms. When the result is applied to a
812 * node, each transform is applied in turn, with the last transform applied
813 * first. @a xforms may contain any number of transforms or no transforms at
814 * all. This function takes ownership of @a xforms and the references therein.
815 * @param[out] out Holds the result.
816 * @param[in] xforms The transforms to apply.
817 * @param num The number of transforms.
818 * @return EOK on success or an error code from errno.h. */
819int bithenge_new_composed_transform(bithenge_transform_t **out,
820 bithenge_transform_t **xforms, size_t num)
821{
822 if (num == 0) {
823 /* TODO: optimize */
824 } else if (num == 1) {
825 *out = xforms[0];
826 free(xforms);
827 return EOK;
828 }
829
830 int rc;
831 compose_transform_t *self = malloc(sizeof(*self));
832 if (!self) {
833 rc = ENOMEM;
834 goto error;
835 }
836 rc = bithenge_init_transform(compose_as_transform(self),
837 &compose_transform_ops, 0);
838 if (rc != EOK)
839 goto error;
840 self->xforms = xforms;
841 self->num = num;
842 *out = compose_as_transform(self);
843 return EOK;
844error:
845 for (size_t i = 0; i < num; i++)
846 bithenge_transform_dec_ref(xforms[i]);
847 free(xforms);
848 free(self);
849 return rc;
850}
851
852/** @}
853 */
Note: See TracBrowser for help on using the repository browser.