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