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

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

Bithenge: add transform_prefix_apply

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