/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/hash.pmc 35697 2009-01-17T09:02:00.190698Z cotto  $

=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"

/*

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

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

RT #46641: PMCs extending Hash should probably register themselves.

=cut

*/

static PMC *get_integer_pmc(PARROT_INTERP, INTVAL base_type) {
    return pmc_new(interp, Parrot_get_ctx_HLL_type(interp, enum_class_Integer));
}


/*

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

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

RT #46641: PMCs extending Hash should probably register themselves.

=cut

*/

static PMC *get_number_pmc(PARROT_INTERP, INTVAL base_type) {
    return pmc_new(interp, Parrot_get_ctx_HLL_type(interp, enum_class_Float));
}

/*

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

Lookup the PMC type which is used for storing strings.

RT #46641: PMCs extending Hash should probably register themselves.

=cut

*/

static PMC *get_string_pmc(PARROT_INTERP, INTVAL base_type) {
    return pmc_new(interp, Parrot_get_ctx_HLL_type(interp, enum_class_String));
}


/*

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

Returns a Parrot string for C<*key>.

=cut

*/


PARROT_CANNOT_RETURN_NULL
static STRING
*make_hash_key(PARROT_INTERP, NOTNULL(PMC *key))
{
    if (!key)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNEXPECTED_NULL,
            "Hash: Cannot use NULL key");
    else {
        STRING *keystr = key_string(interp, key);
        if (STRING_IS_NULL(keystr))
            Parrot_ex_throw_from_c_args(interp, NULL,
                EXCEPTION_UNEXPECTED_NULL, "Hash: Cannot use NULL STRING key");
        else
            return keystr;
    }
}

/* 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 provides hash {

/*

=item C<void init()>

Initializes the instance.

=item C<void destroy()>

Free hash structure.

=cut

*/

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

    VTABLE 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

*/

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

/*

=item C<PMC *clone()>

Creates and returns a clone of the hash.

=cut

*/

    VTABLE PMC *clone() {
        PMC * const dest = pmc_new(INTERP, SELF->vtable->base_type);

        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

*/

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

/*

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

=item C<INTVAL get_integer_keyed_int(INTVAL key)>

=cut

*/

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

        if (!b)
            return 0;

        return VTABLE_get_integer(INTERP, (PMC*) b->value);
    }

    VTABLE INTVAL get_integer_keyed_int(INTVAL key) {
        const Hash * const hash = (Hash *)PMC_struct_val(SELF);
        HashBucket *b;

        if (hash->key_type == Hash_key_type_STRING)
            return SELF.get_integer_keyed_str(string_from_int(INTERP, key));

        b = parrot_hash_get_bucket(INTERP, hash, (void *)key);

        if (!b)
            return 0;

        return (INTVAL)b->value;
    }
/*

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

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

=cut

*/

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

        /* called from iterator with an integer idx in key */
        if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_hash_iterator_FLAGS) {

            /* check if we really have Hash_key_type_int */
            if (hash->key_type == Hash_key_type_int) {
                void *idx = parrot_hash_get_idx(INTERP, hash, key);
                return (INTVAL)idx;
            }
            else {
                const STRING * const s =
                    (STRING *)parrot_hash_get_idx(INTERP, hash, key);
                return string_to_int(INTERP, s);
            }
        }

        keystr  = make_hash_key(INTERP, key);
        b       = parrot_hash_get_bucket(INTERP, hash, keystr);

        if (!b)
            return 0;

        nextkey = key_next(INTERP, key);
        valpmc  = (PMC *)b->value;

        if (!nextkey)
            return VTABLE_get_integer(INTERP, valpmc);

        return VTABLE_get_integer_keyed(INTERP, valpmc, nextkey);
    }

/*

=item C<FLOATVAL get_number()>

Returns the size of the hash.

=cut

*/

    VTABLE FLOATVAL get_number() {
        /* doing this in two steps avoids dodgy cast warnings with -O */
        INTVAL size = parrot_hash_size(INTERP, (Hash *)PMC_struct_val(SELF));
        return (FLOATVAL)size;
    }

/*

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

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

=cut

*/

    VTABLE FLOATVAL get_number_keyed_str(STRING *key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);

        if (!b)
            return 0.0;

        return VTABLE_get_number(INTERP, (PMC *)b->value);
    }

    VTABLE 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

*/

    VTABLE FLOATVAL get_number_keyed(PMC *key) {
        PMC               *nextkey;
        PMC               *valpmc;
        STRING     * const keystr = make_hash_key(INTERP, key);
        HashBucket * const b      = parrot_hash_get_bucket(INTERP,
                                           (Hash*)PMC_struct_val(SELF), keystr);

        if (!b)
            return 0.0;

        nextkey = key_next(INTERP, key);
        valpmc  = (PMC *)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

*/

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

    VTABLE STRING *get_repr() {
        /* RT #44643 use freeze */
        PMC * const  iter = VTABLE_get_iter(INTERP, SELF);
        STRING      *res  = CONST_STRING(INTERP, "{");
        const INTVAL n    = VTABLE_elements(INTERP, SELF);
        INTVAL       j;

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

            for (i = 0; i < (int)key->strlen; ++i) {
                if (!isdigit((unsigned char)((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

*/

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

        /*
         * RT #46647 current behaviour of $S0 = hsh['no_such_key'] in
         *      PGE and possibly more code needs an empty STRING
         *      (--leo)
         */
        if (!b)
            return string_make_empty(INTERP, enum_stringrep_one, 0);

        return VTABLE_get_string(INTERP, (PMC*) b->value);
    }

    VTABLE 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

*/

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

        if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_hash_iterator_FLAGS) {
            /* called from iterator with an integer idx in key */
            if (hash->key_type == Hash_key_type_int) {
                void  *idx = parrot_hash_get_idx(INTERP, hash, key);
                return string_from_int(INTERP, (INTVAL)idx);
            }

            return (STRING *)parrot_hash_get_idx(INTERP, hash, key);
        }

        keystr = make_hash_key(INTERP, key);
        b      = parrot_hash_get_bucket(INTERP, hash, keystr);

        /*
         * RT #46647 current behaviour of $S0 = hsh['no_such_key'] in
         *      PGE and possibly more code needs an empty STRING
         *      (--leo)
         */
        if (!b)
            return string_make_empty(INTERP, enum_stringrep_one, 0);

        nextkey = key_next(INTERP, key);
        valpmc  = (PMC *)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

*/

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

/*

=item C<INTVAL elements()>

Returns the number of elements in the hash.

=cut

*/

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

/*

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

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

=cut

*/

    VTABLE PMC *get_pmc_keyed_str(STRING *key) {
        HashBucket * const b =
            parrot_hash_get_bucket(INTERP, (Hash*) PMC_struct_val(SELF), key);

        if (!b)
            return PMCNULL;

        return (PMC *)b->value;
    }

    VTABLE 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

*/

    VTABLE 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, (Hash *)PMC_struct_val(SELF)) ? 0 : -1;

        return iter;
    }

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

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

        /* called from iterator with an integer idx in key */
        if ((PObj_get_FLAGS(key) & KEY_type_FLAGS) == KEY_hash_iterator_FLAGS) {
            /* check if we really have Hash_key_type_int */
            if (hash->key_type == Hash_key_type_int) {
                void *idx           = parrot_hash_get_idx(INTERP, hash, key);
                const INTVAL i      = (INTVAL)idx;
                result              = pmc_new(INTERP, enum_class_Integer);
                PMC_int_val(result) = i;
            }
            else {
                STRING *s = (STRING *)parrot_hash_get_idx(INTERP, hash, key);
                result    = pmc_new(INTERP, enum_class_String);
                VTABLE_set_string_native(INTERP, result, s);
            }

            return result;
        }

        keystr = make_hash_key(INTERP, key);
        b      = parrot_hash_get_bucket(INTERP, hash, keystr);

        if (!b)
            return PMCNULL;

        nextkey = key_next(INTERP, key);

        if (!nextkey)
            return (PMC *)b->value;

        return VTABLE_get_pmc_keyed(INTERP, (PMC*)b->value, nextkey);
    }

/*

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

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

=cut

*/

    VTABLE 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

*/

    VTABLE 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) {
            PMC *val = get_integer_pmc(INTERP, SELF->vtable->base_type);

            VTABLE_set_integer_native(INTERP, val, value);
            parrot_hash_put(INTERP, (Hash *)PMC_struct_val(SELF), keystr, val);
            return;
        }

        box = SELF.get_pmc_keyed_str(keystr);

        /* autovivify an Hash */
        if (!box)
            box = pmc_new(INTERP, SELF.type());

        VTABLE_set_integer_keyed(INTERP, box, nextkey, value);
    }

    VTABLE void set_integer_keyed_int(INTVAL key, INTVAL value) {
        Hash * const hash = (Hash *)PMC_struct_val(SELF);

        /* check if we really have Hash_key_type_int */
        if (hash->key_type == Hash_key_type_int) {
            parrot_hash_put(INTERP, hash, (void *)key, (void *)value);
        }
        else {
            SUPER(key, value);
        }
    }

/*

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

=cut

*/

    VTABLE 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, (Hash *)PMC_struct_val(SELF), key, val);
    }

/*

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

=cut

*/

    VTABLE 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) {
            PMC *val         = get_number_pmc(INTERP, SELF->vtable->base_type);
            VTABLE_set_number_native(INTERP, val, value);
            parrot_hash_put(INTERP, (Hash *)PMC_struct_val(SELF), keystr, val);
            return;
        }

        box = SELF.get_pmc_keyed_str(keystr);

        /* autovivify an Hash */
        if (!box)
            box = pmc_new(INTERP, SELF.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

*/

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

        parrot_hash_put(INTERP, (Hash *)PMC_struct_val(SELF), key, val);
    }

/*

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

=cut

*/

    VTABLE 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) {
            PMC * const val = get_string_pmc(INTERP, SELF->vtable->base_type);
            VTABLE_set_string_native(INTERP, val, value);
            parrot_hash_put(INTERP, (Hash *)PMC_struct_val(SELF), keystr, val);
            return;
        }

        box = SELF.get_pmc_keyed_str(keystr);

        /* autovivify an Hash */
        if (!box)
            box = pmc_new(INTERP, SELF.type());

        VTABLE_set_string_keyed(INTERP, box, nextkey, value);
    }

/*

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

=cut

*/

    VTABLE 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, (Hash *)PMC_struct_val(SELF), key, val);
    }

/*

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

=cut

*/

    VTABLE 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) {
            parrot_hash_put(INTERP, (Hash*)PMC_struct_val(SELF), keystr, value);
            return;
        }

        box = SELF.get_pmc_keyed_str(keystr);

        /* autovivify an Hash */
        if (!box)
            box = pmc_new(INTERP, SELF.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

*/

    VTABLE void set_pmc_keyed_str(STRING *key, PMC *value) {
        parrot_hash_put(INTERP, (Hash *)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

*/

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

        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;
            INTVAL         result;

            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;

            Parrot_mmd_multi_dispatch_from_c_args(INTERP, "is_equal",
                "PP->I", item1, item2, &result);
            if (!result)
                return 0;
        }

        return 1;
    }

/*

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

=cut

*/

    VTABLE 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

*/

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

        /* no such key */
        if (!b)
            return 0;

        /* lookup stops here */
        if (!key)
            return 1;

        return VTABLE_exists_keyed(INTERP, (PMC *)b->value, key);
    }

/*

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

=cut

*/

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

        /* no such key */
        if (!b)
            return 0;

        return VTABLE_defined(INTERP, (PMC*)b->value);
    }

/*

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

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

=cut

*/

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

        /* no such key */
        if (!b)
            return 0;

        if (!key)
            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

*/

    VTABLE 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

*/

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

        /* no such key */
        if (!b)
            return;

        if (!key)
            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

*/

    VTABLE PMC *slice(PMC *key, INTVAL f) {
        if (f == 0) {
            PMC * const iter = pmc_new_init(INTERP, enum_class_Iterator, SELF);
            PMC_struct_val(iter) = key;
            return iter;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
            "Hash: Unknown slice type");
    }


/*

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

Used during archiving to visit the elements in the hash.

=cut

*/

    VTABLE 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

*/

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

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

/*

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

Used to unarchive the hash.

=cut

*/

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

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

            /* RT #46651 make a better interface for hash creation
             * RT #46653 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);

                /* don't leak the hash if we're going to overwrite it anyway */
                if (PMC_struct_val(SELF))
                    parrot_hash_destroy(INTERP, (Hash *)PMC_struct_val(SELF));

                PMC_struct_val(SELF)  = PMC_struct_val(dummy);
                PMC_struct_val(dummy) = NULL;
            }

            hash            = (Hash *)PMC_struct_val(SELF);

            PARROT_ASSERT((INTVAL)hash->key_type == k_type);
            PARROT_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:
 */
