HelenOS sources

root/uspace/lib/bithenge/src/expression.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. bithenge_init_expression
  2. expression_indestructible
  3. expression_as_binary
  4. binary_as_expression
  5. binary_expression_evaluate
  6. binary_expression_destroy
  7. bithenge_binary_expression
  8. in_node_evaluate
  9. bithenge_in_node_expression
  10. current_node_evaluate
  11. bithenge_current_node_expression
  12. expression_as_param
  13. param_as_expression
  14. param_expression_evaluate
  15. param_expression_destroy
  16. bithenge_param_expression
  17. expression_as_const
  18. const_as_expression
  19. const_expression_evaluate
  20. const_expression_destroy
  21. bithenge_const_expression
  22. expression_as_scope_member
  23. scope_member_as_expression
  24. scope_member_expression_evaluate
  25. scope_member_expression_destroy
  26. bithenge_scope_member_expression
  27. expression_as_subblob
  28. subblob_as_expression
  29. subblob_expression_evaluate
  30. subblob_expression_destroy
  31. bithenge_subblob_expression
  32. param_wrapper_as_transform
  33. transform_as_param_wrapper
  34. param_wrapper_fill_scope
  35. param_wrapper_apply
  36. param_wrapper_prefix_length
  37. param_wrapper_prefix_apply
  38. param_wrapper_destroy
  39. bithenge_param_wrapper
  40. expression_as_transform
  41. transform_as_expression
  42. expression_transform_apply
  43. expression_transform_destroy
  44. bithenge_expression_transform
  45. inputless_transform_prefix_length
  46. inputless_transform_prefix_apply
  47. bithenge_inputless_transform
  48. blob_as_concat
  49. concat_as_blob
  50. concat_blob_evaluate_b
  51. concat_blob_size
  52. concat_blob_read
  53. concat_blob_read_bits
  54. concat_blob_destroy
  55. bithenge_concat_blob
  56. bithenge_concat_blob_lazy

/*
 * 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 <assert.h>
#include <errno.h>
#include <stdlib.h>
#include "common.h"
#include <bithenge/blob.h>
#include <bithenge/expression.h>
#include <bithenge/transform.h>
#include <bithenge/tree.h>

/** 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 = {
        &current_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(&current_node_expression);
        *out = &current_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),
            &param_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),
            &param_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;
}

/** @}
 */

/* [<][>][^][v][top][bottom][index][help] */
HelenOS homepage, sources at GitHub