/* * Copyright (c) 2012 Sean Bartell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @addtogroup bithenge * @{ */ /** * @file * Expressions. */ #include #include #include #include "common.h" #include #include #include #include /** Initialize a new expression. * @param[out] self Expression to initialize. * @param[in] ops Operations provided by the expression. * @return EOK or an error code from errno.h. */ errno_t bithenge_init_expression(bithenge_expression_t *self, const bithenge_expression_ops_t *ops) { assert(ops); assert(ops->evaluate); assert(ops->destroy); if (bithenge_should_fail()) return ENOMEM; self->ops = ops; self->refs = 1; return EOK; } static void expression_indestructible(bithenge_expression_t *self) { assert(false); } /***************** binary_expression *****************/ typedef struct { bithenge_expression_t base; bithenge_binary_op_t op; bithenge_expression_t *a, *b; } binary_expression_t; static inline binary_expression_t *expression_as_binary( bithenge_expression_t *base) { return (binary_expression_t *)base; } static inline bithenge_expression_t *binary_as_expression( binary_expression_t *self) { return &self->base; } static errno_t binary_expression_evaluate(bithenge_expression_t *base, bithenge_scope_t *scope, bithenge_node_t **out) { errno_t rc; binary_expression_t *self = expression_as_binary(base); bithenge_node_t *a, *b; rc = bithenge_expression_evaluate(self->a, scope, &a); if (rc != EOK) return rc; if (self->op != BITHENGE_EXPRESSION_CONCAT) { rc = bithenge_expression_evaluate(self->b, scope, &b); if (rc != EOK) { bithenge_node_dec_ref(a); return rc; } } /* Check types and get values. */ /* Assigning 0 only to make the compiler happy. */ bithenge_int_t a_int = 0, b_int = 0; bool a_bool = false, b_bool = false, out_bool = false; switch (self->op) { case BITHENGE_EXPRESSION_ADD: /* fallthrough */ case BITHENGE_EXPRESSION_SUBTRACT: /* fallthrough */ case BITHENGE_EXPRESSION_MULTIPLY: /* fallthrough */ case BITHENGE_EXPRESSION_INTEGER_DIVIDE: /* fallthrough */ case BITHENGE_EXPRESSION_MODULO: /* fallthrough */ case BITHENGE_EXPRESSION_LESS_THAN: /* fallthrough */ case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL: /* fallthrough */ case BITHENGE_EXPRESSION_GREATER_THAN: /* fallthrough */ case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL: rc = EINVAL; if (bithenge_node_type(a) != BITHENGE_NODE_INTEGER) goto error; if (bithenge_node_type(b) != BITHENGE_NODE_INTEGER) goto error; a_int = bithenge_integer_node_value(a); b_int = bithenge_integer_node_value(b); break; case BITHENGE_EXPRESSION_AND: /* fallthrough */ case BITHENGE_EXPRESSION_OR: rc = EINVAL; if (bithenge_node_type(a) != BITHENGE_NODE_BOOLEAN) goto error; if (bithenge_node_type(b) != BITHENGE_NODE_BOOLEAN) goto error; a_bool = bithenge_boolean_node_value(a); b_bool = bithenge_boolean_node_value(b); break; case BITHENGE_EXPRESSION_CONCAT: if (bithenge_node_type(a) != BITHENGE_NODE_BLOB) goto error; break; default: break; } switch (self->op) { case BITHENGE_EXPRESSION_ADD: rc = bithenge_new_integer_node(out, a_int + b_int); break; case BITHENGE_EXPRESSION_SUBTRACT: rc = bithenge_new_integer_node(out, a_int - b_int); break; case BITHENGE_EXPRESSION_MULTIPLY: rc = bithenge_new_integer_node(out, a_int * b_int); break; case BITHENGE_EXPRESSION_INTEGER_DIVIDE: /* Integer division can behave in three major ways when the operands are signed: truncated, floored, or Euclidean. When * b > 0, we give the same result as floored and Euclidean; * otherwise, we currently raise an error. See * https://en.wikipedia.org/wiki/Modulo_operation and its * references. */ if (b_int <= 0) { rc = EINVAL; break; } rc = bithenge_new_integer_node(out, (a_int / b_int) + (a_int % b_int < 0 ? -1 : 0)); break; case BITHENGE_EXPRESSION_MODULO: /* This is consistent with division; see above. */ if (b_int <= 0) { rc = EINVAL; break; } rc = bithenge_new_integer_node(out, (a_int % b_int) + (a_int % b_int < 0 ? b_int : 0)); break; case BITHENGE_EXPRESSION_LESS_THAN: rc = bithenge_new_boolean_node(out, a_int < b_int); break; case BITHENGE_EXPRESSION_LESS_THAN_OR_EQUAL: rc = bithenge_new_boolean_node(out, a_int <= b_int); break; case BITHENGE_EXPRESSION_GREATER_THAN: rc = bithenge_new_boolean_node(out, a_int > b_int); break; case BITHENGE_EXPRESSION_GREATER_THAN_OR_EQUAL: rc = bithenge_new_boolean_node(out, a_int >= b_int); break; case BITHENGE_EXPRESSION_EQUALS: rc = bithenge_node_equal(&out_bool, a, b); if (rc != EOK) break; rc = bithenge_new_boolean_node(out, out_bool); break; case BITHENGE_EXPRESSION_NOT_EQUALS: rc = bithenge_node_equal(&out_bool, a, b); if (rc != EOK) break; rc = bithenge_new_boolean_node(out, !out_bool); break; case BITHENGE_EXPRESSION_AND: rc = bithenge_new_boolean_node(out, a_bool && b_bool); break; case BITHENGE_EXPRESSION_OR: rc = bithenge_new_boolean_node(out, a_bool || b_bool); break; case BITHENGE_EXPRESSION_MEMBER: rc = bithenge_node_get(a, b, out); b = NULL; break; case BITHENGE_EXPRESSION_CONCAT: bithenge_expression_inc_ref(self->b); bithenge_scope_inc_ref(scope); rc = bithenge_concat_blob_lazy(out, bithenge_node_as_blob(a), self->b, scope); a = NULL; b = NULL; break; case BITHENGE_EXPRESSION_INVALID_BINARY_OP: assert(false); break; } error: bithenge_node_dec_ref(a); bithenge_node_dec_ref(b); return rc; } static void binary_expression_destroy(bithenge_expression_t *base) { binary_expression_t *self = expression_as_binary(base); bithenge_expression_dec_ref(self->a); bithenge_expression_dec_ref(self->b); free(self); } static const bithenge_expression_ops_t binary_expression_ops = { .evaluate = binary_expression_evaluate, .destroy = binary_expression_destroy, }; /** Create a binary expression. Takes ownership of @a a and @a b. * @param[out] out Holds the new expression. * @param op The operator to apply. * @param a The first operand. * @param b The second operand. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_binary_expression(bithenge_expression_t **out, bithenge_binary_op_t op, bithenge_expression_t *a, bithenge_expression_t *b) { errno_t rc; binary_expression_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_expression(binary_as_expression(self), &binary_expression_ops); if (rc != EOK) goto error; self->op = op; self->a = a; self->b = b; *out = binary_as_expression(self); return EOK; error: bithenge_expression_dec_ref(a); bithenge_expression_dec_ref(b); free(self); return rc; } /***************** in_node_expression *****************/ static errno_t in_node_evaluate(bithenge_expression_t *self, bithenge_scope_t *scope, bithenge_node_t **out) { for (; scope; scope = bithenge_scope_outer(scope)) { *out = bithenge_scope_in_node(scope); if (*out) return EOK; } return EINVAL; } static const bithenge_expression_ops_t in_node_ops = { .evaluate = in_node_evaluate, .destroy = expression_indestructible, }; static bithenge_expression_t in_node_expression = { &in_node_ops, 1 }; /** Create an expression that gets the current input node. * @param[out] out Holds the new expression. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_in_node_expression(bithenge_expression_t **out) { if (bithenge_should_fail()) return ENOMEM; bithenge_expression_inc_ref(&in_node_expression); *out = &in_node_expression; return EOK; } /***************** current_node_expression *****************/ static errno_t current_node_evaluate(bithenge_expression_t *self, bithenge_scope_t *scope, bithenge_node_t **out) { *out = bithenge_scope_get_current_node(scope); if (!*out) return EINVAL; return EOK; } static const bithenge_expression_ops_t current_node_ops = { .evaluate = current_node_evaluate, .destroy = expression_indestructible, }; static bithenge_expression_t current_node_expression = { ¤t_node_ops, 1 }; /** Create an expression that gets the current node being created. * @param[out] out Holds the new expression. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_current_node_expression(bithenge_expression_t **out) { bithenge_expression_inc_ref(¤t_node_expression); *out = ¤t_node_expression; return EOK; } /***************** param_expression *****************/ typedef struct { bithenge_expression_t base; int index; } param_expression_t; static inline param_expression_t *expression_as_param( bithenge_expression_t *base) { return (param_expression_t *)base; } static inline bithenge_expression_t *param_as_expression( param_expression_t *self) { return &self->base; } static errno_t param_expression_evaluate(bithenge_expression_t *base, bithenge_scope_t *scope, bithenge_node_t **out) { param_expression_t *self = expression_as_param(base); return bithenge_scope_get_param(scope, self->index, out); } static void param_expression_destroy(bithenge_expression_t *base) { param_expression_t *self = expression_as_param(base); free(self); } static const bithenge_expression_ops_t param_expression_ops = { .evaluate = param_expression_evaluate, .destroy = param_expression_destroy, }; /** Create an expression that returns a parameter. * @param[out] out Holds the created expression. * @param index The index of the parameter to get. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_param_expression(bithenge_expression_t **out, int index) { errno_t rc; param_expression_t *self = malloc(sizeof(*self)); if (!self) return ENOMEM; rc = bithenge_init_expression(param_as_expression(self), ¶m_expression_ops); if (rc != EOK) { free(self); return rc; } self->index = index; *out = param_as_expression(self); return EOK; } /***************** const_expression *****************/ typedef struct { bithenge_expression_t base; bithenge_node_t *node; } const_expression_t; static inline const_expression_t *expression_as_const( bithenge_expression_t *base) { return (const_expression_t *)base; } static inline bithenge_expression_t *const_as_expression( const_expression_t *self) { return &self->base; } static errno_t const_expression_evaluate(bithenge_expression_t *base, bithenge_scope_t *scope, bithenge_node_t **out) { const_expression_t *self = expression_as_const(base); bithenge_node_inc_ref(self->node); *out = self->node; return EOK; } static void const_expression_destroy(bithenge_expression_t *base) { const_expression_t *self = expression_as_const(base); bithenge_node_dec_ref(self->node); free(self); } static const bithenge_expression_ops_t const_expression_ops = { .evaluate = const_expression_evaluate, .destroy = const_expression_destroy, }; /** Create an expression that returns a constant. Takes a reference to @a node. * @param[out] out Holds the created expression. * @param node The constant. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_const_expression(bithenge_expression_t **out, bithenge_node_t *node) { errno_t rc; const_expression_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_expression(const_as_expression(self), &const_expression_ops); if (rc != EOK) goto error; self->node = node; *out = const_as_expression(self); return EOK; error: free(self); bithenge_node_dec_ref(node); return rc; } /***************** scope_member_expression *****************/ typedef struct { bithenge_expression_t base; bithenge_node_t *key; } scope_member_expression_t; static scope_member_expression_t *expression_as_scope_member( bithenge_expression_t *base) { return (scope_member_expression_t *)base; } static bithenge_expression_t *scope_member_as_expression( scope_member_expression_t *expr) { return &expr->base; } static errno_t scope_member_expression_evaluate(bithenge_expression_t *base, bithenge_scope_t *scope, bithenge_node_t **out) { scope_member_expression_t *self = expression_as_scope_member(base); for (; scope && !bithenge_scope_is_barrier(scope); scope = bithenge_scope_outer(scope)) { bithenge_node_t *cur = bithenge_scope_get_current_node(scope); if (!cur) continue; bithenge_node_inc_ref(self->key); errno_t rc = bithenge_node_get(cur, self->key, out); bithenge_node_dec_ref(cur); if (rc != ENOENT) /* EOK or error */ return rc; } return bithenge_scope_error(scope, "No scope member %t", self->key); } static void scope_member_expression_destroy(bithenge_expression_t *base) { scope_member_expression_t *self = expression_as_scope_member(base); bithenge_node_dec_ref(self->key); free(self); } static const bithenge_expression_ops_t scope_member_expression_ops = { .evaluate = scope_member_expression_evaluate, .destroy = scope_member_expression_destroy, }; /** Create an expression that gets a member from one of the current nodes being * created. It searches from the current scope outwards, stopping at barrier * scopes. * @param[out] out Holds the new expression. * @param key The key to search for in nodes being created. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_scope_member_expression(bithenge_expression_t **out, bithenge_node_t *key) { errno_t rc; scope_member_expression_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_expression(scope_member_as_expression(self), &scope_member_expression_ops); if (rc != EOK) goto error; self->key = key; *out = scope_member_as_expression(self); return EOK; error: bithenge_node_dec_ref(key); free(self); return rc; } /***************** subblob_expression *****************/ typedef struct { bithenge_expression_t base; bithenge_expression_t *blob, *start, *limit; bool absolute_limit; } subblob_expression_t; static subblob_expression_t *expression_as_subblob(bithenge_expression_t *base) { return (subblob_expression_t *)base; } static bithenge_expression_t *subblob_as_expression(subblob_expression_t *expr) { return &expr->base; } static errno_t subblob_expression_evaluate(bithenge_expression_t *base, bithenge_scope_t *scope, bithenge_node_t **out) { subblob_expression_t *self = expression_as_subblob(base); bithenge_node_t *start_node; errno_t rc = bithenge_expression_evaluate(self->start, scope, &start_node); if (rc != EOK) return rc; if (bithenge_node_type(start_node) != BITHENGE_NODE_INTEGER) { bithenge_node_dec_ref(start_node); return EINVAL; } bithenge_int_t start = bithenge_integer_node_value(start_node); bithenge_node_dec_ref(start_node); bithenge_int_t limit = -1; if (self->limit) { bithenge_node_t *limit_node; rc = bithenge_expression_evaluate(self->limit, scope, &limit_node); if (rc != EOK) return rc; if (bithenge_node_type(limit_node) != BITHENGE_NODE_INTEGER) { bithenge_node_dec_ref(limit_node); return EINVAL; } limit = bithenge_integer_node_value(limit_node); bithenge_node_dec_ref(limit_node); if (self->absolute_limit) limit -= start; } if (start < 0 || (self->limit && limit < 0)) return EINVAL; bithenge_node_t *blob; rc = bithenge_expression_evaluate(self->blob, scope, &blob); if (rc != EOK) return rc; if (bithenge_node_type(blob) != BITHENGE_NODE_BLOB) { bithenge_node_dec_ref(blob); return EINVAL; } if (self->limit) return bithenge_new_subblob(out, bithenge_node_as_blob(blob), start, limit); else return bithenge_new_offset_blob(out, bithenge_node_as_blob(blob), start); } static void subblob_expression_destroy(bithenge_expression_t *base) { subblob_expression_t *self = expression_as_subblob(base); bithenge_expression_dec_ref(self->blob); bithenge_expression_dec_ref(self->start); bithenge_expression_dec_ref(self->limit); free(self); } static const bithenge_expression_ops_t subblob_expression_ops = { .evaluate = subblob_expression_evaluate, .destroy = subblob_expression_destroy, }; /** Create an expression that gets a subblob. Takes references to @a blob, * @a start, and @a limit. * @param[out] out Holds the new expression. * @param blob Calculates the blob. * @param start Calculates the start offset within the blob. * @param limit Calculates the limit. Can be NULL, in which case an offset blob * is returned. * @param absolute_limit If true, the limit is an absolute offset; otherwise, * it is relative to the start. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_subblob_expression(bithenge_expression_t **out, bithenge_expression_t *blob, bithenge_expression_t *start, bithenge_expression_t *limit, bool absolute_limit) { errno_t rc; subblob_expression_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_expression(subblob_as_expression(self), &subblob_expression_ops); if (rc != EOK) goto error; self->blob = blob; self->start = start; self->limit = limit; self->absolute_limit = absolute_limit; *out = subblob_as_expression(self); return EOK; error: bithenge_expression_dec_ref(blob); bithenge_expression_dec_ref(start); bithenge_expression_dec_ref(limit); free(self); return rc; } /***************** param_wrapper *****************/ typedef struct { bithenge_transform_t base; bithenge_transform_t *transform; bithenge_expression_t **params; } param_wrapper_t; static inline bithenge_transform_t *param_wrapper_as_transform( param_wrapper_t *self) { return &self->base; } static inline param_wrapper_t *transform_as_param_wrapper( bithenge_transform_t *base) { return (param_wrapper_t *)base; } static errno_t param_wrapper_fill_scope(param_wrapper_t *self, bithenge_scope_t *inner, bithenge_scope_t *outer) { errno_t rc; int num_params = bithenge_transform_num_params(self->transform); rc = bithenge_scope_alloc_params(inner, num_params); if (rc != EOK) return rc; for (int i = 0; i < num_params; i++) { bithenge_node_t *node; rc = bithenge_expression_evaluate(self->params[i], outer, &node); if (rc != EOK) return rc; rc = bithenge_scope_set_param(inner, i, node); if (rc != EOK) return rc; } return EOK; } static errno_t param_wrapper_apply(bithenge_transform_t *base, bithenge_scope_t *outer, bithenge_node_t *in, bithenge_node_t **out) { param_wrapper_t *self = transform_as_param_wrapper(base); bithenge_scope_t *inner; errno_t rc = bithenge_scope_new(&inner, outer); if (rc != EOK) return rc; rc = param_wrapper_fill_scope(self, inner, outer); if (rc != EOK) goto error; rc = bithenge_transform_apply(self->transform, inner, in, out); in = NULL; error: bithenge_scope_dec_ref(inner); return rc; } static errno_t param_wrapper_prefix_length(bithenge_transform_t *base, bithenge_scope_t *outer, bithenge_blob_t *in, aoff64_t *out) { param_wrapper_t *self = transform_as_param_wrapper(base); bithenge_scope_t *inner; errno_t rc = bithenge_scope_new(&inner, outer); if (rc != EOK) return rc; rc = param_wrapper_fill_scope(self, inner, outer); if (rc != EOK) goto error; rc = bithenge_transform_prefix_length(self->transform, inner, in, out); in = NULL; error: bithenge_scope_dec_ref(inner); return rc; } static errno_t param_wrapper_prefix_apply(bithenge_transform_t *base, bithenge_scope_t *outer, bithenge_blob_t *in, bithenge_node_t **out_node, aoff64_t *out_length) { param_wrapper_t *self = transform_as_param_wrapper(base); bithenge_scope_t *inner; errno_t rc = bithenge_scope_new(&inner, outer); if (rc != EOK) return rc; rc = param_wrapper_fill_scope(self, inner, outer); if (rc != EOK) goto error; rc = bithenge_transform_prefix_apply(self->transform, inner, in, out_node, out_length); error: bithenge_scope_dec_ref(inner); return rc; } static void param_wrapper_destroy(bithenge_transform_t *base) { param_wrapper_t *self = transform_as_param_wrapper(base); int num_params = bithenge_transform_num_params(self->transform); bithenge_transform_dec_ref(self->transform); for (int i = 0; i < num_params; i++) bithenge_expression_dec_ref(self->params[i]); free(self->params); free(self); } static const bithenge_transform_ops_t param_wrapper_ops = { .apply = param_wrapper_apply, .prefix_length = param_wrapper_prefix_length, .prefix_apply = param_wrapper_prefix_apply, .destroy = param_wrapper_destroy, }; /** Create a transform that calculates parameters for another transform. Takes * ownership of @a transform, @a params, and each element of @a params. The * number of parameters must be correct. * @param[out] out Holds the new transform. * @param transform The transform for which parameters are calculated. * @param params The expressions used to calculate the parameters. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_param_wrapper(bithenge_transform_t **out, bithenge_transform_t *transform, bithenge_expression_t **params) { errno_t rc; int num_params = bithenge_transform_num_params(transform); param_wrapper_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_transform(param_wrapper_as_transform(self), ¶m_wrapper_ops, 0); if (rc != EOK) goto error; self->transform = transform; self->params = params; *out = param_wrapper_as_transform(self); return EOK; error: free(self); for (int i = 0; i < num_params; i++) bithenge_expression_dec_ref(params[i]); free(params); bithenge_transform_dec_ref(transform); return rc; } /***************** expression_transform *****************/ /* Also used by inputless_transform. */ typedef struct { bithenge_transform_t base; bithenge_expression_t *expr; } expression_transform_t; static inline bithenge_transform_t *expression_as_transform( expression_transform_t *self) { return &self->base; } static inline expression_transform_t *transform_as_expression( bithenge_transform_t *base) { return (expression_transform_t *)base; } static errno_t expression_transform_apply(bithenge_transform_t *base, bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out) { expression_transform_t *self = transform_as_expression(base); bithenge_scope_t *inner; errno_t rc = bithenge_scope_new(&inner, scope); if (rc != EOK) return rc; bithenge_scope_set_in_node(inner, in); rc = bithenge_expression_evaluate(self->expr, inner, out); bithenge_scope_dec_ref(inner); return rc; } /* Also used by inputless_transform. */ static void expression_transform_destroy(bithenge_transform_t *base) { expression_transform_t *self = transform_as_expression(base); bithenge_expression_dec_ref(self->expr); free(self); } static const bithenge_transform_ops_t expression_transform_ops = { .apply = expression_transform_apply, .destroy = expression_transform_destroy, }; /** Create a transform that evaluates an expression on the input node. Takes a * reference to the expression. * @param[out] out Holds the new transform. * @param expr The expression to evaluate. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_expression_transform(bithenge_transform_t ** out, bithenge_expression_t *expr) { errno_t rc; expression_transform_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_transform(expression_as_transform(self), &expression_transform_ops, 0); if (rc != EOK) goto error; self->expr = expr; *out = expression_as_transform(self); return EOK; error: free(self); bithenge_expression_dec_ref(expr); return rc; } /***************** inputless_transform *****************/ static errno_t inputless_transform_prefix_length(bithenge_transform_t *base, bithenge_scope_t *scope, bithenge_blob_t *in, aoff64_t *out) { *out = 0; return EOK; } static errno_t inputless_transform_prefix_apply(bithenge_transform_t *base, bithenge_scope_t *scope, bithenge_blob_t *in, bithenge_node_t **out_node, aoff64_t *out_size) { expression_transform_t *self = transform_as_expression(base); if (out_size) *out_size = 0; return bithenge_expression_evaluate(self->expr, scope, out_node); } static const bithenge_transform_ops_t inputless_transform_ops = { .prefix_length = inputless_transform_prefix_length, .prefix_apply = inputless_transform_prefix_apply, .destroy = expression_transform_destroy, }; /** Create a transform that takes an empty blob and produces the result of an * expression. Takes a reference to the expression. * @param[out] out Holds the new transform. * @param expr The expression to evaluate. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_inputless_transform(bithenge_transform_t ** out, bithenge_expression_t *expr) { errno_t rc; expression_transform_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_init_transform(expression_as_transform(self), &inputless_transform_ops, 0); if (rc != EOK) goto error; self->expr = expr; *out = expression_as_transform(self); return EOK; error: free(self); bithenge_expression_dec_ref(expr); return rc; } /***************** concat_blob *****************/ typedef struct { bithenge_blob_t base; bithenge_blob_t *a, *b; aoff64_t a_size; bithenge_expression_t *b_expr; bithenge_scope_t *scope; } concat_blob_t; static inline concat_blob_t *blob_as_concat(bithenge_blob_t *base) { return (concat_blob_t *)base; } static inline bithenge_blob_t *concat_as_blob(concat_blob_t *blob) { return &blob->base; } static errno_t concat_blob_evaluate_b(concat_blob_t *self) { if (self->b) return EOK; bithenge_node_t *b_node; errno_t rc = bithenge_expression_evaluate(self->b_expr, self->scope, &b_node); if (rc != EOK) return rc; if (bithenge_node_type(b_node) != BITHENGE_NODE_BLOB) { bithenge_node_dec_ref(b_node); return bithenge_scope_error(self->scope, "Concatenation arguments must be blobs"); } self->b = bithenge_node_as_blob(b_node); bithenge_expression_dec_ref(self->b_expr); bithenge_scope_dec_ref(self->scope); self->b_expr = NULL; self->scope = NULL; return EOK; } static errno_t concat_blob_size(bithenge_blob_t *base, aoff64_t *size) { concat_blob_t *self = blob_as_concat(base); errno_t rc = concat_blob_evaluate_b(self); if (rc != EOK) return rc; rc = bithenge_blob_size(self->b, size); *size += self->a_size; return rc; } static errno_t concat_blob_read(bithenge_blob_t *base, aoff64_t offset, char *buffer, aoff64_t *size) { errno_t rc; concat_blob_t *self = blob_as_concat(base); aoff64_t a_size = 0, b_size = 0; if (offset < self->a_size) { a_size = *size; rc = bithenge_blob_read(self->a, offset, buffer, &a_size); if (rc != EOK) return rc; } if (offset + *size > self->a_size) { rc = concat_blob_evaluate_b(self); if (rc != EOK) return rc; b_size = *size - a_size; rc = bithenge_blob_read(self->b, offset + a_size - self->a_size, buffer + a_size, &b_size); if (rc != EOK) return rc; } assert(a_size + b_size <= *size); *size = a_size + b_size; return EOK; } static errno_t concat_blob_read_bits(bithenge_blob_t *base, aoff64_t offset, char *buffer, aoff64_t *size, bool little_endian) { errno_t rc; concat_blob_t *self = blob_as_concat(base); aoff64_t a_size = 0, b_size = 0; if (offset < self->a_size) { a_size = *size; rc = bithenge_blob_read_bits(self->a, offset, buffer, &a_size, little_endian); if (rc != EOK) return rc; } if (offset + *size > self->a_size) { rc = concat_blob_evaluate_b(self); if (rc != EOK) return rc; b_size = *size - a_size; assert(a_size % 8 == 0); /* TODO: don't require this */ rc = bithenge_blob_read_bits(self->b, offset + a_size - self->a_size, buffer + a_size / 8, &b_size, little_endian); if (rc != EOK) return rc; } assert(a_size + b_size <= *size); *size = a_size + b_size; return EOK; } static void concat_blob_destroy(bithenge_blob_t *base) { concat_blob_t *self = blob_as_concat(base); bithenge_blob_dec_ref(self->a); bithenge_blob_dec_ref(self->b); bithenge_expression_dec_ref(self->b_expr); bithenge_scope_dec_ref(self->scope); free(self); } static const bithenge_random_access_blob_ops_t concat_blob_ops = { .size = concat_blob_size, .read = concat_blob_read, .read_bits = concat_blob_read_bits, .destroy = concat_blob_destroy, }; /** Create a concatenated blob. Takes references to @a a and @a b. * @param[out] out Holds the new blob. * @param a The first blob. * @param b The second blob. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_concat_blob(bithenge_node_t **out, bithenge_blob_t *a, bithenge_blob_t *b) { assert(out); assert(a); assert(b); errno_t rc; concat_blob_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_blob_size(a, &self->a_size); if (rc != EOK) goto error; rc = bithenge_init_random_access_blob(concat_as_blob(self), &concat_blob_ops); if (rc != EOK) goto error; self->a = a; self->b = b; self->b_expr = NULL; self->scope = NULL; *out = bithenge_blob_as_node(concat_as_blob(self)); return EOK; error: bithenge_blob_dec_ref(a); bithenge_blob_dec_ref(b); free(self); return rc; } /** Create a lazy concatenated blob. Takes references to @a a, @a b_expr, and * @a scope. * @param[out] out Holds the new blob. * @param a The first blob. * @param b_expr An expression to calculate the second blob. * @param scope The scope in which @a b_expr should be evaluated. * @return EOK on success or an error code from errno.h. */ errno_t bithenge_concat_blob_lazy(bithenge_node_t **out, bithenge_blob_t *a, bithenge_expression_t *b_expr, bithenge_scope_t *scope) { assert(out); assert(a); assert(b_expr); assert(scope); errno_t rc; concat_blob_t *self = malloc(sizeof(*self)); if (!self) { rc = ENOMEM; goto error; } rc = bithenge_blob_size(a, &self->a_size); if (rc != EOK) goto error; rc = bithenge_init_random_access_blob(concat_as_blob(self), &concat_blob_ops); if (rc != EOK) goto error; self->a = a; self->b = NULL; self->b_expr = b_expr; self->scope = scope; *out = bithenge_blob_as_node(concat_as_blob(self)); return EOK; error: bithenge_blob_dec_ref(a); bithenge_expression_dec_ref(b_expr); bithenge_scope_dec_ref(scope); free(self); return rc; } /** @} */