/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/resizableintegerarray.pmc 34756 2008-12-31T21:54:57.793837Z particle  $

=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

*/

#include "parrot/parrot.h"


pmclass ResizableIntegerArray extends FixedIntegerArray need_ext provides array {

/*

=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)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                "ResizableIntegerArray: index out of bounds!");

        if (key >= SELF.get_integer())
            return 0;

        data = (INTVAL *)PMC_data(SELF);
        return data[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 *data;

        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);

        data      = (INTVAL *)PMC_data(SELF);
        data[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) {
        if (size < 0)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                    "ResizableIntegerArray: Can't resize!");

        if (!PMC_data(SELF)) {
            /* empty - used fixed routine */
            if (size < 8) {
                SUPER(8);
                PMC_int_val(SELF)  = size;
                PMC_int_val2(SELF) = 8;
            }
            else {
                SUPER(size);
                PMC_int_val2(SELF) = size;
            }
        }
        else if (size <= PMC_int_val2(SELF)) {
            /* we could shrink here if necessary */
            PMC_int_val(SELF) = size;
            return;
        }
        else {
            INTVAL cur = PMC_int_val2(SELF);

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

            PMC_data(SELF) = mem_sys_realloc(PMC_data(SELF),
                    cur * sizeof (INTVAL));
            PMC_int_val2(SELF) = cur;
            PMC_int_val(SELF)  = size;
        }
    }

/*

=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 nextix = SELF.get_integer();
        SELF.set_integer_keyed_int(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, *data;
        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!");

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

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

/*

=item C<void unshift_integer(INTVAL value)>

Add and integer to the start of the array.

=cut

*/

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

        SELF.set_integer_native(size + 1);
        data    = (INTVAL *)PMC_data(SELF);
        mem_sys_memmove(data + 1, data, size * sizeof (INTVAL));
        data[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 *data = (INTVAL *)PMC_data(SELF);
            --size;
            mem_sys_memmove(data + key, data + 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() {
        PMC *copy          = SUPER();

        /* copy trimmed extra space */
        PMC_int_val2(copy) = PMC_int_val(SELF);

        return copy;
    }

}

/*

=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:
 */
