source: mainline/uspace/lib/bithenge/expression.c@ 1c79996

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

Bithenge: fix issues and expand coverage for test.sh

  • Property mode set to 100644
File size: 30.8 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 * Expressions.
35 */
36
37#include <assert.h>
38#include <errno.h>
39#include <stdlib.h>
40#include "blob.h"
41#include "expression.h"
42#include "transform.h"
43#include "tree.h"
44
45/** Initialize a new expression.
46 * @param[out] self Expression to initialize.
47 * @param[in] ops Operations provided by the expression.
48 * @return EOK or an error code from errno.h. */
49int bithenge_init_expression(bithenge_expression_t *self,
50 const bithenge_expression_ops_t *ops)
51{
52 assert(ops);
53 assert(ops->evaluate);
54 assert(ops->destroy);
55 self->ops = ops;
56 self->refs = 1;
57 return EOK;
58}
59
60static void expression_indestructible(bithenge_expression_t *self)
61{
62 assert(false);
63}
64
65
66
67/***************** binary_expression *****************/
68
69typedef struct {
70 bithenge_expression_t base;
71 bithenge_binary_op_t op;
72 bithenge_expression_t *a, *b;
73} binary_expression_t;
74
75static inline binary_expression_t *expression_as_binary(
76 bithenge_expression_t *base)
77{
78 return (binary_expression_t *)base;
79}
80
81static inline bithenge_expression_t *binary_as_expression(
82 binary_expression_t *self)
83{
84 return &self->base;
85}
86
87static int binary_expression_evaluate(bithenge_expression_t *base,
88 bithenge_scope_t *scope, bithenge_node_t **out)
89{
90 int rc;
91 binary_expression_t *self = expression_as_binary(base);
92 bithenge_node_t *a, *b;
93 rc = bithenge_expression_evaluate(self->a, scope, &a);
94 if (rc != EOK)
95 return rc;
96 if (self->op != BITHENGE_EXPRESSION_CONCAT) {
97 rc = bithenge_expression_evaluate(self->b, scope, &b);
98 if (rc != EOK) {
99 bithenge_node_dec_ref(a);
100 return rc;
101 }
102 }
103
104 /* Check types and get values. */
105 bithenge_int_t a_int = 0, b_int = 0;
106 bool a_bool = false, b_bool = false;
107 switch (self->op) {
108 case BITHENGE_EXPRESSION_ADD: /* fallthrough */
109 case BITHENGE_EXPRESSION_SUBTRACT: /* fallthrough */
110 case BITHENGE_EXPRESSION_MULTIPLY: /* fallthrough */
111 case BITHENGE_EXPRESSION_INTEGER_DIVIDE: /* fallthrough */
112 case BITHENGE_EXPRESSION_MODULO: /* fallthrough */
113 case BITHENGE_EXPRESSION_LESS_THAN: /* fallthrough */
114 case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL: /* fallthrough */
115 case BITHENGE_EXPRESSION_GREATER_THAN: /* fallthrough */
116 case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL:
117 rc = EINVAL;
118 if (bithenge_node_type(a) != BITHENGE_NODE_INTEGER)
119 goto error;
120 if (bithenge_node_type(b) != BITHENGE_NODE_INTEGER)
121 goto error;
122 a_int = bithenge_integer_node_value(a);
123 b_int = bithenge_integer_node_value(b);
124 break;
125 case BITHENGE_EXPRESSION_AND: /* fallthrough */
126 case BITHENGE_EXPRESSION_OR:
127 rc = EINVAL;
128 if (bithenge_node_type(a) != BITHENGE_NODE_BOOLEAN)
129 goto error;
130 if (bithenge_node_type(b) != BITHENGE_NODE_BOOLEAN)
131 goto error;
132 a_bool = bithenge_boolean_node_value(a);
133 b_bool = bithenge_boolean_node_value(b);
134 break;
135 case BITHENGE_EXPRESSION_CONCAT:
136 if (bithenge_node_type(a) != BITHENGE_NODE_BLOB)
137 goto error;
138 break;
139 default:
140 break;
141 }
142
143 switch (self->op) {
144 case BITHENGE_EXPRESSION_ADD:
145 rc = bithenge_new_integer_node(out, a_int + b_int);
146 break;
147 case BITHENGE_EXPRESSION_SUBTRACT:
148 rc = bithenge_new_integer_node(out, a_int - b_int);
149 break;
150 case BITHENGE_EXPRESSION_MULTIPLY:
151 rc = bithenge_new_integer_node(out, a_int * b_int);
152 break;
153 case BITHENGE_EXPRESSION_INTEGER_DIVIDE:
154 /* Integer division can behave in three major ways when the
155 operands are signed: truncated, floored, or Euclidean. When
156 * b > 0, we give the same result as floored and Euclidean;
157 * otherwise, we currently raise an error. See
158 * https://en.wikipedia.org/wiki/Modulo_operation and its
159 * references. */
160 if (b_int <= 0) {
161 rc = EINVAL;
162 break;
163 }
164 rc = bithenge_new_integer_node(out,
165 (a_int / b_int) + (a_int % b_int < 0 ? -1 : 0));
166 break;
167 case BITHENGE_EXPRESSION_MODULO:
168 /* This is consistent with division; see above. */
169 if (b_int <= 0) {
170 rc = EINVAL;
171 break;
172 }
173 rc = bithenge_new_integer_node(out,
174 (a_int % b_int) + (a_int % b_int < 0 ? b_int : 0));
175 break;
176 case BITHENGE_EXPRESSION_LESS_THAN:
177 rc = bithenge_new_boolean_node(out, a_int < b_int);
178 break;
179 case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL:
180 rc = bithenge_new_boolean_node(out, a_int <= b_int);
181 break;
182 case BITHENGE_EXPRESSION_GREATER_THAN:
183 rc = bithenge_new_boolean_node(out, a_int > b_int);
184 break;
185 case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL:
186 rc = bithenge_new_boolean_node(out, a_int >= b_int);
187 break;
188 case BITHENGE_EXPRESSION_EQUALS:
189 rc = bithenge_new_boolean_node(out, bithenge_node_equal(a, b));
190 break;
191 case BITHENGE_EXPRESSION_NOT_EQUALS:
192 rc = bithenge_new_boolean_node(out,
193 !bithenge_node_equal(a, b));
194 break;
195 case BITHENGE_EXPRESSION_AND:
196 rc = bithenge_new_boolean_node(out, a_bool && b_bool);
197 break;
198 case BITHENGE_EXPRESSION_OR:
199 rc = bithenge_new_boolean_node(out, a_bool || b_bool);
200 break;
201 case BITHENGE_EXPRESSION_MEMBER:
202 rc = bithenge_node_get(a, b, out);
203 b = NULL;
204 break;
205 case BITHENGE_EXPRESSION_CONCAT:
206 bithenge_expression_inc_ref(self->b);
207 bithenge_scope_inc_ref(scope);
208 rc = bithenge_concat_blob_lazy(out, bithenge_node_as_blob(a),
209 self->b, scope);
210 a = NULL;
211 b = NULL;
212 break;
213 case BITHENGE_EXPRESSION_INVALID_BINARY_OP:
214 assert(false);
215 break;
216 }
217
218error:
219 bithenge_node_dec_ref(a);
220 bithenge_node_dec_ref(b);
221 return rc;
222}
223
224static void binary_expression_destroy(bithenge_expression_t *base)
225{
226 binary_expression_t *self = expression_as_binary(base);
227 bithenge_expression_dec_ref(self->a);
228 bithenge_expression_dec_ref(self->b);
229 free(self);
230}
231
232static const bithenge_expression_ops_t binary_expression_ops = {
233 .evaluate = binary_expression_evaluate,
234 .destroy = binary_expression_destroy,
235};
236
237/** Create a binary expression. Takes ownership of @a a and @a b.
238 * @param[out] out Holds the new expression.
239 * @param op The operator to apply.
240 * @param a The first operand.
241 * @param b The second operand.
242 * @return EOK on success or an error code from errno.h. */
243int bithenge_binary_expression(bithenge_expression_t **out,
244 bithenge_binary_op_t op, bithenge_expression_t *a,
245 bithenge_expression_t *b)
246{
247 int rc;
248 binary_expression_t *self = malloc(sizeof(*self));
249 if (!self) {
250 rc = ENOMEM;
251 goto error;
252 }
253
254 rc = bithenge_init_expression(binary_as_expression(self),
255 &binary_expression_ops);
256 if (rc != EOK)
257 goto error;
258
259 self->op = op;
260 self->a = a;
261 self->b = b;
262 *out = binary_as_expression(self);
263 return EOK;
264
265error:
266 bithenge_expression_dec_ref(a);
267 bithenge_expression_dec_ref(b);
268 free(self);
269 return rc;
270}
271
272
273
274/***************** in_node_expression *****************/
275
276static int in_node_evaluate(bithenge_expression_t *self,
277 bithenge_scope_t *scope, bithenge_node_t **out)
278{
279 for (; scope; scope = bithenge_scope_outer(scope)) {
280 *out = bithenge_scope_in_node(scope);
281 if (*out)
282 return EOK;
283 }
284 return EINVAL;
285}
286
287static const bithenge_expression_ops_t in_node_ops = {
288 .evaluate = in_node_evaluate,
289 .destroy = expression_indestructible,
290};
291
292static bithenge_expression_t in_node_expression = {
293 &in_node_ops, 1
294};
295
296/** Create an expression that gets the current input node.
297 * @param[out] out Holds the new expression.
298 * @return EOK on success or an error code from errno.h. */
299int bithenge_in_node_expression(bithenge_expression_t **out)
300{
301 bithenge_expression_inc_ref(&in_node_expression);
302 *out = &in_node_expression;
303 return EOK;
304}
305
306
307
308/***************** current_node_expression *****************/
309
310static int current_node_evaluate(bithenge_expression_t *self,
311 bithenge_scope_t *scope, bithenge_node_t **out)
312{
313 *out = bithenge_scope_get_current_node(scope);
314 if (!*out)
315 return EINVAL;
316 return EOK;
317}
318
319static const bithenge_expression_ops_t current_node_ops = {
320 .evaluate = current_node_evaluate,
321 .destroy = expression_indestructible,
322};
323
324static bithenge_expression_t current_node_expression = {
325 &current_node_ops, 1
326};
327
328/** Create an expression that gets the current node being created.
329 * @param[out] out Holds the new expression.
330 * @return EOK on success or an error code from errno.h. */
331int bithenge_current_node_expression(bithenge_expression_t **out)
332{
333 bithenge_expression_inc_ref(&current_node_expression);
334 *out = &current_node_expression;
335 return EOK;
336}
337
338
339
340/***************** param_expression *****************/
341
342typedef struct {
343 bithenge_expression_t base;
344 int index;
345} param_expression_t;
346
347static inline param_expression_t *expression_as_param(
348 bithenge_expression_t *base)
349{
350 return (param_expression_t *)base;
351}
352
353static inline bithenge_expression_t *param_as_expression(
354 param_expression_t *self)
355{
356 return &self->base;
357}
358
359static int param_expression_evaluate(bithenge_expression_t *base,
360 bithenge_scope_t *scope, bithenge_node_t **out)
361{
362 param_expression_t *self = expression_as_param(base);
363 return bithenge_scope_get_param(scope, self->index, out);
364}
365
366static void param_expression_destroy(bithenge_expression_t *base)
367{
368 param_expression_t *self = expression_as_param(base);
369 free(self);
370}
371
372static const bithenge_expression_ops_t param_expression_ops = {
373 .evaluate = param_expression_evaluate,
374 .destroy = param_expression_destroy,
375};
376
377/** Create an expression that returns a parameter.
378 * @param[out] out Holds the created expression.
379 * @param index The index of the parameter to get.
380 * @return EOK on success or an error code from errno.h. */
381int bithenge_param_expression(bithenge_expression_t **out, int index)
382{
383 int rc;
384 param_expression_t *self = malloc(sizeof(*self));
385 if (!self)
386 return ENOMEM;
387
388 rc = bithenge_init_expression(param_as_expression(self),
389 &param_expression_ops);
390 if (rc != EOK) {
391 free(self);
392 return rc;
393 }
394
395 self->index = index;
396 *out = param_as_expression(self);
397 return EOK;
398}
399
400
401
402/***************** const_expression *****************/
403
404typedef struct {
405 bithenge_expression_t base;
406 bithenge_node_t *node;
407} const_expression_t;
408
409static inline const_expression_t *expression_as_const(
410 bithenge_expression_t *base)
411{
412 return (const_expression_t *)base;
413}
414
415static inline bithenge_expression_t *const_as_expression(
416 const_expression_t *self)
417{
418 return &self->base;
419}
420
421static int const_expression_evaluate(bithenge_expression_t *base,
422 bithenge_scope_t *scope, bithenge_node_t **out)
423{
424 const_expression_t *self = expression_as_const(base);
425 bithenge_node_inc_ref(self->node);
426 *out = self->node;
427 return EOK;
428}
429
430static void const_expression_destroy(bithenge_expression_t *base)
431{
432 const_expression_t *self = expression_as_const(base);
433 bithenge_node_dec_ref(self->node);
434 free(self);
435}
436
437static const bithenge_expression_ops_t const_expression_ops = {
438 .evaluate = const_expression_evaluate,
439 .destroy = const_expression_destroy,
440};
441
442/** Create an expression that returns a constant. Takes a reference to @a node.
443 * @param[out] out Holds the created expression.
444 * @param node The constant.
445 * @return EOK on success or an error code from errno.h. */
446int bithenge_const_expression(bithenge_expression_t **out,
447 bithenge_node_t *node)
448{
449 int rc;
450 const_expression_t *self = malloc(sizeof(*self));
451 if (!self) {
452 rc = ENOMEM;
453 goto error;
454 }
455
456 rc = bithenge_init_expression(const_as_expression(self),
457 &const_expression_ops);
458 if (rc != EOK)
459 goto error;
460
461 self->node = node;
462 *out = const_as_expression(self);
463 return EOK;
464
465error:
466 free(self);
467 bithenge_node_dec_ref(node);
468 return rc;
469}
470
471
472
473/***************** scope_member_expression *****************/
474
475typedef struct {
476 bithenge_expression_t base;
477 bithenge_node_t *key;
478} scope_member_expression_t;
479
480static scope_member_expression_t *expression_as_scope_member(
481 bithenge_expression_t *base)
482{
483 return (scope_member_expression_t *)base;
484}
485
486static bithenge_expression_t *scope_member_as_expression(
487 scope_member_expression_t *expr)
488{
489 return &expr->base;
490}
491
492static int scope_member_expression_evaluate(bithenge_expression_t *base,
493 bithenge_scope_t *scope, bithenge_node_t **out)
494{
495 scope_member_expression_t *self = expression_as_scope_member(base);
496 for (; scope && !bithenge_scope_is_barrier(scope);
497 scope = bithenge_scope_outer(scope)) {
498 bithenge_node_t *cur = bithenge_scope_get_current_node(scope);
499 if (!cur)
500 continue;
501 bithenge_node_inc_ref(self->key);
502 int rc = bithenge_node_get(cur, self->key, out);
503 bithenge_node_dec_ref(cur);
504 if (rc != ENOENT) /* EOK or error */
505 return rc;
506 }
507 return bithenge_scope_error(scope, "No scope member %t", self->key);
508}
509
510static void scope_member_expression_destroy(bithenge_expression_t *base)
511{
512 scope_member_expression_t *self = expression_as_scope_member(base);
513 bithenge_node_dec_ref(self->key);
514 free(self);
515}
516
517static const bithenge_expression_ops_t scope_member_expression_ops = {
518 .evaluate = scope_member_expression_evaluate,
519 .destroy = scope_member_expression_destroy,
520};
521
522int bithenge_scope_member_expression(bithenge_expression_t **out,
523 bithenge_node_t *key)
524{
525 int rc;
526 scope_member_expression_t *self = malloc(sizeof(*self));
527 if (!self) {
528 rc = ENOMEM;
529 goto error;
530 }
531
532 rc = bithenge_init_expression(scope_member_as_expression(self),
533 &scope_member_expression_ops);
534 if (rc != EOK)
535 goto error;
536
537 self->key = key;
538 *out = scope_member_as_expression(self);
539 return EOK;
540
541error:
542 bithenge_node_dec_ref(key);
543 free(self);
544 return rc;
545}
546
547
548
549/***************** subblob_expression *****************/
550
551typedef struct {
552 bithenge_expression_t base;
553 bithenge_expression_t *blob, *start, *limit;
554 bool absolute_limit;
555} subblob_expression_t;
556
557static subblob_expression_t *expression_as_subblob(bithenge_expression_t *base)
558{
559 return (subblob_expression_t *)base;
560}
561
562static bithenge_expression_t *subblob_as_expression(subblob_expression_t *expr)
563{
564 return &expr->base;
565}
566
567static int subblob_expression_evaluate(bithenge_expression_t *base,
568 bithenge_scope_t *scope, bithenge_node_t **out)
569{
570 subblob_expression_t *self = expression_as_subblob(base);
571 bithenge_node_t *start_node;
572 int rc = bithenge_expression_evaluate(self->start, scope, &start_node);
573 if (rc != EOK)
574 return rc;
575 if (bithenge_node_type(start_node) != BITHENGE_NODE_INTEGER) {
576 bithenge_node_dec_ref(start_node);
577 return EINVAL;
578 }
579 bithenge_int_t start = bithenge_integer_node_value(start_node);
580 bithenge_node_dec_ref(start_node);
581
582 bithenge_int_t limit = -1;
583 if (self->limit) {
584 bithenge_node_t *limit_node;
585 rc = bithenge_expression_evaluate(self->limit, scope,
586 &limit_node);
587 if (rc != EOK)
588 return rc;
589 if (bithenge_node_type(limit_node) != BITHENGE_NODE_INTEGER) {
590 bithenge_node_dec_ref(limit_node);
591 return EINVAL;
592 }
593 limit = bithenge_integer_node_value(limit_node);
594 bithenge_node_dec_ref(limit_node);
595 if (self->absolute_limit)
596 limit -= start;
597 }
598
599 if (start < 0 || (self->limit && limit < 0))
600 return EINVAL;
601
602 bithenge_node_t *blob;
603 rc = bithenge_expression_evaluate(self->blob, scope, &blob);
604 if (rc != EOK)
605 return rc;
606 if (bithenge_node_type(blob) != BITHENGE_NODE_BLOB) {
607 bithenge_node_dec_ref(blob);
608 return EINVAL;
609 }
610
611 if (self->limit)
612 return bithenge_new_subblob(out, bithenge_node_as_blob(blob),
613 start, limit);
614 else
615 return bithenge_new_offset_blob(out,
616 bithenge_node_as_blob(blob), start);
617}
618
619static void subblob_expression_destroy(bithenge_expression_t *base)
620{
621 subblob_expression_t *self = expression_as_subblob(base);
622 bithenge_expression_dec_ref(self->blob);
623 bithenge_expression_dec_ref(self->start);
624 bithenge_expression_dec_ref(self->limit);
625 free(self);
626}
627
628static const bithenge_expression_ops_t subblob_expression_ops = {
629 .evaluate = subblob_expression_evaluate,
630 .destroy = subblob_expression_destroy,
631};
632
633/** Create an expression that gets a subblob. Takes references to @a blob,
634 * @a start, and @a limit.
635 * @param[out] out Holds the new expression.
636 * @param blob Calculates the blob.
637 * @param start Calculates the start offset within the blob.
638 * @param limit Calculates the limit. Can be NULL, in which case an offset blob
639 * is returned.
640 * @param absolute_limit If true, the limit is an absolute offset; otherwise,
641 * it is relative to the start.
642 * @return EOK on success or an error code from errno.h. */
643int bithenge_subblob_expression(bithenge_expression_t **out,
644 bithenge_expression_t *blob, bithenge_expression_t *start,
645 bithenge_expression_t *limit, bool absolute_limit)
646{
647 int rc;
648 subblob_expression_t *self = malloc(sizeof(*self));
649 if (!self) {
650 rc = ENOMEM;
651 goto error;
652 }
653
654 rc = bithenge_init_expression(subblob_as_expression(self),
655 &subblob_expression_ops);
656 if (rc != EOK)
657 goto error;
658
659 self->blob = blob;
660 self->start = start;
661 self->limit = limit;
662 self->absolute_limit = absolute_limit;
663 *out = subblob_as_expression(self);
664 return EOK;
665
666error:
667 bithenge_expression_dec_ref(blob);
668 bithenge_expression_dec_ref(start);
669 bithenge_expression_dec_ref(limit);
670 free(self);
671 return rc;
672}
673
674/***************** param_wrapper *****************/
675
676typedef struct {
677 bithenge_transform_t base;
678 bithenge_transform_t *transform;
679 bithenge_expression_t **params;
680} param_wrapper_t;
681
682static inline bithenge_transform_t *param_wrapper_as_transform(
683 param_wrapper_t *self)
684{
685 return &self->base;
686}
687
688static inline param_wrapper_t *transform_as_param_wrapper(
689 bithenge_transform_t *base)
690{
691 return (param_wrapper_t *)base;
692}
693
694static int param_wrapper_fill_scope(param_wrapper_t *self, bithenge_scope_t
695 *inner, bithenge_scope_t *outer)
696{
697 int rc;
698 int num_params = bithenge_transform_num_params(self->transform);
699 rc = bithenge_scope_alloc_params(inner, num_params);
700 if (rc != EOK)
701 return rc;
702 for (int i = 0; i < num_params; i++) {
703 bithenge_node_t *node;
704 rc = bithenge_expression_evaluate(self->params[i], outer,
705 &node);
706 if (rc != EOK)
707 return rc;
708 rc = bithenge_scope_set_param(inner, i, node);
709 if (rc != EOK)
710 return rc;
711 }
712 return EOK;
713}
714
715static int param_wrapper_apply(bithenge_transform_t *base,
716 bithenge_scope_t *outer, bithenge_node_t *in, bithenge_node_t **out)
717{
718 param_wrapper_t *self = transform_as_param_wrapper(base);
719 bithenge_scope_t *inner;
720 int rc = bithenge_scope_new(&inner, outer);
721 if (rc != EOK)
722 return rc;
723 rc = param_wrapper_fill_scope(self, inner, outer);
724 if (rc != EOK)
725 goto error;
726
727 rc = bithenge_transform_apply(self->transform, inner, in, out);
728 in = NULL;
729
730error:
731 bithenge_scope_dec_ref(inner);
732 return rc;
733}
734
735static int param_wrapper_prefix_length(bithenge_transform_t *base,
736 bithenge_scope_t *outer, bithenge_blob_t *in, aoff64_t *out)
737{
738 param_wrapper_t *self = transform_as_param_wrapper(base);
739 bithenge_scope_t *inner;
740 int rc = bithenge_scope_new(&inner, outer);
741 if (rc != EOK)
742 return rc;
743 rc = param_wrapper_fill_scope(self, inner, outer);
744 if (rc != EOK)
745 goto error;
746
747 rc = bithenge_transform_prefix_length(self->transform, inner, in, out);
748 in = NULL;
749
750error:
751 bithenge_scope_dec_ref(inner);
752 return rc;
753}
754
755static int param_wrapper_prefix_apply(bithenge_transform_t *base,
756 bithenge_scope_t *outer, bithenge_blob_t *in, bithenge_node_t **out_node,
757 aoff64_t *out_length)
758{
759 param_wrapper_t *self = transform_as_param_wrapper(base);
760 bithenge_scope_t *inner;
761 int rc = bithenge_scope_new(&inner, outer);
762 if (rc != EOK)
763 return rc;
764 rc = param_wrapper_fill_scope(self, inner, outer);
765 if (rc != EOK)
766 goto error;
767
768 rc = bithenge_transform_prefix_apply(self->transform, inner, in,
769 out_node, out_length);
770
771error:
772 bithenge_scope_dec_ref(inner);
773 return rc;
774}
775
776static void param_wrapper_destroy(bithenge_transform_t *base)
777{
778 param_wrapper_t *self = transform_as_param_wrapper(base);
779 int num_params = bithenge_transform_num_params(self->transform);
780 bithenge_transform_dec_ref(self->transform);
781 for (int i = 0; i < num_params; i++)
782 bithenge_expression_dec_ref(self->params[i]);
783 free(self->params);
784 free(self);
785}
786
787static const bithenge_transform_ops_t param_wrapper_ops = {
788 .apply = param_wrapper_apply,
789 .prefix_length = param_wrapper_prefix_length,
790 .prefix_apply = param_wrapper_prefix_apply,
791 .destroy = param_wrapper_destroy,
792};
793
794/** Create a transform that calculates parameters for another transform. Takes
795 * ownership of @a transform, @a params, and each element of @a params. The
796 * number of parameters must be correct.
797 * @param[out] out Holds the new transform.
798 * @param transform The transform for which parameters are calculated.
799 * @param params The expressions used to calculate the parameters.
800 * @return EOK on success or an error code from errno.h. */
801int bithenge_param_wrapper(bithenge_transform_t **out,
802 bithenge_transform_t *transform, bithenge_expression_t **params)
803{
804 int rc;
805 int num_params = bithenge_transform_num_params(transform);
806 param_wrapper_t *self = malloc(sizeof(*self));
807 if (!self) {
808 rc = ENOMEM;
809 goto error;
810 }
811
812 rc = bithenge_init_transform(param_wrapper_as_transform(self),
813 &param_wrapper_ops, 0);
814 if (rc != EOK)
815 goto error;
816
817 self->transform = transform;
818 self->params = params;
819 *out = param_wrapper_as_transform(self);
820 return EOK;
821
822error:
823 free(self);
824 for (int i = 0; i < num_params; i++)
825 bithenge_expression_dec_ref(params[i]);
826 free(params);
827 bithenge_transform_dec_ref(transform);
828 return rc;
829}
830
831
832
833/***************** expression_transform *****************/
834
835/* Also used by inputless_transform. */
836typedef struct {
837 bithenge_transform_t base;
838 bithenge_expression_t *expr;
839} expression_transform_t;
840
841static inline bithenge_transform_t *expression_as_transform(
842 expression_transform_t *self)
843{
844 return &self->base;
845}
846
847static inline expression_transform_t *transform_as_expression(
848 bithenge_transform_t *base)
849{
850 return (expression_transform_t *)base;
851}
852
853static int expression_transform_apply(bithenge_transform_t *base,
854 bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
855{
856 expression_transform_t *self = transform_as_expression(base);
857 bithenge_scope_t *inner;
858 int rc = bithenge_scope_new(&inner, scope);
859 if (rc != EOK)
860 return rc;
861 bithenge_scope_set_in_node(inner, in);
862 rc = bithenge_expression_evaluate(self->expr, inner, out);
863 bithenge_scope_dec_ref(inner);
864 return rc;
865}
866
867/* Also used by inputless_transform. */
868static void expression_transform_destroy(bithenge_transform_t *base)
869{
870 expression_transform_t *self = transform_as_expression(base);
871 bithenge_expression_dec_ref(self->expr);
872 free(self);
873}
874
875static const bithenge_transform_ops_t expression_transform_ops = {
876 .apply = expression_transform_apply,
877 .destroy = expression_transform_destroy,
878};
879
880/** Create a transform that evaluates an expression on the input node. Takes a
881 * reference to the expression.
882 * @param[out] out Holds the new transform.
883 * @param expr The expression to evaluate.
884 * @return EOK on success or an error code from errno.h. */
885int bithenge_expression_transform(bithenge_transform_t ** out,
886 bithenge_expression_t *expr)
887{
888 int rc;
889 expression_transform_t *self = malloc(sizeof(*self));
890 if (!self) {
891 rc = ENOMEM;
892 goto error;
893 }
894
895 rc = bithenge_init_transform(expression_as_transform(self),
896 &expression_transform_ops, 0);
897 if (rc != EOK)
898 goto error;
899
900 self->expr = expr;
901 *out = expression_as_transform(self);
902 return EOK;
903
904error:
905 free(self);
906 bithenge_expression_dec_ref(expr);
907 return rc;
908}
909
910
911
912/***************** inputless_transform *****************/
913
914static int inputless_transform_prefix_length(bithenge_transform_t *base,
915 bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out)
916{
917 *out = 0;
918 return EOK;
919}
920
921static int inputless_transform_prefix_apply(bithenge_transform_t *base,
922 bithenge_scope_t *scope, bithenge_blob_t *in, bithenge_node_t **out_node,
923 aoff64_t *out_size)
924{
925 expression_transform_t *self = transform_as_expression(base);
926 if (out_size)
927 *out_size = 0;
928 return bithenge_expression_evaluate(self->expr, scope, out_node);
929}
930
931static const bithenge_transform_ops_t inputless_transform_ops = {
932 .prefix_length = inputless_transform_prefix_length,
933 .prefix_apply = inputless_transform_prefix_apply,
934 .destroy = expression_transform_destroy,
935};
936
937/** Create a transform that takes an empty blob and produces the result of an
938 * expression. Takes a reference to the expression.
939 * @param[out] out Holds the new transform.
940 * @param expr The expression to evaluate.
941 * @return EOK on success or an error code from errno.h. */
942int bithenge_inputless_transform(bithenge_transform_t ** out,
943 bithenge_expression_t *expr)
944{
945 int rc;
946 expression_transform_t *self = malloc(sizeof(*self));
947 if (!self) {
948 rc = ENOMEM;
949 goto error;
950 }
951
952 rc = bithenge_init_transform(expression_as_transform(self),
953 &inputless_transform_ops, 0);
954 if (rc != EOK)
955 goto error;
956
957 self->expr = expr;
958 *out = expression_as_transform(self);
959 return EOK;
960
961error:
962 free(self);
963 bithenge_expression_dec_ref(expr);
964 return rc;
965}
966
967
968
969/***************** concat_blob *****************/
970
971typedef struct {
972 bithenge_blob_t base;
973 bithenge_blob_t *a, *b;
974 aoff64_t a_size;
975 bithenge_expression_t *b_expr;
976 bithenge_scope_t *scope;
977} concat_blob_t;
978
979static inline concat_blob_t *blob_as_concat(bithenge_blob_t *base)
980{
981 return (concat_blob_t *)base;
982}
983
984static inline bithenge_blob_t *concat_as_blob(concat_blob_t *blob)
985{
986 return &blob->base;
987}
988
989static int concat_blob_evaluate_b(concat_blob_t *self)
990{
991 if (self->b)
992 return EOK;
993 bithenge_node_t *b_node;
994 int rc = bithenge_expression_evaluate(self->b_expr, self->scope,
995 &b_node);
996 if (rc != EOK)
997 return rc;
998 if (bithenge_node_type(b_node) != BITHENGE_NODE_BLOB) {
999 bithenge_node_dec_ref(b_node);
1000 return bithenge_scope_error(self->scope,
1001 "Concatenation arguments must be blobs");
1002 }
1003 self->b = bithenge_node_as_blob(b_node);
1004 bithenge_expression_dec_ref(self->b_expr);
1005 bithenge_scope_dec_ref(self->scope);
1006 self->b_expr = NULL;
1007 self->scope = NULL;
1008 return EOK;
1009}
1010
1011static int concat_blob_size(bithenge_blob_t *base, aoff64_t *size)
1012{
1013 concat_blob_t *self = blob_as_concat(base);
1014 int rc = concat_blob_evaluate_b(self);
1015 if (rc != EOK)
1016 return rc;
1017 rc = bithenge_blob_size(self->b, size);
1018 *size += self->a_size;
1019 return rc;
1020}
1021
1022static int concat_blob_read(bithenge_blob_t *base, aoff64_t offset,
1023 char *buffer, aoff64_t *size)
1024{
1025 int rc;
1026 concat_blob_t *self = blob_as_concat(base);
1027
1028 aoff64_t a_size = 0, b_size = 0;
1029 if (offset < self->a_size) {
1030 a_size = *size;
1031 rc = bithenge_blob_read(self->a, offset, buffer, &a_size);
1032 if (rc != EOK)
1033 return rc;
1034 }
1035 if (offset + *size > self->a_size) {
1036 rc = concat_blob_evaluate_b(self);
1037 if (rc != EOK)
1038 return rc;
1039 b_size = *size - a_size;
1040 rc = bithenge_blob_read(self->b,
1041 offset + a_size - self->a_size, buffer + a_size, &b_size);
1042 if (rc != EOK)
1043 return rc;
1044 }
1045 assert(a_size + b_size <= *size);
1046 *size = a_size + b_size;
1047 return EOK;
1048}
1049
1050static int concat_blob_read_bits(bithenge_blob_t *base, aoff64_t offset,
1051 char *buffer, aoff64_t *size, bool little_endian)
1052{
1053 int rc;
1054 concat_blob_t *self = blob_as_concat(base);
1055
1056 aoff64_t a_size = 0, b_size = 0;
1057 if (offset < self->a_size) {
1058 a_size = *size;
1059 rc = bithenge_blob_read_bits(self->a, offset, buffer, &a_size,
1060 little_endian);
1061 if (rc != EOK)
1062 return rc;
1063 }
1064 if (offset + *size > self->a_size) {
1065 rc = concat_blob_evaluate_b(self);
1066 if (rc != EOK)
1067 return rc;
1068 b_size = *size - a_size;
1069 assert(a_size % 8 == 0); /* TODO: don't require this */
1070 rc = bithenge_blob_read_bits(self->b,
1071 offset + a_size - self->a_size, buffer + a_size / 8,
1072 &b_size, little_endian);
1073 if (rc != EOK)
1074 return rc;
1075 }
1076 assert(a_size + b_size <= *size);
1077 *size = a_size + b_size;
1078 return EOK;
1079}
1080
1081static void concat_blob_destroy(bithenge_blob_t *base)
1082{
1083 concat_blob_t *self = blob_as_concat(base);
1084 bithenge_blob_dec_ref(self->a);
1085 bithenge_blob_dec_ref(self->b);
1086 bithenge_expression_dec_ref(self->b_expr);
1087 bithenge_scope_dec_ref(self->scope);
1088 free(self);
1089}
1090
1091static const bithenge_random_access_blob_ops_t concat_blob_ops = {
1092 .size = concat_blob_size,
1093 .read = concat_blob_read,
1094 .read_bits = concat_blob_read_bits,
1095 .destroy = concat_blob_destroy,
1096};
1097
1098/** Create a concatenated blob. Takes references to @a a and @a b.
1099 * @param[out] out Holds the new blob.
1100 * @param a The first blob.
1101 * @param b The second blob.
1102 * @return EOK on success or an error code from errno.h. */
1103int bithenge_concat_blob(bithenge_node_t **out, bithenge_blob_t *a,
1104 bithenge_blob_t *b)
1105{
1106 assert(out);
1107 assert(a);
1108 assert(b);
1109 int rc;
1110 concat_blob_t *self = malloc(sizeof(*self));
1111 if (!self) {
1112 rc = ENOMEM;
1113 goto error;
1114 }
1115
1116 rc = bithenge_blob_size(a, &self->a_size);
1117 if (rc != EOK)
1118 goto error;
1119
1120 rc = bithenge_init_random_access_blob(concat_as_blob(self),
1121 &concat_blob_ops);
1122 if (rc != EOK)
1123 goto error;
1124 self->a = a;
1125 self->b = b;
1126 self->b_expr = NULL;
1127 self->scope = NULL;
1128 *out = bithenge_blob_as_node(concat_as_blob(self));
1129 return EOK;
1130
1131error:
1132 bithenge_blob_dec_ref(a);
1133 bithenge_blob_dec_ref(b);
1134 free(self);
1135 return rc;
1136}
1137
1138/** Create a lazy concatenated blob. Takes references to @a a, @a b_expr, and
1139 * @a scope.
1140 * @param[out] out Holds the new blob.
1141 * @param a The first blob.
1142 * @param b_expr An expression to calculate the second blob.
1143 * @param scope The scope in which @a b_expr should be evaluated.
1144 * @return EOK on success or an error code from errno.h. */
1145int bithenge_concat_blob_lazy(bithenge_node_t **out, bithenge_blob_t *a,
1146 bithenge_expression_t *b_expr, bithenge_scope_t *scope)
1147{
1148 assert(out);
1149 assert(a);
1150 assert(b_expr);
1151 assert(scope);
1152 int rc;
1153 concat_blob_t *self = malloc(sizeof(*self));
1154 if (!self) {
1155 rc = ENOMEM;
1156 goto error;
1157 }
1158
1159 rc = bithenge_blob_size(a, &self->a_size);
1160 if (rc != EOK)
1161 goto error;
1162
1163 rc = bithenge_init_random_access_blob(concat_as_blob(self),
1164 &concat_blob_ops);
1165 if (rc != EOK)
1166 goto error;
1167 self->a = a;
1168 self->b = NULL;
1169 self->b_expr = b_expr;
1170 self->scope = scope;
1171 *out = bithenge_blob_as_node(concat_as_blob(self));
1172 return EOK;
1173
1174error:
1175 bithenge_blob_dec_ref(a);
1176 bithenge_expression_dec_ref(b_expr);
1177 bithenge_scope_dec_ref(scope);
1178 free(self);
1179 return rc;
1180}
1181
1182/** @}
1183 */
Note: See TracBrowser for help on using the repository browser.