1/*
2Copyright (C) 2010-2014, Parrot Foundation.
3
4=head1 NAME
5
6src/pmc/imageiothaw.pmc - ImageIOThaw PMC
7
8=head1 DESCRIPTION
9
10Thaws PMCs from packfile images.
11
12=head1 VTABLES
13
14=over 4
15
16=cut
17
18*/
19
20#include "parrot/imageio.h"
21
22#define BYTECODE_SHIFT_OK(interp, pmc) PARROT_ASSERT( \
23    PARROT_IMAGEIOTHAW(pmc)->curs <= (opcode_t *) \
24    (PARROT_IMAGEIOTHAW(pmc)->img->strstart + \
25    Parrot_str_byte_length((interp), PARROT_IMAGEIOTHAW(pmc)->img)))
26
27
28/* HEADERIZER HFILE: none */
29
30pmclass ImageIOThaw auto_attrs {
31    ATTR STRING              *img;
32    ATTR opcode_t            *curs;
33    ATTR PMC                 *seen;
34    ATTR PMC                 *todo;
35    ATTR PackFile            *pf;
36    ATTR PackFile_ConstTable *pf_ct;
37
38/*
39
40=item C<void init()>
41
42Initializes the PMC.
43
44=cut
45
46*/
47
48    VTABLE void init() {
49        PARROT_IMAGEIOTHAW(SELF)->seen =
50            Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
51        PARROT_IMAGEIOTHAW(SELF)->todo =
52            Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray);
53
54        PObj_flag_CLEAR(private1, SELF);
55
56        PObj_custom_mark_SET(SELF);
57    }
58
59
60/*
61
62=item C<void destroy()>
63
64Destroys the PMC.
65
66=cut
67
68*/
69
70    VTABLE void destroy() :no_wb {
71        Parrot_pf_destroy(INTERP, PARROT_IMAGEIOTHAW(SELF)->pf);
72        PARROT_IMAGEIOTHAW(SELF)->pf = NULL;
73    }
74
75
76/*
77
78=item C<void mark()>
79
80Marks the PMC as alive.
81
82=cut
83
84*/
85
86    VTABLE void mark() :no_wb {
87        Parrot_gc_mark_STRING_alive(INTERP, PARROT_IMAGEIOTHAW(SELF)->img);
88        Parrot_gc_mark_PMC_alive(INTERP, PARROT_IMAGEIOTHAW(SELF)->seen);
89        Parrot_gc_mark_PMC_alive(INTERP, PARROT_IMAGEIOTHAW(SELF)->todo);
90    }
91
92
93/*
94
95=item C<void set_string_native(STRING *image)>
96
97Thaws the PMC contained in C<image>.
98
99=cut
100
101*/
102
103    VTABLE void set_string_native(STRING *image) {
104        if (!PObj_external_TEST(image))
105            Parrot_str_pin(INTERP, image);
106
107        PARROT_IMAGEIOTHAW(SELF)->img  = image;
108        PARROT_IMAGEIOTHAW(SELF)->curs = (opcode_t *)image->strstart;
109
110        if (PObj_flag_TEST(private1, SELF)) {
111            PARROT_IMAGEIOTHAW(SELF)->pf = PARROT_IMAGEIOTHAW(SELF)->pf_ct->base.pf;
112        }
113        else {
114            const UINTVAL header_length =
115                 GROW_TO_16_BYTE_BOUNDARY(PACKFILE_HEADER_BYTES);
116            int unpacked_length;
117
118            PARROT_IMAGEIOTHAW(SELF)->pf   = Parrot_pf_new(INTERP, 0);
119            PObj_custom_destroy_SET(SELF);
120
121            PARROT_IMAGEIOTHAW(SELF)->pf->options |= PFOPT_PMC_FREEZE_ONLY;
122            unpacked_length = Parrot_pf_unpack(INTERP, PARROT_IMAGEIOTHAW(SELF)->pf,
123                                PARROT_IMAGEIOTHAW(SELF)->curs,
124                                Parrot_str_byte_length(interp, image));
125
126            if (unpacked_length)
127                PARROT_IMAGEIOTHAW(SELF)->curs += header_length / sizeof (opcode_t*);
128            else
129                Parrot_ex_throw_from_c_noargs(INTERP,
130                        EXCEPTION_INVALID_STRING_REPRESENTATION,
131                        "PackFile header failed during unpack");
132        }
133
134        STATICSELF.shift_pmc();
135
136        {
137            PMC * const seen = PARROT_IMAGEIOTHAW(SELF)->seen;
138            PMC * const todo = PARROT_IMAGEIOTHAW(SELF)->todo;
139            INTVAL i, n;
140
141            for (i = 0; i < VTABLE_elements(INTERP, todo); i++) {
142                const INTVAL idx = VTABLE_get_integer_keyed_int(INTERP, todo, i);
143                PMC * const current = VTABLE_get_pmc_keyed_int(INTERP, seen, idx);
144                if (PMC_IS_NULL(current))
145                    Parrot_ex_throw_from_c_args(interp, NULL,
146                            EXCEPTION_MALFORMED_PACKFILE,
147                            "NULL current PMC at %d in thaw",
148                            (int)i);
149
150                VTABLE_thaw(INTERP,  current, SELF);
151                VTABLE_visit(INTERP, current, SELF);
152                PMC_metadata(current) = SELF.shift_pmc();
153            }
154
155            n = i;
156
157            /* we're done reading the image */
158            PARROT_ASSERT(image->strstart + Parrot_str_byte_length(interp, image) ==
159                        (char *)PARROT_IMAGEIOTHAW(SELF)->curs);
160
161            for (i = 0; i < n; i++) {
162                const INTVAL idx = VTABLE_get_integer_keyed_int(INTERP, todo, i);
163                PMC * const current = VTABLE_get_pmc_keyed_int(INTERP, seen, idx);
164                VTABLE_thawfinish(INTERP, current, SELF);
165            }
166        }
167
168        if (!PObj_external_TEST(image))
169            Parrot_str_unpin(INTERP, image);
170    }
171
172
173/*
174
175=item C<PMC *get_pmc()>
176
177Get the thawed PMC.
178
179=cut
180
181*/
182
183    VTABLE PMC *get_pmc() :no_wb {
184        if (PObj_flag_TEST(private1, SELF))
185            return PARROT_IMAGEIOTHAW(SELF)->seen;
186        else
187            return VTABLE_get_pmc_keyed_int(INTERP, (PARROT_IMAGEIOTHAW(SELF))->seen, 0);
188    }
189
190
191/*
192
193=item C<INTVAL get_integer()>
194
195Get the visit action.
196
197=cut
198
199*/
200
201    VTABLE INTVAL get_integer() :no_wb {
202        UNUSED(INTERP)
203        UNUSED(SELF)
204        return VISIT_THAW_NORMAL;
205    }
206
207
208/*
209
210=item C<void set_pointer(void *value)>
211
212Set an exterior constant table to use for cross-referencing constants.
213
214=cut
215
216*/
217
218    VTABLE void set_pointer(void *value) {
219        PObj_flag_SET(private1, SELF);
220        PARROT_IMAGEIOTHAW(SELF)->pf_ct = (PackFile_ConstTable *)value;
221    }
222
223
224/*
225
226=item C<INTVAL shift_integer()>
227
228Retrieve an integer as the next item from the image.
229
230=cut
231
232*/
233
234    VTABLE INTVAL shift_integer() :manual_wb {
235        /* inlining PF_fetch_integer speeds up PBC thawing measurably */
236        PackFile * const pf = PARROT_IMAGEIOTHAW(SELF)->pf;
237        const unsigned char *stream    = (const unsigned char *)PARROT_IMAGEIOTHAW(SELF)->curs;
238        const INTVAL         i         = pf->fetch_iv(stream);
239        DECL_CONST_CAST;
240        PARROT_IMAGEIOTHAW(SELF)->curs = (opcode_t *)PARROT_const_cast(unsigned char *,
241                                                                    stream + pf->header->wordsize);
242        BYTECODE_SHIFT_OK(INTERP, SELF);
243        RETURN(INTVAL i);
244    }
245
246
247/*
248
249=item C<FLOATVAL shift_float()>
250
251Retrieve a float as the next item from the image.
252
253=cut
254
255*/
256
257    VTABLE FLOATVAL shift_float() :manual_wb {
258        PackFile * const pf  = PARROT_IMAGEIOTHAW(SELF)->pf;
259        const opcode_t *curs = PARROT_IMAGEIOTHAW(SELF)->curs;
260        const FLOATVAL f     = PF_fetch_number(pf, &curs);
261        DECL_CONST_CAST;
262        PARROT_IMAGEIOTHAW(SELF)->curs = PARROT_const_cast(opcode_t *, curs);
263        BYTECODE_SHIFT_OK(INTERP, SELF);
264        RETURN(FLOATVAL f);
265    }
266
267
268/*
269
270=item C<STRING *shift_string()>
271
272Retrieve a string as the next item from the image.
273
274=cut
275
276*/
277
278    VTABLE STRING *shift_string() :manual_wb {
279        if (PObj_flag_TEST(private1, SELF)) {
280            const INTVAL i = STATICSELF.shift_integer();
281            BYTECODE_SHIFT_OK(INTERP, SELF);
282
283            if (i >= 0) {
284                PackFile_ConstTable *table = PARROT_IMAGEIOTHAW(SELF)->pf_ct;
285                PARROT_GC_WRITE_BARRIER(INTERP, SELF);
286                return table->str.constants[i];
287            }
288
289            /* XXX
290             * only got here because constant table doesn't contain the string
291             * fallback on inline strings
292             */
293        }
294
295        {
296            PackFile * const pf = PARROT_IMAGEIOTHAW(SELF)->pf;
297            const opcode_t *curs           = PARROT_IMAGEIOTHAW(SELF)->curs;
298            STRING   *s                    = PF_fetch_string(INTERP, pf, &curs);
299            DECL_CONST_CAST;
300            PARROT_IMAGEIOTHAW(SELF)->curs = PARROT_const_cast(opcode_t *, curs);
301            BYTECODE_SHIFT_OK(INTERP, SELF);
302            PARROT_GC_WRITE_BARRIER(INTERP, SELF);
303            return s;
304        }
305    }
306
307
308/*
309
310=item C<PMC *shift_pmc()>
311
312Retrieve a PMC as the next item from the image.
313
314=cut
315
316*/
317
318    VTABLE PMC *shift_pmc() :manual_wb {
319        const UINTVAL  n            = SELF.shift_integer();
320        const INTVAL   id           = PackID_get_PMCID(n);
321        const int      packid_flags = PackID_get_FLAGS(n);
322
323        PMC           *pmc          = PMCNULL;
324        PMC           *seen         = PARROT_IMAGEIOTHAW(SELF)->seen;
325        PMC           *todo         = PARROT_IMAGEIOTHAW(SELF)->todo;
326
327        switch (packid_flags) {
328          case enum_PackID_seen:
329            if (id) /* got a non-NULL PMC */
330                pmc = VTABLE_get_pmc_keyed_int(INTERP, seen, id - 1);
331            break;
332          case enum_PackID_pbc_backref:
333            {
334                PackFile_ConstTable *table   = PARROT_IMAGEIOTHAW(SELF)->pf_ct;
335                INTVAL               constno = SELF.shift_integer();
336                INTVAL               idx     = SELF.shift_integer();
337                PMC                 *olist   = table->pmc.constants[constno];
338                pmc                          = VTABLE_get_pmc_keyed_int(INTERP, olist, idx);
339                PARROT_ASSERT(id - 1 == VTABLE_elements(INTERP, seen));
340                VTABLE_set_pmc_keyed_int(INTERP, seen, id - 1, pmc);
341                break;
342            }
343          case enum_PackID_normal:
344            {
345                const INTVAL type = SELF.shift_integer();
346
347                PARROT_ASSERT(id - 1 == VTABLE_elements(INTERP, seen));
348
349                if (type <= 0 || type > INTERP->n_vtable_max)
350                    Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
351                            "Unknown PMC type to thaw %d", type);
352
353                pmc = Parrot_pmc_new_noinit(INTERP, type);
354
355                VTABLE_set_pmc_keyed_int(INTERP, seen, id - 1, pmc);
356                VTABLE_push_integer(INTERP, todo, id - 1);
357            }
358            break;
359          default:
360            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
361                    "Unknown PMC id args thaw %d", packid_flags);
362            break;
363        }
364
365        RETURN(PMC *pmc);
366    }
367
368}
369
370/*
371
372=back
373
374=cut
375
376*/
377
378/*
379 * Local variables:
380 *   c-file-style: "parrot"
381 * End:
382 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
383 */
384
385