321 } |
320 } |
322 |
321 |
323 return (endptr != &buf[len]); |
322 return (endptr != &buf[len]); |
324 } |
323 } |
325 |
324 |
326 static int add_state(CxJson *p, int state) { |
325 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
327 CxArrayReallocator alloc = cx_array_reallocator(NULL, p->states_internal); |
326 CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); |
328 size_t size = p->nstates + 1; |
327 if (v == NULL) { |
329 size_t capacity = p->states_alloc; |
328 return NULL; |
330 // TODO: fix that nstates does not denote the size of the array |
329 } |
331 // TODO: replace with a 16 bit (or maybe even 8 bit) version of cx_array_add() |
330 |
332 int result = cx_array_add( |
331 // initialize the value |
333 &p->states, |
332 if (type == CX_JSON_ARRAY) { |
334 &size, |
333 cx_array_initialize_a(json->allocator, v->value.array.array, 16); |
335 &capacity, |
334 if (v->value.array.array == NULL) { |
336 sizeof(int), |
335 cxFree(json->allocator, v); |
337 &state, |
336 return NULL; |
338 &alloc |
337 } |
339 ); |
338 } else if (type == CX_JSON_OBJECT) { |
340 if (result == 0) { |
339 cx_array_initialize_a(json->allocator, v->value.object.values, 16); |
341 p->nstates = size - 1; |
340 if (v->value.object.values == NULL) { |
342 p->states_alloc = capacity; |
341 cxFree(json->allocator, v); |
343 } |
342 return NULL; |
344 return result; |
343 } |
345 } |
344 } else { |
346 |
345 memset(v, 0, sizeof(CxJsonValue)); |
347 static void end_elm(CxJson *p, CxJsonReaderType type) { |
346 } |
348 p->reader_type = type; |
347 v->type = type; |
349 p->nstates--; |
348 v->allocator = json->allocator; |
350 } |
349 |
351 |
350 // add the new value to a possible parent |
352 #define JP_STATE_VALUE_BEGIN 0 |
351 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); |
353 #define JP_STATE_VALUE_BEGIN_OBJ 1 |
352 if (json->vbuf_size > 0) { |
354 #define JP_STATE_VALUE_BEGIN_AR 2 |
353 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
355 #define JP_STATE_ARRAY_SEP_OR_CLOSE 3 |
354 if (parent->type == CX_JSON_ARRAY) { |
356 #define JP_STATE_OBJ_NAME_OR_CLOSE 4 |
355 cx_array_simple_add_a(&value_realloc, parent->value.array.array, v); |
357 #define JP_STATE_OBJ_NAME 5 |
356 } else if (parent->type == CX_JSON_OBJECT) { |
358 #define JP_STATE_OBJ_COLON 6 |
357 assert(parent->value.object.values_size > 0); |
359 #define JP_STATE_OBJ_SEP_OR_CLOSE 7 |
358 assert(parent->value.object.values[parent->value.object.values_size - 1].value == NULL); |
360 |
359 parent->value.object.values[parent->value.object.values_size - 1].value = v; |
361 static int next_state_after_value(int current) { |
360 } else { |
362 switch (current) { |
361 assert(false); |
363 default: |
362 } |
364 return -1; |
363 } |
365 // after value JSON complete, expect nothing |
364 |
366 case JP_STATE_VALUE_BEGIN: |
365 // add the new value to the stack, if it is an array or object |
367 return -1; |
366 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
368 // after obj value, expect ',' or '}' |
367 CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); |
369 case JP_STATE_VALUE_BEGIN_OBJ: |
368 if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { |
370 return JP_STATE_OBJ_SEP_OR_CLOSE; |
369 cxFree(json->allocator, v); |
371 // after array value, expect ',' or ']' |
370 return NULL; |
372 case JP_STATE_VALUE_BEGIN_AR: |
371 } |
373 return JP_STATE_ARRAY_SEP_OR_CLOSE; |
372 } |
374 } |
373 |
375 } |
374 // if currently no value is parsed, this is now the value of interest |
376 |
375 if (json->parsed == NULL) { |
377 static void clear_valuename(CxJson *p) { |
376 json->parsed = v; |
378 free(p->value_name); |
377 } |
379 p->value_name = NULL; |
378 |
380 p->value_name_len = 0; |
379 return v; |
381 } |
380 } |
382 |
381 |
383 static void clear_values(CxJson *p) { |
382 static int json_obj_add_entry(CxJson *json, char *name) { |
384 free(p->value_str); |
383 CxJsonObjValue kv = {name, NULL}; |
385 p->value_str = NULL; |
384 assert(json->vbuf_size > 0); |
386 p->value_str_len = 0; |
385 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
387 p->value_int = 0; |
386 assert(parent != NULL); |
388 p->value_double = 0; |
387 assert(parent->type == CX_JSON_OBJECT); |
389 } |
388 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); |
390 |
389 return cx_array_simple_add_a(&value_realloc, parent->value.object.values, kv); |
391 static int json_read(CxJson *p) { |
390 } |
392 int state = p->states[p->nstates]; |
391 |
393 clear_values(p); |
392 #define JP_STATE_VALUE_BEGIN 0 |
394 CxJsonToken token = json_parser_next_token(p); |
393 #define JP_STATE_VALUE_END 10 |
395 p->reader_token = token; |
394 #define JP_STATE_VALUE_BEGIN_OBJ 1 |
396 |
395 #define JP_STATE_OBJ_SEP_OR_CLOSE 11 |
397 p->value_ready = 0; |
396 #define JP_STATE_VALUE_BEGIN_AR 2 |
398 |
397 #define JP_STATE_ARRAY_SEP_OR_CLOSE 12 |
|
398 #define JP_STATE_OBJ_NAME_OR_CLOSE 5 |
|
399 #define JP_STATE_OBJ_NAME 6 |
|
400 #define JP_STATE_OBJ_COLON 7 |
|
401 |
|
402 void cxJsonInit(CxJson *json, const CxAllocator *allocator) { |
|
403 if (allocator == NULL) { |
|
404 allocator = cxDefaultAllocator; |
|
405 } |
|
406 |
|
407 memset(json, 0, sizeof(CxJson)); |
|
408 json->allocator = allocator; |
|
409 |
|
410 json->states = json->states_internal; |
|
411 json->states_capacity = cx_nmemb(json->states_internal); |
|
412 json->states[0] = JP_STATE_VALUE_BEGIN; |
|
413 json->states_size = 1; |
|
414 |
|
415 json->vbuf = json->vbuf_internal; |
|
416 json->vbuf_capacity = cx_nmemb(json->vbuf_internal); |
|
417 } |
|
418 |
|
419 void cxJsonDestroy(CxJson *json) { |
|
420 if (json->states != json->states_internal) { |
|
421 free(json->states); |
|
422 } |
|
423 if (json->vbuf != json->vbuf_internal) { |
|
424 free(json->vbuf); |
|
425 } |
|
426 cxJsonValueFree(json->parsed); |
|
427 json->parsed = NULL; |
|
428 } |
|
429 |
|
430 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { |
|
431 // TODO: implement rescue buffer like in CxProperties to allow subsequent fills |
|
432 json->buffer = buf; |
|
433 json->size = size; |
|
434 json->pos = 0; |
|
435 return 0; |
|
436 } |
|
437 |
|
438 static void json_add_state(CxJson *json, int state) { |
|
439 // we have guaranteed the necessary space with cx_array_simple_reserve() |
|
440 // therefore, we can safely add the state in the simplest way possible |
|
441 json->states[json->states_size++] = state; |
|
442 } |
|
443 |
|
444 #define return_rec(code) \ |
|
445 token_destroy(&token); \ |
|
446 return code |
|
447 |
|
448 static int json_parse(CxJson *json) { |
|
449 // Reserve a pointer for a possibly read value |
|
450 CxJsonValue *vbuf = NULL; |
|
451 |
|
452 // grab the next token |
|
453 CxJsonToken token = token_parse_next(json); |
399 if (token.tokentype == CX_JSON_NO_TOKEN) { |
454 if (token.tokentype == CX_JSON_NO_TOKEN) { |
|
455 // nothing found, wait for more data |
400 return 0; |
456 return 0; |
401 } |
457 } |
402 |
458 |
403 int ret = 1; |
459 // pop the current state |
404 |
460 assert(json->states_size > 0); |
405 // 0 JP_STATE_VALUE_BEGIN value begin |
461 int state = json->states[--json->states_size]; |
406 // 1 JP_STATE_VALUE_BEGIN_OBJ value begin (inside object) |
462 |
407 // 2 JP_STATE_VALUE_BEGIN_AR value begin (inside array) |
463 // guarantee that at least two more states fit on the stack |
408 // 3 JP_STATE_ARRAY_SEP_OR_CLOSE array, expect separator or arrayclose |
464 CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); |
409 // 4 JP_STATE_OBJ_NAME_OR_CLOSE object, expect name or objclose |
465 if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { |
410 // 5 JP_STATE_OBJ_NAME object, expect name |
466 return -1; |
411 // 6 JP_STATE_OBJ_COLON object, expect ':' |
467 } |
412 // 7 JP_STATE_OBJ_SEP_OR_CLOSE object, expect separator, objclose |
468 |
413 |
469 |
414 if (state == JP_STATE_VALUE_BEGIN_AR || state == JP_STATE_OBJ_SEP_OR_CLOSE) { |
470 // 0 JP_STATE_VALUE_BEGIN value begin |
415 clear_valuename(p); |
471 // 10 JP_STATE_VALUE_END expect value end |
416 } |
472 // 1 JP_STATE_VALUE_BEGIN_OBJ value begin (inside object) |
|
473 // 11 JP_STATE_OBJ_SEP_OR_CLOSE object, expect separator, objclose |
|
474 // 2 JP_STATE_VALUE_BEGIN_AR value begin (inside array) |
|
475 // 12 JP_STATE_ARRAY_SEP_OR_CLOSE array, expect separator or arrayclose |
|
476 // 5 JP_STATE_OBJ_NAME_OR_CLOSE object, expect name or objclose |
|
477 // 6 JP_STATE_OBJ_NAME object, expect name |
|
478 // 7 JP_STATE_OBJ_COLON object, expect ':' |
417 |
479 |
418 if (state < 3) { |
480 if (state < 3) { |
419 // expect value |
481 // push expected end state to the stack |
420 p->states[p->nstates] = next_state_after_value(state); |
482 json_add_state(json, 10 + state); |
421 p->value_ready = 1; |
|
422 switch (token.tokentype) { |
483 switch (token.tokentype) { |
423 case CX_JSON_TOKEN_BEGIN_ARRAY: { |
484 case CX_JSON_TOKEN_BEGIN_ARRAY: { |
424 p->reader_type = CX_JSON_READER_ARRAY_BEGIN; |
485 if (create_json_value(json, CX_JSON_ARRAY) == NULL) { |
425 ret = add_state(p, JP_STATE_VALUE_BEGIN_AR) ? -1 : 1; |
486 // TODO: error code - no memory |
426 break; |
487 return_rec(-1); |
|
488 } |
|
489 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
|
490 return_rec(1); |
427 } |
491 } |
428 case CX_JSON_TOKEN_BEGIN_OBJECT: { |
492 case CX_JSON_TOKEN_BEGIN_OBJECT: { |
429 p->reader_type = CX_JSON_READER_OBJECT_BEGIN; |
493 if (create_json_value(json, CX_JSON_OBJECT) == NULL) { |
430 ret = add_state(p, JP_STATE_OBJ_NAME_OR_CLOSE) ? -1 : 1; |
494 // TODO: error code - no memory |
431 break; |
495 return_rec(-1); |
432 } |
496 } |
433 case CX_JSON_TOKEN_END_ARRAY: { |
497 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
434 p->value_ready = 0; |
498 return_rec(1); |
435 end_elm(p, CX_JSON_READER_ARRAY_END); |
|
436 break; |
|
437 } |
499 } |
438 case CX_JSON_TOKEN_STRING: { |
500 case CX_JSON_TOKEN_STRING: { |
439 p->reader_type = CX_JSON_READER_STRING; |
501 if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) { |
440 cxmutstr str = unescape_string(p->allocator, token.content, token.length); |
502 // TODO: error code - no memory |
441 if (str.ptr) { |
503 return_rec(-1); |
442 p->value_str = str.ptr; |
504 } |
443 p->value_str_len = str.length; |
505 cxmutstr str = unescape_string(json->allocator, token.content, token.length); |
|
506 if (str.ptr == NULL) { |
|
507 // TODO: error code - no memory |
|
508 return_rec(-1); |
|
509 } |
|
510 vbuf->value.string = str; |
|
511 return_rec(1); |
|
512 } |
|
513 case CX_JSON_TOKEN_INTEGER: |
|
514 case CX_JSON_TOKEN_NUMBER: { |
|
515 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
|
516 if (NULL == (vbuf = create_json_value(json, type))) { |
|
517 // TODO: error code - no memory |
|
518 return_rec(-1); |
|
519 } |
|
520 if (parse_number(token.content, token.length, &vbuf->value,type == CX_JSON_INTEGER)) { |
|
521 // TODO: error code - format error |
|
522 return_rec(-1); |
|
523 } |
|
524 return_rec(1); |
|
525 } |
|
526 case CX_JSON_TOKEN_LITERAL: { |
|
527 if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) { |
|
528 // TODO: error code - no memory |
|
529 return_rec(-1); |
|
530 } |
|
531 const char *l = token.content; |
|
532 size_t token_len = token.length; |
|
533 if (token_len == 4 && !memcmp(l, "true", 4)) { |
|
534 vbuf->value.literal = CX_JSON_TRUE; |
|
535 } else if (token_len == 5 && !memcmp(l, "false", 5)) { |
|
536 vbuf->value.literal = CX_JSON_FALSE; |
444 } else { |
537 } else { |
445 ret = -1; |
538 vbuf->value.literal = CX_JSON_NULL; |
446 } |
539 } |
447 break; |
540 return_rec(1); |
448 } |
541 } |
449 case CX_JSON_TOKEN_INTEGER: { |
542 default: { |
450 p->reader_type = CX_JSON_READER_INTEGER; |
543 // TODO: error code - unexpected token |
451 if (parse_number(token.content, token.length, |
544 return_rec(-1); |
452 &p->value_int, true)) { |
545 } |
453 ret = -1; |
|
454 } |
|
455 break; |
|
456 } |
|
457 case CX_JSON_TOKEN_NUMBER: { |
|
458 p->reader_type = CX_JSON_READER_NUMBER; |
|
459 if (parse_number(token.content, token.length, |
|
460 &p->value_double, false)) { |
|
461 ret = -1; |
|
462 } |
|
463 break; |
|
464 } |
|
465 case CX_JSON_TOKEN_LITERAL: { |
|
466 p->reader_type = CX_JSON_READER_LITERAL; |
|
467 break; |
|
468 } |
|
469 default: ret = -1; |
|
470 } |
546 } |
471 } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) { |
547 } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) { |
472 // expect ',' or ']' |
548 // expect ',' or ']' |
473 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
549 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
474 p->states[p->nstates] = JP_STATE_VALUE_BEGIN_AR; |
550 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
475 ret = json_read(p); |
551 return_rec(1); |
476 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { |
552 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { |
477 end_elm(p, CX_JSON_READER_ARRAY_END); |
553 // discard the array from the value buffer |
478 } else { |
554 json->vbuf_size--; |
479 ret = -1; |
555 return_rec(1); |
|
556 } else { |
|
557 // TODO: error code - unexpected token |
|
558 return_rec(-1); |
480 } |
559 } |
481 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { |
560 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { |
482 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
561 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
483 clear_valuename(p); |
562 // discard the obj from the value buffer |
484 end_elm(p, CX_JSON_READER_OBJECT_END); |
563 json->vbuf_size--; |
|
564 return_rec(1); |
485 } else { |
565 } else { |
486 // expect string |
566 // expect string |
487 if (token.tokentype != CX_JSON_TOKEN_STRING) return -1; |
567 if (token.tokentype != CX_JSON_TOKEN_STRING) { |
488 |
568 // TODO: error code - unexpected token |
489 if (p->value_name) free(p->value_name); |
569 return_rec(-1); |
490 cxmutstr valname = unescape_string(p->allocator, token.content, token.length); |
570 } |
491 p->value_name = valname.ptr; |
571 |
492 p->value_name_len = valname.length; |
572 // add new entry |
|
573 cxmutstr name = unescape_string(json->allocator, token.content, token.length); |
|
574 if (name.ptr == NULL) { |
|
575 // TODO: error code - no mem |
|
576 return_rec(-1); |
|
577 } |
|
578 json_obj_add_entry(json, name.ptr); |
493 |
579 |
494 // next state |
580 // next state |
495 p->states[p->nstates] = JP_STATE_OBJ_COLON; |
581 json_add_state(json, JP_STATE_OBJ_COLON); |
496 ret = json_read(p); |
582 return_rec(1); |
497 } |
583 } |
498 } else if (state == JP_STATE_OBJ_COLON) { |
584 } else if (state == JP_STATE_OBJ_COLON) { |
499 // expect ':' |
585 // expect ':' |
500 if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) return -1; |
586 if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) { |
|
587 // TODO: error code - unexpected token |
|
588 return_rec(-1); |
|
589 } |
501 // next state |
590 // next state |
502 p->states[p->nstates] = JP_STATE_VALUE_BEGIN_OBJ; |
591 json_add_state(json, JP_STATE_VALUE_BEGIN_OBJ); |
503 ret = json_read(p); |
592 return_rec(1); |
504 } else if (state == JP_STATE_OBJ_SEP_OR_CLOSE) { |
593 } else if (state == JP_STATE_OBJ_SEP_OR_CLOSE) { |
505 // expect ',' or '}' |
594 // expect ',' or '}' |
506 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
595 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
507 p->states[p->nstates] = JP_STATE_OBJ_NAME; |
596 json_add_state(json, JP_STATE_OBJ_NAME); |
508 ret = json_read(p); |
597 return_rec(1); |
509 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
598 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
510 end_elm(p, CX_JSON_READER_OBJECT_END); |
599 // discard the obj from the value buffer |
511 } else { |
600 json->vbuf_size--; |
512 ret = -1; |
601 return_rec(1); |
513 } |
602 } else { |
514 } |
603 // TODO: error code - unexpected token |
515 |
604 return_rec(-1); |
516 if (token.alloc > 0) { |
605 } |
517 free((char*)token.content); |
|
518 } |
|
519 |
|
520 return ret; |
|
521 } |
|
522 |
|
523 static CxJsonLiteral json_reader_literal(CxJson *p) { |
|
524 const char *l = p->reader_token.content; |
|
525 size_t token_len = p->reader_token.length; |
|
526 if (token_len == 4 && !memcmp(l, "true", 4)) { |
|
527 return CX_JSON_TRUE; |
|
528 } else if (token_len == 5 && !memcmp(l, "false", 5)) { |
|
529 return CX_JSON_FALSE; |
|
530 } |
|
531 return CX_JSON_NULL; |
|
532 } |
|
533 |
|
534 /* -------------------- read value functions -------------------- */ |
|
535 |
|
536 static int setup_read_value(CxJson *p) { |
|
537 p->readvalue_alloc = PARSER_READVALUE_ALLOC; |
|
538 p->readvalue_nelm = 0; |
|
539 p->readvalue_stack = calloc(p->readvalue_alloc, sizeof(CxJsonValue *)); |
|
540 if (!p->readvalue_stack) return -1; |
|
541 |
|
542 p->read_value = NULL; |
|
543 p->readvalue_stack[0] = NULL; |
|
544 |
|
545 return 0; |
|
546 } |
|
547 |
|
548 static int add_to_parent(CxJson *p, CxJsonValue *parent, CxJsonValue *v) { |
|
549 if (!parent) { |
|
550 return -1; // shouldn't happen but who knows |
|
551 } |
|
552 |
|
553 CxArrayReallocator reallocator = cx_array_reallocator(p->allocator, NULL); |
|
554 if (parent->type == CX_JSON_OBJECT) { |
|
555 if (!p->value_name || p->value_name_len == 0) { |
|
556 return -1; |
|
557 } |
|
558 char *valuename = p->value_name; |
|
559 p->value_name = NULL; |
|
560 |
|
561 CxJsonObjValue newvalue; |
|
562 newvalue.name = valuename; |
|
563 newvalue.value = v; |
|
564 |
|
565 return cx_array_add( |
|
566 &parent->value.object.values, |
|
567 &parent->value.object.values_size, |
|
568 &parent->value.object.values_capacity, |
|
569 sizeof(CxJsonObjValue), |
|
570 &newvalue, |
|
571 &reallocator); |
|
572 } else if (parent->type == CX_JSON_ARRAY) { |
|
573 return cx_array_add( |
|
574 &parent->value.array.array, |
|
575 &parent->value.array.array_size, |
|
576 &parent->value.array.array_capacity, |
|
577 sizeof(CxJsonValue*), |
|
578 &v, |
|
579 &reallocator); |
|
580 } else { |
606 } else { |
581 return -1; // should also never happen |
607 // should be unreachable |
582 } |
608 assert(false); |
583 } |
609 return_rec(-1); |
584 |
610 } |
585 |
611 } |
586 static int readvaluestack_add(CxJson *p, CxJsonValue *v) { |
612 |
587 if (p->readvalue_nelm == p->readvalue_alloc) { |
613 int cxJsonNext(CxJson *json, CxJsonValue **value) { |
588 p->readvalue_alloc *= 2; |
|
589 if (cx_reallocate(&p->readvalue_stack, sizeof(CxJsonValue *) * p->readvalue_alloc)) { |
|
590 return -1; |
|
591 } |
|
592 } |
|
593 p->readvalue_stack[p->readvalue_nelm++] = v; |
|
594 return 0; |
|
595 } |
|
596 |
|
597 void cxJsonInit(const CxAllocator *allocator, CxJson *json) { |
|
598 if (allocator == NULL) { |
|
599 allocator = cxDefaultAllocator; |
|
600 } |
|
601 |
|
602 memset(json, 0, sizeof(CxJson)); |
|
603 json->allocator = allocator; |
|
604 json->states = json->states_internal; |
|
605 json->states_alloc = cx_nmemb(json->states_internal); |
|
606 // TODO: find better way to configure the initial allocation size for arrays and objects |
|
607 json->reader_array_alloc = 8; |
|
608 } |
|
609 |
|
610 void cxJsonDestroy(CxJson *p) { |
|
611 if (p->states != p->states_internal) { |
|
612 free(p->states); |
|
613 } |
|
614 free(p->readvalue_stack); |
|
615 cxJsonValueFree(p->read_value); |
|
616 free(p->value_name); |
|
617 free(p->value_str); |
|
618 } |
|
619 |
|
620 int cxJsonFilln(CxJson *p, const char *buf, size_t size) { |
|
621 // TODO: implement rescue buffer like in CxProperties to allow subsequent fills |
|
622 p->buffer = buf; |
|
623 p->size = size; |
|
624 p->pos = 0; |
|
625 return 0; |
|
626 } |
|
627 |
|
628 int cxJsonNext(CxJson *p, CxJsonValue **value) { |
|
629 // TODO: replace int with a status enum like in CxProperties |
614 // TODO: replace int with a status enum like in CxProperties |
630 |
615 |
631 *value = NULL; // TODO: maybe better initialize with NOTHING? |
616 // initialize output value |
632 if (!p->readvalue_stack) { |
617 *value = &cx_json_value_nothing; |
633 if (setup_read_value(p)) return -1; |
618 |
634 } |
619 // parse data |
635 |
620 int result; |
636 while (p->readvalue_nelm > 0 || !p->read_value) { |
621 do { |
637 if (p->value_ready) { |
622 result = json_parse(json); |
638 // value available without another read |
623 if (result == 1 && json->states_size == 1) { |
639 CxJsonValue *v = cxCalloc(p->allocator, 1, sizeof(CxJsonValue)); |
624 // final state reached |
640 if (!v) return -1; |
625 assert(json->states[0] == JP_STATE_VALUE_END); |
641 v->allocator = p->allocator; |
626 assert(json->vbuf_size == 0); |
642 |
627 |
643 if (p->readvalue_nelm > 0) { |
628 // write output value |
644 if (add_to_parent(p, p->readvalue_stack[p->readvalue_nelm - 1], v)) { |
629 *value = json->parsed; |
645 free(v); |
630 json->parsed = NULL; |
646 return -1; |
631 |
647 } |
632 // re-initialize state machine |
648 } else { |
633 json->states[0] = JP_STATE_VALUE_BEGIN; |
649 // set this value as root |
634 |
650 p->read_value = v; |
635 return 1; |
651 } |
636 } |
652 |
637 } while (result == 1); |
653 switch (p->reader_type) { |
638 |
654 case CX_JSON_READER_OBJECT_BEGIN: { |
639 return result; |
655 v->type = CX_JSON_OBJECT; |
|
656 if (readvaluestack_add(p, v)) { |
|
657 return -1; |
|
658 } |
|
659 break; |
|
660 } |
|
661 case CX_JSON_READER_OBJECT_END: |
|
662 return -1; // should not happen |
|
663 case CX_JSON_READER_ARRAY_BEGIN: { |
|
664 v->type = CX_JSON_ARRAY; |
|
665 if (readvaluestack_add(p, v)) { |
|
666 return -1; |
|
667 } |
|
668 break; |
|
669 } |
|
670 case CX_JSON_READER_ARRAY_END: |
|
671 return -1; // should not happen |
|
672 case CX_JSON_READER_STRING: { |
|
673 v->type = CX_JSON_STRING; |
|
674 if (p->value_str) { |
|
675 v->value.string.ptr = p->value_str; |
|
676 v->value.string.length = p->value_str_len; |
|
677 p->value_str = NULL; |
|
678 } |
|
679 break; |
|
680 } |
|
681 case CX_JSON_READER_INTEGER: { |
|
682 v->type = CX_JSON_INTEGER; |
|
683 v->value.integer = p->value_int; |
|
684 break; |
|
685 } |
|
686 case CX_JSON_READER_NUMBER: { |
|
687 v->type = CX_JSON_NUMBER; |
|
688 v->value.number = p->value_double; |
|
689 break; |
|
690 } |
|
691 case CX_JSON_READER_LITERAL: { |
|
692 v->type = CX_JSON_LITERAL; |
|
693 v->value.literal = json_reader_literal(p); |
|
694 break; |
|
695 } |
|
696 } |
|
697 } else if (p->readvalue_initialized) { |
|
698 CxJsonReaderType rt = p->reader_type; |
|
699 if (rt == CX_JSON_READER_OBJECT_END || rt == CX_JSON_READER_ARRAY_END) { |
|
700 p->readvalue_nelm--; |
|
701 } |
|
702 // else: p->value_ready is 1, this will be handled in the next run |
|
703 } |
|
704 |
|
705 if (p->readvalue_nelm > 0 || !p->read_value) { |
|
706 int r = json_read(p); |
|
707 if (r != 1) { |
|
708 p->readvalue_initialized = 0; |
|
709 return r; |
|
710 } |
|
711 p->readvalue_initialized = 1; |
|
712 } |
|
713 } |
|
714 |
|
715 *value = p->read_value; |
|
716 p->readvalue_initialized = 0; |
|
717 p->read_value = NULL; |
|
718 p->value_ready = 0; |
|
719 |
|
720 return 1; |
|
721 } |
640 } |
722 |
641 |
723 void cxJsonValueFree(CxJsonValue *value) { |
642 void cxJsonValueFree(CxJsonValue *value) { |
724 if (value == NULL || value == &cx_json_value_nothing) return; |
643 if (value == NULL || value == &cx_json_value_nothing) return; |
725 |
644 |