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.