xref: /5.5.2/couchdb/src/ejson/decode.c (revision c6a29006)
1// Licensed under the Apache License, Version 2.0 (the "License"); you may not
2// use this file except in compliance with the License. You may obtain a copy of
3// the License at
4//
5//   http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10// License for the specific language governing permissions and limitations under
11// the License.
12
13#include <assert.h>
14#include <stdio.h>
15#include <string.h>
16
17#include "erl_nif.h"
18#include "erl_nif_compat.h"
19#include "yajl/yajl_parse.h"
20#include "yajl/yajl_parser.h"
21#include "yajl/yajl_lex.h"
22#include "ejson.h"
23
24typedef struct {
25    ERL_NIF_TERM head;
26    ErlNifEnv* env;
27} decode_ctx;
28
29#define ENV(ctx) ((decode_ctx*)ctx)->env
30
31#define CONTINUE 1
32#define CANCEL 0
33
34#define NOERROR 0
35#define ERR_MEMORY 1
36
37
38static void * yajl_internal_malloc(void *ctx, unsigned int sz);
39static void * yajl_internal_realloc(void *ctx, void *previous, unsigned int sz);
40static void yajl_internal_free(void *ctx, void *ptr);
41
42
43static yajl_alloc_funcs allocfuncs = {
44    yajl_internal_malloc,
45    yajl_internal_realloc,
46    yajl_internal_free
47};
48
49
50static ERL_NIF_TERM
51make_error(yajl_handle handle, ErlNifEnv* env)
52{
53    char* yajlError = (char*) yajl_get_error(handle, 0, NULL, 0);
54    ERL_NIF_TERM errMsg;
55
56    if(yajlError != NULL)
57    {
58        errMsg = enif_make_string(env, yajlError, ERL_NIF_LATIN1);
59        yajl_free_error(handle, (unsigned char*) yajlError);
60    }
61    else
62    {
63        errMsg = enif_make_string(env, "unknown parse error", ERL_NIF_LATIN1);
64    }
65
66    return enif_make_tuple(env, 2,
67        enif_make_atom(env, "error"),
68        enif_make_tuple(env, 2,
69            enif_make_uint(env, handle->bytesConsumed),
70            errMsg
71        )
72    );
73}
74
75
76static void
77add_to_head(void* vctx, ERL_NIF_TERM newhead)
78{
79    decode_ctx* ctx = (decode_ctx*)vctx;
80    ctx->head = enif_make_list_cell(ctx->env, newhead, ctx->head);
81}
82
83static int
84decode_null(void* ctx)
85{
86    add_to_head(ctx, enif_make_atom(ENV(ctx), "null"));
87    return CONTINUE;
88}
89
90static int
91decode_boolean(void* ctx, int val)
92{
93    add_to_head(ctx, enif_make_atom(ENV(ctx), val ? "true" : "false"));
94    return CONTINUE;
95}
96
97static int
98decode_number(void * ctx, const char * numberVal, unsigned int numberLen)
99{
100    // scan in the input to see if it's a float or int
101
102    int numberType = 0; // 0 means integer, 1 means float
103    unsigned int i;
104    ErlNifBinary bin;
105    int missingDot = 1;
106    unsigned int expPos;
107
108    for(i=0; i<numberLen; i++) {
109        switch (numberVal[i]) {
110        case '.':
111            missingDot = 0;
112            numberType = 1; // it's  a float
113            goto loopend;
114        case 'E':
115        case 'e':
116            expPos = i;
117            numberType = 1; // it's  a float
118            goto loopend;
119        }
120    }
121loopend:
122    if ((numberType == 1) && missingDot)
123    {
124        if(!enif_alloc_binary_compat(ENV(ctx), numberLen + 2, &bin))
125        {
126            return CANCEL;
127        }
128        memcpy(bin.data, numberVal, expPos);
129        bin.data[expPos] = '.';
130        bin.data[expPos + 1] = '0';
131        memcpy(bin.data + expPos + 2, numberVal + expPos, numberLen - expPos);
132    }
133    else
134    {
135        if(!enif_alloc_binary_compat(ENV(ctx), numberLen, &bin))
136        {
137            return CANCEL;
138        }
139        memcpy(bin.data, numberVal, numberLen);
140    }
141    add_to_head(ctx, enif_make_tuple(ENV(ctx), 2,
142                        enif_make_int(ENV(ctx), numberType),
143                        enif_make_binary(ENV(ctx), &bin)));
144    return CONTINUE;
145}
146
147
148
149static int
150decode_string(void* ctx, const unsigned char* data, unsigned int size)
151{
152    ErlNifBinary bin;
153    if(!enif_alloc_binary_compat(ENV(ctx), size, &bin))
154    {
155        return CANCEL;
156    }
157    memcpy(bin.data, data, size);
158    add_to_head(ctx, enif_make_binary(ENV(ctx), &bin));
159    return CONTINUE;
160}
161
162static int
163decode_start_array(void* ctx)
164{
165    add_to_head(ctx, enif_make_int(ENV(ctx), 0));
166    return CONTINUE;
167}
168
169
170static int
171decode_end_array(void* ctx)
172{
173    add_to_head(ctx, enif_make_int(ENV(ctx), 1));
174    return CONTINUE;
175}
176
177
178static int
179decode_start_map(void* ctx)
180{
181    add_to_head(ctx, enif_make_int(ENV(ctx), 2));
182    return CONTINUE;
183}
184
185
186static int
187decode_end_map(void* ctx)
188{
189    add_to_head(ctx, enif_make_int(ENV(ctx), 3));
190    return CONTINUE;
191}
192
193
194static int
195decode_map_key(void* ctx, const unsigned char* data, unsigned int size)
196{
197    ErlNifBinary bin;
198    if(!enif_alloc_binary_compat(ENV(ctx), size, &bin))
199    {
200       return CANCEL;
201    }
202    memcpy(bin.data, data, size);
203    add_to_head(ctx, enif_make_tuple(ENV(ctx), 2,
204                        enif_make_int(ENV(ctx), 3),
205                        enif_make_binary(ENV(ctx), &bin)));
206    return CONTINUE;
207}
208
209static yajl_callbacks
210decoder_callbacks = {
211    decode_null,
212    decode_boolean,
213    NULL,
214    NULL,
215    decode_number,
216    decode_string,
217    decode_start_map,
218    decode_map_key,
219    decode_end_map,
220    decode_start_array,
221    decode_end_array
222};
223
224
225static int
226check_rest(unsigned char* data, unsigned int size, unsigned int used)
227{
228    unsigned int i = 0;
229    for(i = used; i < size; i++)
230    {
231        switch(data[i])
232        {
233            case ' ':
234            case '\t':
235            case '\r':
236            case '\n':
237                continue;
238            default:
239                return CANCEL;
240        }
241    }
242
243    return CONTINUE;
244}
245
246ERL_NIF_TERM
247validate_doc(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
248{
249    yajl_parser_config conf = {0, 1};
250    yajl_handle handle;
251
252    yajl_status status;
253    ErlNifBinary json;
254    ERL_NIF_TERM ret;
255
256    handle = yajl_alloc(NULL, &conf, &allocfuncs, NULL);
257
258    if(!enif_inspect_iolist_as_binary(env, argv[0], &json))
259    {
260        ret = enif_make_badarg(env);
261        goto done;
262    }
263
264    status = yajl_parse(handle, json.data, json.size);
265
266    if(status == yajl_status_insufficient_data &&
267       handle->bytesConsumed == json.size)
268    {
269        status = yajl_parse_complete(handle);
270    }
271
272    if(status == yajl_status_ok)
273    {
274        if(handle->bytesConsumed != json.size &&
275            check_rest(json.data, json.size, handle->bytesConsumed) == CANCEL)
276        {
277            ret = enif_make_tuple(env, 2,
278                enif_make_atom(env, "error"),
279                enif_make_atom(env, "garbage_after_value")
280            );
281        }
282        else
283        {
284            ret = enif_make_atom(env, "ok");
285        }
286    }
287    else
288    {
289        ret = enif_make_tuple(env, 2,
290                enif_make_atom(env, "error"),
291                enif_make_atom(env, "invalid_json")
292                );
293    }
294
295done:
296    if(handle != NULL) yajl_free(handle);
297    return ret;
298}
299
300ERL_NIF_TERM
301reverse_tokens(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
302{
303    decode_ctx ctx;
304    yajl_parser_config conf = {0, 1}; // No comments, check utf8
305    yajl_handle handle = yajl_alloc(&decoder_callbacks, &conf, &allocfuncs, &ctx);
306    yajl_status status;
307    unsigned int used;
308    ErlNifBinary bin;
309    ERL_NIF_TERM ret;
310
311    ctx.env = env;
312    ctx.head = enif_make_list_from_array(env, NULL, 0);
313
314    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin))
315    {
316        ret = enif_make_badarg(env);
317        goto done;
318    }
319
320    status = yajl_parse(handle, bin.data, bin.size);
321    used = handle->bytesConsumed;
322
323    // Parsing something like "2.0" (without quotes) will
324    // cause a spurious semi-error. We add the extra size
325    // check so that "2008-20-10" doesn't pass.
326    if(status == yajl_status_insufficient_data && used == bin.size)
327    {
328        status = yajl_parse_complete(handle);
329    }
330
331    if(status == yajl_status_ok && used != bin.size)
332    {
333        if(check_rest(bin.data, bin.size, used) == CANCEL)
334        {
335            ret = enif_make_tuple(env, 2,
336                enif_make_atom(env, "error"),
337                enif_make_atom(env, "garbage_after_value")
338            );
339            goto done;
340        }
341    }
342
343    switch(status)
344    {
345        case yajl_status_ok:
346            ret = enif_make_tuple(env, 2, enif_make_atom(env, "ok"), ctx.head);
347            goto done;
348
349        case yajl_status_error:
350            ret = make_error(handle, env);
351            goto done;
352
353        case yajl_status_insufficient_data:
354            ret = enif_make_tuple(env, 2,
355                enif_make_atom(env, "error"),
356                enif_make_atom(env, "insufficient_data")
357            );
358            goto done;
359
360        case yajl_status_client_canceled:
361        /* the only time we do this is when we can't allocate a binary. */
362            ret = enif_make_tuple(env, 2,
363                enif_make_atom(env, "error"),
364                enif_make_atom(env, "insufficient_memory")
365            );
366            goto done;
367
368        default:
369            ret = enif_make_tuple(env, 2,
370                enif_make_atom(env, "error"),
371                enif_make_atom(env, "unknown")
372            );
373            goto done;
374    }
375
376done:
377    if(handle != NULL) yajl_free(handle);
378    return ret;
379}
380
381
382static void * yajl_internal_malloc(void *ctx, unsigned int sz)
383{
384    return enif_alloc(sz);
385}
386
387static void * yajl_internal_realloc(void *ctx, void *previous, unsigned int sz)
388{
389    return enif_realloc(previous, sz);
390}
391
392static void yajl_internal_free(void *ctx, void *ptr)
393{
394    enif_free(ptr);
395}
396