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 VTABLE void push_float(FLOATVAL v)
Pushes the float VTABLE void push_string(STRING *v)
Pushes the string VTABLE void push_pmc(PMC *v)
Pushes a reference to pmc 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.
v
onto the end of the image.
v
onto the end of the image.
*v
onto the end of the image.
*v
onto the end of the image. If *v
hasn't been seen yet, it is also pushed onto the todo list.