Coverage Report

Created: 2018-06-21 18:56

/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
206
static void gc_mark(MVMThreadContext *tc, MVMSTable *st, void *data, MVMGCWorklist *worklist) {
27
206
    MVMDecoderBody *decoder = (MVMDecoderBody*)data;
28
206
    if (decoder->ds) {
29
206
        MVM_gc_worklist_add(tc, worklist, &(decoder->ds->replacement));
30
206
    }
31
206
}
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
}