/*
Copyright (C) 2001-2006, The Perl Foundation.
$Id: hash.pmc 16275 2006-12-28 01:20:44Z particle $

=head1 NAME

src/pmc/hash.pmc - Hash PMC

=head1 DESCRIPTION

These are the vtable functions for the Hash PMC.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"
#include <assert.h>


/*

=item C<static PMC * get_integer_pmc(Interp *interp, INTVAL base_type )>

Lookup the PMC type which is used for storing native integers.

TODO: PMCs extending Hash should probably register themselves.

=cut

*/

static PMC* get_integer_pmc(Interp *interp, INTVAL base_type ) {

    return pmc_new(interp,
                   Parrot_get_ctx_HLL_type(interp, enum_class_Integer) );
}


/*

=item C<static PMC * get_number_pmc(Interp *interp, INTVAL base_type )>

Lookup the PMC type which is used for floating point numbers.

TODO: PMCs extending Hash should probably register themselves.

=cut

*/

static PMC * get_number_pmc(Interp *interp, INTVAL base_type ) {

    return pmc_new(interp,
                   Parrot_get_ctx_HLL_type(interp, enum_class_Float) );
}

/*

=item C<static PMC * get_string_pmc(Interp *interp, INTVAL base_type )>

Lookup the PMC type which is used for storing strings.

TODO: PMCs extending Hash should probably register themselves.

=cut

*/

static PMC * get_string_pmc(Interp *interp, INTVAL base_type ) {

    return pmc_new(interp,
                   Parrot_get_ctx_HLL_type(interp, enum_class_String) );
}


/*

=item C<static STRING* make_hash_key(Interp *interp, PMC *key)>

Returns a Parrot string for C<*key>.

=cut

*/

static STRING* make_hash_key(Interp *interp, PMC * key)
{
    if (key == NULL) {
        real_exception(interp, NULL, E_KeyError,
                "Hash: Cannot use NULL key");
        return NULL;
    }
    return key_string(interp, key);
}

/* Albeit the Hash PMC doesn't use PMC_data, it needs the next_for_GC pointer
 * We would get recursive marking of a deeply nested HoHoH...
 */
pmclass Hash need_ext does hash {

/*

=item C<void init()>

Initializes the instance.

=item C<void destroy()>

Free hash structure.

=cut

*/

    void init() {
        PMC_struct_val(SELF) = NULL;
        PObj_custom_mark_destroy_SETALL(SELF);
        parrot_new_pmc_hash(INTERP, SELF);
    }

    void destroy() {
        if (PMC_struct_val(SELF)) {
            parrot_hash_destroy(INTERP, (Hash*) PMC_struct_val(SELF));
            PMC_struct_val(SELF) = NULL;
        }
    }
/*

=item C<void mark()>

Marks the hash as live.

=cut

*/

    void mark() {
        if (PMC_struct_val(SELF))
            parrot_mark_hash(INTERP, PMC_struct_val(SELF));
    }

/*

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

Returns the type of the element for C<*key>.

=cut

*/

    INTVAL type_keyed_str(STRING* key) {
        HashBucket* const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);
        if (b == NULL) {
            return 0;
        }
        return VTABLE_type(INTERP, (PMC*) b->value);
    }

/*

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

Returns the type of the element for C<*key>.

=cut

*/

    INTVAL type_keyed(PMC* key) {
        PMC* valpmc;
        PMC* nextkey;
        STRING* const keystr = make_hash_key(INTERP, key);
        HashBucket* const b =
            parrot_hash_get_bucket(INTERP, (Hash*)PMC_struct_val(SELF), keystr);
        if (b == NULL) {
            return 0;
        }
        nextkey = key_next(INTERP, key);
        valpmc = b->value;
        if (!nextkey)
            return VTABLE_type(INTERP, valpmc);
        return VTABLE_type_keyed(INTERP, valpmc, nextkey);
    }

/*

=item C<PMC *clone()>

Creates and returns a clone of the hash.

=cut

*/

    PMC* clone() {
        PMC* const dest = pmc_new_noinit(INTERP, SELF->vtable->base_type);
        PObj_custom_mark_destroy_SETALL(dest);
        parrot_hash_clone(INTERP, (Hash *)PMC_struct_val(SELF),
                   (Hash**)&PMC_struct_val(dest));
        ((Hash*)PMC_struct_val(dest))->container = dest;
        return dest;
    }

/*

=item C<INTVAL get_integer()>

Returns the size of the hash.

=cut

*/

    INTVAL get_integer() {
        return parrot_hash_size(INTERP, PMC_struct_val(SELF));
    }

/*

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

=item C<INTVAL get_integer_keyed_int(INTVAL key)>

=cut

*/

    INTVAL get_integer_keyed_str(STRING* key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);
        if (b == NULL) {
            return 0;
        }
        return VTABLE_get_integer(INTERP, (PMC*) b->value);
    }

    INTVAL get_integer_keyed_int(INTVAL key) {
        STRING * const s = string_from_int(INTERP, key);
        return SELF.get_integer_keyed_str(s);
    }
/*

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

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

=cut

*/

    INTVAL get_integer_keyed(PMC* key) {
        PMC* valpmc;
        STRING* keystr;
        Hash * const hash = PMC_struct_val(SELF);
        HashBucket *b;
        PMC* nextkey;

        switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
            case KEY_hash_iterator_FLAGS:
                /* called from iterator with an integer idx in key
                 * check if we really have Hash_key_type_int
                 */
                if (hash->key_type == Hash_key_type_int) {
                    return (INTVAL)parrot_hash_get_idx(INTERP, hash, key);
                }
                else {
                    STRING *s = parrot_hash_get_idx(INTERP, hash, key);
                    return string_to_int(INTERP, s);
                }
            default:
                keystr = make_hash_key(INTERP, key);
        }
        b = parrot_hash_get_bucket(INTERP, hash, keystr);
        if (b == NULL) {
            return 0;
        }
        nextkey = key_next(INTERP, key);
        valpmc = b->value;
        if (!nextkey)
            return VTABLE_get_integer(INTERP, valpmc);
        return VTABLE_get_integer_keyed(INTERP, valpmc, nextkey);
    }

/*

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

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

=cut

*/

    FLOATVAL get_number_keyed_str(STRING* key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);
        if (b == NULL) {
            return 0.0;
        }
        return VTABLE_get_number(INTERP, (PMC*) b->value);
    }

    FLOATVAL get_number_keyed_int(INTVAL key) {
        STRING * const s = string_from_int(INTERP, key);
        return SELF.get_number_keyed_str(s);
    }
/*

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

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

=cut

*/

    FLOATVAL get_number_keyed(PMC* key) {
        PMC* valpmc;
        STRING* const keystr = make_hash_key(INTERP, key);
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*)PMC_struct_val(SELF), keystr);
        PMC* nextkey;
        if (b == NULL) {
            return 0.0;
        }
        nextkey = key_next(INTERP, key);
        valpmc = b->value;
        if (!nextkey)
            return VTABLE_get_number(INTERP, valpmc);
        return VTABLE_get_number_keyed(INTERP, valpmc, nextkey);
    }


/*

=item C<STRING *get_string()>

Returns a string representation of the hash, showing its class name and
memory address.

=item  C<STRING *get_repr()>

Return a representation of the hash contents.

=cut

*/

    STRING* get_string() {
        return Parrot_sprintf_c(INTERP, "Hash[0x%x]", SELF);
    }

    STRING* get_repr() {
        /* TODO use freeze */
        PMC * const iter = VTABLE_get_iter(INTERP, SELF);
        STRING *res = string_from_cstring(INTERP, "{", 0);
        const INTVAL n = VTABLE_elements(INTERP, SELF);
        INTVAL j;

        for (j = 0; j < n; ++j) {
            STRING * const key = VTABLE_shift_string(INTERP, iter);
            int i,all_digit = 1;
            PMC *val;

            for (i = 0; i < (int)key->strlen; ++i) {
                if (!isdigit(((char *)key->strstart)[i])) {
                    all_digit = 0;
                    break;
                }
            }
            if (all_digit) {
                res = string_append(INTERP, res, key);
            }
            else {
                res = string_append(INTERP, res,
                        const_string(INTERP, "'"));
                res = string_append(INTERP, res, key);
                res = string_append(INTERP, res,
                        const_string(INTERP, "'"));
            }
            res = string_append(INTERP, res,
                    const_string(INTERP, ": "));
            val = SELF.get_pmc_keyed_str(key);
            res = string_append(INTERP, res,
                    VTABLE_get_string(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_str(STRING *key)>

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

=cut

*/

    STRING* get_string_keyed_str(STRING* key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);
        if (b == NULL) {
            /*
             * TODO current behaviour of $S0 = hsh['no_such_key'] in
             *      PGE and possibly more code needs an empty STRING
             *      (--leo)
             */
            return string_make_empty(INTERP,enum_stringrep_one,0);
        }
        return VTABLE_get_string(INTERP, (PMC*) b->value);
    }

    STRING* get_string_keyed_int(INTVAL key) {
        STRING * const s = string_from_int(INTERP, key);
        return SELF.get_string_keyed_str(s);
    }

/*

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

Returns the string value for the element at C<*key>.

=cut

*/

    STRING* get_string_keyed(PMC* key) {
        PMC* valpmc;
        STRING* keystr;
        HashBucket *b;
        Hash * const hash = PMC_struct_val(SELF);
        PMC* nextkey;

        switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
            case KEY_hash_iterator_FLAGS:
                /* called from iterator with an integer idx in key */
                if (hash->key_type == Hash_key_type_int) {
                    INTVAL i = (INTVAL)parrot_hash_get_idx(INTERP, hash, key);
                    return string_from_int(INTERP, i);
                }
                return parrot_hash_get_idx(INTERP, hash, key);
            default:
                keystr = make_hash_key(INTERP, key);
        }
        b = parrot_hash_get_bucket(INTERP, hash, keystr);
        if (b == NULL) {
            /*
             * TODO current behaviour of $S0 = hsh['no_such_key'] in
             *      PGE and possibly more code needs an empty STRING
             *      (--leo)
             */
            return string_make_empty(INTERP,enum_stringrep_one,0);
        }
        nextkey = key_next(INTERP, key);
        valpmc = b->value;
        if (!nextkey)
            return VTABLE_get_string(INTERP, valpmc);
        return VTABLE_get_string_keyed(INTERP, valpmc, nextkey);
    }

/*

=item C<INTVAL get_bool()>

Returns true if the hash size is not zero.

=cut

*/

    INTVAL get_bool() {
        return parrot_hash_size(INTERP, PMC_struct_val(SELF)) != 0;
    }

/*

=item C<INTVAL elements()>

Returns the number of elements in the hash.

=cut

*/

    INTVAL elements() {
        return parrot_hash_size(INTERP, PMC_struct_val(SELF));
    }

/*

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

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

=cut

*/

    PMC* get_pmc_keyed_str(STRING* key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);
        if (b == NULL) {
            return PMCNULL;
        }
        return b->value;
    }

    PMC* get_pmc_keyed_int(INTVAL key) {
        STRING * const s = string_from_int(INTERP, key);
        return SELF.get_pmc_keyed_str(s);
    }

/*

=item C<PMC* get_iter()>

Return a new iterator for the slice PMC C<key>

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

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

=cut

*/

    PMC* get_iter() {
        PMC * const iter = pmc_new_init(INTERP, enum_class_Iterator, SELF);
        PMC * const key =  pmc_new(INTERP, enum_class_Key);

        PMC_struct_val(iter) = key;
        PObj_get_FLAGS(key) |= KEY_hash_iterator_FLAGS;
        PMC_data(key) = (void *)INITBucketIndex;
        /* -1 for an empty hash, 0 for a non-empty hash.  */
        PMC_int_val(key) =
            parrot_hash_size(INTERP, PMC_struct_val(SELF)) ? 0 : -1;
        return iter;
    }

    PMC* slice(PMC* key) {
        return NULL;
    }

    PMC* get_pmc_keyed(PMC* key) {
        Hash * const hash = PMC_struct_val(SELF);
        STRING* keystr;
        HashBucket *b;
        PMC* nextkey;
        PMC* result;

        switch (PObj_get_FLAGS(key) & KEY_type_FLAGS) {
            case KEY_hash_iterator_FLAGS:
                /* called from iterator with an integer idx in key
                 * check if we really have Hash_key_type_int
                 */
                if (hash->key_type == Hash_key_type_int) {
                    const INTVAL i = (INTVAL)parrot_hash_get_idx(INTERP,
                                                                 hash, key);
                    result = pmc_new(INTERP, enum_class_Integer);
                    PMC_int_val(result) = i;
                }
                else {
                    STRING * const s = parrot_hash_get_idx(INTERP, hash, key);
                    result = pmc_new(INTERP, enum_class_String);
                    VTABLE_set_string_native(INTERP, result, s);
                }
                return result;
            default:
                keystr = make_hash_key(INTERP, key);
        }
        b = parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF),
                                        keystr);
        if (b == NULL) {
            return PMCNULL;
        }
        nextkey = key_next(INTERP, key);
        if (!nextkey)
            return b->value;
        return VTABLE_get_pmc_keyed(INTERP, (PMC*)b->value, nextkey);
    }

/*

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

Returns whether the hash is the same as C<*other>.

=cut

*/

    INTVAL is_same(PMC* other) {
        return (INTVAL)( other->vtable == SELF->vtable &&
            PMC_struct_val(other) == PMC_struct_val(SELF));
    }

/*

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

=cut

*/

    void set_integer_keyed(PMC* key, INTVAL value) {
        STRING* keystr;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
        keystr = make_hash_key(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            PMC* val = get_integer_pmc(INTERP, SELF->vtable->base_type);

            VTABLE_set_integer_native(INTERP, val, value);
            parrot_hash_put(INTERP, PMC_struct_val(SELF), keystr, val);
            return;
        }
        box = SELF.get_pmc_keyed_str(keystr);
        if (box == NULL) {
            /* autovivify an Hash */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_integer_keyed(INTERP, box, nextkey, value);
    }

/*

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

=cut

*/

    void set_integer_keyed_str(STRING* key, INTVAL value) {
        PMC* const val = get_integer_pmc(INTERP, SELF->vtable->base_type);

        PMC_int_val(val) = value;
        parrot_hash_put(INTERP, PMC_struct_val(SELF), key, val);
    }

/*

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

=cut

*/

    void set_number_keyed(PMC* key, FLOATVAL value) {
        STRING* keystr;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
        keystr = make_hash_key(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            PMC* val = get_number_pmc(INTERP, SELF->vtable->base_type);
            PMC_num_val(val) = value;
            parrot_hash_put(INTERP, PMC_struct_val(SELF), keystr, val);
            return;
        }
        box = SELF.get_pmc_keyed_str(keystr);
        if (box == NULL) {
            /* autovivify an Hash */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_number_keyed(INTERP, box, nextkey, value);
    }

/*

=item C<void set_number_keyed_str(STRING *key, FLOATVAL value)>

Sets C<value> as the value for C<*key>.

=cut

*/

    void set_number_keyed_str(STRING* key, FLOATVAL value) {
        PMC* const val = get_number_pmc(INTERP, SELF->vtable->base_type);

        PMC_num_val(val) = value;
        parrot_hash_put(INTERP, PMC_struct_val(SELF), key, val);
    }

/*

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

=cut

*/

    void set_string_keyed(PMC* key, STRING* value) {
        STRING* keystr;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
        keystr = make_hash_key(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            PMC* const val = get_string_pmc(INTERP, SELF->vtable->base_type);
            VTABLE_set_string_native(INTERP, val, value);
            parrot_hash_put(INTERP, PMC_struct_val(SELF), keystr, val);
            return;
        }
        box = SELF.get_pmc_keyed_str(keystr);
        if (box == NULL) {
            /* autovivify an Hash */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_string_keyed(INTERP, box, nextkey, value);
    }

/*

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

=cut

*/

    void set_string_keyed_str(STRING* key, STRING* value) {
        PMC* const val = get_string_pmc(INTERP, SELF->vtable->base_type);

        VTABLE_set_string_native(INTERP, val, value);
        parrot_hash_put(INTERP, PMC_struct_val(SELF), key, val);
    }

/*

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

=cut

*/

    void set_pmc_keyed(PMC* key, PMC* value) {
        STRING* keystr;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
        keystr = make_hash_key(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            parrot_hash_put(INTERP, PMC_struct_val(SELF), keystr, value);
            return;
        }
        box = SELF.get_pmc_keyed_str(keystr);
        if (box == NULL) {
            /* autovivify an Hash */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_pmc_keyed(INTERP, box, nextkey, value);
    }

/*

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

Sets C<*value> as the value for C<*key>.

=cut

*/

    void set_pmc_keyed_str(STRING* key, PMC* value) {
        parrot_hash_put(INTERP, PMC_struct_val(SELF), key, value);
    }

/*

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

The C<==> operation.

Check if two hashes hold the same keys and values.

=cut

*/

    INTVAL is_equal(PMC* value) {
        PMC * const iter = VTABLE_get_iter(INTERP, SELF);
        INTVAL j, n;

        /* TODO: comparing Hash and PerlHash should probably be possible */
        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) {
            STRING * const key = VTABLE_shift_string(INTERP, iter);
            PMC *item1, *item2;
            if (!VTABLE_exists_keyed_str(INTERP, value, key))
                return 0;
            item1 = SELF.get_pmc_keyed_str(key);
            item2 = VTABLE_get_pmc_keyed_str(INTERP, value, key);
            if (item1 == item2)
                continue;
            if (!mmd_dispatch_i_pp(INTERP, item1, item2, MMD_EQ))
                return 0;
        }
        return 1;
    }

/*

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

=cut

*/

    INTVAL exists_keyed_str(STRING* key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);
        return b != NULL;
    }

/*

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

Returns whether a key C<*key> exists in the hash.

=cut

*/

    INTVAL exists_keyed(PMC* key) {
        Hash * const h = (Hash *)PMC_struct_val(SELF);
        STRING * const sx = key_string(INTERP, key);
        HashBucket *b;

        key = key_next(INTERP, key);
        b = parrot_hash_get_bucket(INTERP, h, sx);
        if (b == NULL)
            return 0;       /* no such key */
        if (key == NULL)
            return 1;       /* lookup stops here */
        return VTABLE_exists_keyed(INTERP, (PMC*)b->value, key);
    }

/*

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

=cut

*/

    INTVAL defined_keyed_str(STRING* key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);

        if (b == NULL)
            return 0;       /* no such key */
        return VTABLE_defined(INTERP, (PMC*)b->value);
    }

/*

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

Returns whether the value for C<*key> is defined.

=cut

*/

    INTVAL defined_keyed(PMC* key) {
        Hash * const h = (Hash *)PMC_struct_val(SELF);
        STRING * const sx = key_string(INTERP, key);
        HashBucket *b;

        key = key_next(INTERP, key);
        b = parrot_hash_get_bucket(INTERP, h, sx);
        if (b == NULL)
            return 0;       /* no such key */
        if (key == NULL)
            return VTABLE_defined(INTERP, (PMC*)b->value);
        else
            return VTABLE_defined_keyed( INTERP, (PMC*)b->value, key);
    }

/*

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

=cut

*/

    void delete_keyed_str(STRING* key) {
        parrot_hash_delete(INTERP, (Hash *)PMC_struct_val(SELF), key);
    }

/*

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

Deletes the element associated with C<*key>.

=cut

*/

    void delete_keyed(PMC* key) {
        Hash * const h = (Hash *)PMC_struct_val(SELF);
        STRING * const sx = key_string(INTERP, key);
        HashBucket *b;

        key = key_next(INTERP, key);
        b = parrot_hash_get_bucket(INTERP, h, sx);
        if (b == NULL)
            return;  /* no such key */
        if (key == NULL)
            parrot_hash_delete(INTERP, h, sx);
        else
            VTABLE_delete_keyed(INTERP, (PMC*)b->value, key);
    }

/*

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

Return a new iterator for the slice PMC C<key>

=cut

*/

    PMC* slice(PMC* key, INTVAL f) {
        switch (f) {
            case 0:
                {
                    PMC * const iter = pmc_new_init(INTERP,
                            enum_class_Iterator, SELF);
                    PMC_struct_val(iter) = key;
                    return iter;
                }
        }
        real_exception(INTERP, NULL, E_IndexError,
                "Hash: Unknown slice type");
        return NULL;
    }


/*

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

Used during archiving to visit the elements in the hash.

=cut

*/

    void visit(visit_info *info) {
        info->container = SELF;
        parrot_hash_visit(INTERP, (Hash*)PMC_struct_val(SELF), info);
        SUPER(info);
    }

/*

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

Used to archive the hash.

=cut

*/

    void freeze(visit_info *info) {
        IMAGE_IO * const io = info->image_io;
        Hash * const hash = (Hash *)PMC_struct_val(SELF);;

        SUPER(info);
        io->vtable->push_integer(INTERP, io, VTABLE_elements(INTERP, SELF));
        io->vtable->push_integer(INTERP, io, hash->key_type);
        io->vtable->push_integer(INTERP, io, hash->entry_type);
    }

/*

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

Used to unarchive the hash.

=cut

*/

    void thaw(visit_info *info) {
        IMAGE_IO * const io = info->image_io;

        SUPER(info);
        if (info->extra_flags == EXTRA_IS_NULL) {
            const INTVAL elems = io->vtable->shift_integer(INTERP, io);
            const INTVAL k_type = io->vtable->shift_integer(INTERP, io);
            const INTVAL v_type = io->vtable->shift_integer(INTERP, io);
            Hash *hash;

            /* TODO make a better interface for hash creation
             * TODO create hash with needed size in the first place
             */
            if (k_type == Hash_key_type_int && v_type == enum_hash_int) {
                PMC * const dummy = Parrot_new_INTVAL_hash(INTERP, 0);
                PMC_struct_val(SELF) = PMC_struct_val(dummy);
                PMC_struct_val(dummy) = NULL;
            }
            else
                SELF.init();
            hash = PMC_struct_val(SELF);
            assert((INTVAL)hash->key_type == k_type);
            assert(hash->entry_type == v_type);
            hash->container = SELF;
            hash->entries = elems;
        }
    }
}

/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd08_keys.pod>.

=cut

*/

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