/*
Copyright (C) 2008-2009, The Perl Foundation.
$Id: /mirror/trunk/languages/pipp/src/pmc/phparray.pmc 34918 2009-01-03T15:43:35.483185Z bernhard  $

=head1 NAME

src/pmc/phparray.pmc - PHP Array

=head1 DESCRIPTION

Implementation of the Array datatype.

=head2 VTABLE Functions

=over 4

=cut

*/

#include "php_private.h"
#include "src/pmc/pipp_hash.h"

pmclass PhpArray
    dynpmc
    need_ext
    hll      PHP
    group    php_group
    provides array
    provides hash
    maps     Array
    maps     Hash {

    ATTR struct pipp_hash_table *hash_table;

/*

=item C<void init()>

Intialize this PHPArray.

=cut

*/
    VTABLE void init() {

        Parrot_PhpArray_attributes *arr =
            mem_allocate_zeroed_typed(Parrot_PhpArray_attributes);

        arr->hash_table = pipp_hash_create(INTERP, 4);
        PMC_data(SELF)  = arr;

        PObj_custom_mark_destroy_SETALL(SELF);
    }
/*

=item C<void init_pmc(PMC *p)>

Initialize this PHPArray with the passed-in PMC C<p>.  If C<p> is a PHPArray,
its contents are copied directly.  If C<p> is a PMC that provides a hash or
array interface, its contents are copied via the appropriate VTABLE functions.
In any other case, an exception will be thrown.

=cut

*/
    VTABLE void init_pmc(PMC *p) {
        if (PMC_IS_NULL(p))
            SELF.init();
        else
            SELF.assign_pmc(p);
    }

/*

=item C<void destroy()>

Non-recursively free all memory used by this PHPArray.

=cut

*/
    VTABLE void destroy() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        pipp_hash_destroy(INTERP, ht);
        mem_sys_free(PMC_data(SELF));
    }

/*

=item C<void mark()>

Recursively mark this PHPArray and all its contents as live.

=cut

*/
    VTABLE void mark() {

        PippBucket    *b;
        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        for (b = ht->tableHead; b != NULL; b = b->tableNext) {
            pobject_lives(INTERP, (PObj *)b->key);
            pobject_lives(INTERP, (PObj *)b->value);
        }
    }

/*

=item C<INTVAL elements()>

Return the number of elements in this PHPArray.

=cut

*/
    VTABLE INTVAL elements() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return ht->elementCount;
    }

/*

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

Store C<i_val> in this PHPArray, indexed by the Key PMC C<key>.

=cut

*/
    VTABLE void set_integer_keyed(PMC *key, INTVAL i_val) {

        PMC *box;

        /* peel off the next key */
        PMC    *next_key = key_next(INTERP, key);
        STRING *s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to set_integer_keyed_str */
            SELF.set_integer_keyed_str(s_key, i_val);

            return;
        }

        /* get the box where the thing will be stored */
        box = SELF.get_pmc_keyed_str(s_key);

        /* autovivify if needed */
        if (PMC_IS_NULL(box)) {
            box = pmc_new(INTERP, PMC_type(SELF));
            SELF.set_pmc_keyed_str(s_key, box);
        }

        /* recurse into that box */
        VTABLE_set_integer_keyed(INTERP, box, next_key, i_val);
    }

/*

=item C<void set_integer_keyed_str (STRING *s_key, INTVAL i_val)>

Store C<i_val> in this PHPArray, indexed by the STRING C<s_key>.

=cut

*/
    VTABLE void set_integer_keyed_str(STRING *s_key, INTVAL i_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Integer);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_integer_native(INTERP, p_val, i_val);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

=item C<void set_integer_keyed_int (INTVAL i_key, INTVAL i_val)>

Store C<i_val> in this PHPArray, indexed by the INTVAL C<i_key>.

=cut

*/
    VTABLE void set_string_keyed_int(INTVAL i_key, INTVAL i_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Integer);
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_integer_native(INTERP, p_val, i_val);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

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

Store C<val> in this PHPArray, indexed by the Key PMC C<key>.

=cut

*/
    VTABLE void set_pmc_keyed(PMC *key, PMC *p_val) {

        PMC *box;

        /* peel off the next key */
        PMC    *next_key = key_next(INTERP, key);
        STRING *s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to set_pmc_keyed_str */
            SELF.set_pmc_keyed_str(s_key, p_val);

            return;
        }

        /* get the box where the thing will be stored */
        box = SELF.get_pmc_keyed_str(s_key);

        /* autovivify if needed */
        if (PMC_IS_NULL(box)) {
            box = pmc_new(INTERP, PMC_type(SELF));
            SELF.set_pmc_keyed_str(s_key, box);
        }

        /* recurse into that box */
        VTABLE_set_pmc_keyed(INTERP, box, next_key, p_val);
    }

/*

=item C<void set_pmc_keyed_str(STRING *s_key, PMC *val)>

Store C<val> in this PHPArray, indexed by the STRING C<s_key>.

=cut

*/
    VTABLE void set_pmc_keyed_str(STRING *s_key, PMC *p_val) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

=item C<void set_pmc_keyed_int(INTVAL i_key, PMC *val)>

Store C<val> in this PHPArray, indexed by the INTVAL C<i_key>.

=cut

*/
    VTABLE void set_pmc_keyed_int(INTVAL i_key, PMC *p_val) {

        PippHashTable *ht;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

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

Store C<s_val> in this PHPArray, indexed by the Key PMC C<key>.

=cut

*/
    VTABLE void set_string_keyed(PMC *key, STRING *s_val) {

        PMC *box;

        /* peel off the next key */
        PMC    *next_key = key_next(INTERP, key);
        STRING *s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to set_string_keyed_str */
            SELF.set_string_keyed_str(s_key, s_val);

            return;
        }

        /* get the box where the thing will be stored */
        box = SELF.get_pmc_keyed_str(s_key);

        /* autovivify if needed */
        if (PMC_IS_NULL(box)) {
            box = pmc_new(INTERP, PMC_type(SELF));
            SELF.set_pmc_keyed_str(s_key, box);
        }

        /* recurse into that box */
        VTABLE_set_string_keyed(INTERP, box, next_key, s_val);
    }

/*

=item C<void set_string_keyed_str(STRING *s_key, STRING *s_val)>

Store C<s_val> in this PHPArray, indexed by the STRING C<s_key>.

=cut

*/
    VTABLE void set_string_keyed_str(STRING *s_key, STRING *s_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_String);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_string_native(INTERP, p_val, s_val);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

=item C<void set_string_keyed_int(INTVAL i_key, STRING *s_val)>

Store C<s_val> in this PHPArray, indexed by the INTVAL C<i_key>.

=cut

*/
    VTABLE void set_string_keyed_int(INTVAL i_key, STRING *s_val) {

        PippHashTable *ht;
        STRING        *s_key = string_from_int(INTERP, i_key);
        PMC           *p_val = pmc_new(INTERP, enum_class_String);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_string_native(INTERP, p_val, s_val);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

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

Store C<f_val> in this PHPArray, indexed by the Key PMC C<key>.

=cut

*/
    VTABLE void set_number_keyed(PMC *key, FLOATVAL f_val) {

        PMC *box;

        /* peel off the next key */
        PMC    *next_key = key_next(INTERP, key);
        STRING *s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to set_number_keyed_str */
            SELF.set_number_keyed_str(s_key, f_val);

            return;
        }

        /* get the box where the thing will be stored */
        box = SELF.get_pmc_keyed_str(s_key);

        /* autovivify if needed */
        if (PMC_IS_NULL(box)) {
            box = pmc_new(INTERP, PMC_type(SELF));
            SELF.set_pmc_keyed_str(s_key, box);
        }

        /* recurse into that box */
        VTABLE_set_number_keyed(INTERP, box, next_key, f_val);
    }

/*

=item C<void set_number_keyed_str(STRING *s_key, FLOATVAL f_val)>

Store C<f_val> in this PHPArray, indexed by the STRING C<s_key>.

=cut

*/
    VTABLE void set_number_keyed_str(STRING *s_key, FLOATVAL f_val) {


        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Float);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_number_native(INTERP, p_val, f_val);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

=item C<void set_number_keyed_int(INTVAL i_key, FLOATVAL f_val)>

Store C<f_val> in this PHPArray, indexed by the INTVAL C<i_key>.

=cut

*/
    VTABLE void set_number_keyed_int(INTVAL i_key, FLOATVAL f_val) {

        PippHashTable *ht;
        STRING        *s_key = string_from_int(INTERP, i_key);
        PMC           *p_val = pmc_new(INTERP, enum_class_Float);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_number_native(INTERP, p_val, f_val);
        pipp_hash_put(INTERP, ht, s_key, p_val);
    }

/*

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

Retrieve the value from this PHPArray which is associated with the Key PMC
C<key>.

=cut

*/
    VTABLE INTVAL get_integer_keyed(PMC *key) {

        PMC    *next_key, *box;
        STRING *s_key;

        if (PObj_get_FLAGS(key) & KEY_pipp_iterator_FLAG)
            Parrot_ex_throw_from_c_args(INTERP, NULL, 1,
                    "can't use an int as an Iterator key");

        /* peel off the next key */
        next_key = key_next(INTERP, key);
        s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to get_integer_keyed_str */

            return SELF.get_integer_keyed_str(s_key);
        }

        /* get the box where the thing should be stored */
        box = SELF.get_pmc_keyed_str(s_key);
        if (PMC_IS_NULL(box))
            return (INTVAL)0;

        /* recurse into that box */
        return VTABLE_get_integer_keyed(INTERP, box, next_key);
    }

/*

=item C<INTVAL get_integer_keyed_str(STRING *s_key)>

Retrieve the value from this PHPArray which is associated with the STRING
C<s_key>.

=cut

*/
    VTABLE INTVAL get_integer_keyed_str(STRING *s_key) {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);
        p_val = pipp_hash_get(INTERP, ht, s_key);

        return VTABLE_get_integer(INTERP, p_val);
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL i_key)>

Retrieve the value from this PHPArray which is associated with the INTVAL
C<i_key>.

=cut

*/
    VTABLE INTVAL get_integer_keyed_int(INTVAL i_key) {

        PippHashTable *ht;
        PMC           *p_val;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        p_val = pipp_hash_get(INTERP, ht, s_key);

        return VTABLE_get_integer(INTERP, p_val);
    }

/*

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

Retrieve the value from this PHPArray which is associated with the Key PMC
C<key>.

=cut

*/
    VTABLE PMC *get_pmc_keyed(PMC *key) {

        PMC           *next_key, *box;
        STRING        *s_key;

        /* deal with Iterator stuff */
        if (PObj_get_FLAGS(key) & KEY_pipp_iterator_FLAG) {

            PippHashTable *ht;

            GET_ATTR_hash_table(INTERP, SELF, ht);

            s_key               = ht->internalPointer->key;
            ht->internalPointer = ht->internalPointer->tableNext;
            next_key            = pmc_new(INTERP, enum_class_String);
            VTABLE_set_string_native(INTERP, next_key, s_key);

            return next_key;
        }

        /* peel off the next key */
        next_key = key_next(INTERP, key);
        s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to get_pmc_keyed_str */
            return SELF.get_pmc_keyed_str(s_key);
        }

        /* get the box where the thing should be stored */
        box = SELF.get_pmc_keyed_str(s_key);
        if (PMC_IS_NULL(box))
            return PMCNULL;

        /* recurse into that box */
        return VTABLE_get_pmc_keyed(INTERP, box, next_key);
    }

/*

=item C<PMC *get_pmc_keyed_str(STRING *s_key)>

Retrieve the value from this PHPArray which is associated with the STRING
C<s_key>.

=cut

*/
    VTABLE PMC *get_pmc_keyed_str(STRING *s_key) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return pipp_hash_get(INTERP, ht, s_key);
    }

/*

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

Retrieve the value from this PHPArray which is associated with the INTVAL
C<i_key>.

=cut

*/
    VTABLE PMC *get_pmc_keyed_int(INTVAL i_key) {

        PippHashTable *ht;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return pipp_hash_get(INTERP, ht, s_key);
    }

/*

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

Retrieve the value from this PHPArray which is associated with the Key PMC
C<key>.

=cut

*/
    VTABLE STRING *get_string_keyed(PMC *key) {

        PMC           *next_key, *box;
        STRING        *s_key;

        /* deal with Iterator stuff */
        if (PObj_get_FLAGS(key) & KEY_pipp_iterator_FLAG) {

            PippHashTable *ht;

            GET_ATTR_hash_table(INTERP, SELF, ht);

            s_key               = ht->internalPointer->key;
            ht->internalPointer = ht->internalPointer->tableNext;

            return s_key;
        }

        /* peel off the next key */
        next_key = key_next(INTERP, key);
        s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to get_string_keyed_str */
            return SELF.get_string_keyed_str(s_key);
        }

        /* get the box where the thing should be stored */
        box = SELF.get_pmc_keyed_str(s_key);
        if (PMC_IS_NULL(box))
            return (INTVAL)0;

        /* recurse into that box */
        return VTABLE_get_string_keyed(INTERP, box, next_key);
    }

/*

=item C<STRING *get_string_keyed_str(STRING *s_key)>

Retrieve the value from this PHPArray which is associated with the STRING
C<s_key>.

=cut

*/
    VTABLE STRING *get_string_keyed_str(STRING *s_key) {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_get(INTERP, ht, s_key);

        return VTABLE_get_string(INTERP, p_val);
    }

/*

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

Retrieve the value from this PHPArray which is associated with the INTVAL
C<i_key>.

=cut

*/
    VTABLE STRING *get_string_keyed_int(INTVAL i_key) {

        PippHashTable *ht;
        PMC           *p_val;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_get(INTERP, ht, s_key);

        return VTABLE_get_string(INTERP, p_val);
    }

/*

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

Retrieve the value from this PHPArray which is associated with the Key PMC
C<key>.

=cut

*/
    VTABLE FLOATVAL get_number_keyed(PMC *key) {

        PMC    *box, *next_key;
        STRING *s_key;

        if (PObj_get_FLAGS(key) & KEY_pipp_iterator_FLAG)
            Parrot_ex_throw_from_c_args(INTERP, NULL, 1,
                    "You can't use a float as an Iterator key.");

        /* peel off the next key */
        next_key = key_next(INTERP, key);
        s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to get_number_keyed_str */
            return SELF.get_number_keyed_str(s_key);
        }

        /* get the box where the thing should be stored */
        box = SELF.get_pmc_keyed_str(s_key);
        if (PMC_IS_NULL(box))
            return (INTVAL)0;

        /* recurse into that box */
        return VTABLE_get_number_keyed(INTERP, box, next_key);
    }

/*

=item C<FLOATVAL get_number_keyed_str(STRING *s_key)>

Retrieve the value from this PHPArray which is associated with the STRING
C<s_key>.

=cut

*/
    VTABLE FLOATVAL get_number_keyed_str(STRING *s_key) {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_get(INTERP, ht, s_key);

        return VTABLE_get_number(INTERP, p_val);
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL i_key)>

Retrieve the value from this PHPArray which is associated with the INTVAL
C<i_key>.

=cut

*/
    VTABLE FLOATVAL get_number_keyed_int(INTVAL i_key) {

        PippHashTable *ht;
        PMC           *p_val;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_get(INTERP, ht, s_key);

        return VTABLE_get_number(INTERP, p_val);
    }

/*

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

Return non-zero if an element keyed by C<key> exists.

=cut

*/
    VTABLE INTVAL exists_keyed(PMC *key) {

        PMC    *box;

        /* peel off the next key */
        PMC    *next_key = key_next(INTERP, key);
        STRING *s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to exists_keyed_str */
            return SELF.exists_keyed_str(s_key);
        }

        /* get the box where the thing should be stored */
        box = SELF.get_pmc_keyed_str(s_key);
        if (PMC_IS_NULL(box))
            return (INTVAL)0;

        /* recurse into that box */
        return VTABLE_exists_keyed(INTERP, box, next_key);
    }

/*

=item C<INTVAL exists_keyed_str(STRING *s_key)>

Return non-zero if an element keyed by C<s_key> exists.

=cut

*/
    VTABLE INTVAL exists_keyed_str(STRING *s_key) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return pipp_hash_find(INTERP, ht, s_key);
    }

/*

=item C<INTVAL exists_keyed_int(INTVAL i_key)>

Return non-zero if an element keyed by C<i_key> exists.

=cut

*/
    VTABLE INTVAL exists_keyed_int(INTVAL i_key) {

        PippHashTable *ht;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return pipp_hash_find(INTERP, ht, s_key);
    }

/*

=item C<void delete_keyed(PMC *key)>

If an element in this PHPArray is keyed by C<key>, delete it.  Otherwise do
nothing.

=cut

*/
    VTABLE void delete_keyed(PMC *key) {

        PMC    *box;

        /* peel off the next key */
        PMC    *next_key = key_next(INTERP, key);
        STRING *s_key    = VTABLE_get_string(INTERP, key);

        /* if there isn't one (i.e. key is null) */
        if (next_key == NULL) {
            /* dispatch to delete_keyed_str */
            SELF.delete_keyed_str(s_key);
            return;
        }

        /* get the box where the thing should be stored */
        box = SELF.get_pmc_keyed_str(s_key);
        if (PMC_IS_NULL(box))
            return;

        /* recurse into that box */
        VTABLE_delete_keyed(INTERP, box, next_key);

        return;
    }

/*

=item C<void delete_keyed_str(STRING *s_key)>

If an element in this PHPArray is keyed by C<key>, delete it.  Otherwise do
nothing.

=cut

*/
    VTABLE void delete_keyed_str(STRING *s_key) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        pipp_hash_delete(INTERP, ht, s_key);
    }

/*

=item C<void delete_keyed_int(INTVAL i_key)>

If an element in this PHPArray is keyed by C<key>, delete it.  Otherwise do
nothing.

=cut

*/
    VTABLE void delete_keyed_int(INTVAL i_key) {

        PippHashTable *ht;
        STRING        *s_key = string_from_int(INTERP, i_key);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        pipp_hash_delete(INTERP, ht, s_key);
    }

/*

=item C<void push_integer(INTVAL i)>

Add an integer with the value C<i> to the end of this PHPArray.

=cut

*/
    VTABLE void push_integer(INTVAL i_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Integer);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        VTABLE_set_integer_native(INTERP, p_val, i_val);
        pipp_hash_push(INTERP, ht, p_val);
    }

/*

=item C<void push_pmc(PMC *p_val)>

Add a PMC with the value C<p_val> to the end of this PHPArray.

=cut

*/
    VTABLE void push_pmc(PMC *p_val) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        pipp_hash_push(INTERP, ht, p_val);
    }

/*

=item C<void push_string(STRING *s_val)>

Add a STRING with the value C<s_val> to the end of this PHPArray.

=cut

*/
    VTABLE void push_string(STRING *s_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_String);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        VTABLE_set_string_native(INTERP, p_val, s_val);
        pipp_hash_push(INTERP, ht, p_val);
    }

/*

=item C<void push_float(FLOATVAL f_val)>

Add a FLOATVAL with the value C<f_val> to the end of this PHPArray.

=cut

*/
    VTABLE void push_float(FLOATVAL f_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Float);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        VTABLE_set_number_native(INTERP, p_val, f_val);
        pipp_hash_push(INTERP, ht, p_val);
    }

/*

=item C<INTVAL pop_integer()>

Delete the element at the end of this PHPArray, returning its value as an INTVAL.

=cut

*/
    VTABLE INTVAL pop_integer() {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_pop(INTERP, ht);

        return VTABLE_get_integer(INTERP, p_val);
    }

/*

=item C<PMC* pop_pmc()>

Delete the element at the end of this PHPArray, returning its value as a PMC.

=cut

*/
    VTABLE PMC *pop_pmc() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return pipp_hash_pop(INTERP, ht);
    }

/*

=item C<STRING* pop_string()>

Delete the element at the end of this PHPArray, returning its value as a STRING.

=cut

*/
    VTABLE STRING *pop_string() {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_pop(INTERP, ht);

        return VTABLE_get_string(INTERP, p_val);
    }

/*

=item C<FLOATVAL pop_float()>

Delete the element at the end of this PHPArray, returning its value as a FLOATVAL.

=cut

*/
    VTABLE FLOATVAL pop_float() {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_pop(INTERP, ht);

        return VTABLE_get_number(INTERP, p_val);
    }

/*

=item C<void unshift_integer(INTVAL i_val)>

Add an integer with the value C<i_val> to the beginning of this PHPArray.
C<i_val> will be keyed by the INTVAL 0 and all other numerically indexed
elements will be renumbered according to insertion order.

=cut

*/
    VTABLE void unshift_integer(INTVAL i_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Integer);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_integer_native(INTERP, p_val, i_val);

        pipp_hash_unshift(INTERP, ht, p_val);
    }

/*

=item C<void unshift_pmc(PMC *p_val)>

Add a PMC with the value C<p_val> to the beginning of this PHPArray.  C<p_val>
will be keyed by the INTVAL 0 and all other numerically indexed elements will
be renumbered according to insertion order.

=cut

*/
    VTABLE void unshift_pmc(PMC *p_val) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        pipp_hash_unshift(INTERP, ht, p_val);
    }

/*

=item C<void unshift_string(STRING *s_val)>

Add a STRING with the value C<s_val> to the beginning of this PHPArray.
C<s_val> will be keyed by the INTVAL 0 and all other numerically indexed
elements will be renumbered according to insertion order.

=cut

*/
    VTABLE void unshift_string(STRING *s_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_String);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_string_native(INTERP, p_val, s_val);

        pipp_hash_unshift(INTERP, ht, p_val);
    }

/*

=item C<void unshift_float(FLOATVAL f_val)>

Add a FLOATVAL with the value C<f_val> to the beginning of this PHPArray.
C<f_val> will be keyed by the INTVAL 0 and all other numerically indexed
elements will be renumbered according to insertion order.

=cut

*/
    VTABLE void unshift_float(FLOATVAL f_val) {

        PippHashTable *ht;
        PMC           *p_val = pmc_new(INTERP, enum_class_Float);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        VTABLE_set_number_native(INTERP, p_val, f_val);

        pipp_hash_unshift(INTERP, ht, p_val);
    }

/*

=item C<INTVAL shift_integer()>

Delete the element at the beginning of this PHPArray, returning its value as an INTVAL.

=cut

*/
    VTABLE INTVAL shift_integer() {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_shift(INTERP, ht);

        return VTABLE_get_integer(INTERP, p_val);
    }

/*

=item C<PMC* shift_pmc()>

Delete the element at the beginning of this PHPArray, returning its value as a PMC.

=cut

*/
    VTABLE PMC *shift_pmc() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return pipp_hash_shift(INTERP, ht);
    }

/*

=item C<STRING* shift_string()>

Delete the element at the beginning of this PHPArray, returning its value as a STRING.

=cut

*/
    VTABLE STRING *shift_string() {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_shift(INTERP, ht);

        return VTABLE_get_string(INTERP, p_val);
    }

/*

=item C<FLOATVAL shift_float()>

Delete the element at the beginning of this PHPArray, returning its value as a FLOATVAL.

=cut

*/
    VTABLE FLOATVAL shift_float() {

        PippHashTable *ht;
        PMC           *p_val;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        p_val = pipp_hash_shift(INTERP, ht);

        return VTABLE_get_number(INTERP, p_val);
    }

/*

=item C<STRING* name()>

Return the string 'array'.

=cut

*/
    VTABLE STRING* name() {
        return const_string(INTERP, "array");
    }

/*

=item C<STRING* get_string()>

Return the string 'Array'.

=cut

*/
    VTABLE STRING* get_string() {
        return const_string(INTERP, "Array");
    }

/*

=item C<INTVAL get_bool()>

Return 1 if this PHPArray has 1 or more elements.

=cut

*/
    VTABLE INTVAL get_bool() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        return ht->elementCount > 0;
    }


/*

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

Freeze or thaw the contents of this PHPArray.

=cut

*/
    VTABLE void visit(visit_info *info) {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);
        info->container = SELF;
        pipp_hash_visit(INTERP, ht, info);
        SUPER(info);
    }

/*

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

After C<visit()> has been called during the freeze process, this VTABLE function
is used to add additional information about the PHPArray to the visit_info
struct.

=cut

*/
    VTABLE void freeze(visit_info *info) {

        PippHashTable *ht;
        PippBucket    *bkt;
        IMAGE_IO      *io;
        UINTVAL        ip_pos;

        SUPER(info);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        io = info->image_io;
        VTABLE_push_integer(INTERP, io, ht->nextIndex);
        VTABLE_push_integer(INTERP, io, ht->capacity);
        VTABLE_push_integer(INTERP, io, ht->elementCount);

        if (ht->internalPointer == NULL) {
            VTABLE_push_integer(INTERP, io, -1);
        }
        else {
            ip_pos = 0;
            for (bkt = ht->tableHead; bkt != ht->internalPointer; bkt = bkt->tableNext)
                ip_pos++;
            VTABLE_push_integer(INTERP, io, ip_pos);
        }
    }

/*

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

Before C<visit()> is called during the thaw process, this VTABLE function is used
to initialize the empty PHPArray.

=cut

*/
    VTABLE void thaw(visit_info *info) {

        PippHashTable *ht;
        IMAGE_IO      *io;
        INTVAL         next_index, ip_pos;
        UINTVAL        capacity, element_count;

        SUPER(info);
        io = info->image_io;
        next_index    = VTABLE_shift_integer(INTERP, io);
        capacity      = VTABLE_shift_integer(INTERP, io);
        element_count = VTABLE_shift_integer(INTERP, io);
        ip_pos        = VTABLE_shift_integer(INTERP, io);

        GET_ATTR_hash_table(INTERP, SELF, ht);
        pipp_hash_resize(INTERP, ht, capacity);

        ht->elementCount  = element_count;
        ht->nextIndex     = next_index;

        /* used during thawfinish */
        info->extra       = (void *)ip_pos;
    }

/*

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

After C<visit()> is called during the thaw process, this VTABLE function is
used to make any changes to the newly thawed PHPArray which could not be made
during the thaw process.

=cut

*/
    VTABLE void thawfinish(visit_info *info) {

        PippHashTable *ht;
        INTVAL         ip_pos = (INTVAL)info->extra;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        if (ip_pos == -1) {
            ht->internalPointer = NULL;
        }
        else {

            PMC *key;

            for (ht->internalPointer = ht->tableHead; ip_pos > 0; ip_pos--) {
                ht->internalPointer = ht->internalPointer->tableNext;
            }
        }
    }

/*

=item C<PMC *get_iter()>

Return an Iterator over this PHPArray.  When using an Iterator over PHPArrays,
it is a very good idea to use the C<iter> opcode as follows:

 $P0 = new 'PhpArray'
 $P1 = iter $P0

This ensures that the Iterator and the PHPArray's internal pointer stay in
sync.  Using an Iterator and a PHPArray any other way may lead to mysterious
failures.

=cut

*/
    VTABLE PMC *get_iter() {

        PippHashTable *ht;
        PippBucket    *bkt;
        PMC           *iter  = pmc_new_init(INTERP, enum_class_Iterator, SELF);
        PMC           *key   = (PMC *)PMC_struct_val(iter);

        GET_ATTR_hash_table(INTERP, SELF, ht);

        ht->iter             = iter;
        PMC_int_val(key)     = ht->elementCount ? 0 : -1;

        /* set the key's flags so get_x_keyed() knows when it's being called
         * from an Iterator. */
        PObj_get_FLAGS(key) |= KEY_pipp_iterator_FLAG;

        /* Get the Iterator and ht->internalPointer in sync. */
        bkt = ht->tableHead;

        while (bkt != ht->internalPointer) {
            VTABLE_nextkey_keyed(INTERP, key, SELF, ITERATE_GET_NEXT);
            bkt = bkt->tableNext;
        }

        return iter;
    }

/*

=item C<PMC* clone()>

Return a deep clone of this PHPArray.

=cut

*/
    VTABLE PMC *clone() {

        PMC           *cloned_value;
        STRING        *cloned_key;
        PippBucket    *bkt;
        PippHashTable *orig_ht, *cloned_ht;
        PMC           *cloned_pmc = pmc_new(INTERP, PMC_type(SELF));

        GET_ATTR_hash_table(INTERP, SELF, orig_ht);
        GET_ATTR_hash_table(INTERP, cloned_pmc, cloned_ht);
        pipp_hash_create(INTERP, orig_ht->capacity);

        for (bkt = orig_ht->tableHead; bkt != NULL; bkt = bkt->tableNext) {
            cloned_key   = string_copy(INTERP, bkt->key);
            cloned_value = VTABLE_clone(INTERP, bkt->value);
            pipp_hash_put(INTERP, cloned_ht, cloned_key, cloned_value);
        }
        return cloned_pmc;
    }


/*

=item C<void assign_pmc(PMC *src)>

If C<src> is an aggregate PMC (i.e. one that does hash and/or array), use an
Iterator to copy each key/value pair into this PHPArray.  If C<src> is a
PHPArray, just make a clone.

=cut

*/
    VTABLE void assign_pmc(PMC *src) {

        PippHashTable *my_ht;

        GET_ATTR_hash_table(INTERP, SELF, my_ht);

        if (PMC_type(src) == PMC_type(SELF)) {

            PippBucket    *bkt;
            PippHashTable *src_ht;

            GET_ATTR_hash_table(INTERP, src, src_ht);

            pipp_hash_empty(INTERP, my_ht);
            pipp_hash_resize(INTERP, my_ht, src_ht->elementCount);

            for (bkt = src_ht->tableHead; bkt != NULL; bkt = bkt->tableNext) {
                STRING *key_copy   = string_copy(INTERP,  bkt->key);
                PMC    *value_copy = VTABLE_clone(INTERP, bkt->value);
                pipp_hash_put(INTERP, my_ht, key_copy, value_copy);
            }
        }
        else if (VTABLE_does(INTERP, src, CONST_STRING(INTERP, "array")) ||
                 VTABLE_does(INTERP, src, CONST_STRING(INTERP, "hash"))) {

            PMC *iter = VTABLE_get_iter(INTERP, src);

            pipp_hash_empty(INTERP, my_ht);
            pipp_hash_resize(INTERP, my_ht, VTABLE_elements(INTERP, src));

            while (VTABLE_get_bool(INTERP, iter)) {
                PMC    *key        = VTABLE_shift_pmc(INTERP, iter);
                PMC    *value_copy = VTABLE_get_pmc_keyed(INTERP, src, key);
                STRING *key_copy   = VTABLE_get_string(INTERP, key);

                pipp_hash_put(INTERP, my_ht, key_copy, value_copy);
            }
        }
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL,
                    EXCEPTION_PARROT_USAGE_ERROR,
                    "can't assign a non-aggregate PMC to a PhpArray");
    }
/*

=item C<PMC* add(PMC *src, PMC *dest)>

Copy all key/value pairs in C<SELF> and C<src> into C<dest>.  All values in
C<dest> are deleted.  If the same key is in both C<src> and C<SELF>, the value
from C<SELF> will be used.

=cut

*/
    VTABLE PMC *add(PMC *src, PMC *dest) {

        dest = SELF.clone();
        VTABLE_i_add(INTERP, dest, src);

        return dest;
    }
/*

=item C<PMC* i_add(PMC *src)>

Copy all key/value pairs from C<src> into C<SELF>.  If the same key is in both
C<src> and C<SELF>, the value from C<SELF> will be used.

=cut

*/
    VTABLE void i_add(PMC *src) {

        STRING        *key_copy;
        PMC           *value_copy;
        PippHashTable *my_ht;

        GET_ATTR_hash_table(INTERP, SELF, my_ht);

        if (PMC_type(SELF) == PMC_type(src)) {

            PippBucket    *bkt;
            PippHashTable *src_ht;

            GET_ATTR_hash_table(INTERP, src, src_ht);

            for (bkt = src_ht->tableHead; bkt != NULL; bkt = bkt->tableNext) {
                key_copy   = string_copy(INTERP, bkt->key);
                value_copy = VTABLE_clone(INTERP, bkt->value);
                /* TODO: This isn't especially efficient. */
                if (!pipp_hash_find(INTERP, my_ht, key_copy))
                    pipp_hash_put(INTERP, my_ht, key_copy, value_copy);
            }
        }
        else if (VTABLE_does(INTERP, src, CONST_STRING(INTERP, "array")) ||
                VTABLE_does(INTERP, src, CONST_STRING(INTERP, "hash"))) {

            PMC *key, *iter;

            iter = VTABLE_get_iter(INTERP, src);
            pipp_hash_empty(INTERP, my_ht);
            pipp_hash_resize(INTERP, my_ht, VTABLE_elements(INTERP, src));

            while (VTABLE_get_bool(INTERP, iter)) {
                key        = VTABLE_shift_pmc(INTERP, iter);
                value_copy = VTABLE_get_pmc_keyed(INTERP, src, key);
                key_copy   = VTABLE_get_string(INTERP, key);

                pipp_hash_put(INTERP, my_ht, key_copy, value_copy);
            }
        }
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL,
                    EXCEPTION_PARROT_USAGE_ERROR,
                    "can't add a non-aggregate PMC to a PhpArray");
    }

/*

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

Return true if C<SELF> and C<other> contain the same key/value pairs and have
the same number of elements.  Order is not important.

=cut

*/

    VTABLE INTVAL is_equal(PMC *other) {
        return !Parrot_PhpArray_cmp(INTERP, SELF, other);
    }

/*

=item C<INTVAL is_equal_num(PMC *other)>

Determine equality between a PHPArray and a number.  PHP's documentation says
that an array is always greater than a number, so this function returns 0.
See http://us.php.net/manual/en/language.operators.comparison.php .

=cut

*/

    VTABLE INTVAL is_equal_num(PMC *other) {
        return 0;
    }

/*

=item C<INTVAL is_equal_string(PMC *other)>

Determine equality between a PHPArray and a string.  PHP's documentation says
that an array is always greater than a string, so this function returns 0.
See http://us.php.net/manual/en/language.operators.comparison.php .

=cut

*/

    VTABLE INTVAL is_equal_string(PMC *other) {
        return 0;
    }

/*

=item C<INTVAL is_same(PMC *other)>

Return true if C<SELF> and C<other> point to the same place in memory.

=cut

*/

    VTABLE INTVAL is_same(PMC *other) {
        return SELF == other;
    }

/*

=item C<INTVAL cmp(PMC *other)>

Compare C<SELF> to C<other>.  A PHPArray with fewer elements is always
considered to be less than one with more elements.  Two PHPArrays of the same
size are comparable if they have the same keys.  In this case, C<a> > C<b> if
the first unequal value of C<a> is greater, according to the internal ordering
of C<a>.  This means it's possible for both C<a> > C<b> and C<b> > C<a> to be
true.

If the arrays are comparable and C<SELF> is larger, cmp returns 1.  If they're
equal, cmp returns 0.  If C<other> is larger, cmp returns -1.

If C<SELF> and the other PMC are not comparable, an exception is thrown.  This
is to allow the PHP implementation to return null for a comparison between two
non-comparable PHPArrays.

=cut

*/

    VTABLE INTVAL cmp(PMC *other) {

        PippBucket    *my_bkt;
        PippHashTable *my_ht;

        GET_ATTR_hash_table(INTERP, SELF, my_ht);

        if (PMC_type(SELF) == PMC_type(other)) {

            PippHashTable *other_ht;

            GET_ATTR_hash_table(INTERP, other, other_ht);

            if (SELF == other)
                return 0;

            if (my_ht->elementCount != other_ht->elementCount)
                return my_ht->elementCount > other_ht->elementCount ? 1 : -1;

            for (my_bkt = my_ht->tableHead; my_bkt != NULL; my_bkt = my_bkt->tableNext) {

                INTVAL cmp;
                PippBucket *other_bkt = pipp_hash_get_bucket(INTERP, other_ht, my_bkt->key);

                if (other_bkt == NULL)
                    Parrot_ex_throw_from_c_args(INTERP, NULL, 1, "non-comparable PhpArrays");

                cmp = VTABLE_cmp(INTERP, my_bkt->value, other_bkt->value);

                if (cmp)
                    return cmp;
            }
            return 0;
        }
        else if (VTABLE_does(INTERP, other, CONST_STRING(INTERP, "array")) ||
                 VTABLE_does(INTERP, other, CONST_STRING(INTERP, "hash"))) {

            PMC    *other_value;
            STRING *other_key;
            INTVAL  cmp;
            PMC    *iter = VTABLE_get_iter(INTERP, other);

            while (VTABLE_get_bool(INTERP, iter)) {

                other_key = VTABLE_shift_string(INTERP, iter);
                my_bkt    = pipp_hash_get_bucket(INTERP, my_ht, other_key);

                if (my_bkt == NULL)
                    Parrot_ex_throw_from_c_args(INTERP, NULL, 1,
                            "this PhpArray is non-comparable with this %Ss",
                            VTABLE_name(INTERP, other));

                other_value = VTABLE_get_pmc_keyed_str(INTERP, other, other_key);
                cmp = VTABLE_cmp(INTERP, my_bkt->value, other_value);

                if (cmp != 0)
                    return cmp;
            }
            return 0;
        }
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL,
                    EXCEPTION_PARROT_USAGE_ERROR,
                    "can't assign a non-aggregate PMC to a PhpArray");
    }

/*

=item C<INTVAL cmp_num(PMC *other)>

In PHP, arrays are always greater than numeric types.  This function always
returns 1.

=cut

*/

    VTABLE INTVAL cmp_num(PMC *other) {
        return 1;
    }


/*

=item C<INTVAL cmp_string(PMC *other)>

In PHP, arrays are always greater than strings.  This function always returns
1.

=cut

*/

    VTABLE INTVAL cmp_string(PMC *other) {
        return 1;
    }


/*

=item C<STRING *get_repr()>

Return a string representation of the contents of this PhpArray.  The format of
this string is the same as a Parrot Hash's C<get_repr()>:

 {0: 4, 4: 323, 'foo': 9999999}

=cut

*/

    VTABLE STRING *get_repr() {

        PippHashTable *ht;
        PippBucket    *bkt;
        STRING        *key_str, *val_str;
        STRING        *repr = CONST_STRING(INTERP, "{");
        PMC           *undef;

        GET_ATTR_hash_table(INTERP, SELF, ht);
        bkt = ht->tableHead;
        undef = pmc_new(INTERP, enum_class_Undef);

        while (bkt != NULL) {

            if (bkt->keyIsInt)
                key_str = Parrot_sprintf_c(INTERP, "%Ss: ", bkt->key);
            else
                key_str = Parrot_sprintf_c(INTERP, "\"%Ss\": ", bkt->key);

            repr    = string_append(INTERP, repr, key_str);
            val_str = VTABLE_get_string(INTERP, bkt->value);
            repr    = string_append(INTERP, repr, val_str);

            if (bkt->tableNext != NULL)
                repr = string_append(INTERP, repr, CONST_STRING(INTERP, ", "));

            bkt = bkt->tableNext;
        }
        repr = string_append(INTERP, repr, CONST_STRING(INTERP, "}"));
        return repr;
    }

/*

=back

=head2 Methods

=over 4

=item C<METHOD void next()>

Advance the internal pointer to the next element.  This method emulates the
behavior of PHP's C<next()> function.

=cut

*/
    METHOD next() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        if (ht->internalPointer != NULL) {
            if (ht->iter) {
                PMC *key = (PMC *)PMC_struct_val(ht->iter);
                VTABLE_nextkey_keyed(INTERP, key, SELF, ITERATE_GET_NEXT);
            }
            ht->internalPointer = ht->internalPointer->tableNext;
        }
        RETURN(void);
    }

/*

=item C<METHOD void prev()>

Move the internal pointer to the previous element.  This method emulates the
behavior of PHP's C<prev()> function.

=cut

*/
    METHOD prev() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        if (ht->internalPointer != NULL) {
            if (ht->iter) {
                PMC           *key = (PMC *)PMC_struct_val(ht->iter);
                VTABLE_nextkey_keyed(INTERP, key, SELF, ITERATE_GET_PREV);
            }
            ht->internalPointer = ht->internalPointer->tablePrev;
        }
        RETURN(void);
    }

/*

=item C<METHOD void reset()>

Move the internal pointer to the first element of this PhpArray, according to
insertion order.  This method emulates the behavior of PHP's C<reset()>
function.

=cut

*/
    METHOD reset() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        ht->internalPointer = ht->tableHead;
        if (ht->iter) {
            PMC *key = (PMC *)PMC_struct_val(ht->iter);
            VTABLE_nextkey_keyed(INTERP, key, SELF, ITERATE_FROM_START);
        }
        RETURN(void);
    }

/*

=item C<METHOD void end()>

Move the internal pointer to the last element of this PhpArray, according to
insertion order.  This method emulates the behavior of PHP's C<end()> function.

=cut

*/
    METHOD end() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        ht->internalPointer = ht->tableTail;
        if (ht->iter) {
            PMC *key = (PMC *)PMC_struct_val(ht->iter);
            VTABLE_nextkey_keyed(INTERP, key, SELF, ITERATE_FROM_END);
        }
        RETURN(void);
    }

/*

=item C<METHOD STRING* key()>

Return the key of the element pointed to by the internal pointer.  If the
internal pointer does not point at a valid element, the empty string is
returned.  This method has no side-effects and does not modify the internal
pointer.

=cut

*/
    METHOD key() {

        STRING        *s_key;
        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        if (ht->internalPointer)
            s_key = string_copy(INTERP, ht->internalPointer->key);
        else
            s_key = CONST_STRING(INTERP, "");

        RETURN(STRING *s_key);
    }

/*

=item C<METHOD PMC* current()>

Return a clone of value of the element pointed to by the internal pointer.  If
the internal pointer does not point at a valid element, PMCNULL is returned.
This method has no side-effects and does not modify the internal pointer.

=cut

*/
    METHOD current() {

        PMC           *p_val;
        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        if (ht->internalPointer)
            p_val = VTABLE_clone(INTERP, ht->internalPointer->value);
        else
            p_val = PMCNULL;

        RETURN(PMC *p_val);
    }

/*

=item C<METHOD PMC* each()>

Return a PhpArray containing the key/value pair pointed to by the internal
pointer, then advance the internal pointer to the next pair.  The returned
PhpArray has the following key/value pairs, in this order:

=over 4

=item * 1       => value

=item * "value" => value

=item * 0       => key

=item * "key"   => key

=back

where C<key> and C<value> are the key and value from this PhpArray.  If the
internal pointer does not point to a valid element, a PhpBoolean C<false> will
be returned.

=cut

*/
    METHOD each() {

        PMC           *keys, *p_val, *p_key;
        STRING        *s_key;
        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        if (ht->internalPointer != NULL) {

            PippHashTable *keys_ht;

            p_val = ht->internalPointer->value;
            s_key = ht->internalPointer->key;
            p_key = pmc_new(INTERP, enum_class_String);
            VTABLE_set_string_native(INTERP, p_key, s_key);

            keys = pmc_new(INTERP, PMC_type(SELF));
            GET_ATTR_hash_table(INTERP, keys, keys_ht);
            pipp_hash_put(INTERP, keys_ht, CONST_STRING(INTERP, "1"), VTABLE_clone(INTERP, p_val));
            pipp_hash_put(INTERP, keys_ht, CONST_STRING(INTERP, "value"), p_val);
            pipp_hash_put(INTERP, keys_ht, CONST_STRING(INTERP, "0"), VTABLE_clone(INTERP, p_key));
            pipp_hash_put(INTERP, keys_ht, CONST_STRING(INTERP, "key"), p_key);

            ht->internalPointer = ht->internalPointer->tableNext;
            if (ht->iter) {
                PMC *key = (PMC *)PMC_struct_val(ht->iter);
                VTABLE_nextkey_keyed(INTERP, key, SELF, ITERATE_GET_NEXT);
            }

        }
        else {
            keys = pmc_new(INTERP, dynpmc_PhpBoolean);
            VTABLE_set_integer_native(INTERP, keys, 0);
        }

        RETURN(PMC *keys);
    }

/*



=item C<PMC* to_number()>

=cut

*/
    METHOD to_number() {
        RETURN(PMC *SELF);
    }

/*

=item C<METHOD void sanity_check()>

Check if everything about the PippHashTable looks sane.  If anything looks
broken, the code will throw an exception.

=cut

*/
    METHOD sanity_check() {

        PippHashTable *ht;

        GET_ATTR_hash_table(INTERP, SELF, ht);

        pipp_hash_sanity_check(INTERP, ht);
    }

}

/*

=back

=head1 AUTHORS

=over 4

=item Christoph Otto

=item Francois Perrad

=back

=cut

*/


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