NAME

src/pmc/imageio.pmc - ImageIO PMC

DESCRIPTION

Freezes and thaws other PMCs.

*/

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

/* preallocate freeze image for aggregates with this estimation */ #define FREEZE_BYTES_PER_ITEM 9

/* macros/constants to handle packing/unpacking of PMC IDs and flags * the 2 LSBs are used for flags, all other bits are used for PMC ID */ #define PackID_new(id, flags) (((UINTVAL)(id) * 4) | ((UINTVAL)(flags) & 3)) #define PackID_get_PMCID(id) ((UINTVAL)(id) / 4) #define PackID_set_PMCID(lv, id) (lv) = PackID_new((id), PackID_get_FLAGS(lv)) #define PackID_get_FLAGS(id) ((UINTVAL)(id) & 3) #define PackID_set_FLAGS(lv, flags) (lv) = PackID_new(PackID_get_PMCID(lv), (flags))

enum { enum_PackID_normal = 0, enum_PackID_seen = 1, };

PARROT_INLINE static opcode_t * GET_VISIT_CURSOR(PMC *pmc){ char *buf = (char *)Buffer_bufstart(PARROT_IMAGEIO(pmc)->buffer); size_t pos = PARROT_IMAGEIO(pmc)->pos; return (opcode_t *)(buf + pos); }

PARROT_INLINE static void SET_VISIT_CURSOR(PMC *pmc, char *cursor) { char *bufstart = (char *)Buffer_bufstart(PARROT_IMAGEIO(pmc)->buffer); PARROT_IMAGEIO(pmc)->pos = (cursor - bufstart); }

PARROT_INLINE static void INC_VISIT_CURSOR(PMC *pmc, UINTVAL inc) { PARROT_IMAGEIO(pmc)->pos += inc; }

#define BYTECODE_SHIFT_OK(pmc) PARROT_ASSERT( \ PARROT_IMAGEIO(pmc)->pos <= PARROT_IMAGEIO(pmc)->input_length)

static void create_buffer(PARROT_INTERP, PMC *pmc, PMC *info) { INTVAL len;

    if (!PMC_IS_NULL(pmc)) {
        STRING *array = CONST_STRING(interp, "array");
        STRING *hash  = CONST_STRING(interp, "hash");
        INTVAL  items = 1;

        if (VTABLE_does(interp, pmc, array) || VTABLE_does(interp, pmc, hash)) {
            items += VTABLE_elements(interp, pmc);
        }
        len = items * FREEZE_BYTES_PER_ITEM;
    }
    else
        len = FREEZE_BYTES_PER_ITEM;

    PARROT_IMAGEIO(info)->buffer =
        Parrot_gc_new_bufferlike_header(interp, sizeof (Buffer));
    Parrot_gc_allocate_buffer_storage_aligned(interp,
        PARROT_IMAGEIO(info)->buffer, len);
    SET_VISIT_CURSOR(info, (char *)Buffer_bufstart(PARROT_IMAGEIO(info)->buffer));
}

/* static void ensure_buffer_size(PARROT_INTERP, PMC *io, size_t len)

Checks the size of the buffer to see if it can accommodate 'len' more bytes. If not, expands the buffer.

*/

PARROT_INLINE static void ensure_buffer_size(PARROT_INTERP, ARGIN(PMC *io), size_t len) { Buffer *buf = PARROT_IMAGEIO(io)->buffer; const size_t used = PARROT_IMAGEIO(io)->pos; const int need_free = Buffer_buflen(buf) - used - len;

    /* grow by factor 1.5 or such */
    if (need_free <= 16) {
        size_t new_size = (size_t) (Buffer_buflen(buf) * 1.5);
        if (new_size < Buffer_buflen(buf) - need_free + 512)
            new_size = Buffer_buflen(buf) - need_free + 512;
        Parrot_gc_reallocate_buffer_storage(interp, buf, new_size);
        PARROT_ASSERT(Buffer_buflen(buf) - used - len >= 15);
    }

#ifndef DISABLE_GC_DEBUG Parrot_gc_compact_memory_pool(INTERP); #endif

}

PARROT_INLINE static INTVAL INFO_HAS_DATA(ARGIN(PMC *io)) { return PARROT_IMAGEIO(io)->pos < PARROT_IMAGEIO(io)->input_length; }

PARROT_INLINE static PMC* id_list_get(PARROT_INTERP, PMC *io, UINTVAL id) { return VTABLE_get_pmc_keyed_int(interp, PARROT_IMAGEIO(io)->id_list, id); }

PARROT_INLINE static void visit_todo_list_thaw(PARROT_INTERP, SHIM(PMC* pmc_not_used), ARGIN(PMC* info)) { UINTVAL n = VTABLE_shift_integer(interp, info); UINTVAL id = PackID_get_PMCID(n); int packid_flags = PackID_get_FLAGS(n); PMC *pmc = PMCNULL;

    PARROT_ASSERT(PARROT_IMAGEIO(info)->what == VISIT_THAW_NORMAL);

    switch (packid_flags) {
      case enum_PackID_seen:
        if (id) /* got a non-NULL PMC */
            pmc = id_list_get(interp, info, id);
        break;
      case enum_PackID_normal:
        {
            INTVAL type = VTABLE_shift_integer(interp, info);
            if (type <= 0 || type > interp->n_vtable_max)
                Parrot_ex_throw_from_c_args(interp, NULL, 1, "Unknown PMC type to thaw %d", type);

            pmc = pmc_new_noinit(interp, type);
            VTABLE_thaw(interp, pmc, info);

            {
                PMC * const todo    = PARROT_IMAGEIO(info)->todo;
                PMC * const id_list = PARROT_IMAGEIO(info)->id_list;
                VTABLE_set_pmc_keyed_int(interp, id_list, id, pmc);
                /* remember nested aggregates depth first */
                VTABLE_push_pmc(interp, todo, pmc);
            }
        }
        break;
      default:
        Parrot_ex_throw_from_c_args(interp, NULL, 1, "Unknown PMC id args thaw %d", packid_flags);
        break;
    }

    *(PARROT_IMAGEIO(info)->thaw_ptr) = pmc;
}

static void visit_todo_list_freeze(PARROT_INTERP, PMC* pmc, PMC* info) { UINTVAL id; int packid_type;

    PARROT_ASSERT(PARROT_IMAGEIO(info)->what == VISIT_FREEZE_NORMAL);

    if (PMC_IS_NULL(pmc)) {
        id   = 0;
        packid_type = enum_PackID_seen;
    }
    else {
        Hash *hash = (Hash *)VTABLE_get_pointer(interp, PARROT_IMAGEIO(info)->seen);
        HashBucket * const b = parrot_hash_get_bucket(interp, hash, pmc);
        if (b) {
            id = (UINTVAL) b->value;
            packid_type = enum_PackID_seen;
        }
        else {
            PARROT_IMAGEIO(info)->id++; /* next id to freeze */
            id = PARROT_IMAGEIO(info)->id;
            packid_type = enum_PackID_normal;
        }
    }

    VTABLE_push_integer(interp, info, PackID_new(id, packid_type));

    if (packid_type == enum_PackID_normal) {
        Hash *hash = (Hash *)VTABLE_get_pointer(interp, PARROT_IMAGEIO(info)->seen);
        PARROT_ASSERT(pmc);
        VTABLE_push_integer(interp, info,
                PObj_is_object_TEST(pmc) ? enum_class_Object : pmc->vtable->base_type);
        parrot_hash_put(interp, hash, pmc, (void *)id);
        VTABLE_push_pmc(interp, PARROT_IMAGEIO(info)->todo, pmc);
        VTABLE_freeze(interp, pmc, info);
    }
}

static void visit_loop_todo_list(PARROT_INTERP, PMC *current, PMC *info) { PMC * const todo = PARROT_IMAGEIO(info)->todo; const int thawing = PARROT_IMAGEIO(info)->what == VISIT_THAW_NORMAL;

    (PARROT_IMAGEIO(info)->visit_pmc_now)(interp, current, info);

    /* can't cache upper limit, visit may append items */
    while (VTABLE_get_bool(interp, todo)) {
        current = VTABLE_pop_pmc(interp, todo);
        if (!current)
            Parrot_ex_throw_from_c_args(interp, NULL, 1,
                    "NULL current PMC in visit_loop_todo_list");

        PARROT_ASSERT(current->vtable);

        VTABLE_visit(interp, current, info);

        VISIT_PMC(interp, info, PMC_metadata(current));
    }

    if (thawing)
        /* we're done reading the image */
        PARROT_ASSERT(!INFO_HAS_DATA(info));

    if (thawing) {
        /* on thawing call thawfinish for each processed PMC */
        const INTVAL n = VTABLE_elements(interp, PARROT_IMAGEIO(info)->id_list);
        int          i;

        /*
         * Thaw in reverse order. We have to fully thaw younger PMCs
         * before use them in older.
         *
         * XXX There are no younger or older pmcs in a directed graph
         *     that allows cycles. Any code that requires a specific
         *      order here is likely broken.
         */
        for (i = n-1; i >= 0; --i) {
            current = VTABLE_get_pmc_keyed_int(interp, PARROT_IMAGEIO(info)->id_list, i);
            if (!PMC_IS_NULL(current))
                VTABLE_thawfinish(interp, current, info);
        }
    }
}

pmclass ImageIO auto_attrs { ATTR visit_f visit_pmc_now; ATTR Buffer *buffer; /* buffer to store the image */ ATTR size_t pos; /* current read/write position in buffer */ ATTR size_t input_length; ATTR INTVAL what; ATTR PMC **thaw_ptr; /* where to thaw a new PMC */ ATTR PMC *seen; /* seen hash */ ATTR PMC *todo; /* todo list */ ATTR PMC *id_list; /* seen list used by thaw */ ATTR UINTVAL id; /* freze ID of PMC */ ATTR INTVAL extra_flags; /* concerning to extra */ ATTR struct PackFile *pf;

/*

VTABLES

void init()
Initializes the PMC.
void destroy()
Destroys the PMC.
void mark()
Marks the PMC as alive.
STRING *get_string()
Returns the content of the image as a string.
VTABLE void set_pointer()
Sets the location where to thaw a new PMC.
VTABLE INTVAL get_integer()
Returns the flags describing the visit action
VTABLE void push_integer(INTVAL v)
Pushes the integer v onto the end of the image.
VTABLE void push_float(FLOATVAL v)
Pushes the float v onto the end of the image.
VTABLE void push_string(STRING *v)
Pushes the string *v onto the end of the image.
VTABLE void push_pmc(PMC *v)
Pushes a reference to pmc *v onto the end of the image. If *v hasn't been seen yet, it is also pushed onto the todo list.
VTABLE INTVAL shift_integer()
Removes and returns an integer from the start of the image.
VTABLE FLOATVAL shift_float()
Removes and returns an number from the start of the image.
VTABLE STRING* shift_string()
Removes and returns a string from the start of the image.
static PMC *shift_pmc()
Removes and returns a reference to a pmc from the start of the image.