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

=head1 NAME

src/pmc/unmanagedstruct.pmc - C struct with unmanaged memory

=head1 DESCRIPTION

PMC class to hold C C<struct>s that Parrot's not responsible for
disposing of.

Buffer can be accessed using keyed assignments to PMC. Out of
bounds access will very likely segfault.

=head2 Functions

=over 4

=cut

*/

#include "parrot/compiler.h"
#include "parrot/string_funcs.h"

/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */

PARROT_WARN_UNUSED_RESULT
static int calc_align(PARROT_INTERP,
    ARGIN(PMC *type_pmc),
    int type,
    size_t offs)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void calc_offsets(PARROT_INTERP,
    ARGIN(PMC *pmc),
    ARGIN(PMC *value),
    size_t toff)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT
PARROT_CAN_RETURN_NULL
static char * char_offset_int(PARROT_INTERP,
    ARGIN(PMC *pmc),
    INTVAL ix,
    ARGOUT(int *type))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*type);

PARROT_WARN_UNUSED_RESULT
PARROT_CAN_RETURN_NULL
static char * char_offset_key(PARROT_INTERP,
    ARGIN(PMC *pmc),
    ARGIN(PMC *key),
    ARGOUT(int *type))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*type);

PARROT_WARN_UNUSED_RESULT
static INTVAL key_2_idx(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(PMC *key))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT
static FLOATVAL ret_float(PARROT_INTERP, ARGIN(const char *p), int type)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
static INTVAL ret_int(PARROT_INTERP, ARGIN(const char *p), int type)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static PMC* ret_pmc(PARROT_INTERP,
    ARGIN(PMC *pmc),
    ARGIN(char *p),
    int type,
    INTVAL idx)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static STRING* ret_string(PARROT_INTERP, ARGIN(char *p), int type)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2);

static void set_float(PARROT_INTERP,
    ARGOUT(char *p),
    int type,
    FLOATVAL value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        FUNC_MODIFIES(*p);

static void set_int(PARROT_INTERP, ARGOUT(char *p), int type, INTVAL value)
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        FUNC_MODIFIES(*p);

static void set_string(PARROT_INTERP,
    ARGOUT(char *p),
    int type,
    ARGIN(STRING *value))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(4)
        FUNC_MODIFIES(*p);

#define ASSERT_ARGS_calc_align __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(type_pmc))
#define ASSERT_ARGS_calc_offsets __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(pmc) \
    , PARROT_ASSERT_ARG(value))
#define ASSERT_ARGS_char_offset_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(pmc) \
    , PARROT_ASSERT_ARG(type))
#define ASSERT_ARGS_char_offset_key __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(pmc) \
    , PARROT_ASSERT_ARG(key) \
    , PARROT_ASSERT_ARG(type))
#define ASSERT_ARGS_key_2_idx __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(pmc) \
    , PARROT_ASSERT_ARG(key))
#define ASSERT_ARGS_ret_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(p))
#define ASSERT_ARGS_ret_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(p))
#define ASSERT_ARGS_ret_pmc __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(pmc) \
    , PARROT_ASSERT_ARG(p))
#define ASSERT_ARGS_ret_string __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(p))
#define ASSERT_ARGS_set_float __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(p))
#define ASSERT_ARGS_set_int __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(p))
#define ASSERT_ARGS_set_string __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(p) \
    , PARROT_ASSERT_ARG(value))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
/* HEADERIZER END: static */

/*

=item C<static char * char_offset_int(PARROT_INTERP, PMC *pmc, INTVAL ix, int
*type)>

Returns the pointer for the element at index C<ix>, and sets the element
type in C<*type>.

=cut

*/

PARROT_WARN_UNUSED_RESULT
PARROT_CAN_RETURN_NULL
static char *
char_offset_int(PARROT_INTERP, ARGIN(PMC *pmc), INTVAL ix, ARGOUT(int *type))
{
    ASSERT_ARGS(char_offset_int)
    size_t offs, n;
    ix *= 3;

    if (!PARROT_UNMANAGEDSTRUCT(pmc)->init)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNEXPECTED_NULL,
            "Missing struct initializer");

    n = (size_t)VTABLE_elements(interp, PARROT_UNMANAGEDSTRUCT(pmc)->init);

    if ((size_t)ix >= n)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNEXPECTED_NULL,
            "Non existent elements in struct ix = %d n=%d", (int)ix, (int)n);

    /* use structure init */
    *type = (int) VTABLE_get_integer_keyed_int(interp,
        PARROT_UNMANAGEDSTRUCT(pmc)->init, ix);

    offs  = (size_t) VTABLE_get_integer_keyed_int(interp,
        PARROT_UNMANAGEDSTRUCT(pmc)->init, ix + 2);

    return ((char *)VTABLE_get_pointer(interp, pmc)) + offs;
}

/*

=item C<static INTVAL key_2_idx(PARROT_INTERP, PMC *pmc, PMC *key)>

Returns the index for the element associated with key C<*key>. Raises an
exception if the key doesn't exist.

=cut

*/

PARROT_WARN_UNUSED_RESULT
static INTVAL
key_2_idx(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(PMC *key))
{
    ASSERT_ARGS(key_2_idx)
    int ix = 0;

    if (!PARROT_UNMANAGEDSTRUCT(pmc)->init)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_UNEXPECTED_NULL,
            "Missing struct initializer");

    if (PObj_get_FLAGS(key) & KEY_string_FLAG) {
        PMC * const types = PARROT_UNMANAGEDSTRUCT(pmc)->init;

        if (types->vtable->base_type == enum_class_OrderedHash) {
            const Hash * const hash = (Hash *)VTABLE_get_pointer(interp,
                    VTABLE_get_pmc(interp, types));
            const HashBucket * const b = Parrot_hash_get_bucket(interp, hash,
                    Parrot_hash_key_from_string(interp, hash, VTABLE_get_string(interp, key)));

            if (!b)
                Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_KEY_NOT_FOUND,
                    "key doesn't exist");

            ix = b - hash->buckets;
        }
        else
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "unhandled type aggregate");

        ix /= 3;
    }
    else
        ix = VTABLE_get_integer(interp, key);

    return ix;
}

/*

=item C<static char * char_offset_key(PARROT_INTERP, PMC *pmc, PMC *key, int
*type)>

Returns the pointer for the element associated with key C<*key>, and
sets the element type in C<*type>.

=cut

*/

PARROT_WARN_UNUSED_RESULT
PARROT_CAN_RETURN_NULL
static char *
char_offset_key(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(PMC *key), ARGOUT(int *type))
{
    ASSERT_ARGS(char_offset_key)
    size_t count, size, max;
    int    ix;
    PMC   *next, *init;
    char  *p;

#ifdef STRUCT_DEBUG
    trace_key_dump(interp, key);
#endif

    ix   = key_2_idx(interp, pmc, key);
    next = Parrot_key_next(interp, key);
    p    = char_offset_int(interp, pmc, ix, type);
    ix  *= 3;

    if (!next)
        return p;

    if (PObj_get_FLAGS(next) & KEY_integer_FLAG)
        count = VTABLE_get_integer(interp, next);
    else
        count = 1;

    init = PARROT_UNMANAGEDSTRUCT(pmc)->init;
    max  = (size_t)VTABLE_get_integer_keyed_int(interp, init, ix + 1);

#ifdef STRUCT_DEBUG
    Parrot_io_eprintf(interp, " count = %d ix = %d max = %d\n",
            (int)count, (int)ix, (int)max);
#endif

    if (*type == enum_type_struct_ptr || *type == enum_type_struct) {
        /* the struct PMC is hanging off the initializer element
         * as property "_struct" */
        PMC * const ptr = VTABLE_get_pmc_keyed_int(interp, init, ix);
        init            = Parrot_pmc_getprop(interp, ptr, CONST_STRING(interp, "_struct"));

        PARROT_ASSERT(init
            && (init->vtable->base_type == enum_class_UnManagedStruct
            ||  init->vtable->base_type == enum_class_ManagedStruct));

        /* array of structs */
        if (max > 1) {
            size_t offs;
            if (Parrot_key_next(interp, next))
                next = Parrot_key_next(interp, next);

            offs = VTABLE_get_integer(interp, init);

#ifdef STRUCT_DEBUG
            Parrot_io_eprintf(interp, "offs = %d\n", (int)offs);
#endif

            p += offs * count;
        }

        if (init->vtable->base_type == enum_class_UnManagedStruct) {
            /* now point ptr to the real data */
            if (*type == enum_type_struct_ptr) {
                /* that is either a pointer */
                VTABLE_set_pointer(interp, init, *(void**)p);
            }

            /* or just an offset for nested structs */
            else
                VTABLE_set_pointer(interp, init, p);
        }
        else if (init->vtable->base_type == enum_class_ManagedStruct
             && *type                    == enum_type_struct_ptr) {
            /* a nested struct pointer belonging to us
             * p is the location of the struct pointer in the
             * outer struct, the inner is at PMC_data(init) */

            *(void **)p = VTABLE_get_pointer(interp, init);
        }

        return char_offset_key(interp, init, next, type);
    }

    if (count >= max)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_OUT_OF_BOUNDS,
            "Non existent array element in struct: "
            "count = %d max=%d", (int)count, (int)max);

    size = data_types[*type - enum_first_type].size;
    return p + count * size;
}


/*

=item C<static INTVAL ret_int(PARROT_INTERP, const char *p, int type)>

Returns the element of type C<type> starting at C<*p> as an C<INTVAL>.

If, for example, C<char> or c<short> type size doesn't match, this will fail
we need some more configure support for type sizes.

=cut

*/

PARROT_WARN_UNUSED_RESULT
static INTVAL
ret_int(PARROT_INTERP, ARGIN(const char *p), int type)
{
    ASSERT_ARGS(ret_int)
    switch (type) {
      case enum_type_INTVAL:
        return *(const INTVAL*) p;

#if INT_SIZE == 4
      case enum_type_int32:
      case enum_type_uint32:
#endif

#if INT_SIZE == 8
      case enum_type_int64:
      case enum_type_uint64:
#endif

      case enum_type_int:
        return *(const int *)p;

#if (LONG_SIZE == 4) && !(INT_SIZE == 4) /* Unlikely combination. */
      case enum_type_int32:
      case enum_type_uint32:
#endif

#if (LONG_SIZE == 8) && !(INT_SIZE == 8)
      case enum_type_int64:
      case enum_type_uint64:
#endif

      case enum_type_long:
      case enum_type_ulong:
        return *(const long *)p;

#if SHORT_SIZE == 2
      case enum_type_int16:
      case enum_type_uint16:
#endif

        /* If SHORT_SIZE != 2 getting int16s requires extra tricks. */
      case enum_type_short:
        return *(const short *)p;
      case enum_type_uint8:
      case enum_type_uchar:
        {
            const unsigned char *uc = (const unsigned char *)p;
            return (INTVAL)*uc;
        }
      case enum_type_int8:
      case enum_type_char:
        return *p;
      default:
        Parrot_ex_throw_from_c_args(interp, NULL, 1,
                "returning unhandled int type in struct");
    }
}

/*

=item C<static FLOATVAL ret_float(PARROT_INTERP, const char *p, int type)>

Returns the element of type C<type> starting at C<*p> as a C<FLOATVAL>.

=cut

*/

PARROT_WARN_UNUSED_RESULT
static FLOATVAL
ret_float(PARROT_INTERP, ARGIN(const char *p), int type)
{
    ASSERT_ARGS(ret_float)
    switch (type) {
      case enum_type_FLOATVAL:
        return (FLOATVAL) *(const FLOATVAL *)p;
      case enum_type_float:
        return (FLOATVAL) *(const float *)p;
      case enum_type_double:
        return (FLOATVAL) *(const double *)p;
      default:
        Parrot_ex_throw_from_c_args(interp, NULL, 1,
                "returning unhandled float type in struct");
    }
}

/*

=item C<static STRING* ret_string(PARROT_INTERP, char *p, int type)>

Returns the element of type C<type> starting at C<*p> as a Parrot string.

=cut

*/

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static STRING*
ret_string(PARROT_INTERP, ARGIN(char *p), int type)
{
    ASSERT_ARGS(ret_string)
    if (type == enum_type_cstr) {
        const char * const cstr = *(const char **) p;
        if (cstr) {
            const size_t len  = strlen(cstr);
            /* TODO
               Can't assume that the C string is constant during the
               life of the returned parrot string, so can't be
               flagged as external in general.
               If avoid the copy for speed reasons is a concern,
               other type or flag must be added.
               Need to provide some way to specify charset and encoding.
             */
            return Parrot_str_new_init(interp, cstr, len,
                    Parrot_latin1_encoding_ptr, 0);
        }
        else
            return STRINGNULL;
    }

    Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
        "returning unhandled string type in struct");
}

/*

=item C<static PMC* ret_pmc(PARROT_INTERP, PMC *pmc, char *p, int type, INTVAL
idx)>

Returns the element of type C<type> starting at C<*p> as a PMC.

=cut

*/

PARROT_WARN_UNUSED_RESULT
PARROT_CANNOT_RETURN_NULL
static PMC*
ret_pmc(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(char *p), int type, INTVAL idx)
{
    ASSERT_ARGS(ret_pmc)
    PMC * const init = PARROT_UNMANAGEDSTRUCT(pmc)->init;
    PMC * const ptr  = VTABLE_get_pmc_keyed_int(interp, init, idx * 3);
    PMC *ret  = NULL;

    switch (type) {
      case enum_type_func_ptr:
        /* this is a raw function pointer - not a PMC */
        ret  = *(PMC**) p;

        /* now check if initializer has a signature attached */
        if (PMC_metadata(ptr)) {
            STRING *signature_str = CONST_STRING(interp, "_signature");
            PMC * const sig = Parrot_pmc_getprop(interp, ptr, signature_str);
            if (VTABLE_defined(interp, sig)) {
                STRING * const sig_str = VTABLE_get_string(interp, sig);
                ret             = Parrot_pmc_new(interp, enum_class_NCI);
                VTABLE_set_pointer_keyed_str(interp, ret, sig_str,
                            *(PMC **)p);
            }
        }

        return ret;
      case enum_type_struct_ptr:
        /* check the metadata for an initializer */

        /* grab the struct from the metadata */
        if (PMC_metadata(ptr))
            ret = Parrot_pmc_getprop(interp, ptr, CONST_STRING(interp, "_struct"));
        else {
            Parrot_ex_throw_from_c_args(interp, NULL,
                    EXCEPTION_INVALID_OPERATION,
                    "no initializer available for nested struct");
        }

        VTABLE_set_pointer(interp, ret, *(void**)p);

        return ret;
      default:
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                    "returning unhandled pmc type (%d) in struct", type);
    }
}

/*

=item C<static void set_int(PARROT_INTERP, char *p, int type, INTVAL value)>

Set int value based on int type

=cut

*/

static void
set_int(PARROT_INTERP, ARGOUT(char *p), int type, INTVAL value)
{
    ASSERT_ARGS(set_int)
    switch (type) {
      case enum_type_uint8:
      case enum_type_int8:
      case enum_type_char:
      case enum_type_uchar:
        *(char *)p = (char)(value & 0xff);
        break;
      case enum_type_INTVAL:
        *(INTVAL *)p = value;
        break;
      case enum_type_int:
        *(int *)p = value;
        break;
      case enum_type_int16:
      case enum_type_uint16:
      case enum_type_short:
        *(short *)p = (short)value;
        break;
      default:
        Parrot_ex_throw_from_c_args(interp, NULL, 1,
                "setting unhandled int type in struct");
        break;
    }
}

/*

=item C<static void set_float(PARROT_INTERP, char *p, int type, FLOATVAL value)>

Sets the value of the element of type C<type> starting at C<*p> to
C<value>.

=cut

*/

static void
set_float(PARROT_INTERP, ARGOUT(char *p), int type, FLOATVAL value)
{
    ASSERT_ARGS(set_float)
    switch (type) {
      case enum_type_FLOATVAL:
        *(FLOATVAL *)p = (FLOATVAL)value;
        break;
      case enum_type_float:
        *(float *)p = (float)value;
        break;
      case enum_type_double:
        *(double *)p = (double)value;
        break;
      default:
        Parrot_ex_throw_from_c_args(interp, NULL, 1,
                "setting unhandled float type in struct");
        break;
    }
}

/*

=item C<static void set_string(PARROT_INTERP, char *p, int type, STRING *value)>

Sets the value of the element of type C<type> starting at C<*p> to
C<*value>.

=cut

*/

static void
set_string(PARROT_INTERP, ARGOUT(char *p), int type, ARGIN(STRING *value))
{
    ASSERT_ARGS(set_string)
    if (type == enum_type_cstr) {
        /* assuming 0-terminated C-string here;
         * we can't use Parrot_str_to_cstring easily */
        char * const cstr  = (char *)Buffer_bufstart(value);
        *(char **)p = cstr;
    }
    else
        Parrot_ex_throw_from_c_args(interp, NULL, 1,
            "setting unhandled string type in struct (%d)", type);
}

/*

=item C<static int calc_align(PARROT_INTERP, PMC *type_pmc, int type, size_t
offs)>

Alignment of contained structures is the alignment of the biggest item in that
C<struct>.

i386: C<long long> or C<double> is aligned on 4.

This is recursive as structure definitions.

=cut

*/

PARROT_WARN_UNUSED_RESULT
static int
calc_align(PARROT_INTERP, ARGIN(PMC *type_pmc), int type, size_t offs)
{
    ASSERT_ARGS(calc_align)
    int  align       = data_types[type - enum_first_type].size;
    PMC *nested      = NULL;
    PMC *nested_init = NULL;

    if (type == enum_type_struct || type == enum_type_struct_ptr) {
        /* a nested structs alignment is the biggest item in it
         * so go through that struct and check */
        nested = Parrot_pmc_getprop(interp, type_pmc, CONST_STRING(interp, "_struct"));
        nested_init = PARROT_UNMANAGEDSTRUCT(nested)->init;
    }
    if (type == enum_type_struct) {
        INTVAL i;
        const INTVAL n = (size_t)VTABLE_elements(interp, nested_init);
        int    a_max   = 0;

        if (n % 3)
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                    "Illegal initializer for struct");

        for (i = 0; i < n; i += 3) {
            PMC * const nested_type_pmc = VTABLE_get_pmc_keyed_int(interp,
                    nested_init, i);
            const int nested_type     = (int)VTABLE_get_integer(interp,
                nested_type_pmc);
            const int new_offs = calc_align(interp, nested_type_pmc, nested_type, offs);

            if (new_offs > a_max)
                a_max = new_offs;
        }

        align = a_max;
    }

    if (align && offs % align) {
        const int diff = align - (offs % align);
        offs += diff;
    }

    if (type == enum_type_struct || type == enum_type_struct_ptr)
        calc_offsets(interp, nested, nested_init, 0);

    return offs;
}

/*

=item C<static void calc_offsets(PARROT_INTERP, PMC *pmc, PMC *value, size_t
toff)>

Calculates the offsets for the C<struct>. See C<init_pmc()> for a description
of C<*value>.

=cut

*/

static void
calc_offsets(PARROT_INTERP, ARGIN(PMC *pmc), ARGIN(PMC *value), size_t toff)
{
    ASSERT_ARGS(calc_offsets)
    STRING * const _struct = CONST_STRING(interp, "_struct");
    const INTVAL n         = (size_t)VTABLE_elements(interp, value);
    INTVAL  i;

    if (n % 3)
        Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Illegal initializer for struct");

    for (i = 0; i < n; i += 3) {
        PMC * const type_pmc = VTABLE_get_pmc_keyed_int(interp, value, i);
        const int type       = (int)VTABLE_get_integer(interp, type_pmc);

        int  count    = (int)VTABLE_get_integer_keyed_int(interp, value, i + 1);
        int  offs     = (int)VTABLE_get_integer_keyed_int(interp, value, i + 2);
        int  size;

        if (type < enum_first_type || type >= enum_last_type)
            Parrot_ex_throw_from_c_args(interp, NULL,
                EXCEPTION_INVALID_OPERATION,
                "Illegal type (%d) in initializer for struct", type);

        if (count <= 0) {
            count = 1;
            VTABLE_set_integer_keyed_int(interp, value, i + 1, count);
        }

        if (offs <= 0) {
            offs = toff = calc_align(interp, type_pmc, type, toff);
            VTABLE_set_integer_keyed_int(interp, value, i + 2, offs);
        }
        else
            toff = offs;

        if (type == enum_type_struct) {
            PMC * const nested = Parrot_pmc_getprop(interp, type_pmc, _struct);
            size        = VTABLE_get_integer(interp, nested);
        }
        else if (type == enum_type_struct_ptr) {
            PMC * const nested = Parrot_pmc_getprop(interp, type_pmc, _struct);

            /* must clone this struct so as not to share its memory */
            if (nested->vtable->base_type == enum_class_ManagedStruct)
                Parrot_pmc_setprop(interp, type_pmc, _struct,
                    VTABLE_clone(interp, nested));

            size = data_types[type - enum_first_type].size;
        }
        else
            size = data_types[type - enum_first_type].size;

        toff += count * size;

        /* set / allocate size */
        if (i == n - 3)
            VTABLE_set_integer_native(interp, pmc, (INTVAL)toff);
    }
}

pmclass UnManagedStruct auto_attrs no_ro {
    ATTR void   *ptr;   /* the struct that this UnManagedStruct isn't managing */
    ATTR PMC    *init;  /* the initializer used with this UnManagedStruct */
    ATTR INTVAL  size;  /* the size of the struct */

/*

=back

=head2 Vtable Functions

=over 4

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

Initialize the struct with some data.

C<*value> should be an array of triples of:

=over 4

=item 0

The datatype. See the C<enum> in F<include/parrot/datatypes.h>.

=item 1

The count.

=item 2

The offset.

=back

=cut

*/

    VTABLE void init_pmc(PMC *value) {
        SELF.set_pmc(value);
    }

/*

=item C<PMC *clone()>

Return a clone of this PMC.

=cut

*/

    VTABLE PMC *clone() {
        PMC * const clone = Parrot_pmc_new(INTERP, enum_class_UnManagedStruct);

        memmove(PMC_data(clone), PMC_data(SELF),
                sizeof (Parrot_UnManagedStruct_attributes));
        PARROT_UNMANAGEDSTRUCT(clone)->init =
            VTABLE_clone(INTERP, PARROT_UNMANAGEDSTRUCT(SELF)->init);
        return clone;
    }

/*

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

Sets C<*value> (see C<init_pmc()> and calculates the offsets.

=cut

*/

    VTABLE void set_pmc(PMC *value) {
        PARROT_UNMANAGEDSTRUCT(SELF)->init = value;
        PObj_custom_mark_SET(SELF);
        calc_offsets(INTERP, SELF, value, 0);
    }

/*

=item C<void mark()>

Marks the C<struct> as live.

=cut

*/

    VTABLE void mark() {
        PMC * const init = PARROT_UNMANAGEDSTRUCT(SELF)->init;
        Parrot_gc_mark_PMC_alive(INTERP, init);
    }

/*

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

Returns whether the two C<struct>s are equivalent.

=cut

*/

    VTABLE INTVAL is_equal(PMC *value) {
        return (SELF->vtable   == value->vtable
        &&      SELF.get_pointer() == VTABLE_get_pointer(INTERP, value));
    }

/*

=item C<INTVAL get_bool()>

=item C<INTVAL defined()>

Returns whether the C<struct> is defined.

=cut

*/

    VTABLE INTVAL get_bool() {
        return SELF.get_pointer() != NULL;
    }

    VTABLE INTVAL defined() {
        return SELF.get_pointer() != NULL;
    }

/*

=item C<INTVAL get_integer()>

Returns the size of the C<struct>.

=cut

*/

    VTABLE INTVAL get_integer() {
        UNUSED(INTERP);
        return PARROT_UNMANAGEDSTRUCT(SELF)->size;
    }

/*

=item C<void set_integer_native(INTVAL size)>

Sets the size of the C<struct>.

=cut

*/

    VTABLE void set_integer_native(INTVAL size) {
        UNUSED(INTERP);
        PARROT_UNMANAGEDSTRUCT(SELF)->size = size;
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL ix)>

Returns the integer value at index C<ix>.

=cut

*/

    VTABLE INTVAL get_integer_keyed_int(INTVAL ix) {
        int   type;
        const char * const p = char_offset_int(INTERP, SELF, ix, &type);
        return ret_int(INTERP, p, type);
    }

/*

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

Returns the integer value associated with C<*key>.

=cut

*/

    VTABLE INTVAL get_integer_keyed(PMC *key) {
        int   type;
        const char * const p = char_offset_key(INTERP, SELF, key, &type);
        return ret_int(INTERP, p, type);
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

Returns the floating-point value at index C<ix>.

=cut

*/

    VTABLE FLOATVAL get_number_keyed_int(INTVAL key) {
        int   type;
        const char * const p = char_offset_int(INTERP, SELF, key, &type);
        return ret_float(INTERP, p, type);
    }

/*

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

Returns the floating-point value associated with C<*key>.

=cut

*/

    VTABLE FLOATVAL get_number_keyed(PMC *key) {
        int   type;
        const char * const p = char_offset_key(INTERP, SELF, key, &type);
        return ret_float(INTERP, p, type);
    }

/*

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

Returns the Parrot string value at index C<ix>.

=cut

*/

    VTABLE STRING *get_string_keyed_int(INTVAL key) {
        int   type;
        char * const p = char_offset_int(INTERP, SELF, key, &type);
        return ret_string(INTERP, p, type);
    }

/*

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

Returns the Parrot string value associated with C<*key>.

=cut

*/

    VTABLE STRING *get_string_keyed(PMC *key) {
        int   type;
        char * const p = char_offset_key(INTERP, SELF, key, &type);
        return ret_string(INTERP, p, type);
    }

/*

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

Returns the PMC value at index C<ix>.

=cut

*/

    VTABLE PMC *get_pmc_keyed_int(INTVAL key) {
        int   type;
        char * const p = char_offset_int(INTERP, SELF, key, &type);
        return ret_pmc(INTERP, SELF, p, type, key);
    }

/*

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

Returns the PMC value associated with C<*key>.

=cut

*/

    VTABLE PMC *get_pmc_keyed(PMC *key) {
        int   type;
        char * const p = char_offset_key(INTERP, SELF, key, &type);
        return ret_pmc(INTERP, SELF, p, type, key_2_idx(INTERP, SELF, key));
    }

/*

=item C<void *get_pointer()>

Returns the pointer to the actual C C<struct>.

=cut

*/

    VTABLE void *get_pointer() {
        UNUSED(INTERP);
        return PARROT_UNMANAGEDSTRUCT(SELF)->ptr;
    }

/*

=item C<void set_pointer(void *value)>

Set the pointer to the actual C C<struct>.

=cut

*/

    VTABLE void set_pointer(void *value) {
        UNUSED(INTERP);
        PARROT_UNMANAGEDSTRUCT(SELF)->ptr = value;
    }

/*

=item C<void set_integer_keyed_int(INTVAL ix, INTVAL value)>

Sets the value of the element at index C<ix> to C<value>.

=cut

*/

    VTABLE void set_integer_keyed_int(INTVAL ix, INTVAL value) {
        int   type;
        char * const p = char_offset_int(INTERP, SELF, ix, &type);
        set_int(INTERP, p, type, value);
    }

/*

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

Sets the value of the element associated with key C<*key> to C<value>.

May cause segfaults if value is out of bounds.

=cut

*/

    VTABLE void set_integer_keyed(PMC *key, INTVAL value) {
        int   type;
        char * const p = char_offset_key(INTERP, SELF, key, &type);
        set_int(INTERP, p, type, value);
    }

/*

=item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>

Sets the value of the element at index C<ix> to C<value>.

=cut

*/

    VTABLE void set_number_keyed_int(INTVAL key, FLOATVAL value) {
        int   type;
        char * const p = char_offset_int(INTERP, SELF, key, &type);
        set_float(INTERP, p, type, value);
    }

/*

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

Sets the value of the element associated with key C<*key> to C<value>.

=cut

*/

    VTABLE void set_number_keyed(PMC *key, FLOATVAL value) {
        int type;
        char * const p = char_offset_key(INTERP, SELF, key, &type);
        set_float(INTERP, p, type, value);
    }

/*

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

Sets the value of the element at index C<key> to C<*value>.

=cut

*/

    VTABLE void set_string_keyed_int(INTVAL key, STRING *value) {
        int   type;
        char * const p = char_offset_int(INTERP, SELF, key, &type);
        set_string(INTERP, p, type, value);
    }

/*

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

Sets the value of the element associated with key C<*key> to C<*value>.

=cut

*/

    VTABLE void set_string_keyed(PMC *key, STRING *value) {
        int   type;
        char * const p = char_offset_key(INTERP, SELF, key, &type);
        set_string(INTERP, p, type, value);
    }

/*

=back

=head2 Methods

=over 4

=item C<as_string(string encodingname)>

Create a string from the buffer, assumed to be a C string, with the encoding
specified. If the encoding is omitted or null, use platform encoding.

=cut

*/

    METHOD as_string(STRING *encodingname :optional) {
        const char * content = (const char *)SELF.get_pointer();
        STRING * const result = Parrot_str_new_from_cstring(INTERP, content, encodingname);
        RETURN(STRING result);
    }

}

/*

=back

=head1 SEE ALSO

F<docs/pmc/struct.pod>

=cut

*/

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