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