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