/*
Copyright (C) 2001-2008, Parrot Foundation.
$Id: resizableintegerarray.pmc 47534 2010-06-10 13:34:22Z NotFound $

=head1 NAME

src/pmc/resizableintegerarray.pmc - resizable array for integers only

=head1 DESCRIPTION

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

=head2 Functions

=over 4

=cut

*/

/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* HEADERIZER END: static */

pmclass ResizableIntegerArray extends FixedIntegerArray auto_attrs provides array {
    ATTR INTVAL resize_threshold; /* max size before array needs to be resized */

/*

=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 *int_array;
        INTVAL  size;

        if (key < 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "ResizableIntegerArray: index out of bounds!");

        GET_ATTR_size(INTERP, SELF, size);

        if (key >= size)
            return 0;

        GET_ATTR_int_array(INTERP, SELF, int_array);
        return int_array[key];
    }

/*

=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 *int_array;

        if (key < 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "ResizableIntegerArray: index out of bounds!");

        if (key >= SELF.get_integer())
            SELF.set_integer_native(key + 1);

        GET_ATTR_int_array(INTERP, SELF, int_array);
        int_array[key] = value;
    }

/*

=item C<void set_integer_native(INTVAL size)>

Resizes the array to C<size> elements.

=cut

*/

    VTABLE void set_integer_native(INTVAL size) {
        INTVAL *int_array;
        INTVAL  resize_threshold;

        if (size < 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    "ResizableIntegerArray: Can't resize!");

        GET_ATTR_int_array(INTERP, SELF, int_array);
        GET_ATTR_resize_threshold(INTERP, SELF, resize_threshold);
        if (!int_array) {
            /* empty - used fixed routine */
            if (size < 8) {
                SUPER(8);
                SET_ATTR_size(INTERP, SELF, size);
                SET_ATTR_resize_threshold(INTERP, SELF, 8);
            }
            else {
                SUPER(size);
                SET_ATTR_resize_threshold(INTERP, SELF, size);
            }
        }
        else if (size <= resize_threshold) {
            /* we could shrink here if necessary */
            SET_ATTR_size(INTERP, SELF, size);
            return;
        }
        else {
            INTVAL  cur = resize_threshold;
            INTVAL *int_array;

            if (cur < 8192)
                cur = size < 2 * cur ? 2 * cur : size;
            else {
                INTVAL needed = size - cur;
                cur          += needed + 4096;
                cur          &= ~0xfff;
            }

            GET_ATTR_int_array(INTERP, SELF, int_array);
            int_array = mem_gc_realloc_n_typed(INTERP, int_array, cur, INTVAL);
            SET_ATTR_int_array(INTERP, SELF, int_array);
            SET_ATTR_size(INTERP, SELF, size);
            SET_ATTR_resize_threshold(INTERP, SELF, cur);
        }
    }

/*

=item C<void push_integer(INTVAL value)>

Extends the array by adding an element of value C<value> to the end
of the array.

=cut

*/

    VTABLE void push_integer(INTVAL value) {
        INTVAL *int_array;
        INTVAL  nextix;
        INTVAL  resize_threshold;

        GET_ATTR_size(INTERP, SELF, nextix);
        GET_ATTR_resize_threshold(INTERP, SELF, resize_threshold);

        /* can't always avoid the resize, but don't duplicate the code */
        if ((nextix && (nextix >= resize_threshold)) || !nextix)
            SELF.set_integer_native(nextix + 1);
        else
            SET_ATTR_size(INTERP, SELF, nextix + 1);

        /* fetch the array only after resize check; realloc may move it */
        GET_ATTR_int_array(INTERP, SELF, int_array);
        int_array[nextix] = value;
    }

/*

=item C<INTVAL pop_integer()>

Removes and returns the last element in the array.

=cut

*/

    VTABLE INTVAL pop_integer() {
        INTVAL value;
        INTVAL size = SELF.get_integer();

        if (size == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    "ResizableIntegerArray: Can't pop from an empty array!");

        value = SELF.get_integer_keyed_int(size-1);
        SELF.set_integer_native(size - 1);
        return value;
    }
/*

=item C<INTVAL shift_integer()>

Removes and returns an item from the start of the array.

=cut

*/

    VTABLE INTVAL shift_integer() {
        INTVAL value, *int_array;
        INTVAL size = SELF.get_integer();

        if (size == 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    "ResizableIntegerArray: Can't shift from an empty array!");

        GET_ATTR_int_array(INTERP, SELF, int_array);
        value = int_array[0];

        --size;
        mem_sys_memmove(int_array, int_array + 1, size * sizeof (INTVAL));
        SELF.set_integer_native(size);
        return value;
    }

/*

=item C<void unshift_integer(INTVAL value)>

Add an integer to the start of the array.

=cut

*/

    VTABLE void unshift_integer(INTVAL value) {
        INTVAL *int_array;
        INTVAL size = SELF.get_integer();

        SELF.set_integer_native(size + 1);
        GET_ATTR_int_array(INTERP, SELF, int_array);
        mem_sys_memmove(int_array + 1, int_array, size * sizeof (INTVAL));
        int_array[0] = value;
    }

/*

=item C<void delete_keyed_int(INTVAL key)>

Removes the element at C<key>.

=cut

*/

    VTABLE void delete_keyed_int(INTVAL key) {
        INTVAL size = SELF.get_integer();

        if (size < 1 || key >= size) {
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "ResizableIntegerArray: index out of bounds!");
        }
        else {
            INTVAL *int_array;
            GET_ATTR_int_array(INTERP, SELF, int_array);
            --size;
            mem_sys_memmove(int_array + key, int_array + key + 1, (size - key) * sizeof (INTVAL));
            SELF.set_integer_native(size);
        }
    }

/*

=item C<PMC *clone()>

Creates and returns a copy of the array.

=cut

*/

    VTABLE PMC *clone() {
        INTVAL  size;
        PMC    *copy = SUPER();

        /* copy trimmed extra space */
        GET_ATTR_size(INTERP, SELF, size);
        SET_ATTR_resize_threshold(INTERP, copy, size);

        return copy;
    }

/*

=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 freeze(PMC *info) {
        INTVAL   *int_array;
        INTVAL    i, n, rt;

        SUPER(info);

        n  = SELF.get_integer();
        GET_ATTR_resize_threshold(INTERP, SELF, rt);
        VTABLE_push_integer(INTERP, info, n);
        VTABLE_push_integer(INTERP, info, rt);

        GET_ATTR_int_array(INTERP, SELF, int_array);

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

    VTABLE void thaw(PMC *info) {
        INTVAL n, rt;

        SUPER(info);

        n = VTABLE_shift_integer(INTERP, info);
        rt = VTABLE_shift_integer(INTERP, info);

        SET_ATTR_size(INTERP, SELF, 0);
        SET_ATTR_resize_threshold(INTERP, SELF, rt);
        SET_ATTR_int_array(INTERP, SELF, NULL);

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

            SELF.set_integer_native(n);
            GET_ATTR_int_array(INTERP, SELF, int_array);

            for (i = 0; i < n; ++i)
                int_array[i] = VTABLE_shift_integer(INTERP, info);
        }
    }


}
/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd17_basic_types.pod>.

=head1 HISTORY

Initial version                  - Matt Fowles 2004-06-11
Changed allocator to double size - Matt Fowles 2004-06-15
Added push_integer               - Bernhard Schmalhofer 2004-10-17
moved available size to int_val2 - Matt Fowles 2005-07-22

=cut

*/

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