/*
Copyright (C) 2010, Parrot Foundation.
$Id: imageiosize.pmc 48447 2010-08-13 07:36:33Z plobsing $

=head1 NAME

src/pmc/imageiosize.pmc - ImageIOSize PMC

=head1 DESCRIPTION

Gets the size of an ImageIO image without the allocation costs.

=cut

*/

#define GROW_TO_16_BYTE_BOUNDARY(size) ((size) + ((size) % 16 ? 16 - (size) % 16 : 0))

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

static void visit_todo_list_freeze(PARROT_INTERP,
    ARGIN(PMC* pmc),
    ARGMOD(PMC* info))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(* info);

#define ASSERT_ARGS_visit_todo_list_freeze __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(pmc) \
    , PARROT_ASSERT_ARG(info))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
/* HEADERIZER END: static */

/*

=head1 METHODS

=over 4

=item C<static void visit_todo_list_freeze(PARROT_INTERP, PMC* pmc, PMC* info)>

=back

=cut

   */

static void
visit_todo_list_freeze(PARROT_INTERP, ARGIN(PMC* pmc), ARGMOD(PMC* info))
{
    int is_new = 0;

    if (!PMC_IS_NULL(pmc)) {
        Hash * const hash = (Hash *)VTABLE_get_pointer(interp, PARROT_IMAGEIOSIZE(info)->seen);
        HashBucket * const b = parrot_hash_get_bucket(interp, hash, pmc);
        is_new = !b;
    }

    VTABLE_push_integer(interp, info, 0);

    if (is_new) {
        Hash * const hash = (Hash *)VTABLE_get_pointer(interp, PARROT_IMAGEIOSIZE(info)->seen);

        parrot_hash_put(interp, hash, pmc, pmc);

        /* workaround to keep ParrotInterpreter PBC hack working */
        if (pmc->vtable->base_type == enum_class_ParrotInterpreter)
            PObj_flag_CLEAR(private1, info);

        VTABLE_push_integer(interp, info, pmc->vtable->base_type);
        VTABLE_push_pmc(interp, PARROT_IMAGEIOSIZE(info)->todo, pmc);
    }
}

pmclass ImageIOSize auto_attrs {
    ATTR PMC                        *seen; /* seen hash */
    ATTR PMC                        *todo; /* todo list */
    ATTR struct PackFile            *pf;
    ATTR struct PackFile_ConstTable *pf_ct;
    ATTR INTVAL                      size;

/*

=head1 VTABLES

=over 4

=cut

*/

/*

=item C<void init()>

Initializes the PMC.

=cut

*/
    VTABLE void init() {
        PARROT_IMAGEIOSIZE(SELF)->todo  = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
        PARROT_IMAGEIOSIZE(SELF)->pf    = PackFile_new(INTERP, 0);
        PARROT_IMAGEIOSIZE(SELF)->pf_ct = NULL;
        PARROT_IMAGEIOSIZE(SELF)->size  = 0;

        PARROT_IMAGEIOSIZE(SELF)->seen = Parrot_pmc_new(INTERP, enum_class_Hash);
        VTABLE_set_pointer(INTERP, PARROT_IMAGEIOSIZE(SELF)->seen,
            parrot_new_intval_hash(INTERP));

        PObj_flag_CLEAR(private1, SELF);

        PObj_custom_mark_destroy_SETALL(SELF);
    }

/*

=item C<void init_pmc()>

Initializes the PMC with a pre-existing C<PackFile_ConstTable>.

=cut

*/
    VTABLE void init_pmc(PMC *pf_ct) {
        PARROT_IMAGEIOSIZE(SELF)->todo  = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
        PARROT_IMAGEIOSIZE(SELF)->pf    = PackFile_new(INTERP, 0);
        PARROT_IMAGEIOSIZE(SELF)->pf_ct =
            (PackFile_ConstTable *)VTABLE_get_pointer(INTERP, pf_ct);
        PARROT_IMAGEIOSIZE(SELF)->size  = 0;

        PARROT_IMAGEIOSIZE(SELF)->seen = Parrot_pmc_new(INTERP, enum_class_Hash);
        VTABLE_set_pointer(INTERP, PARROT_IMAGEIOSIZE(SELF)->seen,
            parrot_new_intval_hash(INTERP));

        PObj_flag_SET(private1, SELF);

        PObj_custom_mark_destroy_SETALL(SELF);
    }


/*

=item C<void destroy()>

Destroys the PMC.

=cut

*/
    VTABLE void destroy() {
        PackFile_destroy(INTERP, PARROT_IMAGEIOSIZE(SELF)->pf);
    }

/*

=item C<void mark()>

Marks the PMC as alive.

=cut

*/
    VTABLE void mark() {
        Parrot_gc_mark_PMC_alive(INTERP, PARROT_IMAGEIOSIZE(SELF)->todo);
        Parrot_gc_mark_PMC_alive(INTERP, PARROT_IMAGEIOSIZE(SELF)->seen);
    }

/*

=item C<VTABLE PMC *get_pmc()>

Gets the result PMC after a thaw.

=cut

*/

    VTABLE PMC *get_pmc() {
        PMC * const ret = Parrot_pmc_new_init_int(INTERP, enum_class_Integer,
                PARROT_IMAGEIOSIZE(SELF)->size);
        return ret;
    }

/*

=item C<VTABLE PMC *get_iter()>

Get the C<todo> list for this freeze/thaw for iterating over.

=cut

*/

    VTABLE PMC *get_iter() {
        return PARROT_IMAGEIOSIZE(SELF)->todo;
    }

/*

=item C<VTABLE INTVAL get_integer()>

Returns the flags describing the visit action

=cut

*/

    VTABLE INTVAL get_integer() {
        return VISIT_FREEZE_NORMAL;
    }

/*

=item C<VTABLE void push_integer(INTVAL v)>

Pushes the integer C<v> onto the end of the image.

=cut

*/

    VTABLE void push_integer(INTVAL v) {
        const size_t len = PF_size_integer() * sizeof (opcode_t);
        PARROT_IMAGEIOSIZE(SELF)->size += len;
    }


/*

=item C<VTABLE void push_float(FLOATVAL v)>

Pushes the float C<v> onto the end of the image.

=cut

*/

    VTABLE void push_float(FLOATVAL v)
    {
        const size_t len = PF_size_number() * sizeof (opcode_t);
        PARROT_IMAGEIOSIZE(SELF)->size += len;
    }


/*

=item C<VTABLE void push_string(STRING *v)>

Pushes the string C<*v> onto the end of the image.

=cut

*/

    VTABLE void push_string(STRING *v) {
        if (PObj_flag_TEST(private1, SELF)) {
            /* store a reference to constant table entry of string */
            PMC *v_pmc = key_new_string(interp, v);
            PackFile_ConstTable *table = PARROT_IMAGEIOSIZE(SELF)->pf_ct;
            int idx = PackFile_ConstTable_rlookup(INTERP, table, v_pmc, PFC_STRING);

            if (idx >= 0) {
                STATICSELF.push_integer(idx);
                return;
            }

            /* XXX
             * handle cases where the PMC has changed after Parrot_freeze_strings was called
             * eg: :immediate subs
             */
            STATICSELF.push_integer(-1);
            /* TODO
             * should really be:
             * PANIC(INTERP, "string not previously in constant table when freezing to packfile");
             */
        }

        {
            const size_t len = PF_size_string(v) * sizeof (opcode_t);
            PARROT_IMAGEIOSIZE(SELF)->size += len;
        }
    }

/*

=item C<VTABLE void push_pmc(PMC *v)>

Pushes a reference to pmc C<*v> onto the end of the image. If C<*v>
hasn't been seen yet, it is also pushed onto the todo list.

=cut

*/

    VTABLE void push_pmc(PMC *v) {
        visit_todo_list_freeze(INTERP, v, SELF);
    }

    VTABLE void set_pmc(PMC *p)
    {
        if (!PObj_flag_TEST(private1, SELF)) {
            UINTVAL header_length = GROW_TO_16_BYTE_BOUNDARY(PACKFILE_HEADER_BYTES);
            PARROT_IMAGEIOSIZE(SELF)->size += header_length;
        }

        visit_todo_list_freeze(INTERP, p, SELF);
        Parrot_visit_loop_visit(INTERP, SELF);
    }

/*

=back

=cut

*/

}

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