The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
Copyright (C) 2001-2011, Parrot Foundation.

=head1 NAME

src/pmc/fixedpmcarray.pmc - fixed size array for PMCs only

=head1 DESCRIPTION

This class, FixedPMCArray, implements an array of fixed size which stores PMCs.
It puts things into Integer, Float, or String PMCs as appropriate

=head2 Note

The flag C<PObj_private0_FLAG> is used in the C<NameSpace> PMC and should
never be set for user arrays.

=head2 Functions

=over 4

=cut

*/

#define PMC_size(x)  ((Parrot_FixedPMCArray_attributes *)PMC_data(x))->size
#define PMC_array(x) ((Parrot_FixedPMCArray_attributes *)PMC_data(x))->pmc_array

/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */

PARROT_DOES_NOT_RETURN
static void cannot_autovivify_nested(PARROT_INTERP)
        __attribute__nonnull__(1);

#define ASSERT_ARGS_cannot_autovivify_nested __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
/* HEADERIZER END: static */

pmclass FixedPMCArray auto_attrs provides array {
    ATTR INTVAL   size;      /* number of elements in the array */
    ATTR PMC    **pmc_array; /* pointer to PMC array */

/*

=item C<METHOD sort(PMC *cmp_func)>

Sort this array, optionally using the provided cmp_func

=cut

*/

    METHOD sort(PMC *cmp_func :optional) {
        const INTVAL n = SELF.elements();

        if (n > 1) {
            /* XXX Workaround for TT #218 */
            if (PObj_is_object_TEST(SELF)) {
                PMC * const parent = SELF.get_attr_str(CONST_STRING(INTERP, "proxy"));
                Parrot_pcc_invoke_method_from_c_args(INTERP, parent, CONST_STRING(INTERP, "sort"), "P->", cmp_func);
            }
            else
                Parrot_util_quicksort(INTERP, (void **)PMC_array(SELF), n, cmp_func, "PP->I");
        }
        RETURN(PMC *SELF);
    }


/*

=item C<METHOD reverse()>

Reverse the contents of the array.

=cut

*/
    METHOD reverse() {
        INTVAL n = SELF.elements();
        if (n > 1) {
            PMC *val;
            PMC **data = PMC_array(SELF);
            INTVAL i;

            for (i = 0; i <= --n; i++) {
                val = data[i];
                data[i] = data[n];
                data[n] = val;
            }
        }
    }


/*

=back

=head2 Methods

=over 4

=item C<void init_int(INTVAL size)>

Initializes the array.

=cut

*/

    VTABLE void init_int(INTVAL size) {
        if (size < 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    _("FixedPMCArray: Cannot set array size to a negative number (%d)"), size);

        SELF.set_integer_native(size);
    }

/*

=item C<void destroy()>

Destroys the array.

=cut

*/

    VTABLE void destroy() {
        if (PMC_array(SELF))
            mem_gc_free(INTERP, PMC_array(SELF));
    }

/*

=item C<PMC *clone()>

Creates and returns a copy of the array.

=cut

*/

    VTABLE PMC *clone() {
        PMC * const dest  = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
        const INTVAL size = PMC_size(SELF);

        if (size) {
            PMC_size(dest)  = size;
            PMC_array(dest) = mem_gc_allocate_n_typed(INTERP, size, PMC *);
            mem_copy_n_typed(PMC_array(dest), PMC_array(SELF), size, PMC *);
            PObj_custom_mark_destroy_SETALL(dest);
        }

        return dest;
    }

/*

=item C<INTVAL get_bool()>

Returns whether the array has any elements (meaning been initialized, for a
fixed sized array).

=cut

*/
    VTABLE INTVAL get_bool() {
        const INTVAL size = SELF.elements();
        return (INTVAL)(size != 0);
    }

/*

=item C<INTVAL elements()>

=cut

*/

    VTABLE INTVAL elements() {
        return PMC_size(SELF);
    }

/*

=item C<INTVAL get_integer()>

Returns the number of elements in the array.

=cut

*/

    VTABLE INTVAL get_integer() {
        return SELF.elements();
    }

/*

=item C<FLOATVAL get_number()>

Returns the number of elements in the array.

=cut

*/

    VTABLE FLOATVAL get_number() {
        const INTVAL e = SELF.elements();
        return (FLOATVAL)e;
    }

/*

=item C<STRING *get_string()>

Returns the number of elements in the array as a Parrot string. (??? -leo)

=item C<STRING *get_repr()>

Returns a string representation of the array contents.
TT #1229: implement freeze/thaw and use that instead.

=cut

*/

    VTABLE STRING *get_string() {
        return Parrot_str_from_int(INTERP, SELF.elements());
    }

    VTABLE STRING *get_repr() {
        STRING *res    = CONST_STRING(INTERP, "(");
        const INTVAL n = VTABLE_elements(INTERP, SELF);
        INTVAL  i;

        for (i = 0; i < n; ++i) {
            PMC * const val = SELF.get_pmc_keyed_int(i);
            if (i > 0)
                res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, ", "));

            res = Parrot_str_concat(INTERP, res, VTABLE_get_repr(INTERP, val));
        }

        res = Parrot_str_concat(INTERP, res, CONST_STRING(INTERP, ")"));

        return res;
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL key)>

Returns the integer value of the element at index C<key>.

=cut

*/

    VTABLE INTVAL get_integer_keyed_int(INTVAL key) {
        PMC * const tempPMC = SELF.get_pmc_keyed_int(key);
        if (PMC_IS_NULL(tempPMC))
            return 0;
        return VTABLE_get_integer(INTERP, tempPMC);
    }

/*

=item C<INTVAL get_integer_keyed(PMC *key)>

Returns the integer value of the element at index C<*key>.

=cut

*/

    VTABLE INTVAL get_integer_keyed(PMC *key) {
        PMC * const tempPMC = SELF.get_pmc_keyed(key);
        return VTABLE_get_integer(INTERP, tempPMC);
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

Returns the floating-point value of the element at index C<key>.

=cut

*/

    VTABLE FLOATVAL get_number_keyed_int(INTVAL key) {
        PMC * const tempPMC = SELF.get_pmc_keyed_int(key);
        return VTABLE_get_number(INTERP, tempPMC);
    }

/*

=item C<FLOATVAL get_number_keyed(PMC *key)>

Returns the floating-point value of the element at index C<*key>.

=cut

*/

    VTABLE FLOATVAL get_number_keyed(PMC *key) {
        PMC * const tempPMC = SELF.get_pmc_keyed(key);
        return VTABLE_get_number(INTERP, tempPMC);
    }

/*

=item C<STRING *get_string_keyed_int(INTVAL key)>

Returns the Parrot string value of the element at index C<key>.

=cut

*/

    VTABLE STRING *get_string_keyed_int(INTVAL key) {
        PMC * const retval = SELF.get_pmc_keyed_int(key);

        if (PMC_IS_NULL(retval))
            return CONST_STRING(INTERP, "");

        return VTABLE_get_string(INTERP, retval);
    }

/*

=item C<STRING *get_string_keyed(PMC *key)>

Returns the Parrot string value of the element at index C<*key>.

=cut

*/

    VTABLE STRING *get_string_keyed(PMC *key) {
        PMC * const tempPMC = SELF.get_pmc_keyed(key);
        return VTABLE_get_string(INTERP, tempPMC);
    }

/*

=item C<PMC *get_pmc_keyed_int(INTVAL key)>

Returns the PMC value of the element at index C<key>.

=cut

*/

    VTABLE PMC *get_pmc_keyed_int(INTVAL key) {
        PMC **data;

        if (key < 0 || key >= PMC_size(SELF))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                _("FixedPMCArray: index out of bounds!"));

        data = PMC_array(SELF);
        return data[key];
    }

/*

=item C<PMC *get_pmc_keyed(PMC *key)>

Returns the PMC value of the element at index C<*key>.

=cut

*/

    VTABLE PMC *get_pmc_keyed(PMC *key) {
        const INTVAL k        = VTABLE_get_integer(INTERP, key);
        PMC   * const nextkey = Parrot_key_next(INTERP, key);
        PMC   *box;

        if (!nextkey)
            return SELF.get_pmc_keyed_int(k);

        box = SELF.get_pmc_keyed_int(k);

        /* TT #1561, return NULL early if we must autovivify. */
        if (PMC_IS_NULL(box))
            return PMCNULL;

        return VTABLE_get_pmc_keyed(INTERP, box, nextkey);
    }

/*

=item C<void set_integer_native(INTVAL size)>

Sizes the array to C<size> elements. Can't be used to resize an
array.

=cut

*/

    VTABLE void set_integer_native(INTVAL size) {
        int i;
        PMC **data;

        if (PMC_size(SELF) && size)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    _("FixedPMCArray: Can't resize!"));
        if (!size)
            return;

        if (size < 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    _("FixedPMCArray: Cannot set array size to a negative number"));

        PMC_size(SELF) = size;
        data           = mem_gc_allocate_n_typed(INTERP, size, PMC *);

        for (i = 0; i < size; ++i)
            data[i] = PMCNULL;

        PObj_custom_mark_destroy_SETALL(SELF);
        PMC_array(SELF) = data;
    }

/*

=item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>

Sets the integer value of the element at index C<key> to C<value>.

=cut

*/

    VTABLE void set_integer_keyed_int(INTVAL key, INTVAL value) {
        PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
                    enum_class_Integer));

        VTABLE_set_integer_native(INTERP, val, value);
        SELF.set_pmc_keyed_int(key, val);
    }

/*

=item C<void set_integer_keyed(PMC *key, INTVAL value)>

Sets the integer value of the element at index C<key> to C<value>.

=cut

*/

    VTABLE void set_integer_keyed(PMC *key, INTVAL value) {
        PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
                    enum_class_Integer));

        VTABLE_set_integer_native(INTERP, val, value);

        /* Let set_pmc_keyed worry about multi keys */
        SELF.set_pmc_keyed(key, val);
    }

/*

=item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>

Sets the floating-point value of the element at index C<key> to
C<value>.

=cut

*/

    VTABLE void set_number_keyed_int(INTVAL key, FLOATVAL value) {
        PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
                    enum_class_Float));

        VTABLE_set_number_native(INTERP, val, value);
        SELF.set_pmc_keyed_int(key, val);
    }

/*

=item C<void set_number_keyed(PMC *key, FLOATVAL value)>

Sets the floating-point value of the element at index C<key> to
C<value>.

=cut

*/

    VTABLE void set_number_keyed(PMC *key, FLOATVAL value) {
        const INTVAL k        = VTABLE_get_integer(INTERP, key);
        PMC   * const nextkey = Parrot_key_next(INTERP, key);

        if (nextkey == NULL) {
            SELF.set_number_keyed_int(k, value);
        }
        else {
            PMC * const box = SELF.get_pmc_keyed_int(k);

            if (PMC_IS_NULL(box))
                cannot_autovivify_nested(INTERP);

            VTABLE_set_number_keyed(INTERP, box, nextkey, value);
        }
    }

/*

=item C<void set_string_keyed_int(INTVAL key, STRING *value)>

Sets the Parrot string value of the element at index C<key> to C<value>.

=cut

*/

    VTABLE void set_string_keyed_int(INTVAL key, STRING *value) {
        PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
                    enum_class_String));

        VTABLE_set_string_native(INTERP, val, value);
        SELF.set_pmc_keyed_int(key, val);
    }

/*

=item C<void set_string_keyed(PMC *key, STRING *value)>

Sets the string value of the element at index C<key> to
C<value>.

=cut

*/

    VTABLE void set_string_keyed(PMC *key, STRING *value) {
        PMC * const val = Parrot_pmc_new(INTERP, Parrot_hll_get_ctx_HLL_type(INTERP,
                    enum_class_String));

        VTABLE_set_string_native(INTERP, val, value);

        /* Let set_pmc_keyed worry about multi keys */
        SELF.set_pmc_keyed(key, val);
    }

/*

=item C<void set_pmc_keyed_int(INTVAL key, PMC *src)>

Sets the PMC value of the element at index C<key> to C<*src>.

=cut

*/

    VTABLE void set_pmc_keyed_int(INTVAL key, PMC *src) {
        PMC **data;

        if (key < 0 || key >= PMC_size(SELF))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                _("FixedPMCArray: index out of bounds!"));

        data      = PMC_array(SELF);
        data[key] = src;
    }

/*

=item C<void set_pmc_keyed(PMC *key, PMC *value)>

Sets the PMC at index C<key> to C<value>.

=cut

*/

    VTABLE void set_pmc_keyed(PMC *key, PMC *value) {
        const INTVAL k = VTABLE_get_integer(INTERP, key);
        PMC * const nextkey = Parrot_key_next(INTERP, key);

        if (!nextkey) {
            SELF.set_pmc_keyed_int(k, value);
        }
        else {
            PMC * const box = SELF.get_pmc_keyed_int(k);

            if (PMC_IS_NULL(box))
                cannot_autovivify_nested(INTERP);

            VTABLE_set_pmc_keyed(INTERP, box, nextkey, value);
        }
    }

/*

=item C<INTVAL is_equal(PMC *value)>

The C<==> operation. Compares two array to hold equal elements.

=cut

*/

    VTABLE INTVAL is_equal(PMC *value) {
        INTVAL j, n;

        if (value->vtable->base_type != SELF->vtable->base_type)
            return 0;

        n = SELF.elements();

        if (VTABLE_elements(INTERP, value) != n)
            return 0;

        for (j = 0; j < n; ++j) {
            PMC * const item1 = SELF.get_pmc_keyed_int(j);
            PMC * const item2 = VTABLE_get_pmc_keyed_int(INTERP, value, j);

            if (item1 == item2)
                continue;

            if (item1->vtable->base_type == enum_class_Null
            ||  item2->vtable->base_type == enum_class_Null)
                return 0;

            if (!VTABLE_is_equal(INTERP, item1, item2))
                return 0;
        }

        return 1;
    }


/*

=item C<PMC *get_iter()>

Return a new iterator for SELF.

=cut

*/

    VTABLE PMC *get_iter() {
        return Parrot_pmc_new_init(INTERP, enum_class_ArrayIterator, SELF);
    }

/*

=item C<INTVAL exists_keyed_int(INTVAL key)>

=item C<INTVAL exists_keyed_int(PMC *key)>

Returns TRUE is the element at C<key> exists; otherwise returns false.

=cut

*/
    VTABLE INTVAL exists_keyed_int(INTVAL key) {
        PMC **data;
        if (key < 0 || key >= PMC_size(SELF))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                _("FixedPMCArray: index out of bounds!"));

        data = PMC_array(SELF);
        return !PMC_IS_NULL(data[key]);
    }

    VTABLE INTVAL exists_keyed(PMC *key) {
        const INTVAL ix = VTABLE_get_integer(INTERP, key);
        return SELF.exists_keyed_int(ix);
    }

/*

=item C<void splice(PMC *value, INTVAL offset, INTVAL count)>

Replaces C<count> elements starting at C<offset> with the elements in C<value>.

If C<count> is 0 then the elements in C<value> will be inserted after
C<offset>.

This throws an exception if any of the spliced in values are out of the range
of this array.

=cut

*/

    VTABLE void splice(PMC *value, INTVAL offset, INTVAL count) {
        if (count + offset > PMC_size(SELF))
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                _("FixedPMCArray: index out of bounds!"));

        for (count--; count >= 0; --count) {
            VTABLE_set_pmc_keyed_int(INTERP, SELF, offset + count, value);
        }
    }


/*

=item C<void visit(PMC *info)>

This is used by freeze/thaw to visit the contents of the array.

C<*info> is the visit info, (see F<include/parrot/pmc_freeze.h>).

=item C<void freeze(PMC *info)>

Used to archive the array.

=item C<void thaw(PMC *info)>

Used to unarchive the array.

=cut

*/

    VTABLE void visit(PMC *info) {
        INTVAL  i;
        const INTVAL n = VTABLE_elements(INTERP, SELF);
        PMC   **pos    = PMC_array(SELF);

        for (i = 0; i < n; ++i, ++pos) {
            VISIT_PMC(INTERP, info, *pos);
        }

        SUPER(info);
    }

    VTABLE void freeze(PMC *info) {
        VTABLE_push_integer(INTERP, info, VTABLE_elements(INTERP, SELF));
    }

    VTABLE void thaw(PMC *info) {
        SELF.init_int(VTABLE_shift_integer(INTERP, info));
    }

/*

=item C<INTVAL defined_keyed_int(INTVAL key)>

Returns TRUE is the element at C<key> is defined; otherwise returns false.

=cut

*/

    VTABLE INTVAL defined_keyed_int(INTVAL key) {
        PMC * const val = SELF.get_pmc_keyed_int(key);

        if (PMC_IS_NULL(val))
            return 0;

        return VTABLE_defined(INTERP, val);
    }

/*

=item C<void mark(void)>

Mark the array.

=cut

*/

    VTABLE void mark() {
        PMC ** const data = PMC_array(SELF);
        if (data) {
            INTVAL i;
            for (i = PMC_size(SELF) - 1; i >= 0; --i)
                Parrot_gc_mark_PMC_alive(INTERP, data[i]);
        }
    }


}

/*

=back

=head1 Auxiliary functions

=over 4

=item C<static void cannot_autovivify_nested(PARROT_INTERP)>

Throw exception when trying to autovivify nested arrays

=cut

*/

PARROT_DOES_NOT_RETURN
static void
cannot_autovivify_nested(PARROT_INTERP)
{
    ASSERT_ARGS(cannot_autovivify_nested)
    Parrot_ex_throw_from_c_args(interp, NULL,
            EXCEPTION_INVALID_OPERATION,
            "Cannot autovivify nested arrays");
}


/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd17_basic_types.pod>.

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
 */