/home/travis/build/MoarVM/MoarVM/src/6model/reprs/Decoder.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include "moar.h" |
2 | | |
3 | | /* This representation's function pointer table. */ |
4 | | static const MVMREPROps Decoder_this_repr; |
5 | | |
6 | | /* Creates a new type object of this representation, and associates it with |
7 | | * the given HOW. */ |
8 | 2 | static MVMObject * type_object_for(MVMThreadContext *tc, MVMObject *HOW) { |
9 | 2 | MVMSTable *st = MVM_gc_allocate_stable(tc, &Decoder_this_repr, HOW); |
10 | 2 | |
11 | 2 | MVMROOT(tc, st, { |
12 | 2 | MVMObject *obj = MVM_gc_allocate_type_object(tc, st); |
13 | 2 | MVM_ASSIGN_REF(tc, &(st->header), st->WHAT, obj); |
14 | 2 | st->size = sizeof(MVMDecoder); |
15 | 2 | }); |
16 | 2 | |
17 | 2 | return st->WHAT; |
18 | 2 | } |
19 | | |
20 | | /* Copies the body of one object to another. */ |
21 | 0 | static void copy_to(MVMThreadContext *tc, MVMSTable *st, void *src, MVMObject *dest_root, void *dest) { |
22 | 0 | MVM_exception_throw_adhoc(tc, "Cannot copy object with representation Decoder"); |
23 | 0 | } |
24 | | |
25 | | /* Called by the VM to mark any GCable items. */ |
26 | 207 | static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) { |
27 | 207 | MVMDecoderBody *decoder = (MVMDecoderBody*)data; |
28 | 207 | if (decoder->ds) { |
29 | 207 | MVM_gc_worklist_add(tc, worklist, &(decoder->ds->replacement)); |
30 | 207 | } |
31 | 207 | } |
32 | | |
33 | | /* Called by the VM in order to free memory associated with this object. */ |
34 | 91 | static void gc_free(MVMThreadContext *tc, MVMObject *obj) { |
35 | 91 | MVMDecoder *decoder = (MVMDecoder *)obj; |
36 | 91 | if (decoder->body.ds) |
37 | 91 | MVM_string_decodestream_destroy(tc, decoder->body.ds); |
38 | 91 | if (decoder->body.sep_spec) |
39 | 91 | MVM_string_decode_stream_sep_destroy(tc, decoder->body.sep_spec); |
40 | 91 | MVM_free(decoder->body.sep_spec); |
41 | 91 | } |
42 | | |
43 | | static const MVMStorageSpec storage_spec = { |
44 | | MVM_STORAGE_SPEC_REFERENCE, /* inlineable */ |
45 | | 0, /* bits */ |
46 | | 0, /* align */ |
47 | | MVM_STORAGE_SPEC_BP_NONE, /* boxed_primitive */ |
48 | | 0, /* can_box */ |
49 | | 0, /* is_unsigned */ |
50 | | }; |
51 | | |
52 | | |
53 | | /* Gets the storage specification for this representation. */ |
54 | 0 | static const MVMStorageSpec * get_storage_spec(MVMThreadContext *tc, MVMSTable *st) { |
55 | 0 | return &storage_spec; |
56 | 0 | } |
57 | | |
58 | | /* Compose the representation. */ |
59 | 2 | static void compose(MVMThreadContext *tc, MVMSTable *st, MVMObject *info) { |
60 | 2 | /* Nothing to do for this REPR. */ |
61 | 2 | } |
62 | | |
63 | | /* Set the size of the STable. */ |
64 | 146 | static void deserialize_stable_size(MVMThreadContext *tc, MVMSTable *st, MVMSerializationReader *reader) { |
65 | 146 | st->size = sizeof(MVMDecoder); |
66 | 146 | } |
67 | | |
68 | | /* Initializes the representation. */ |
69 | 144 | const MVMREPROps * MVMDecoder_initialize(MVMThreadContext *tc) { |
70 | 144 | return &Decoder_this_repr; |
71 | 144 | } |
72 | | |
73 | | static const MVMREPROps Decoder_this_repr = { |
74 | | type_object_for, |
75 | | MVM_gc_allocate_object, |
76 | | NULL, /* initialize */ |
77 | | copy_to, |
78 | | MVM_REPR_DEFAULT_ATTR_FUNCS, |
79 | | MVM_REPR_DEFAULT_BOX_FUNCS, |
80 | | MVM_REPR_DEFAULT_POS_FUNCS, |
81 | | MVM_REPR_DEFAULT_ASS_FUNCS, |
82 | | MVM_REPR_DEFAULT_ELEMS, |
83 | | get_storage_spec, |
84 | | NULL, /* change_type */ |
85 | | NULL, /* serialize */ |
86 | | NULL, /* deserialize */ |
87 | | NULL, /* serialize_repr_data */ |
88 | | NULL, /* deserialize_repr_data */ |
89 | | deserialize_stable_size, |
90 | | gc_mark, |
91 | | gc_free, |
92 | | NULL, /* gc_cleanup */ |
93 | | NULL, /* gc_mark_repr_data */ |
94 | | NULL, /* gc_free_repr_data */ |
95 | | compose, |
96 | | NULL, /* spesh */ |
97 | | "Decoder", /* name */ |
98 | | MVM_REPR_ID_Decoder, |
99 | | NULL, /* unmanaged_size */ |
100 | | NULL, /* describe_refs */ |
101 | | }; |
102 | | |
103 | | /* Assert that the passed object really is a decoder; throw if not. */ |
104 | 1.84k | void MVM_decoder_ensure_decoder(MVMThreadContext *tc, MVMObject *decoder, const char *op) { |
105 | 1.84k | if (MVM_UNLIKELY(REPR(decoder)->ID != MVM_REPR_ID_Decoder || !IS_CONCRETE(decoder))) |
106 | 0 | MVM_exception_throw_adhoc(tc, |
107 | 0 | "Operation '%s' can only work on an object with the Decoder representation", |
108 | 0 | op); |
109 | 1.84k | } |
110 | | |
111 | | /* Checks and sets the decoder single-user sanity check flag. */ |
112 | 1.79k | static void enter_single_user(MVMThreadContext *tc, MVMDecoder *decoder) { |
113 | 1.79k | if (!MVM_trycas(&(decoder->body.in_use), 0, 1)) |
114 | 1 | MVM_exception_throw_adhoc(tc, "Decoder may not be used concurrently"); |
115 | 1.79k | } |
116 | | |
117 | | /* Releases the decoder single-user sanity check flag. */ |
118 | 1.79k | static void exit_single_user(MVMThreadContext *tc, MVMDecoder *decoder) { |
119 | 1.79k | decoder->body.in_use = 0; |
120 | 1.79k | } |
121 | | |
122 | | /* Configures the decoder with the specified encoding and other configuration. */ |
123 | 661 | static int should_translate_newlines(MVMThreadContext *tc, MVMObject *config) { |
124 | 661 | if (IS_CONCRETE(config) && REPR(config)->ID == MVM_REPR_ID_MVMHash) { |
125 | 661 | MVMObject *value = MVM_repr_at_key_o(tc, config, |
126 | 661 | tc->instance->str_consts.translate_newlines); |
127 | 661 | return IS_CONCRETE(value) && MVM_repr_get_int(tc, value) != 0; |
128 | 661 | } |
129 | 0 | return 0; |
130 | 661 | } |
131 | 661 | static MVMString * has_replacement(MVMThreadContext *tc, MVMObject *config) { |
132 | 661 | if (IS_CONCRETE(config) && REPR(config)->ID == MVM_REPR_ID_MVMHash) { |
133 | 661 | MVMObject *value = MVM_repr_at_key_o(tc, config, |
134 | 661 | tc->instance->str_consts.replacement); |
135 | 661 | return IS_CONCRETE(value) |
136 | 0 | ? MVM_repr_get_str(tc, value) |
137 | 661 | : NULL; |
138 | 661 | } |
139 | 0 | return NULL; |
140 | 661 | } |
141 | 661 | static int has_config(MVMThreadContext *tc, MVMObject *config) { |
142 | 661 | if (IS_CONCRETE(config) && REPR(config)->ID == MVM_REPR_ID_MVMHash) { |
143 | 661 | MVMObject *value = MVM_repr_at_key_o(tc, config, |
144 | 661 | tc->instance->str_consts.config); |
145 | 661 | return IS_CONCRETE(value) ? MVM_repr_get_int(tc, value) : 0; |
146 | 661 | } |
147 | 0 | return 0; |
148 | 661 | |
149 | 661 | } |
150 | | void MVM_decoder_configure(MVMThreadContext *tc, MVMDecoder *decoder, |
151 | 663 | MVMString *encoding, MVMObject *config) { |
152 | 663 | if (!decoder->body.ds) { |
153 | 662 | MVMuint8 encid = MVM_string_find_encoding(tc, encoding); |
154 | 662 | enter_single_user(tc, decoder); |
155 | 662 | decoder->body.ds = MVM_string_decodestream_create(tc, encid, 0, |
156 | 662 | should_translate_newlines(tc, config)); |
157 | 662 | decoder->body.sep_spec = MVM_malloc(sizeof(MVMDecodeStreamSeparators)); |
158 | 662 | MVM_string_decode_stream_sep_default(tc, decoder->body.sep_spec); |
159 | 662 | MVM_ASSIGN_REF(tc, &(decoder->common.header), decoder->body.ds->replacement, |
160 | 662 | has_replacement(tc, config)); |
161 | 662 | decoder->body.ds->config = has_config(tc, config); |
162 | 662 | exit_single_user(tc, decoder); |
163 | 662 | } |
164 | 1 | else { |
165 | 1 | MVM_exception_throw_adhoc(tc, "Decoder already configured"); |
166 | 1 | } |
167 | 663 | } |
168 | | |
169 | | /* Obtains the DecodeStream object provided it's initialized, throwing if not. */ |
170 | 1.18k | static MVMDecodeStream * get_ds(MVMThreadContext *tc, MVMDecoder *decoder) { |
171 | 1.18k | MVMDecodeStream *ds = decoder->body.ds; |
172 | 1.18k | if (!ds) |
173 | 7 | MVM_exception_throw_adhoc(tc, "Decoder not yet configured"); |
174 | 1.18k | return ds; |
175 | 1.18k | } |
176 | | |
177 | | /* Gets the separators specification for the decoder. */ |
178 | 716 | MVM_STATIC_INLINE MVMDecodeStreamSeparators * get_sep_spec(MVMThreadContext *tc, MVMDecoder *decoder) { |
179 | 716 | return decoder->body.sep_spec; |
180 | 716 | } |
181 | | |
182 | | /* Sets the separators to be used by this decode stream. */ |
183 | 656 | void MVM_decoder_set_separators(MVMThreadContext *tc, MVMDecoder *decoder, MVMObject *seps) { |
184 | 656 | MVMint32 is_str_array = REPR(seps)->pos_funcs.get_elem_storage_spec(tc, |
185 | 656 | STABLE(seps)).boxed_primitive == MVM_STORAGE_SPEC_BP_STR; |
186 | 656 | get_ds(tc, decoder); /* Ensure we're sufficiently initialized. */ |
187 | 656 | if (is_str_array) { |
188 | 655 | MVMString **c_seps = NULL; |
189 | 655 | MVMuint64 i; |
190 | 655 | MVMuint64 num_seps = MVM_repr_elems(tc, seps); |
191 | 655 | if (num_seps > 0xFFFFFF) |
192 | 0 | MVM_exception_throw_adhoc(tc, "Too many line separators"); |
193 | 655 | c_seps = MVM_malloc((num_seps ? num_seps : 1) * sizeof(MVMString *)); |
194 | 1.96k | for (i = 0; i < num_seps; i++) |
195 | 1.30k | c_seps[i] = MVM_repr_at_pos_s(tc, seps, i); |
196 | 655 | enter_single_user(tc, decoder); |
197 | 655 | MVM_string_decode_stream_sep_from_strings(tc, get_sep_spec(tc, decoder), |
198 | 655 | c_seps, num_seps); |
199 | 655 | exit_single_user(tc, decoder); |
200 | 655 | MVM_free(c_seps); |
201 | 655 | } |
202 | 1 | else { |
203 | 1 | MVM_exception_throw_adhoc(tc, "Set separators requires a native string array"); |
204 | 1 | } |
205 | 656 | } |
206 | | |
207 | | /* Adds bytes to the decode stream. */ |
208 | 214 | void MVM_decoder_add_bytes(MVMThreadContext *tc, MVMDecoder *decoder, MVMObject *buffer) { |
209 | 214 | MVMDecodeStream *ds = get_ds(tc, decoder); |
210 | 214 | if (REPR(buffer)->ID == MVM_REPR_ID_VMArray) { |
211 | 213 | /* To be safe, we need to make a copy of data in a resizable array; it |
212 | 213 | * may change/move under us. */ |
213 | 213 | char *output = NULL, *copy = NULL; |
214 | 213 | MVMint64 output_size; |
215 | 213 | switch (((MVMArrayREPRData *)STABLE(buffer)->REPR_data)->slot_type) { |
216 | 213 | case MVM_ARRAY_U8: |
217 | 213 | case MVM_ARRAY_I8: |
218 | 213 | output = (char *)(((MVMArray *)buffer)->body.slots.i8 + ((MVMArray *)buffer)->body.start); |
219 | 213 | output_size = ((MVMArray *)buffer)->body.elems; |
220 | 213 | break; |
221 | 0 | case MVM_ARRAY_U16: |
222 | 0 | case MVM_ARRAY_I16: |
223 | 0 | output = (char *)(((MVMArray *)buffer)->body.slots.i16 + ((MVMArray *)buffer)->body.start); |
224 | 0 | output_size = ((MVMArray *)buffer)->body.elems * 2; |
225 | 0 | break; |
226 | 0 | case MVM_ARRAY_U32: |
227 | 0 | case MVM_ARRAY_I32: |
228 | 0 | output = (char *)(((MVMArray *)buffer)->body.slots.i32 + ((MVMArray *)buffer)->body.start); |
229 | 0 | output_size = ((MVMArray *)buffer)->body.elems * 4; |
230 | 0 | break; |
231 | 0 | default: |
232 | 0 | MVM_exception_throw_adhoc(tc, "Can only add bytes from an int array to a decoder"); |
233 | 213 | } |
234 | 213 | copy = MVM_malloc(output_size); |
235 | 213 | memcpy(copy, output, output_size); |
236 | 213 | enter_single_user(tc, decoder); |
237 | 213 | MVM_string_decodestream_add_bytes(tc, ds, copy, output_size); |
238 | 213 | exit_single_user(tc, decoder); |
239 | 213 | } |
240 | 1 | else { |
241 | 1 | MVM_exception_throw_adhoc(tc, "Cannot add bytes to a decoder with a %s", |
242 | 1 | REPR(buffer)->name); |
243 | 1 | } |
244 | 214 | } |
245 | | |
246 | | /* Takes the specified number of chars from the decoder, or all if there |
247 | | * is not enough. */ |
248 | | MVMString * MVM_decoder_take_chars(MVMThreadContext *tc, MVMDecoder *decoder, MVMint64 chars, |
249 | 9 | MVMint64 eof) { |
250 | 9 | MVMString *result = NULL; |
251 | 9 | enter_single_user(tc, decoder); |
252 | 9 | MVMROOT(tc, decoder, { |
253 | 9 | result = MVM_string_decodestream_get_chars(tc, get_ds(tc, decoder), (MVMint32)chars, eof); |
254 | 9 | }); |
255 | 9 | exit_single_user(tc, decoder); |
256 | 9 | return result; |
257 | 9 | } |
258 | | |
259 | | /* Takes all chars from the decoder. */ |
260 | 196 | MVMString * MVM_decoder_take_all_chars(MVMThreadContext *tc, MVMDecoder *decoder) { |
261 | 196 | MVMString *result = NULL; |
262 | 196 | enter_single_user(tc, decoder); |
263 | 196 | MVMROOT(tc, decoder, { |
264 | 196 | result = MVM_string_decodestream_get_all(tc, get_ds(tc, decoder)); |
265 | 196 | }); |
266 | 196 | exit_single_user(tc, decoder); |
267 | 196 | return result; |
268 | 196 | } |
269 | | |
270 | | /* Takes all available chars from the decoder. */ |
271 | 1 | MVMString * MVM_decoder_take_available_chars(MVMThreadContext *tc, MVMDecoder *decoder) { |
272 | 1 | MVMString *result = NULL; |
273 | 1 | enter_single_user(tc, decoder); |
274 | 1 | MVMROOT(tc, decoder, { |
275 | 1 | result = MVM_string_decodestream_get_available(tc, get_ds(tc, decoder)); |
276 | 1 | }); |
277 | 1 | exit_single_user(tc, decoder); |
278 | 1 | return result; |
279 | 1 | } |
280 | | |
281 | | /* Takes a line from the decoder. */ |
282 | | MVMString * MVM_decoder_take_line(MVMThreadContext *tc, MVMDecoder *decoder, |
283 | 62 | MVMint64 chomp, MVMint64 incomplete_ok) { |
284 | 62 | MVMDecodeStream *ds = get_ds(tc, decoder); |
285 | 62 | MVMDecodeStreamSeparators *sep_spec = get_sep_spec(tc, decoder); |
286 | 62 | MVMString *result = NULL; |
287 | 62 | enter_single_user(tc, decoder); |
288 | 62 | MVMROOT(tc, decoder, { |
289 | 62 | result = incomplete_ok |
290 | 62 | ? MVM_string_decodestream_get_until_sep_eof(tc, ds, sep_spec, (MVMint32)chomp) |
291 | 62 | : MVM_string_decodestream_get_until_sep(tc, ds, sep_spec, (MVMint32)chomp); |
292 | 62 | }); |
293 | 62 | exit_single_user(tc, decoder); |
294 | 62 | return result; |
295 | 62 | } |
296 | | |
297 | | /* Returns true if the decoder is empty. */ |
298 | 25 | MVMint64 MVM_decoder_empty(MVMThreadContext *tc, MVMDecoder *decoder) { |
299 | 25 | return MVM_string_decodestream_is_empty(tc, get_ds(tc, decoder)); |
300 | 25 | } |
301 | | |
302 | | /* Gets the number of (undecoded) bytes available in the decoder. */ |
303 | 15 | MVMint64 MVM_decoder_bytes_available(MVMThreadContext *tc, MVMDecoder *decoder) { |
304 | 15 | return MVM_string_decodestream_bytes_available(tc, get_ds(tc, decoder)); |
305 | 15 | } |
306 | | |
307 | | /* Takes bytes from the decode stream and places them into a buffer. If there |
308 | | * are less available than requested, hand back null. */ |
309 | | MVMObject * MVM_decoder_take_bytes(MVMThreadContext *tc, MVMDecoder *decoder, |
310 | 3 | MVMObject *buf_type, MVMint64 bytes) { |
311 | 3 | MVMDecodeStream *ds = get_ds(tc, decoder); |
312 | 3 | char *buf = NULL; |
313 | 3 | MVMint64 read; |
314 | 3 | MVMObject *result = NULL; |
315 | 3 | |
316 | 3 | /* Ensure the target is in the correct form. */ |
317 | 3 | if (REPR(buf_type)->ID != MVM_REPR_ID_VMArray) |
318 | 0 | MVM_exception_throw_adhoc(tc, "decodertakebytes requires a native array type"); |
319 | 3 | if (((MVMArrayREPRData *)STABLE(buf_type)->REPR_data)->slot_type != MVM_ARRAY_U8 |
320 | 0 | && ((MVMArrayREPRData *)STABLE(buf_type)->REPR_data)->slot_type != MVM_ARRAY_I8) |
321 | 0 | MVM_exception_throw_adhoc(tc, "decodertakebytes requires a native array type of uint8 or int8"); |
322 | 3 | if (bytes < 0 || bytes > 0x7FFFFFFF) |
323 | 0 | MVM_exception_throw_adhoc(tc, |
324 | 0 | "Out of range: attempted to read %"PRId64" bytes from decoder", |
325 | 0 | bytes); |
326 | 3 | |
327 | 3 | if (MVM_string_decodestream_bytes_available(tc, ds) < bytes) |
328 | 1 | return tc->instance->VMNull; |
329 | 3 | |
330 | 2 | result = MVM_repr_alloc_init(tc, buf_type); |
331 | 2 | enter_single_user(tc, decoder); |
332 | 2 | read = MVM_string_decodestream_bytes_to_buf(tc, ds, &buf, bytes); |
333 | 2 | exit_single_user(tc, decoder); |
334 | 2 | ((MVMArray *)result)->body.slots.i8 = (MVMint8 *)buf; |
335 | 2 | ((MVMArray *)result)->body.start = 0; |
336 | 2 | ((MVMArray *)result)->body.ssize = read; |
337 | 2 | ((MVMArray *)result)->body.elems = read; |
338 | 2 | return result; |
339 | 3 | } |