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 
24 typedef 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 
38 static void * yajl_internal_malloc(void *ctx, unsigned int sz);
39 static void * yajl_internal_realloc(void *ctx, void *previous, unsigned int sz);
40 static void yajl_internal_free(void *ctx, void *ptr);
41 
42 
43 static yajl_alloc_funcs allocfuncs = {
44     yajl_internal_malloc,
45     yajl_internal_realloc,
46     yajl_internal_free
47 };
48 
49 
50 static ERL_NIF_TERM
make_error(yajl_handle handle, ErlNifEnv* env)51 make_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 
76 static void
add_to_head(void* vctx, ERL_NIF_TERM newhead)77 add_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 
83 static int
decode_null(void* ctx)84 decode_null(void* ctx)
85 {
86     add_to_head(ctx, enif_make_atom(ENV(ctx), "null"));
87     return CONTINUE;
88 }
89 
90 static int
decode_boolean(void* ctx, int val)91 decode_boolean(void* ctx, int val)
92 {
93     add_to_head(ctx, enif_make_atom(ENV(ctx), val ? "true" : "false"));
94     return CONTINUE;
95 }
96 
97 static int
decode_number(void * ctx, const char * numberVal, unsigned int numberLen)98 decode_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     }
121 loopend:
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 
149 static int
decode_string(void* ctx, const unsigned char* data, unsigned int size)150 decode_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 
162 static int
decode_start_array(void* ctx)163 decode_start_array(void* ctx)
164 {
165     add_to_head(ctx, enif_make_int(ENV(ctx), 0));
166     return CONTINUE;
167 }
168 
169 
170 static int
decode_end_array(void* ctx)171 decode_end_array(void* ctx)
172 {
173     add_to_head(ctx, enif_make_int(ENV(ctx), 1));
174     return CONTINUE;
175 }
176 
177 
178 static int
decode_start_map(void* ctx)179 decode_start_map(void* ctx)
180 {
181     add_to_head(ctx, enif_make_int(ENV(ctx), 2));
182     return CONTINUE;
183 }
184 
185 
186 static int
decode_end_map(void* ctx)187 decode_end_map(void* ctx)
188 {
189     add_to_head(ctx, enif_make_int(ENV(ctx), 3));
190     return CONTINUE;
191 }
192 
193 
194 static int
decode_map_key(void* ctx, const unsigned char* data, unsigned int size)195 decode_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 
209 static yajl_callbacks
210 decoder_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 
225 static int
check_rest(unsigned char* data, unsigned int size, unsigned int used)226 check_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 
246 ERL_NIF_TERM
validate_doc(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])247 validate_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 
295 done:
296     if(handle != NULL) yajl_free(handle);
297     return ret;
298 }
299 
300 ERL_NIF_TERM
reverse_tokens(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])301 reverse_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 
376 done:
377     if(handle != NULL) yajl_free(handle);
378     return ret;
379 }
380 
381 
yajl_internal_malloc(void *ctx, unsigned int sz)382 static void * yajl_internal_malloc(void *ctx, unsigned int sz)
383 {
384     return enif_alloc(sz);
385 }
386 
yajl_internal_realloc(void *ctx, void *previous, unsigned int sz)387 static void * yajl_internal_realloc(void *ctx, void *previous, unsigned int sz)
388 {
389     return enif_realloc(previous, sz);
390 }
391 
yajl_internal_free(void *ctx, void *ptr)392 static void yajl_internal_free(void *ctx, void *ptr)
393 {
394     enif_free(ptr);
395 }
396