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

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

Bithenge: struct node performance improvement

  • Property mode set to 100644
File size: 24.2 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 bithenge_scope_t scope;
387 struct struct_transform *transform;
388 bithenge_blob_t *blob;
389 aoff64_t *ends;
390 size_t num_ends;
391 bool prefix;
392} struct_node_t;
393
394typedef struct struct_transform {
395 bithenge_transform_t base;
396 bithenge_named_transform_t *subtransforms;
397 size_t num_subtransforms;
398} struct_transform_t;
399
400static bithenge_node_t *struct_as_node(struct_node_t *node)
401{
402 return &node->base;
403}
404
405static struct_node_t *node_as_struct(bithenge_node_t *node)
406{
407 return (struct_node_t *)node;
408}
409
410static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
411{
412 return &xform->base;
413}
414
415static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
416{
417 return (struct_transform_t *)xform;
418}
419
420static int struct_node_field_offset(struct_node_t *self, aoff64_t *out,
421 size_t index)
422{
423 if (index == 0) {
424 *out = 0;
425 return EOK;
426 }
427 index--;
428 aoff64_t prev_offset =
429 self->num_ends ? self->ends[self->num_ends - 1] : 0;
430 for (; self->num_ends <= index; self->num_ends++) {
431 bithenge_node_t *subblob_node;
432 bithenge_blob_inc_ref(self->blob);
433 int rc = bithenge_new_offset_blob(&subblob_node, self->blob,
434 prev_offset);
435 if (rc != EOK)
436 return rc;
437
438 bithenge_blob_t *subblob = bithenge_node_as_blob(subblob_node);
439 aoff64_t field_size;
440 rc = bithenge_transform_prefix_length(
441 self->transform->subtransforms[self->num_ends].transform,
442 &self->scope, subblob, &field_size);
443 bithenge_node_dec_ref(subblob_node);
444 if (rc != EOK)
445 return rc;
446
447 prev_offset = self->ends[self->num_ends] =
448 prev_offset + field_size;
449 }
450 *out = self->ends[index];
451 return EOK;
452}
453
454static int struct_node_subtransform(struct_node_t *self, bithenge_node_t **out,
455 size_t index)
456{
457 aoff64_t start_pos, end_pos;
458 int rc = struct_node_field_offset(self, &start_pos, index);
459 if (rc != EOK)
460 return rc;
461 rc = struct_node_field_offset(self, &end_pos, index + 1);
462 if (rc != EOK)
463 return rc;
464
465 bithenge_node_t *blob_node;
466 bithenge_blob_inc_ref(self->blob);
467 rc = bithenge_new_subblob(&blob_node, self->blob, start_pos,
468 end_pos - start_pos);
469 if (rc != EOK)
470 return rc;
471
472 rc = bithenge_transform_apply(
473 self->transform->subtransforms[index].transform, &self->scope,
474 blob_node, out);
475 bithenge_node_dec_ref(blob_node);
476 if (rc != EOK)
477 return rc;
478
479 return EOK;
480}
481
482static int struct_node_for_each(bithenge_node_t *base,
483 bithenge_for_each_func_t func, void *data)
484{
485 int rc = EOK;
486 struct_node_t *self = node_as_struct(base);
487 bithenge_named_transform_t *subxforms =
488 self->transform->subtransforms;
489
490 for (size_t i = 0; subxforms[i].transform; i++) {
491 bithenge_node_t *subxform_result;
492 rc = struct_node_subtransform(self, &subxform_result, i);
493 if (rc != EOK)
494 return rc;
495
496 if (subxforms[i].name) {
497 bithenge_node_t *name_node;
498 rc = bithenge_new_string_node(&name_node,
499 subxforms[i].name, false);
500 if (rc == EOK) {
501 rc = func(name_node, subxform_result, data);
502 subxform_result = NULL;
503 }
504 } else {
505 if (bithenge_node_type(subxform_result) !=
506 BITHENGE_NODE_INTERNAL) {
507 rc = EINVAL;
508 } else {
509 rc = bithenge_node_for_each(subxform_result,
510 func, data);
511 }
512 }
513 bithenge_node_dec_ref(subxform_result);
514 if (rc != EOK)
515 return rc;
516 }
517
518 if (!self->prefix) {
519 aoff64_t blob_size, end_pos;
520 rc = bithenge_blob_size(self->blob, &blob_size);
521 if (rc != EOK)
522 return rc;
523 rc = struct_node_field_offset(self, &end_pos,
524 self->transform->num_subtransforms);
525 if (rc != EOK)
526 return rc;
527 if (blob_size != end_pos) {
528 rc = EINVAL;
529 return rc;
530 }
531 }
532
533 return rc;
534}
535
536static int struct_node_get(bithenge_node_t *base, bithenge_node_t *key,
537 bithenge_node_t **out)
538{
539 struct_node_t *self = node_as_struct(base);
540
541 if (bithenge_node_type(key) != BITHENGE_NODE_STRING) {
542 bithenge_node_dec_ref(key);
543 return ENOENT;
544 }
545 const char *name = bithenge_string_node_value(key);
546
547 for (size_t i = 0; self->transform->subtransforms[i].transform; i++) {
548 if (self->transform->subtransforms[i].name
549 && !str_cmp(name, self->transform->subtransforms[i].name)) {
550 bithenge_node_dec_ref(key);
551 return struct_node_subtransform(self, out, i);
552 }
553 }
554
555 for (size_t i = 0; self->transform->subtransforms[i].transform; i++) {
556 if (self->transform->subtransforms[i].name)
557 continue;
558 bithenge_node_t *subxform_result;
559 int rc = struct_node_subtransform(self, &subxform_result, i);
560 if (rc != EOK) {
561 bithenge_node_dec_ref(key);
562 return rc;
563 }
564 if (bithenge_node_type(subxform_result) !=
565 BITHENGE_NODE_INTERNAL) {
566 bithenge_node_dec_ref(subxform_result);
567 bithenge_node_dec_ref(key);
568 return EINVAL;
569 }
570 bithenge_node_inc_ref(key);
571 rc = bithenge_node_get(subxform_result, key, out);
572 bithenge_node_dec_ref(subxform_result);
573 if (rc != ENOENT) {
574 bithenge_node_dec_ref(key);
575 return rc;
576 }
577 }
578
579 bithenge_node_dec_ref(key);
580 return ENOENT;
581}
582
583static void struct_node_destroy(bithenge_node_t *base)
584{
585 struct_node_t *node = node_as_struct(base);
586
587 /* We didn't inc_ref for the scope in struct_transform_make_node, so
588 * make sure it doesn't try to dec_ref. */
589 node->scope.current_node = NULL;
590 bithenge_scope_destroy(&node->scope);
591
592 bithenge_transform_dec_ref(struct_as_transform(node->transform));
593 bithenge_blob_dec_ref(node->blob);
594 free(node->ends);
595 free(node);
596}
597
598static const bithenge_internal_node_ops_t struct_node_ops = {
599 .for_each = struct_node_for_each,
600 .get = struct_node_get,
601 .destroy = struct_node_destroy,
602};
603
604static int struct_transform_make_node(struct_transform_t *self,
605 bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
606 bool prefix)
607{
608 struct_node_t *node = malloc(sizeof(*node));
609 if (!node)
610 return ENOMEM;
611
612 bithenge_scope_init(&node->scope);
613 int rc = bithenge_scope_copy(&node->scope, scope);
614 if (rc != EOK) {
615 free(node);
616 return rc;
617 }
618
619 node->ends = malloc(sizeof(*node->ends) * self->num_subtransforms);
620 if (!node->ends) {
621 bithenge_scope_destroy(&node->scope);
622 free(node);
623 return ENOMEM;
624 }
625
626 rc = bithenge_init_internal_node(struct_as_node(node),
627 &struct_node_ops);
628 if (rc != EOK) {
629 bithenge_scope_destroy(&node->scope);
630 free(node->ends);
631 free(node);
632 return rc;
633 }
634
635 bithenge_transform_inc_ref(struct_as_transform(self));
636 bithenge_blob_inc_ref(blob);
637 node->transform = self;
638 node->blob = blob;
639 node->prefix = prefix;
640 node->num_ends = 0;
641 *out = struct_as_node(node);
642
643 /* We should inc_ref(*out) here, but that would make a cycle. Instead,
644 * we leave it 1 too low, so that when the only remaining use of *out
645 * is the scope, *out will be destroyed. Also see the comment in
646 * struct_node_destroy. */
647 bithenge_scope_set_current_node(&node->scope, *out);
648
649 return EOK;
650}
651
652static int struct_transform_apply(bithenge_transform_t *base,
653 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
654{
655 struct_transform_t *self = transform_as_struct(base);
656 if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
657 return EINVAL;
658 return struct_transform_make_node(self, out, scope,
659 bithenge_node_as_blob(in), false);
660}
661
662static int struct_transform_prefix_length(bithenge_transform_t *base,
663 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
664{
665 struct_transform_t *self = transform_as_struct(base);
666 bithenge_node_t *struct_node;
667 int rc = struct_transform_make_node(self, &struct_node, scope, blob,
668 true);
669 if (rc != EOK)
670 return rc;
671
672 rc = struct_node_field_offset(node_as_struct(struct_node), out,
673 self->num_subtransforms);
674 bithenge_node_dec_ref(struct_node);
675 return rc;
676}
677
678static void free_subtransforms(bithenge_named_transform_t *subtransforms)
679{
680 for (size_t i = 0; subtransforms[i].transform; i++) {
681 free((void *)subtransforms[i].name);
682 bithenge_transform_dec_ref(subtransforms[i].transform);
683 }
684 free(subtransforms);
685}
686
687static void struct_transform_destroy(bithenge_transform_t *base)
688{
689 struct_transform_t *self = transform_as_struct(base);
690 free_subtransforms(self->subtransforms);
691 free(self);
692}
693
694static bithenge_transform_ops_t struct_transform_ops = {
695 .apply = struct_transform_apply,
696 .prefix_length = struct_transform_prefix_length,
697 .destroy = struct_transform_destroy,
698};
699
700/** Create a struct transform. The transform will apply its subtransforms
701 * sequentially to a blob to create an internal node. Each result is either
702 * given a key from @a subtransforms or, if the name is NULL, the result's keys
703 * and values are merged into the struct transform's result. This function
704 * takes ownership of @a subtransforms and the names and references therein.
705 * @param[out] out Stores the created transform.
706 * @param subtransforms The subtransforms and field names.
707 * @return EOK on success or an error code from errno.h. */
708int bithenge_new_struct(bithenge_transform_t **out,
709 bithenge_named_transform_t *subtransforms)
710{
711 int rc;
712 struct_transform_t *self = malloc(sizeof(*self));
713 if (!self) {
714 rc = ENOMEM;
715 goto error;
716 }
717 rc = bithenge_init_transform(struct_as_transform(self),
718 &struct_transform_ops, 0);
719 if (rc != EOK)
720 goto error;
721 self->subtransforms = subtransforms;
722 self->num_subtransforms = 0;
723 for (self->num_subtransforms = 0;
724 subtransforms[self->num_subtransforms].transform;
725 self->num_subtransforms++);
726 *out = struct_as_transform(self);
727 return EOK;
728error:
729 free_subtransforms(subtransforms);
730 free(self);
731 return rc;
732}
733
734typedef struct {
735 bithenge_transform_t base;
736 bithenge_transform_t **xforms;
737 size_t num;
738} compose_transform_t;
739
740static bithenge_transform_t *compose_as_transform(compose_transform_t *xform)
741{
742 return &xform->base;
743}
744
745static compose_transform_t *transform_as_compose(bithenge_transform_t *xform)
746{
747 return (compose_transform_t *)xform;
748}
749
750static int compose_apply(bithenge_transform_t *base, bithenge_scope_t *scope,
751 bithenge_node_t *in, bithenge_node_t **out)
752{
753 int rc;
754 compose_transform_t *self = transform_as_compose(base);
755 bithenge_node_inc_ref(in);
756
757 /* i ranges from (self->num - 1) to 0 inside the loop. */
758 for (size_t i = self->num; i--; ) {
759 bithenge_node_t *tmp;
760 rc = bithenge_transform_apply(self->xforms[i], scope, in,
761 &tmp);
762 bithenge_node_dec_ref(in);
763 if (rc != EOK)
764 return rc;
765 in = tmp;
766 }
767
768 *out = in;
769 return rc;
770}
771
772static int compose_prefix_length(bithenge_transform_t *base,
773 bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
774{
775 compose_transform_t *self = transform_as_compose(base);
776 return bithenge_transform_prefix_length(self->xforms[self->num - 1],
777 scope, blob, out);
778}
779
780static void compose_destroy(bithenge_transform_t *base)
781{
782 compose_transform_t *self = transform_as_compose(base);
783 for (size_t i = 0; i < self->num; i++)
784 bithenge_transform_dec_ref(self->xforms[i]);
785 free(self->xforms);
786 free(self);
787}
788
789static const bithenge_transform_ops_t compose_transform_ops = {
790 .apply = compose_apply,
791 .prefix_length = compose_prefix_length,
792 .destroy = compose_destroy,
793};
794
795/** Create a composition of multiple transforms. When the result is applied to a
796 * node, each transform is applied in turn, with the last transform applied
797 * first. @a xforms may contain any number of transforms or no transforms at
798 * all. This function takes ownership of @a xforms and the references therein.
799 * @param[out] out Holds the result.
800 * @param[in] xforms The transforms to apply.
801 * @param num The number of transforms.
802 * @return EOK on success or an error code from errno.h. */
803int bithenge_new_composed_transform(bithenge_transform_t **out,
804 bithenge_transform_t **xforms, size_t num)
805{
806 if (num == 0) {
807 /* TODO: optimize */
808 } else if (num == 1) {
809 *out = xforms[0];
810 free(xforms);
811 return EOK;
812 }
813
814 int rc;
815 compose_transform_t *self = malloc(sizeof(*self));
816 if (!self) {
817 rc = ENOMEM;
818 goto error;
819 }
820 rc = bithenge_init_transform(compose_as_transform(self),
821 &compose_transform_ops, 0);
822 if (rc != EOK)
823 goto error;
824 self->xforms = xforms;
825 self->num = num;
826 *out = compose_as_transform(self);
827 return EOK;
828error:
829 for (size_t i = 0; i < num; i++)
830 bithenge_transform_dec_ref(xforms[i]);
831 free(xforms);
832 free(self);
833 return rc;
834}
835
836/** @}
837 */
Note: See TracBrowser for help on using the repository browser.