2 Copyright (C) 2010, Parrot Foundation.
7 src/pmc/imageio.pmc - ImageIO PMC
11 Freezes and thaws other PMCs.
21 #define GROW_TO_16_BYTE_BOUNDARY(size) ((size) + ((size) % 16 ? 16 - (size) % 16 : 0))
23 /* preallocate freeze image for aggregates with this estimation */
24 #define FREEZE_BYTES_PER_ITEM 9
26 /* macros/constants to handle packing/unpacking of PMC IDs and flags
27 * the 2 LSBs are used for flags, all other bits are used for PMC ID
29 #define PackID_new(id, flags) (((UINTVAL)(id) * 4) | ((UINTVAL)(flags) & 3))
30 #define PackID_get_PMCID(id) ((UINTVAL)(id) / 4)
31 #define PackID_set_PMCID(lv, id) (lv) = PackID_new((id), PackID_get_FLAGS(lv))
32 #define PackID_get_FLAGS(id) ((UINTVAL)(id) & 3)
33 #define PackID_set_FLAGS(lv, flags) (lv) = PackID_new(PackID_get_PMCID(lv), (flags))
36 enum_PackID_normal = 0,
40 /* HEADERIZER HFILE: none */
41 /* HEADERIZER BEGIN: static */
42 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
44 static void create_buffer(PARROT_INTERP,
45 ARGIN_NULLOK(PMC *pmc),
47 __attribute__nonnull__(1)
48 __attribute__nonnull__(3)
52 static void ensure_buffer_size(PARROT_INTERP, ARGIN(PMC *io), size_t len)
53 __attribute__nonnull__(1)
54 __attribute__nonnull__(2);
57 PARROT_CANNOT_RETURN_NULL
58 PARROT_WARN_UNUSED_RESULT
59 static opcode_t * GET_VISIT_CURSOR(ARGIN(const PMC *pmc))
60 __attribute__nonnull__(1);
62 PARROT_WARN_UNUSED_RESULT
63 PARROT_CAN_RETURN_NULL
65 static PMC* id_list_get(PARROT_INTERP, ARGIN(const PMC *io), UINTVAL id)
66 __attribute__nonnull__(1)
67 __attribute__nonnull__(2);
70 static void INC_VISIT_CURSOR(ARGMOD(PMC *pmc), UINTVAL inc)
71 __attribute__nonnull__(1)
74 PARROT_WARN_UNUSED_RESULT
76 static INTVAL INFO_HAS_DATA(ARGIN(const PMC *io))
77 __attribute__nonnull__(1);
80 static void SET_VISIT_CURSOR(ARGMOD(PMC *pmc), ARGIN(const char *cursor))
81 __attribute__nonnull__(1)
82 __attribute__nonnull__(2)
85 #define ASSERT_ARGS_create_buffer __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
86 PARROT_ASSERT_ARG(interp) \
87 , PARROT_ASSERT_ARG(info))
88 #define ASSERT_ARGS_ensure_buffer_size __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
89 PARROT_ASSERT_ARG(interp) \
90 , PARROT_ASSERT_ARG(io))
91 #define ASSERT_ARGS_GET_VISIT_CURSOR __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
92 PARROT_ASSERT_ARG(pmc))
93 #define ASSERT_ARGS_id_list_get __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
94 PARROT_ASSERT_ARG(interp) \
95 , PARROT_ASSERT_ARG(io))
96 #define ASSERT_ARGS_INC_VISIT_CURSOR __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
97 PARROT_ASSERT_ARG(pmc))
98 #define ASSERT_ARGS_INFO_HAS_DATA __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
99 PARROT_ASSERT_ARG(io))
100 #define ASSERT_ARGS_SET_VISIT_CURSOR __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
101 PARROT_ASSERT_ARG(pmc) \
102 , PARROT_ASSERT_ARG(cursor))
103 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
104 /* HEADERIZER END: static */
108 =item C<static opcode_t * GET_VISIT_CURSOR(const PMC *pmc)>
115 PARROT_CANNOT_RETURN_NULL
116 PARROT_WARN_UNUSED_RESULT
118 GET_VISIT_CURSOR(ARGIN(const PMC *pmc))
120 ASSERT_ARGS(GET_VISIT_CURSOR)
122 char * const buf = (char *)Buffer_bufstart(PARROT_IMAGEIO(pmc)->buffer);
123 const size_t pos = PARROT_IMAGEIO(pmc)->pos;
124 return (opcode_t *)(buf + pos);
129 =item C<static void SET_VISIT_CURSOR(PMC *pmc, const char *cursor)>
138 SET_VISIT_CURSOR(ARGMOD(PMC *pmc), ARGIN(const char *cursor))
140 ASSERT_ARGS(SET_VISIT_CURSOR)
142 const char * const bufstart = (const char *)Buffer_bufstart(PARROT_IMAGEIO(pmc)->buffer);
143 PARROT_IMAGEIO(pmc)->pos = (cursor - bufstart);
148 =item C<static void INC_VISIT_CURSOR(PMC *pmc, UINTVAL inc)>
157 INC_VISIT_CURSOR(ARGMOD(PMC *pmc), UINTVAL inc)
159 ASSERT_ARGS(INC_VISIT_CURSOR)
161 PARROT_IMAGEIO(pmc)->pos += inc;
165 #define BYTECODE_SHIFT_OK(pmc) PARROT_ASSERT( \
166 PARROT_IMAGEIO(pmc)->pos <= PARROT_IMAGEIO(pmc)->input_length)
170 =item C<static void create_buffer(PARROT_INTERP, PMC *pmc, PMC *info)>
177 create_buffer(PARROT_INTERP, ARGIN_NULLOK(PMC *pmc), ARGMOD(PMC *info))
179 ASSERT_ARGS(create_buffer)
183 if (!PMC_IS_NULL(pmc)) {
184 STRING * const array = CONST_STRING(interp, "array");
185 STRING * const hash = CONST_STRING(interp, "hash");
188 if (VTABLE_does(interp, pmc, array) || VTABLE_does(interp, pmc, hash))
189 items += VTABLE_elements(interp, pmc);
191 len = items * FREEZE_BYTES_PER_ITEM;
194 len = FREEZE_BYTES_PER_ITEM;
196 PARROT_IMAGEIO(info)->buffer =
197 Parrot_gc_new_bufferlike_header(interp, sizeof (Buffer));
198 Parrot_gc_allocate_buffer_storage_aligned(interp,
199 PARROT_IMAGEIO(info)->buffer, len);
200 SET_VISIT_CURSOR(info,
201 (const char *)Buffer_bufstart(PARROT_IMAGEIO(info)->buffer));
206 =item C<static void ensure_buffer_size(PARROT_INTERP, PMC *io, size_t len)>
208 Checks the size of the buffer to see if it can accommodate C<len> more
209 bytes. If not, expands the buffer.
217 ensure_buffer_size(PARROT_INTERP, ARGIN(PMC *io), size_t len)
219 ASSERT_ARGS(ensure_buffer_size)
221 Buffer * const buf = PARROT_IMAGEIO(io)->buffer;
222 const size_t used = PARROT_IMAGEIO(io)->pos;
223 const int need_free = Buffer_buflen(buf) - used - len;
225 /* grow by factor 1.5 or such */
226 if (need_free <= 16) {
227 size_t new_size = (size_t) (Buffer_buflen(buf) * 1.5);
229 if (new_size < Buffer_buflen(buf) - need_free + 512)
230 new_size = Buffer_buflen(buf) - need_free + 512;
232 Parrot_gc_reallocate_buffer_storage(interp, buf, new_size);
233 PARROT_ASSERT(Buffer_buflen(buf) - used - len >= 15);
236 #ifndef DISABLE_GC_DEBUG
237 Parrot_gc_compact_memory_pool(interp);
243 =item C<static INTVAL INFO_HAS_DATA(const PMC *io)>
249 PARROT_WARN_UNUSED_RESULT
252 INFO_HAS_DATA(ARGIN(const PMC *io))
254 ASSERT_ARGS(INFO_HAS_DATA)
256 return PARROT_IMAGEIO(io)->pos < PARROT_IMAGEIO(io)->input_length;
261 =item C<static PMC* id_list_get(PARROT_INTERP, const PMC *io, UINTVAL id)>
267 PARROT_WARN_UNUSED_RESULT
268 PARROT_CAN_RETURN_NULL
271 id_list_get(PARROT_INTERP, ARGIN(const PMC *io), UINTVAL id)
273 ASSERT_ARGS(id_list_get)
275 return VTABLE_get_pmc_keyed_int(interp, PARROT_IMAGEIO(io)->todo, id - 1);
278 pmclass ImageIO auto_attrs {
279 ATTR Buffer *buffer; /* buffer to store the image */
280 ATTR size_t pos; /* current read/write buf position */
281 ATTR size_t input_length;
283 ATTR PMC *seen; /* seen hash */
284 ATTR PMC *todo; /* todo list */
285 ATTR UINTVAL id; /* freze ID of PMC */
286 ATTR struct PackFile *pf;
287 ATTR PackFile_ConstTable *pf_ct;
311 PARROT_IMAGEIO(SELF)->seen = PMCNULL;
312 PARROT_IMAGEIO(SELF)->todo =
313 Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
315 PObj_flag_CLEAR(private1, SELF);
317 PObj_custom_mark_SET(SELF);
323 =item C<void destroy()>
330 VTABLE void destroy() {
331 PackFile_destroy(INTERP, PARROT_IMAGEIO(SELF)->pf);
332 PARROT_IMAGEIO(SELF)->pf = NULL;
340 Marks the PMC as alive.
346 PObj * const buffer = (PObj *)(PARROT_IMAGEIO(SELF)->buffer);
348 Parrot_gc_mark_PObj_alive(INTERP, buffer);
349 Parrot_gc_mark_PMC_alive(INTERP, PARROT_IMAGEIO(SELF)->todo);
350 Parrot_gc_mark_PMC_alive(INTERP, PARROT_IMAGEIO(SELF)->seen);
356 =item C<STRING *get_string()>
358 Returns the content of the image as a string.
364 VTABLE STRING *get_string() {
365 return Parrot_str_new_from_buffer(INTERP,
366 PARROT_IMAGEIO(SELF)->buffer,
367 PARROT_IMAGEIO(SELF)->pos);
373 =item C<VTABLE PMC *get_pmc()>
375 Gets the result PMC after a thaw.
381 VTABLE PMC *get_pmc() {
382 return VTABLE_get_pmc_keyed_int(INTERP,
383 (PARROT_IMAGEIO(SELF))->todo, 0);
389 =item C<VTABLE PMC *get_iter()>
391 Get the C<todo> list for this freeze/thaw for iterating over.
397 VTABLE PMC *get_iter() {
398 return PARROT_IMAGEIO(SELF)->todo;
403 =item C<VTABLE INTVAL get_integer()>
405 Returns the flags describing the visit action.
411 VTABLE INTVAL get_integer() {
412 return PARROT_IMAGEIO(SELF)->what;
418 =item C<VTABLE void push_integer(INTVAL v)>
420 Pushes the integer C<v> onto the end of the image.
426 VTABLE void push_integer(INTVAL v) {
427 const size_t len = PF_size_integer() * sizeof (opcode_t);
428 ensure_buffer_size(INTERP, SELF, len);
429 SET_VISIT_CURSOR(SELF,
430 (const char *)PF_store_integer(GET_VISIT_CURSOR(SELF), v));
436 =item C<VTABLE void push_float(FLOATVAL v)>
438 Pushes the float C<v> onto the end of the image.
444 VTABLE void push_float(FLOATVAL v) {
445 const size_t len = PF_size_number() * sizeof (opcode_t);
446 ensure_buffer_size(INTERP, SELF, len);
447 SET_VISIT_CURSOR(SELF,
448 (const char *)PF_store_number(GET_VISIT_CURSOR(SELF), &v));
454 =item C<VTABLE void push_string(STRING *v)>
456 Pushes the string C<*v> onto the end of the image.
462 VTABLE void push_string(STRING *v) {
463 if (PObj_flag_TEST(private1, SELF)) {
464 /* store a reference to constant table entry of string */
465 PMC * const v_pmc = key_new_string(interp, v);
466 PackFile_ConstTable * const table = PARROT_IMAGEIO(SELF)->pf_ct;
468 PackFile_ConstTable_rlookup(INTERP, table, v_pmc, PFC_STRING);
471 STATICSELF.push_integer(idx);
475 /* XXX handle cases where the PMC has changed after
476 * Parrot_freeze_strings was called eg: :immediate subs */
477 STATICSELF.push_integer(-1);
481 * PANIC(INTERP, "string not previously in constant table "
482 * "when freezing to packfile"); */
486 const size_t len = PF_size_string(v) * sizeof (opcode_t);
487 ensure_buffer_size(INTERP, SELF, len);
488 SET_VISIT_CURSOR(SELF,
489 (const char *)PF_store_string(GET_VISIT_CURSOR(SELF), v));
496 =item C<VTABLE void push_pmc(PMC *v)>
498 Pushes a reference to pmc C<*v> onto the end of the image. If C<*v>
499 hasn't been seen yet, it is also pushed onto the todo list.
505 VTABLE void push_pmc(PMC *v) {
509 PARROT_ASSERT(PARROT_IMAGEIO(SELF)->what == VISIT_FREEZE_NORMAL);
511 if (PMC_IS_NULL(v)) {
513 packid_type = enum_PackID_seen;
516 Hash * const hash = (Hash *)VTABLE_get_pointer(INTERP,
517 PARROT_IMAGEIO(SELF)->seen);
518 HashBucket * const b = parrot_hash_get_bucket(INTERP, hash, v);
521 id = (UINTVAL)b->value;
522 packid_type = enum_PackID_seen;
525 ++PARROT_IMAGEIO(SELF)->id; /* next id to freeze */
526 id = PARROT_IMAGEIO(SELF)->id;
527 packid_type = enum_PackID_normal;
531 SELF.push_integer(PackID_new(id, packid_type));
533 if (packid_type == enum_PackID_normal) {
534 Hash * const hash = (Hash *)VTABLE_get_pointer(INTERP,
535 PARROT_IMAGEIO(SELF)->seen);
539 /* workaround to keep ParrotInterpreter PBC hack working */
540 if (v->vtable->base_type == enum_class_ParrotInterpreter)
541 PObj_flag_CLEAR(private1, SELF);
544 PObj_is_object_TEST(v)
545 ? (INTVAL) enum_class_Object
546 : v->vtable->base_type);
548 parrot_hash_put(INTERP, hash, v, (void *)id);
549 VTABLE_push_pmc(INTERP, PARROT_IMAGEIO(SELF)->todo, v);
556 =item C<void set_pointer(void *value)>
558 Sets the constant table of this ImageIO PMC.
564 VTABLE void set_pointer(void *value) {
565 PObj_flag_SET(private1, SELF);
566 PARROT_IMAGEIO(SELF)->pf_ct = (PackFile_ConstTable *)value;
572 =item C<VTABLE INTVAL shift_integer()>
574 Removes and returns an integer from the start of the image.
580 VTABLE INTVAL shift_integer() {
581 /* inlining PF_fetch_integer speeds up PBC thawing measurably */
582 const PackFile *pf = PARROT_IMAGEIO(SELF)->pf;
583 const opcode_t *pos = GET_VISIT_CURSOR(SELF);
584 const unsigned char *stream = (const unsigned char *)pos;
585 const INTVAL i = pf->fetch_iv(stream);
587 SET_VISIT_CURSOR(SELF, (const char *)pos + pf->header->wordsize);
588 BYTECODE_SHIFT_OK(SELF);
595 =item C<VTABLE FLOATVAL shift_float()>
597 Removes and returns an number from the start of the image.
603 VTABLE FLOATVAL shift_float() {
604 const opcode_t *pos = GET_VISIT_CURSOR(SELF);
605 FLOATVAL f = PF_fetch_number(PARROT_IMAGEIO(SELF)->pf, &pos);
606 SET_VISIT_CURSOR(SELF, (const char *)pos);
607 BYTECODE_SHIFT_OK(SELF);
614 =item C<VTABLE STRING* shift_string()>
616 Removes and returns a string from the start of the image.
622 VTABLE STRING *shift_string() {
623 if (PObj_flag_TEST(private1, SELF)) {
624 const INTVAL i = STATICSELF.shift_integer();
627 PackFile_ConstTable *table = PARROT_IMAGEIO(SELF)->pf_ct;
629 if (!table->constants[i].type)
630 Parrot_ex_throw_from_c_args(interp, NULL,
631 EXCEPTION_MALFORMED_PACKFILE,
632 "Reference to constant not yet unpacked %d", i);
633 return table->constants[i].u.string;
637 * only got here because constant table doesn't contain the string
638 * fallback on inline strings
643 const opcode_t * pos = GET_VISIT_CURSOR(SELF);
644 STRING * const s = PF_fetch_string(INTERP,
645 PARROT_IMAGEIO(SELF)->pf, &pos);
646 SET_VISIT_CURSOR(SELF, (const char *)pos);
647 BYTECODE_SHIFT_OK(SELF);
655 =item C<static PMC *shift_pmc()>
657 Removes and returns a reference to a pmc from the start of the image.
663 VTABLE PMC *shift_pmc() {
664 const UINTVAL n = SELF.shift_integer();
665 const INTVAL id = PackID_get_PMCID(n);
666 const int packid_flags = PackID_get_FLAGS(n);
669 PARROT_ASSERT(PARROT_IMAGEIO(SELF)->what == VISIT_THAW_NORMAL);
671 switch (packid_flags) {
672 case enum_PackID_seen:
673 if (id) /* got a non-NULL PMC */
674 pmc = id_list_get(INTERP, SELF, id);
676 case enum_PackID_normal:
678 PMC * const todo = PARROT_IMAGEIO(SELF)->todo;
679 const INTVAL type = VTABLE_shift_integer(INTERP, SELF);
682 == VTABLE_elements(INTERP, PARROT_IMAGEIO(SELF)->todo));
684 if (type <= 0 || type > INTERP->n_vtable_max)
685 Parrot_ex_throw_from_c_args(INTERP, NULL, 1,
686 "Unknown PMC type to thaw %d", type);
688 /* workaround to keep ParrotInterpreter PBC hack working */
689 if (type == enum_class_ParrotInterpreter)
690 PObj_flag_CLEAR(private1, SELF);
692 pmc = Parrot_pmc_new_noinit(INTERP, type);
694 VTABLE_set_pmc_keyed_int(INTERP, todo, id - 1, pmc);
698 Parrot_ex_throw_from_c_args(INTERP, NULL, 1,
699 "Unknown PMC id args thaw %d", packid_flags);
706 VTABLE void set_pmc(PMC *p)
708 PARROT_IMAGEIO(SELF)->what = VISIT_FREEZE_NORMAL;
710 create_buffer(INTERP, p, SELF);
711 if (PObj_flag_TEST(private1, SELF)) {
712 PARROT_IMAGEIO(SELF)->pf = PARROT_IMAGEIO(SELF)->pf_ct->base.pf;
715 const UINTVAL header_length =
716 GROW_TO_16_BYTE_BOUNDARY(PACKFILE_HEADER_BYTES);
718 PARROT_IMAGEIO(SELF)->pf = PackFile_new(INTERP, 0);
719 PObj_custom_destroy_SET(SELF);
721 ensure_buffer_size(INTERP, SELF, header_length);
722 mem_sys_memcopy(GET_VISIT_CURSOR(SELF),
723 PARROT_IMAGEIO(SELF)->pf->header, PACKFILE_HEADER_BYTES);
724 INC_VISIT_CURSOR(SELF, header_length);
727 PARROT_IMAGEIO(SELF)->seen = Parrot_pmc_new(INTERP, enum_class_Hash);
728 VTABLE_set_pointer(INTERP, PARROT_IMAGEIO(SELF)->seen,
729 parrot_new_intval_hash(INTERP));
731 STATICSELF.push_pmc(p);
732 Parrot_visit_loop_visit(INTERP, SELF);
735 VTABLE void set_string_native(STRING *image) {
737 PARROT_IMAGEIO(SELF)->what = VISIT_THAW_NORMAL;
738 PARROT_IMAGEIO(SELF)->buffer = (Buffer *)image;
740 PARROT_ASSERT(image->_bufstart == image->strstart);
742 SET_VISIT_CURSOR(SELF,
743 (const char *)Buffer_bufstart(PARROT_IMAGEIO(SELF)->buffer));
744 PARROT_IMAGEIO(SELF)->input_length = image->strlen;
746 if (PObj_flag_TEST(private1, SELF)) {
747 PARROT_IMAGEIO(SELF)->pf = PARROT_IMAGEIO(SELF)->pf_ct->base.pf;
750 const UINTVAL header_length =
751 GROW_TO_16_BYTE_BOUNDARY(PACKFILE_HEADER_BYTES);
754 PARROT_IMAGEIO(SELF)->pf = PackFile_new(INTERP, 0);
755 PObj_custom_destroy_SET(SELF);
757 PARROT_IMAGEIO(SELF)->pf->options |= PFOPT_PMC_FREEZE_ONLY;
758 unpacked_length = PackFile_unpack(INTERP, PARROT_IMAGEIO(SELF)->pf,
759 GET_VISIT_CURSOR(SELF), PARROT_IMAGEIO(SELF)->input_length);
762 INC_VISIT_CURSOR(SELF, header_length);
764 Parrot_ex_throw_from_c_args(INTERP, NULL,
765 EXCEPTION_INVALID_STRING_REPRESENTATION,
766 "PackFile header failed during unpack");
769 unused = STATICSELF.shift_pmc();
770 Parrot_visit_loop_visit(INTERP, SELF);
772 /* we're done reading the image */
773 PARROT_ASSERT(!INFO_HAS_DATA(SELF));
774 Parrot_visit_loop_thawfinish(INTERP, SELF);
790 * c-file-style: "parrot"
792 * vim: expandtab shiftwidth=4: