/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/fixedintegerarray.pmc 35819 2009-01-20T05:19:27.074079Z chromatic  $

=head1 NAME

src/pmc/fixedintegerarray.pmc - fixed size array for integers only

=head1 DESCRIPTION

This class, FixedIntegerArray, implements an array of fixed size which stores
INTVALs.  It uses Integer PMCs for all of the conversions.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"

pmclass FixedIntegerArray need_ext provides array {

/*

=back

=head2 Methods

=over 4

=item C<void init()>

Initializes the array.

=cut

*/

    VTABLE void init() {
        PMC_int_val(SELF) = 0;
        PMC_data(SELF)    = NULL;
    }

/*

=item C<PMC instantiate_str(STRING *rep, INTVAL flags)>

Class method to construct an array from the string representation C<rep>,
which is a string I<"(el0, el1, ...)">. C<flags> may have the
C<PObj_constant_FLAG> bit set, which will be honored to create a PMC
in the constant PMC pool.

=cut

*/
    VTABLE PMC *instantiate_str(STRING *rep, INTVAL flags) {
        const INTVAL type = SELF->vtable->base_type;
        INTVAL  n, elem, i, l;
        char   *source, *p, *start;
        int     base;

        if (flags & PObj_constant_FLAG)
            SELF = constant_pmc_new(INTERP, type);
        else
            SELF = pmc_new(INTERP, type);

        l = string_length(INTERP, rep);

        if (!l)
            return SELF;

        if (rep->encoding != Parrot_fixed_8_encoding_ptr)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_ENCODING,
                    "unhandled string encoding in constructor");

        source = string_to_cstring(interp, rep);

        /* "()" - no args */
        if (l <= 2 && *source == '(') {
            string_cstring_free(source);
            return SELF;
        }

        /* count commas */
        p = source;
        n = 0;
        while (*p) {
            if (*p == ',')
                ++n;
            p++;
        }

        /* presize the array */
        SELF.set_integer_native(n + 1);

        /* parse string */
        p = source;

        for (i = l, n = 0; i; --i, ++p) {
            switch (*p) {
                case ' ': continue;
                case '\t': continue;
                case '(': continue;
                case ')': break;
                case ',':
                          n++;
                          break;
                default:
                          base = 10;
                          if (*p == '0') {
                              ++p;
                              --i;
                              if (*p == 'b' || *p == 'B') {
                                  base = 2;
                                  ++p;
                                  --i;
                              }
                              else if (*p == 'x' || *p == 'X') {
                                  base = 16;
                                  ++p;
                                  --i;
                              }
                          }
                          start = p;
                          elem  = strtoul(p, &p, base);
                          --p;
                          i -= (p - start);
                          SELF.set_integer_keyed_int(n, elem);
                          break;
            }
        }

        string_cstring_free(source);
        return SELF;
    }

/*

=item C<void destroy()>

Destroys the array.

=cut

*/

    VTABLE void destroy() {
        if (PMC_data(SELF))
            mem_sys_free(PMC_data(SELF));

        PMC_data(SELF)    = NULL;
        PMC_int_val(SELF) = 0;
    }

/*

=item C<PMC *clone()>

Creates and returns a copy of the array.

=cut

*/

    VTABLE PMC *clone() {
        /* a quick hack to create a clone in the constant PMC arena
         * this is needed for the call signatures
         *
         * a better way would be probably to supply a flag to the clone
         * vtable
         */

        PMC * const dest =
            PObj_constant_TEST(SELF)
                ? constant_pmc_new(INTERP, SELF->vtable->base_type)
                : pmc_new(INTERP, SELF->vtable->base_type);

        if (PMC_data(SELF)) {
            const INTVAL size = PMC_int_val(SELF);
            PMC_int_val(dest) = size;
            PMC_data(dest)    = mem_allocate_n_typed(size, INTVAL);

            mem_sys_memcopy(PMC_data(dest), PMC_data(SELF), size * sizeof (INTVAL));
            PObj_active_destroy_SET(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.get_integer();
        return (INTVAL)(size != 0);
    }

/*

=item C<INTVAL elements()>

=cut

*/

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

/*

=item C<INTVAL get_integer()>

Returns the number of elements in the array.

=cut

*/

    VTABLE INTVAL get_integer() {
        return PMC_int_val(SELF);
    }


/*

=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) {
        INTVAL *data;

        if (key < 0 || key >= PMC_int_val(SELF))
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "FixedIntegerArray: index out of bounds!");

        data = (INTVAL *)PMC_data(SELF);
        return data[key];
    }

/*

=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) {
        /* simple int keys only */
        const INTVAL k = key_integer(INTERP, key);
        return SELF.get_integer_keyed_int(k);
    }


/*

=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) {
        const INTVAL i = SELF.get_integer_keyed_int(key);
        return (FLOATVAL)i;
    }

/*

=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) {
        const INTVAL k = key_integer(INTERP, key);
        return SELF.get_number_keyed_int(k);
    }

/*

=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 temp = SELF.get_pmc_keyed_int(key);
        return VTABLE_get_string(INTERP, temp);
    }

/*

=item C<STRING *get_repr()>

Returns the Parrot string representation C<key>.

=cut

*/

    VTABLE STRING *get_repr() {
        STRING *res    = CONST_STRING(INTERP, "[ ");
        const INTVAL n = SELF.get_integer();
        INTVAL  j;

        for (j = 0; j < n; ++j) {
            PMC * const val = SELF.get_pmc_keyed_int(j);
            res = string_append(INTERP, res, VTABLE_get_repr(INTERP, val));

            if (j < n - 1)
                res = string_append(INTERP, res, CONST_STRING(INTERP, ", "));
        }

        res = string_append(INTERP, res, CONST_STRING(INTERP, " ]"));
        return res;
    }


/*

=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) {
        const INTVAL k = key_integer(INTERP, key);
        return SELF.get_string_keyed_int(k);
    }


/*

=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  * const ret = pmc_new(INTERP, enum_class_Integer);
        const INTVAL val = SELF.get_integer_keyed_int(key);
        VTABLE_set_integer_native(INTERP, ret, val);
        return ret;
    }

/*

=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 = key_integer(INTERP, key);
        return SELF.get_pmc_keyed_int(k);
    }

/*

=item C<void set_integer_native(INTVAL size)>

Resizes the array to C<size> elements.

=cut

*/

    VTABLE void set_integer_native(INTVAL size) {
        if (PMC_int_val(SELF) || size < 1)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "FixedIntegerArray: Can't resize!");

        PMC_int_val(SELF) = size;
        mem_realloc_n_typed(PMC_data(SELF), size, INTVAL);
        PObj_active_destroy_SET(SELF);
    }

/*

=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) {
        INTVAL *data;

        if (key < 0 || key >= PMC_int_val(SELF))
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "FixedIntegerArray: index out of bounds!");

        data      = (INTVAL *)PMC_data(SELF);
        data[key] = value;
    }

/*

=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) {
        const INTVAL k = key_integer(INTERP, key);
        SELF.set_integer_keyed_int(k, value);
    }

/*

=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) {
        SELF.set_integer_keyed_int(key, (INTVAL)value);
    }

/*

=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 = key_integer(INTERP, key);
        SELF.set_number_keyed_int(k, 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) {
        INTVAL tempInt;
        PMC   * const tempPMC = temporary_pmc_new(INTERP, enum_class_Integer);
        VTABLE_set_string_native(INTERP, tempPMC, value);
        tempInt        = VTABLE_get_integer(INTERP, tempPMC);
        temporary_pmc_free(interp, tempPMC);
        SELF.set_integer_keyed_int(key, tempInt);
    }

/*

=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) {
        const INTVAL k = key_integer(INTERP, key);
        SELF.set_string_keyed_int(k, value);
    }

/*

=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) {
        const INTVAL tempInt = VTABLE_get_integer(INTERP, src);
        SELF.set_integer_keyed_int(key, tempInt);
    }

/*

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

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

=cut

*/

    VTABLE void set_pmc_keyed(PMC *key, PMC *value) {
        const INTVAL k = key_integer(INTERP, key);
        SELF.set_pmc_keyed_int(k, value);
    }

    METHOD sort(PMC *cmp_func) {
        UINTVAL n = (UINTVAL)PMC_int_val(SELF);

        if (n > 1)
            Parrot_quicksort(interp, PMC_data_typed(SELF, void **), n, cmp_func);
    }

/*

=item C<void visit(visit_info *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(visit_info *info)>

Used to archive the array.

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

Used to unarchive the array.

=cut

*/

    VTABLE void visit(visit_info *info) {
        SUPER(info);
    }

    VTABLE void freeze(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        INTVAL   *ar;
        INTVAL    i, n;

        SUPER(info);

        n  = SELF.get_integer();
        VTABLE_push_integer(INTERP, io, n);
        ar = (INTVAL *)PMC_data(SELF);

        for (i = 0; i < n; ++i)
            VTABLE_push_integer(INTERP, io, ar[i]);
    }

    VTABLE void thaw(visit_info *info) {

        if (info->extra_flags == EXTRA_IS_NULL) {
            IMAGE_IO * const io = info->image_io;
            const INTVAL n = VTABLE_shift_integer(INTERP, io);

            PMC_int_val(SELF) = 0;
            PMC_data(SELF)    = NULL;

            if (n) {
                INTVAL  i;
                INTVAL *ar;

                SELF.set_integer_native(n);
                ar = (INTVAL *)PMC_data(SELF);

                for (i = 0; i < n; ++i)
                    ar[i] = VTABLE_shift_integer(INTERP, io);
            }
        }
        else
            SUPER(info);
    }
}

/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd17_basic_types.pod>.

=cut

*/

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