/*
Copyright (C) 2001-2009, Parrot Foundation.
$Id: managedstruct.pmc 46300 2010-05-05 03:58:50Z jimmy $

=head1 DESCRIPTION

src/pmc/managedstruct.pmc - Memory-managed C struct

=head1 DESCRIPTION

C<ManagedStruct> extends C<UnManagedStruct> to provide a class to hold C
C<struct> values that Parrot is responsible for disposing of.

=head2 Methods

=over 4

=cut

*/

typedef void (*custom_free_func_t)(PARROT_INTERP, void *ptr, void *priv);
typedef PMC * (*custom_clone_func_t)(PARROT_INTERP, PMC *ptr, void *priv);

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

pmclass ManagedStruct extends UnManagedStruct auto_attrs {
    /* if custom_free_func and ptr (inherited from UnManagedStruct) are both set,
     * custom_free_func is called before the normal destroy() function does any
     * work.
     */
    ATTR void (*custom_free_func)(PARROT_INTERP, void *, void *);
    ATTR void *custom_free_priv;
    /* if custom_clone_func is set, it will be called *instead* of the normal
     * clone() function logic.
     */
    ATTR PMC * (*custom_clone_func)(PARROT_INTERP, PMC *ptr, void *priv);
    ATTR void *custom_clone_priv;

/*

=item C<void init()>

Initializes an empty struct.

=cut

*/

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

/*

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

Initializes the struct with C<*value>.

=cut

*/

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

/*

=item C<void destroy()>

Destroys the struct, freeing the allocated memory.

If the "custom_free_func" attribute is set, it is called to free the pointer.
Otherwise, mem_gc_free() is used.

=cut

*/

    VTABLE void destroy() {
        void *ptr = PARROT_MANAGEDSTRUCT(SELF)->ptr;
        if (ptr) {
            custom_free_func_t free_func = PARROT_MANAGEDSTRUCT(SELF)->custom_free_func;
            if (free_func) {
                void *free_data = PARROT_MANAGEDSTRUCT(SELF)->custom_free_priv;
                free_func(INTERP, ptr, free_data);
            } else
                mem_gc_free(INTERP, ptr);
        }
    }

/*

=item C<void set_integer_native(INTVAL value)>

(Re)allocates C<value> bytes for the struct.

=cut

*/

    VTABLE void set_integer_native(INTVAL value) {
        Parrot_ManagedStruct_attributes * attrs = PARROT_MANAGEDSTRUCT(SELF);

        if (attrs->ptr && !value) {
            mem_gc_free(INTERP, attrs->ptr);
            attrs->ptr  = NULL;
            attrs->size = 0;
        }
        else if (value && !attrs->ptr) {
            attrs->ptr  = Parrot_gc_allocate_memory_chunk_with_interior_pointers(
                    INTERP, (size_t)value);
            attrs->size = value;
        }
        else if (value && attrs->ptr) {
            if (attrs->size != value) {
                attrs->ptr =
                    Parrot_gc_reallocate_memory_chunk_with_interior_pointers(INTERP,
                        attrs->ptr, (size_t)value, attrs->size);
                attrs->size = value;
            }
        }

        return;
    }


/*

=item C<PMC * clone()>

Creates a clone of this PMC; clones any unmanaged memory it holds too.

If the "custom_clone_func" attribute is set, it is called to clone the PMC.
Otherwise, a basic (shallow copy) clone is performed, as there's no general way
of knowing how to make a deep copy of the pointer contents.

=cut

*/

    VTABLE PMC *clone() {
        custom_clone_func_t clone_func = PARROT_MANAGEDSTRUCT(SELF)->custom_clone_func;
        PMC *dest;
        if (clone_func) {
            void *clone_data = PARROT_MANAGEDSTRUCT(SELF)->custom_clone_priv;
            return clone_func(INTERP, SELF, clone_data);
        }
        dest = Parrot_pmc_new_init(INTERP, SELF->vtable->base_type,
            PARROT_MANAGEDSTRUCT(SELF)->init);

        if (PARROT_MANAGEDSTRUCT(SELF)->ptr)
            memmove(PARROT_MANAGEDSTRUCT(dest)->ptr,
                    PARROT_MANAGEDSTRUCT(SELF)->ptr,
                    PARROT_MANAGEDSTRUCT(SELF)->size);

        return dest;
    }

}

/*

=back

=head1 HISTORY

Initial revision by sean 2002/08/04.

=cut

*/

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