HelenOS sources

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

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

DEFINITIONS

This source file includes following definitions.
  1. seq_as_node
  2. node_as_seq
  3. seq_node_field_offset
  4. seq_node_subtransform
  5. seq_node_complete
  6. seq_node_destroy
  7. seq_node_set_num_xforms
  8. seq_node_scope
  9. seq_node_init
  10. struct_as_transform
  11. transform_as_struct
  12. struct_as_seq
  13. seq_as_struct
  14. struct_as_node
  15. node_as_struct
  16. struct_node_for_each
  17. struct_node_get
  18. struct_node_destroy
  19. struct_node_get_transform
  20. struct_transform_make_node
  21. struct_transform_apply
  22. struct_transform_prefix_length
  23. struct_transform_prefix_apply
  24. free_subtransforms
  25. struct_transform_destroy
  26. bithenge_new_struct
  27. repeat_as_transform
  28. transform_as_repeat
  29. repeat_as_seq
  30. seq_as_repeat
  31. repeat_as_node
  32. node_as_repeat
  33. repeat_node_for_each
  34. repeat_node_get
  35. repeat_node_destroy
  36. repeat_node_get_transform
  37. repeat_transform_make_node
  38. repeat_transform_apply
  39. repeat_transform_prefix_apply
  40. repeat_transform_destroy
  41. bithenge_repeat_transform
  42. do_while_as_transform
  43. transform_as_do_while
  44. do_while_as_seq
  45. seq_as_do_while
  46. do_while_as_node
  47. node_as_do_while
  48. do_while_node_for_each
  49. do_while_node_destroy
  50. do_while_node_get_transform
  51. do_while_transform_make_node
  52. for_each_noop
  53. do_while_transform_prefix_apply
  54. do_while_transform_destroy
  55. bithenge_do_while_transform

/*
 * 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
 * Sequence transforms.
 */

#include <stdlib.h>
#include <bithenge/blob.h>
#include <bithenge/expression.h>
#include <bithenge/sequence.h>
#include <bithenge/tree.h>
#include "common.h"

/*
 * seq_node
 */

typedef struct {
        bithenge_node_t base;
        const struct seq_node_ops *ops;
        bithenge_blob_t *blob;
        bithenge_scope_t *scope;
        aoff64_t *ends;
        size_t num_ends;
        bool end_on_empty;
        bithenge_int_t num_xforms;
} seq_node_t;

typedef struct seq_node_ops {
        errno_t (*get_transform)(seq_node_t *self, bithenge_transform_t **out,
            bithenge_int_t index);
} seq_node_ops_t;

static bithenge_node_t *seq_as_node(seq_node_t *node)
{
        return &node->base;
}

static seq_node_t *node_as_seq(bithenge_node_t *node)
{
        return (seq_node_t *)node;
}

static errno_t seq_node_field_offset(seq_node_t *self, aoff64_t *out, size_t index)
{
        if (index == 0) {
                *out = 0;
                return EOK;
        }
        index--;
        aoff64_t prev_offset =
            self->num_ends ? self->ends[self->num_ends - 1] : 0;
        for (; self->num_ends <= index; self->num_ends++) {
                bithenge_transform_t *subxform;
                errno_t rc = self->ops->get_transform(self, &subxform,
                    self->num_ends);
                if (rc != EOK)
                        return rc;

                bithenge_node_t *subblob_node;
                bithenge_blob_inc_ref(self->blob);
                rc = bithenge_new_offset_blob(&subblob_node, self->blob,
                    prev_offset);
                if (rc != EOK) {
                        bithenge_transform_dec_ref(subxform);
                        return rc;
                }

                if (self->end_on_empty) {
                        bool empty;
                        rc = bithenge_blob_empty(
                            bithenge_node_as_blob(subblob_node), &empty);
                        if (rc == EOK && empty) {
                                self->num_xforms = self->num_ends;
                                rc = ENOENT;
                        }
                        if (rc != EOK) {
                                bithenge_transform_dec_ref(subxform);
                                bithenge_node_dec_ref(subblob_node);
                                return rc;
                        }
                }

                bithenge_blob_t *subblob = bithenge_node_as_blob(subblob_node);
                aoff64_t field_size;
                rc = bithenge_transform_prefix_length(subxform, self->scope,
                    subblob, &field_size);
                bithenge_node_dec_ref(subblob_node);
                bithenge_transform_dec_ref(subxform);
                if (rc != EOK)
                        return rc;

                if (self->num_xforms == -1) {
                        aoff64_t *new_ends = realloc(self->ends,
                            (self->num_ends + 1) * sizeof(*new_ends));
                        if (!new_ends)
                                return ENOMEM;
                        self->ends = new_ends;
                }

                prev_offset = self->ends[self->num_ends] =
                    prev_offset + field_size;
        }
        *out = self->ends[index];
        return EOK;
}

static errno_t seq_node_subtransform(seq_node_t *self, bithenge_node_t **out,
    size_t index)
{
        aoff64_t start_pos;
        errno_t rc = seq_node_field_offset(self, &start_pos, index);
        if (rc != EOK)
                return rc;

        bithenge_transform_t *subxform;
        rc = self->ops->get_transform(self, &subxform, index);
        if (rc != EOK)
                return rc;

        if (index == self->num_ends) {
                /*
                 * We can apply the subtransform and cache its prefix length at
                 * the same time.
                 */
                bithenge_node_t *blob_node;
                bithenge_blob_inc_ref(self->blob);
                rc = bithenge_new_offset_blob(&blob_node, self->blob,
                    start_pos);
                if (rc != EOK) {
                        bithenge_transform_dec_ref(subxform);
                        return rc;
                }

                if (self->end_on_empty) {
                        bool empty;
                        rc = bithenge_blob_empty(
                            bithenge_node_as_blob(blob_node), &empty);
                        if (rc == EOK && empty) {
                                self->num_xforms = self->num_ends;
                                rc = ENOENT;
                        }
                        if (rc != EOK) {
                                bithenge_transform_dec_ref(subxform);
                                bithenge_node_dec_ref(blob_node);
                                return rc;
                        }
                }

                aoff64_t size;
                rc = bithenge_transform_prefix_apply(subxform, self->scope,
                    bithenge_node_as_blob(blob_node), out, &size);
                bithenge_node_dec_ref(blob_node);
                bithenge_transform_dec_ref(subxform);
                if (rc != EOK)
                        return rc;

                if (self->num_xforms == -1) {
                        aoff64_t *new_ends = realloc(self->ends,
                            (self->num_ends + 1) * sizeof(*new_ends));
                        if (!new_ends)
                                return ENOMEM;
                        self->ends = new_ends;
                }
                self->ends[self->num_ends++] = start_pos + size;
        } else {
                aoff64_t end_pos;
                errno_t rc = seq_node_field_offset(self, &end_pos, index + 1);
                if (rc != EOK) {
                        bithenge_transform_dec_ref(subxform);
                        return rc;
                }

                bithenge_node_t *blob_node;
                bithenge_blob_inc_ref(self->blob);
                rc = bithenge_new_subblob(&blob_node, self->blob, start_pos,
                    end_pos - start_pos);
                if (rc != EOK) {
                        bithenge_transform_dec_ref(subxform);
                        return rc;
                }

                rc = bithenge_transform_apply(subxform, self->scope, blob_node,
                    out);
                bithenge_node_dec_ref(blob_node);
                bithenge_transform_dec_ref(subxform);
                if (rc != EOK)
                        return rc;
        }

        return EOK;
}

static errno_t seq_node_complete(seq_node_t *self, bool *out)
{
        aoff64_t blob_size, end_pos;
        errno_t rc = bithenge_blob_size(self->blob, &blob_size);
        if (rc != EOK)
                return rc;
        rc = seq_node_field_offset(self, &end_pos, self->num_xforms);
        if (rc != EOK)
                return rc;
        *out = blob_size == end_pos;
        return EOK;
}

static void seq_node_destroy(seq_node_t *self)
{
        bithenge_scope_dec_ref(self->scope);
        bithenge_blob_dec_ref(self->blob);
        free(self->ends);
}

static void seq_node_set_num_xforms(seq_node_t *self,
    bithenge_int_t num_xforms)
{
        self->num_xforms = num_xforms;
}

static bithenge_scope_t *seq_node_scope(seq_node_t *self)
{
        return self->scope;
}

static errno_t seq_node_init(seq_node_t *self, const seq_node_ops_t *ops,
    bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_int_t num_xforms,
    bool end_on_empty)
{
        self->ops = ops;
        if (num_xforms != -1) {
                self->ends = malloc(sizeof(*self->ends) * num_xforms);
                if (!self->ends)
                        return ENOMEM;
        } else
                self->ends = NULL;
        bithenge_blob_inc_ref(blob);
        self->blob = blob;
        self->num_xforms = num_xforms;
        self->num_ends = 0;
        self->end_on_empty = end_on_empty;
        self->scope = scope;
        if (self->scope)
                bithenge_scope_inc_ref(self->scope);
        return EOK;
}

/*
 * bithenge_new_struct
 */

typedef struct {
        bithenge_transform_t base;
        bithenge_named_transform_t *subtransforms;
        size_t num_subtransforms;
} struct_transform_t;

static bithenge_transform_t *struct_as_transform(struct_transform_t *xform)
{
        return &xform->base;
}

static struct_transform_t *transform_as_struct(bithenge_transform_t *xform)
{
        return (struct_transform_t *)xform;
}

typedef struct {
        seq_node_t base;
        struct_transform_t *transform;
        bool prefix;
} struct_node_t;

static seq_node_t *struct_as_seq(struct_node_t *node)
{
        return &node->base;
}

static struct_node_t *seq_as_struct(seq_node_t *base)
{
        return (struct_node_t *)base;
}

static bithenge_node_t *struct_as_node(struct_node_t *node)
{
        return seq_as_node(struct_as_seq(node));
}

static struct_node_t *node_as_struct(bithenge_node_t *node)
{
        return seq_as_struct(node_as_seq(node));
}

static errno_t struct_node_for_each(bithenge_node_t *base,
    bithenge_for_each_func_t func, void *data)
{
        errno_t rc = EOK;
        struct_node_t *self = node_as_struct(base);
        bithenge_named_transform_t *subxforms =
            self->transform->subtransforms;

        for (bithenge_int_t i = 0; subxforms[i].transform; i++) {
                bithenge_node_t *subxform_result;
                rc = seq_node_subtransform(struct_as_seq(self),
                    &subxform_result, i);
                if (rc != EOK)
                        return rc;

                if (subxforms[i].name) {
                        bithenge_node_t *name_node;
                        rc = bithenge_new_string_node(&name_node,
                            subxforms[i].name, false);
                        if (rc == EOK) {
                                rc = func(name_node, subxform_result, data);
                                subxform_result = NULL;
                        }
                } else {
                        if (bithenge_node_type(subxform_result) !=
                            BITHENGE_NODE_INTERNAL) {
                                rc = EINVAL;
                        } else {
                                rc = bithenge_node_for_each(subxform_result,
                                    func, data);
                        }
                }
                bithenge_node_dec_ref(subxform_result);
                if (rc != EOK)
                        return rc;
        }

        if (!self->prefix) {
                bool complete;
                rc = seq_node_complete(struct_as_seq(self), &complete);
                if (rc != EOK)
                        return rc;
                if (!complete)
                        return EINVAL;
        }

        return rc;
}

static errno_t struct_node_get(bithenge_node_t *base, bithenge_node_t *key,
    bithenge_node_t **out)
{
        struct_node_t *self = node_as_struct(base);

        errno_t rc = ENOENT;
        if (bithenge_node_type(key) != BITHENGE_NODE_STRING)
                goto end;
        const char *name = bithenge_string_node_value(key);

        const bithenge_named_transform_t *subxforms =
            self->transform->subtransforms;
        for (bithenge_int_t i = 0; subxforms[i].transform; i++) {
                if (subxforms[i].name && !str_cmp(name, subxforms[i].name)) {
                        rc = seq_node_subtransform(struct_as_seq(self), out, i);
                        goto end;
                }
        }

        for (bithenge_int_t i = 0; subxforms[i].transform; i++) {
                if (subxforms[i].name)
                        continue;
                bithenge_node_t *subxform_result;
                rc = seq_node_subtransform(struct_as_seq(self),
                    &subxform_result, i);
                if (rc != EOK)
                        goto end;
                if (bithenge_node_type(subxform_result) !=
                    BITHENGE_NODE_INTERNAL) {
                        bithenge_node_dec_ref(subxform_result);
                        rc = EINVAL;
                        goto end;
                }
                bithenge_node_inc_ref(key);
                rc = bithenge_node_get(subxform_result, key, out);
                bithenge_node_dec_ref(subxform_result);
                if (rc != ENOENT)
                        goto end;
        }

        rc = ENOENT;
end:
        bithenge_node_dec_ref(key);
        return rc;
}

static void struct_node_destroy(bithenge_node_t *base)
{
        struct_node_t *node = node_as_struct(base);

        /*
         * Treat the scope carefully because of the circular reference. In
         * struct_transform_make_node, things are set up so node owns a
         * reference to the scope, but scope doesn't own a reference to node,
         * so node's reference count is too low.
         */
        bithenge_scope_t *scope = seq_node_scope(struct_as_seq(node));
        if (scope->refs == 1) {
                /*
                 * Mostly normal destroy, but we didn't inc_ref(node) for the
                 * scope in struct_transform_make_node, so make sure it doesn't
                 * try to dec_ref.
                 */
                scope->current_node = NULL;
                seq_node_destroy(struct_as_seq(node));
        } else if (scope->refs > 1) {
                /*
                 * The scope is still needed, but node isn't otherwise needed.
                 * Switch things around so scope owns a reference to node, but
                 * not vice versa, and scope's reference count is too low.
                 */
                bithenge_node_inc_ref(base);
                bithenge_scope_dec_ref(scope);
                return;
        } else {
                /*
                 * This happens after the previous case, when scope is no
                 * longer used and is being destroyed. Since scope is already
                 * being destroyed, set it to NULL here so we don't try to
                 * destroy it twice.
                 */
                struct_as_seq(node)->scope = NULL;
                seq_node_destroy(struct_as_seq(node));
        }

        bithenge_transform_dec_ref(struct_as_transform(node->transform));
        free(node);
}

static const bithenge_internal_node_ops_t struct_node_ops = {
        .for_each = struct_node_for_each,
        .get = struct_node_get,
        .destroy = struct_node_destroy,
};

static errno_t struct_node_get_transform(seq_node_t *base,
    bithenge_transform_t **out, bithenge_int_t index)
{
        struct_node_t *self = seq_as_struct(base);
        *out = self->transform->subtransforms[index].transform;
        bithenge_transform_inc_ref(*out);
        return EOK;
}

static const seq_node_ops_t struct_node_seq_ops = {
        .get_transform = struct_node_get_transform,
};

static errno_t struct_transform_make_node(struct_transform_t *self,
    bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
    bool prefix)
{
        struct_node_t *node = malloc(sizeof(*node));
        if (!node)
                return ENOMEM;

        errno_t rc = bithenge_init_internal_node(struct_as_node(node),
            &struct_node_ops);
        if (rc != EOK) {
                free(node);
                return rc;
        }
        bithenge_scope_t *inner;
        rc = bithenge_scope_new(&inner, scope);
        if (rc != EOK) {
                free(node);
                return rc;
        }

        rc = seq_node_init(struct_as_seq(node), &struct_node_seq_ops, inner,
            blob, self->num_subtransforms, false);
        if (rc != EOK) {
                bithenge_scope_dec_ref(inner);
                free(node);
                return rc;
        }

        bithenge_transform_inc_ref(struct_as_transform(self));
        node->transform = self;
        node->prefix = prefix;

        /*
         * We should inc_ref(node) here, but that would make a cycle. Instead,
         * we leave it 1 too low, so that when the only remaining use of node
         * is the scope, node will be destroyed. Also see the comment in
         * struct_node_destroy.
         */
        bithenge_scope_set_current_node(inner, struct_as_node(node));
        bithenge_scope_dec_ref(inner);

        *out = struct_as_node(node);

        return EOK;
}

static errno_t struct_transform_apply(bithenge_transform_t *base,
    bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
{
        struct_transform_t *self = transform_as_struct(base);
        if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
                return EINVAL;
        return struct_transform_make_node(self, out, scope,
            bithenge_node_as_blob(in), false);
}

static errno_t struct_transform_prefix_length(bithenge_transform_t *base,
    bithenge_scope_t *scope, bithenge_blob_t *blob, aoff64_t *out)
{
        struct_transform_t *self = transform_as_struct(base);
        bithenge_node_t *struct_node;
        errno_t rc = struct_transform_make_node(self, &struct_node, scope, blob,
            true);
        if (rc != EOK)
                return rc;

        rc = seq_node_field_offset(node_as_seq(struct_node), out,
            self->num_subtransforms);
        bithenge_node_dec_ref(struct_node);
        return rc;
}

static errno_t struct_transform_prefix_apply(bithenge_transform_t *base,
    bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
    aoff64_t *out_size)
{
        struct_transform_t *self = transform_as_struct(base);
        errno_t rc = struct_transform_make_node(self, out_node, scope, blob,
            true);
        if (rc != EOK)
                return rc;

        if (out_size) {
                rc = seq_node_field_offset(node_as_seq(*out_node), out_size,
                    self->num_subtransforms);
                if (rc != EOK) {
                        bithenge_node_dec_ref(*out_node);
                        return rc;
                }
        }

        return EOK;
}

static void free_subtransforms(bithenge_named_transform_t *subtransforms)
{
        for (size_t i = 0; subtransforms[i].transform; i++) {
                free((void *)subtransforms[i].name);
                bithenge_transform_dec_ref(subtransforms[i].transform);
        }
        free(subtransforms);
}

static void struct_transform_destroy(bithenge_transform_t *base)
{
        struct_transform_t *self = transform_as_struct(base);
        free_subtransforms(self->subtransforms);
        free(self);
}

static bithenge_transform_ops_t struct_transform_ops = {
        .apply = struct_transform_apply,
        .prefix_length = struct_transform_prefix_length,
        .prefix_apply = struct_transform_prefix_apply,
        .destroy = struct_transform_destroy,
};

/** Create a struct transform. The transform will apply its subtransforms
 * sequentially to a blob to create an internal node. Each result is either
 * given a key from @a subtransforms or, if the name is NULL, the result's keys
 * and values are merged into the struct transform's result. This function
 * takes ownership of @a subtransforms and the names and references therein.
 * @param[out] out Stores the created transform.
 * @param subtransforms The subtransforms and field names.
 * @return EOK on success or an error code from errno.h.
 */
errno_t bithenge_new_struct(bithenge_transform_t **out,
    bithenge_named_transform_t *subtransforms)
{
        errno_t rc;
        struct_transform_t *self = malloc(sizeof(*self));
        if (!self) {
                rc = ENOMEM;
                goto error;
        }
        rc = bithenge_init_transform(struct_as_transform(self),
            &struct_transform_ops, 0);
        if (rc != EOK)
                goto error;
        self->subtransforms = subtransforms;
        self->num_subtransforms = 0;
        while (subtransforms[self->num_subtransforms].transform)
                self->num_subtransforms++;
        *out = struct_as_transform(self);
        return EOK;
error:
        free_subtransforms(subtransforms);
        free(self);
        return rc;
}

/*
 * bithenge_repeat_transform
 */

/* TODO: ignore errors */

typedef struct {
        bithenge_transform_t base;
        bithenge_expression_t *expr;
        bithenge_transform_t *xform;
} repeat_transform_t;

static inline bithenge_transform_t *repeat_as_transform(
    repeat_transform_t *self)
{
        return &self->base;
}

static inline repeat_transform_t *transform_as_repeat(
    bithenge_transform_t *base)
{
        return (repeat_transform_t *)base;
}

typedef struct {
        seq_node_t base;
        bool prefix;
        bithenge_int_t count;
        bithenge_transform_t *xform;
} repeat_node_t;

static seq_node_t *repeat_as_seq(repeat_node_t *self)
{
        return &self->base;
}

static repeat_node_t *seq_as_repeat(seq_node_t *base)
{
        return (repeat_node_t *)base;
}

static bithenge_node_t *repeat_as_node(repeat_node_t *self)
{
        return seq_as_node(repeat_as_seq(self));
}

static repeat_node_t *node_as_repeat(bithenge_node_t *base)
{
        return seq_as_repeat(node_as_seq(base));
}

static errno_t repeat_node_for_each(bithenge_node_t *base,
    bithenge_for_each_func_t func, void *data)
{
        errno_t rc = EOK;
        repeat_node_t *self = node_as_repeat(base);

        for (bithenge_int_t i = 0; self->count == -1 || i < self->count; i++) {
                bithenge_node_t *subxform_result;
                rc = seq_node_subtransform(repeat_as_seq(self),
                    &subxform_result, i);
                if ((rc == EINVAL || rc == ENOENT) && self->count == -1) {
                        self->count = i;
                        seq_node_set_num_xforms(repeat_as_seq(self),
                            self->count);
                        rc = EOK;
                        break;
                }
                if (rc != EOK)
                        return rc;

                bithenge_node_t *key_node;
                rc = bithenge_new_integer_node(&key_node, i);
                if (rc != EOK) {
                        bithenge_node_dec_ref(subxform_result);
                        return rc;
                }
                rc = func(key_node, subxform_result, data);
                if (rc != EOK)
                        return rc;
        }

        if (!self->prefix) {
                bool complete;
                rc = seq_node_complete(repeat_as_seq(self), &complete);
                if (rc != EOK)
                        return rc;
                if (!complete)
                        return EINVAL;
        }

        return rc;
}

static errno_t repeat_node_get(bithenge_node_t *base, bithenge_node_t *key,
    bithenge_node_t **out)
{
        repeat_node_t *self = node_as_repeat(base);

        if (bithenge_node_type(key) != BITHENGE_NODE_INTEGER) {
                bithenge_node_dec_ref(key);
                return ENOENT;
        }

        bithenge_int_t index = bithenge_integer_node_value(key);
        bithenge_node_dec_ref(key);
        if (index < 0 || (self->count != -1 && index >= self->count))
                return ENOENT;
        return seq_node_subtransform(repeat_as_seq(self), out, index);
}

static void repeat_node_destroy(bithenge_node_t *base)
{
        repeat_node_t *self = node_as_repeat(base);
        seq_node_destroy(repeat_as_seq(self));
        bithenge_transform_dec_ref(self->xform);
        free(self);
}

static const bithenge_internal_node_ops_t repeat_node_ops = {
        .for_each = repeat_node_for_each,
        .get = repeat_node_get,
        .destroy = repeat_node_destroy,
};

static errno_t repeat_node_get_transform(seq_node_t *base,
    bithenge_transform_t **out, bithenge_int_t index)
{
        repeat_node_t *self = seq_as_repeat(base);
        *out = self->xform;
        bithenge_transform_inc_ref(*out);
        return EOK;
}

static const seq_node_ops_t repeat_node_seq_ops = {
        .get_transform = repeat_node_get_transform,
};

static errno_t repeat_transform_make_node(repeat_transform_t *self,
    bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob,
    bool prefix)
{
        bithenge_int_t count = -1;
        if (self->expr != NULL) {
                bithenge_node_t *count_node;
                errno_t rc = bithenge_expression_evaluate(self->expr, scope,
                    &count_node);
                if (rc != EOK)
                        return rc;
                if (bithenge_node_type(count_node) != BITHENGE_NODE_INTEGER) {
                        bithenge_node_dec_ref(count_node);
                        return EINVAL;
                }
                count = bithenge_integer_node_value(count_node);
                bithenge_node_dec_ref(count_node);
                if (count < 0)
                        return EINVAL;
        }

        repeat_node_t *node = malloc(sizeof(*node));
        if (!node)
                return ENOMEM;

        errno_t rc = bithenge_init_internal_node(repeat_as_node(node),
            &repeat_node_ops);
        if (rc != EOK) {
                free(node);
                return rc;
        }

        rc = seq_node_init(repeat_as_seq(node), &repeat_node_seq_ops, scope,
            blob, count, count == -1);
        if (rc != EOK) {
                free(node);
                return rc;
        }

        bithenge_transform_inc_ref(self->xform);
        node->xform = self->xform;
        node->count = count;
        node->prefix = prefix;
        *out = repeat_as_node(node);
        return EOK;
}

static errno_t repeat_transform_apply(bithenge_transform_t *base,
    bithenge_scope_t *scope, bithenge_node_t *in, bithenge_node_t **out)
{
        repeat_transform_t *self = transform_as_repeat(base);
        if (bithenge_node_type(in) != BITHENGE_NODE_BLOB)
                return EINVAL;
        return repeat_transform_make_node(self, out, scope,
            bithenge_node_as_blob(in), false);
}

static errno_t repeat_transform_prefix_apply(bithenge_transform_t *base,
    bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
    aoff64_t *out_size)
{
        repeat_transform_t *self = transform_as_repeat(base);
        errno_t rc = repeat_transform_make_node(self, out_node, scope, blob, true);
        if (rc != EOK)
                return rc;

        if (out_size) {
                bithenge_int_t count = node_as_repeat(*out_node)->count;
                if (count != -1) {
                        rc = seq_node_field_offset(node_as_seq(*out_node),
                            out_size, count);
                        if (rc != EOK) {
                                bithenge_node_dec_ref(*out_node);
                                return rc;
                        }
                } else {
                        *out_size = 0;
                        for (count = 1; ; count++) {
                                aoff64_t size;
                                rc = seq_node_field_offset(
                                    node_as_seq(*out_node), &size, count);
                                if (rc == EINVAL || rc == ENOENT)
                                        break;
                                if (rc != EOK) {
                                        bithenge_node_dec_ref(*out_node);
                                        return rc;
                                }
                                *out_size = size;
                        }
                }
        }
        return EOK;
}

static void repeat_transform_destroy(bithenge_transform_t *base)
{
        repeat_transform_t *self = transform_as_repeat(base);
        bithenge_transform_dec_ref(self->xform);
        bithenge_expression_dec_ref(self->expr);
        free(self);
}

static const bithenge_transform_ops_t repeat_transform_ops = {
        .apply = repeat_transform_apply,
        .prefix_apply = repeat_transform_prefix_apply,
        .destroy = repeat_transform_destroy,
};

/** Create a transform that applies its subtransform repeatedly. Takes a
 * reference to @a xform and @a expr.
 * @param[out] out Holds the new transform.
 * @param xform The subtransform to apply repeatedly.
 * @param expr Used to calculate the number of times @a xform will be applied.
 * May be NULL, in which case @a xform will be applied indefinitely.
 * @return EOK on success or an error code from errno.h.
 */
errno_t bithenge_repeat_transform(bithenge_transform_t **out,
    bithenge_transform_t *xform, bithenge_expression_t *expr)
{
        errno_t rc;
        repeat_transform_t *self = malloc(sizeof(*self));
        if (!self) {
                rc = ENOMEM;
                goto error;
        }

        rc = bithenge_init_transform(repeat_as_transform(self),
            &repeat_transform_ops, 0);
        if (rc != EOK)
                goto error;

        self->expr = expr;
        self->xform = xform;
        *out = repeat_as_transform(self);
        return EOK;

error:
        free(self);
        bithenge_expression_dec_ref(expr);
        bithenge_transform_dec_ref(xform);
        return rc;
}

/*
 * bithenge_do_while_transform
 */

typedef struct {
        bithenge_transform_t base;
        bithenge_expression_t *expr;
        bithenge_transform_t *xform;
} do_while_transform_t;

static inline bithenge_transform_t *do_while_as_transform(
    do_while_transform_t *self)
{
        return &self->base;
}

static inline do_while_transform_t *transform_as_do_while(
    bithenge_transform_t *base)
{
        return (do_while_transform_t *)base;
}

typedef struct {
        seq_node_t base;
        bithenge_expression_t *expr;
        bithenge_transform_t *xform;
        bithenge_int_t count;
} do_while_node_t;

static seq_node_t *do_while_as_seq(do_while_node_t *self)
{
        return &self->base;
}

static do_while_node_t *seq_as_do_while(seq_node_t *base)
{
        return (do_while_node_t *)base;
}

static bithenge_node_t *do_while_as_node(do_while_node_t *self)
{
        return seq_as_node(do_while_as_seq(self));
}

static do_while_node_t *node_as_do_while(bithenge_node_t *base)
{
        return seq_as_do_while(node_as_seq(base));
}

static errno_t do_while_node_for_each(bithenge_node_t *base,
    bithenge_for_each_func_t func, void *data)
{
        errno_t rc = EOK;
        do_while_node_t *self = node_as_do_while(base);

        for (bithenge_int_t i = 0; ; i++) {
                bithenge_node_t *subxform_result;
                rc = seq_node_subtransform(do_while_as_seq(self),
                    &subxform_result, i);
                if (rc != EOK)
                        return rc;

                bithenge_node_t *key_node;
                rc = bithenge_new_integer_node(&key_node, i);
                if (rc != EOK) {
                        bithenge_node_dec_ref(subxform_result);
                        return rc;
                }
                bithenge_node_inc_ref(subxform_result);
                rc = func(key_node, subxform_result, data);
                if (rc != EOK) {
                        bithenge_node_dec_ref(subxform_result);
                        return rc;
                }

                bithenge_scope_t *scope;
                rc = bithenge_scope_new(&scope,
                    seq_node_scope(do_while_as_seq(self)));
                if (rc != EOK) {
                        bithenge_node_dec_ref(subxform_result);
                        return rc;
                }
                bithenge_scope_set_current_node(scope, subxform_result);
                bithenge_node_t *expr_result;
                rc = bithenge_expression_evaluate(self->expr, scope,
                    &expr_result);
                bithenge_scope_dec_ref(scope);
                if (rc != EOK)
                        return rc;
                if (bithenge_node_type(expr_result) != BITHENGE_NODE_BOOLEAN) {
                        bithenge_node_dec_ref(expr_result);
                        return EINVAL;
                }
                bool cond = bithenge_boolean_node_value(expr_result);
                bithenge_node_dec_ref(expr_result);
                if (!cond) {
                        self->count = i + 1;
                        seq_node_set_num_xforms(do_while_as_seq(self),
                            self->count);
                        break;
                }
        }

        return rc;
}

static void do_while_node_destroy(bithenge_node_t *base)
{
        do_while_node_t *self = node_as_do_while(base);
        seq_node_destroy(do_while_as_seq(self));
        bithenge_expression_dec_ref(self->expr);
        bithenge_transform_dec_ref(self->xform);
        free(self);
}

static const bithenge_internal_node_ops_t do_while_node_ops = {
        .for_each = do_while_node_for_each,
        .destroy = do_while_node_destroy,
};

static errno_t do_while_node_get_transform(seq_node_t *base,
    bithenge_transform_t **out, bithenge_int_t index)
{
        do_while_node_t *self = seq_as_do_while(base);
        *out = self->xform;
        bithenge_transform_inc_ref(*out);
        return EOK;
}

static const seq_node_ops_t do_while_node_seq_ops = {
        .get_transform = do_while_node_get_transform,
};

static errno_t do_while_transform_make_node(do_while_transform_t *self,
    bithenge_node_t **out, bithenge_scope_t *scope, bithenge_blob_t *blob)
{
        do_while_node_t *node = malloc(sizeof(*node));
        if (!node)
                return ENOMEM;

        errno_t rc = bithenge_init_internal_node(do_while_as_node(node),
            &do_while_node_ops);
        if (rc != EOK) {
                free(node);
                return rc;
        }

        rc = seq_node_init(do_while_as_seq(node), &do_while_node_seq_ops,
            scope, blob, -1, false);
        if (rc != EOK) {
                free(node);
                return rc;
        }

        bithenge_transform_inc_ref(self->xform);
        node->xform = self->xform;
        bithenge_expression_inc_ref(self->expr);
        node->expr = self->expr;
        node->count = -1;
        *out = do_while_as_node(node);
        return EOK;
}

static errno_t for_each_noop(bithenge_node_t *key, bithenge_node_t *value,
    void *data)
{
        bithenge_node_dec_ref(key);
        bithenge_node_dec_ref(value);
        return EOK;
}

static errno_t do_while_transform_prefix_apply(bithenge_transform_t *base,
    bithenge_scope_t *scope, bithenge_blob_t *blob, bithenge_node_t **out_node,
    aoff64_t *out_size)
{
        do_while_transform_t *self = transform_as_do_while(base);
        errno_t rc = do_while_transform_make_node(self, out_node, scope, blob);
        if (rc != EOK)
                return rc;

        if (out_size) {
                rc = bithenge_node_for_each(*out_node, for_each_noop, NULL);
                if (rc != EOK) {
                        bithenge_node_dec_ref(*out_node);
                        return rc;
                }

                rc = seq_node_field_offset(node_as_seq(*out_node), out_size,
                    node_as_do_while(*out_node)->count);
                if (rc != EOK) {
                        bithenge_node_dec_ref(*out_node);
                        return rc;
                }
        }

        return EOK;
}

static void do_while_transform_destroy(bithenge_transform_t *base)
{
        do_while_transform_t *self = transform_as_do_while(base);
        bithenge_transform_dec_ref(self->xform);
        bithenge_expression_dec_ref(self->expr);
        free(self);
}

static const bithenge_transform_ops_t do_while_transform_ops = {
        .prefix_apply = do_while_transform_prefix_apply,
        .destroy = do_while_transform_destroy,
};

/** Create a transform that applies its subtransform while an expression on the
 * result returns true. Takes a reference to @a xform and @a expr.
 * @param[out] out Holds the new transform.
 * @param xform The subtransform to apply repeatedly.
 * @param expr Applied in the result of each application of @a xform to
 * determine whether there will be more.
 * @return EOK on success or an error code from errno.h.
 */
errno_t bithenge_do_while_transform(bithenge_transform_t **out,
    bithenge_transform_t *xform, bithenge_expression_t *expr)
{
        errno_t rc;
        do_while_transform_t *self = malloc(sizeof(*self));
        if (!self) {
                rc = ENOMEM;
                goto error;
        }

        rc = bithenge_init_transform(do_while_as_transform(self),
            &do_while_transform_ops, 0);
        if (rc != EOK)
                goto error;

        self->expr = expr;
        self->xform = xform;
        *out = do_while_as_transform(self);
        return EOK;

error:
        free(self);
        bithenge_expression_dec_ref(expr);
        bithenge_transform_dec_ref(xform);
        return rc;
}

/** @}
 */

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