The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
Copyright (C) 2001-2009, Parrot Foundation.

=head1 NAME

src/pmc/key.pmc - Key PMC

=head1 DESCRIPTION

These are the vtable functions for the Key PMC class.

=head2 Methods

=over 4

=cut

*/

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

pmclass Key auto_attrs {
    ATTR PMC      *next_key; /* Sometimes it's the next key, sometimes it's
                                not.  The Key code is like that. */
    ATTR INTVAL    int_key;  /* int value of this key, or something magical if
                                it's a hash iterator key */
    ATTR STRING   *str_key;  /* STRING value of this key, if any */
                             /* Theoretically there'd also be a pmc_key here,
                              * but that code looks broken and unneeded. */


/*

=item C<void init()>

Initializes the key.

=cut

*/

    VTABLE void init() {
        PObj_custom_mark_SET(SELF);
    }

/*

=item C<PMC *clone()>

Creates and returns a clone of the key.

=cut

*/

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

        for (; key ;) {
            switch (KEY_get_FLAGS(key)) {
              case KEY_integer_FLAG:
              case KEY_integer_FLAG | KEY_register_FLAG:
                Parrot_key_set_integer(INTERP, dkey, Parrot_key_integer(INTERP, key));
                break;
              case KEY_string_FLAG:
              case KEY_string_FLAG | KEY_register_FLAG:
                Parrot_key_set_string(INTERP, dkey, VTABLE_get_string(INTERP, key));
                break;
              case KEY_pmc_FLAG:
              case KEY_pmc_FLAG | KEY_register_FLAG:
                Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
                        "this is broken - see TT #1683");
              default:
                break;
            }

            key = Parrot_key_next(INTERP, key);

            if (key) {
                PMC * const p = Parrot_key_new(INTERP);
                Parrot_key_append(INTERP, dkey, p);
                dkey = p;
            }
        }

        return dest;
    }

/*

=item C<void mark()>

Marks the key as live.

=cut

*/

    VTABLE void mark() {
        Parrot_key_mark(INTERP, SELF);
    }

/*

=item C<INTVAL get_integer()>

Returns the integer value of the key.

=cut

*/

    VTABLE INTVAL get_integer() {
        return Parrot_key_integer(INTERP, SELF);
    }

/*

=item C<STRING *get_string()>

Returns the Parrot string value of the key.

=cut

*/

    VTABLE STRING *get_string() {
        /* Parrot_key_string() is only useful if this PMC has a key type */
        if (KEY_get_FLAGS(SELF))
            return Parrot_key_string(INTERP, SELF);

        return CONST_STRING(INTERP, "");
    }

/*

=item C<PMC *get_pmc()>

Returns the PMC value of the key.

=cut

*/

    VTABLE PMC *get_pmc() {
        return Parrot_key_pmc(INTERP, SELF);
    }

/*

=item C<void set_integer_native(INTVAL value)>

=cut

*/

    VTABLE void set_integer_native(INTVAL value) {
        Parrot_key_set_integer(INTERP, SELF, value);
    }

/*

=item C<void set_string_native(STRING *value)>


=cut

*/

    VTABLE void set_string_native(STRING *value) {
        Parrot_key_set_string(INTERP, SELF, value);
    }

/*

=item C<void set_pmc(PMC *value)>

Sets the value of the key to C<*value>.

=cut

*/

    VTABLE void set_pmc(PMC *value) {
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNIMPLEMENTED,
                "this is broken - see TT #1683");
    }

/*

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

Appends C<*value> to the key.

=cut

*/

    void push_pmc(PMC *value) {
        if (value->vtable->base_type != enum_class_Key)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                "Can only push another Key onto a Key PMC.");

        Parrot_key_append(INTERP, SELF, value);
    }

/*

=item C<PMC *shift_pmc()>

Returns the next key.

Actually doesn't remove the entry but might be useful to traverse a key
chain.

=cut

*/

    VTABLE PMC *shift_pmc() {
        PMC *next_key;

        GET_ATTR_next_key(INTERP, SELF, next_key);
        return next_key;
    }

/*

=back

=head2 Iterator Interface

=over 4

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

Returns the key itself.

=cut

*/

    VTABLE PMC *get_pmc_keyed(PMC *key) {
        return key;
    }

/*

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

Archives the Key.

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

Unarchives the Key.

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

Called after the Key has been thawed: convert last PMC_NULL key to NULL.

=cut

*/

    void freeze(PMC *info) {
        int size;
        PMC *k;

        for (size = 0, k = SELF; k; size++)
            GET_ATTR_next_key(interp, k, k);

        VTABLE_push_integer(INTERP, info, size);

        for (k = SELF; k;) {
            const INTVAL flags = KEY_get_FLAGS(k);
            VTABLE_push_integer(INTERP, info, flags);

            switch (flags) {
              case KEY_integer_FLAG | KEY_register_FLAG:
              case KEY_string_FLAG  | KEY_register_FLAG:
              case KEY_pmc_FLAG     | KEY_register_FLAG:
              case KEY_integer_FLAG:
                {
                    INTVAL i;
                    GET_ATTR_int_key(INTERP, k, i);
                    VTABLE_push_integer(INTERP, info, i);
                }
                break;

              case KEY_string_FLAG:
                {
                    STRING *s;
                    GET_ATTR_str_key(INTERP, k, s);
                    VTABLE_push_string(INTERP, info, s);
                }
                break;

              default:
                Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
                        "Unsupported key type in Key.freeze");
                break;
            }

            GET_ATTR_next_key(interp, k, k);
        }
    }

    void thaw(PMC *info) {
        int  size;
        PMC *k = SELF;

        PObj_custom_mark_SET(SELF);

        for (size = VTABLE_shift_integer(INTERP, info); size; size--) {
            const INTVAL flags  = VTABLE_shift_integer(INTERP, info) & KEY_type_FLAGS;

            PObj_get_FLAGS(k) |= flags;

            /* get contents */
            switch (flags) {
              case KEY_integer_FLAG | KEY_register_FLAG:
              case KEY_string_FLAG  | KEY_register_FLAG:
              case KEY_pmc_FLAG     | KEY_register_FLAG:
              case KEY_integer_FLAG:
                SET_ATTR_int_key(INTERP, k, VTABLE_shift_integer(INTERP, info));
                break;

              case KEY_string_FLAG:
                VTABLE_set_string_native(INTERP, k, VTABLE_shift_string(INTERP, info));
                break;

              default:
                Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_KEY_NOT_FOUND,
                        "Unsupported key type in Key.thaw");
                break;
            }

            if (size == 1) {
                SET_ATTR_next_key(INTERP, k, NULL);
            }
            else {
                SET_ATTR_next_key(INTERP, k, Parrot_pmc_new(INTERP, enum_class_Key));
                GET_ATTR_next_key(INTERP, k, k);
            }
        }
    }

    VTABLE void thawfinish(PMC *info) {
        PMC *key = SELF;
        UNUSED(info)

        while (1) {
            PMC *next;
            GET_ATTR_next_key(INTERP, key, next);

            if (PMC_IS_NULL(next)) {
                SET_ATTR_next_key(INTERP, key, NULL);
                break;
            }

            key = next;
        }
    }

    VTABLE STRING* get_repr() {
        return Parrot_key_set_to_string(INTERP, SELF);
    }

/*
=item C<set_register(reg_no, type)>

Set key to hold particular register.

=cut
*/
    METHOD set_register(INTVAL reg_no, INTVAL reg_type) {
        Parrot_key_set_register(INTERP, SELF, reg_no, reg_type);
    }

/*

=item C<INTVAL elements()>

=item C<INTVAL get_integer_keyed_int(INTVAL n)>

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

=item C<PMC *get_pmc_keyed_init(INTVAL n)>

Aggregate interface.

=cut

*/

    VTABLE INTVAL elements() {
        INTVAL n = 0;
        for (; SELF; SELF = PARROT_KEY(SELF)->next_key)
            n++;
        return n;
    }

    VTABLE INTVAL get_integer_keyed_int(INTVAL n) {
        for (; SELF && n; SELF = PARROT_KEY(SELF)->next_key, n--);
        if (n)
            Parrot_ex_throw_from_c_args(INTERP, NULL, 0, "Key access out of bounds");
        return Parrot_key_integer(INTERP, SELF);
    }

    VTABLE STRING *get_string_keyed_int(INTVAL n) {
        for (; SELF && n; SELF = PARROT_KEY(SELF)->next_key, n--);
        if (n)
            Parrot_ex_throw_from_c_args(INTERP, NULL, 0, "Key access out of bounds");
        return Parrot_key_string(INTERP, SELF);
    }

    VTABLE PMC *get_pmc_keyed_int(INTVAL n) {
        for (; SELF && n; SELF = PARROT_KEY(SELF)->next_key, n--);
        if (n)
            Parrot_ex_throw_from_c_args(INTERP, NULL, 0, "Key access out of bounds");
        return Parrot_key_pmc(INTERP, SELF);
    }

    METHOD make_register_key(STRING * set, INTVAL idx)
    {
        INTVAL first_char = Parrot_str_indexed(INTERP, set, 0);
        KEY_flags key_type;
        switch (first_char) {
            case 'S':
                key_type = KEY_string_FLAG;
                break;
            case 'I':
                key_type = KEY_integer_FLAG;
                break;
            case 'P':
                key_type = KEY_pmc_FLAG;
                break;
            default:
                Parrot_ex_throw_from_c_args(INTERP, NULL, 0,
                    "Key: Unknown register set %Ss", set);
        }
        Parrot_key_set_register(INTERP, SELF, idx, (INTVAL)key_type);
    }

    METHOD is_register_reference()
    {
        INTVAL is_reg_ref = KEY_register_TEST(SELF) ? 1 : 0;
        RETURN(INTVAL is_reg_ref);
    }

    METHOD get_register_idx()
    {
        if (!KEY_register_TEST(SELF))
            Parrot_ex_throw_from_c_args(INTERP, NULL, 0,
                "Key: Key is not a register reference");
        else {
            const INTVAL idx = Parrot_key_integer(INTERP, SELF);
            RETURN(INTVAL idx);
        }
    }

    METHOD get_register_contents(PMC *ctx :optional, INTVAL has_ctx :opt_flag)
    {
        INTVAL int_key;
        if (!KEY_register_TEST(SELF))
            Parrot_ex_throw_from_c_args(INTERP, NULL, 0,
                "Key: Key is not a register reference");

        GETATTR_Key_int_key(interp, SELF, int_key);
        if (!has_ctx || PMC_IS_NULL(ctx)) {
            switch (KEY_get_FLAGS(SELF)) {
                case KEY_string_FLAG | KEY_register_FLAG: {
                    STRING * const str_val =  REG_STR(interp, int_key);
                    RETURN(STRING *str_val);
                }
                case KEY_pmc_FLAG | KEY_register_FLAG: {
                    PMC * const pmc_val = REG_PMC(interp, int_key);
                    RETURN(PMC *pmc_val);
                }
                case KEY_integer_FLAG | KEY_register_FLAG: {
                    const INTVAL int_val = REG_INT(interp, int_key);
                    RETURN(INTVAL int_val);
                }
                default:
                    Parrot_ex_throw_from_c_args(INTERP, NULL, 0,
                        "Key: Unknown Key type %d", KEY_get_FLAGS(SELF));
            }
        }
        else {
            switch (KEY_get_FLAGS(SELF)) {
                case KEY_string_FLAG | KEY_register_FLAG: {
                    STRING * const str_val =  *Parrot_pcc_get_STRING_reg(INTERP, ctx, int_key);
                    RETURN(STRING *str_val);
                }
                case KEY_pmc_FLAG | KEY_register_FLAG: {
                    PMC * const pmc_val = *Parrot_pcc_get_PMC_reg(INTERP, ctx, int_key);
                    RETURN(PMC *pmc_val);
                }
                case KEY_integer_FLAG | KEY_register_FLAG: {
                    const INTVAL int_val = *Parrot_pcc_get_INTVAL_reg(INTERP, ctx, int_key);
                    RETURN(INTVAL int_val);
                }
                default:
                    Parrot_ex_throw_from_c_args(INTERP, NULL, 0,
                        "Key: Unknown Key type %d", KEY_get_FLAGS(SELF));
            }
        }

    }

    /* returns integer, values taken from PCC */
    METHOD get_type() {
        INTVAL ret;
        switch (KEY_get_FLAGS(SELF)) {
            case KEY_integer_FLAG:
            case KEY_integer_FLAG | KEY_register_FLAG:
                ret = 0;
                break;
            case KEY_string_FLAG:
            case KEY_string_FLAG | KEY_register_FLAG:
                ret = 1;
                break;
            case KEY_pmc_FLAG:
            case KEY_pmc_FLAG | KEY_register_FLAG:
                ret = 2;
                break;
        }

        RETURN(INTVAL ret);
    }
}

/*

=back

=cut

*/

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