Coverage Report

Created: 2017-04-15 07:07

/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
}