src/json.c

changeset 937
10123f4d5618
child 938
9d02bb5dcc3c
equal deleted inserted replaced
936:9b9385fcdfd5 937:10123f4d5618
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2024 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <string.h>
30 #include <ctype.h>
31
32 #include "cx/json.h"
33 #include "cx/allocator.h"
34
35 /*
36 * RFC 8259
37 * https://tools.ietf.org/html/rfc8259
38 */
39
40 #define PARSER_STATES_ALLOC 32
41
42 static CxJsonValue cx_json_value_nothing = {CX_JSON_NOTHING, {0}};
43
44
45 static int token_append(CxJsonToken *token, const char *buf, size_t len) {
46 if (len == 0) {
47 return 0;
48 }
49
50 size_t newlen = token->length + len;
51 if (token->alloc < newlen) {
52 char *newbuf = realloc(
53 token->alloc == 0 ? NULL : (char *) token->content,
54 newlen);
55 if (!newbuf) {
56 return 1;
57 }
58 token->content = newbuf;
59 token->alloc = newlen;
60 }
61
62 memcpy((char *) token->content + token->length, buf, len);
63 token->length = newlen;
64 return 0;
65 }
66
67 static CxJsonToken get_content(CxJson *p, size_t start, size_t end) {
68 CxJsonToken token = {0};
69 size_t part2 = end - start;
70 if (p->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
71 token.content = p->buffer + start;
72 token.length = part2;
73 } else if (part2 == 0) {
74 token = p->uncompleted;
75 } else {
76 if (token_append(&p->uncompleted, p->buffer + start, end - start)) {
77 // TODO: this does certainly not lead to correct error handling
78 return (CxJsonToken){0};
79 }
80 token = p->uncompleted;
81 }
82 p->uncompleted = (CxJsonToken){0};
83 return token;
84 }
85
86 static int token_isliteral(const char *content, size_t length) {
87 if (length == 4) {
88 if (!memcmp(content, "true", 4)) {
89 return 1;
90 } else if (!memcmp(content, "null", 4)) {
91 return 1;
92 }
93 } else if (length == 5 && !memcmp(content, "false", 5)) {
94 return 1;
95 }
96 return 0;
97 }
98
99 static int num_isexp(const char *content, size_t length, size_t pos) {
100 if (pos >= length) {
101 return 0;
102 }
103
104 int ok = 0;
105 for (size_t i = pos; i < length; i++) {
106 char c = content[i];
107 if (isdigit(c)) {
108 ok = 1;
109 } else if (i == pos) {
110 if (!(c == '+' || c == '-')) {
111 return 0;
112 }
113 } else {
114 return 0;
115 }
116 }
117
118 return ok;
119 }
120
121 static CxJsonTokenType token_numbertype(const char *content, size_t length) {
122 if (length == 0) return CX_JSON_TOKEN_ERROR;
123
124 if (content[0] != '-' && !isdigit(content[0])) {
125 return CX_JSON_TOKEN_ERROR;
126 }
127
128 CxJsonTokenType type = CX_JSON_TOKEN_INTEGER;
129 for (size_t i = 1; i < length; i++) {
130 if (content[i] == '.') {
131 if (type == CX_JSON_TOKEN_NUMBER) {
132 return CX_JSON_TOKEN_ERROR; // more than one decimal separator
133 }
134 type = CX_JSON_TOKEN_NUMBER;
135 } else if (content[i] == 'e' || content[i] == 'E') {
136 return num_isexp(content, length, i + 1) ? CX_JSON_TOKEN_NUMBER : CX_JSON_TOKEN_ERROR;
137 } else if (!isdigit(content[i])) {
138 return CX_JSON_TOKEN_ERROR; // char is not a digit, decimal separator or exponent sep
139 }
140 }
141
142 return type;
143 }
144
145 static CxJsonToken get_token(CxJson *p, size_t start, size_t end) {
146 CxJsonToken token = get_content(p, start, end);
147 if (token_isliteral(token.content, token.length)) {
148 token.tokentype = CX_JSON_TOKEN_LITERAL;
149 } else {
150 token.tokentype = token_numbertype(token.content, token.length);
151 }
152 p->pos = end;
153 return token;
154 }
155
156 static CxJsonTokenType char2ttype(char c) {
157 switch (c) {
158 case '[': {
159 return CX_JSON_TOKEN_BEGIN_ARRAY;
160 }
161 case '{': {
162 return CX_JSON_TOKEN_BEGIN_OBJECT;
163 }
164 case ']': {
165 return CX_JSON_TOKEN_END_ARRAY;
166 }
167 case '}': {
168 return CX_JSON_TOKEN_END_OBJECT;
169 }
170 case ':': {
171 return CX_JSON_TOKEN_NAME_SEPARATOR;
172 }
173 case ',': {
174 return CX_JSON_TOKEN_VALUE_SEPARATOR;
175 }
176 case '"': {
177 return CX_JSON_TOKEN_STRING;
178 }
179 default: {
180 if (isspace(c)) {
181 return CX_JSON_TOKEN_SPACE;
182 }
183 }
184 }
185 return CX_JSON_NO_TOKEN;
186 }
187
188 static CxJsonToken json_parser_next_token(CxJson *p) {
189 // current token type and start index
190 CxJsonTokenType ttype = p->uncompleted.tokentype;
191 size_t token_start = p->pos;
192
193 for (size_t i = p->pos; i < p->size; i++) {
194 char c = p->buffer[i];
195 if (ttype != CX_JSON_TOKEN_STRING) {
196 // currently non-string token
197
198 CxJsonTokenType ctype = char2ttype(c); // start of new token?
199
200 if (ttype == CX_JSON_NO_TOKEN) {
201 if (ctype == CX_JSON_TOKEN_SPACE) {
202 continue;
203 } else if (ctype == CX_JSON_TOKEN_STRING) {
204 // begin string
205 ttype = CX_JSON_TOKEN_STRING;
206 token_start = i;
207 } else if (ctype != CX_JSON_NO_TOKEN) {
208 // single-char token
209 p->pos = i + 1;
210 CxJsonToken token = {ctype, NULL, 0, 0};
211 return token;
212 } else {
213 ttype = CX_JSON_TOKEN_LITERAL; // number or literal
214 token_start = i;
215 }
216 } else {
217 // finish token
218 if (ctype != CX_JSON_NO_TOKEN) {
219 return get_token(p, token_start, i);
220 }
221 }
222 } else {
223 // currently inside a string
224 if (!p->tokenizer_escape) {
225 if (c == '"') {
226 CxJsonToken ret = get_content(p, token_start, i + 1);
227 ret.tokentype = CX_JSON_TOKEN_STRING;
228 p->pos = i + 1;
229 return ret;
230 } else if (c == '\\') {
231 p->tokenizer_escape = 1;
232 }
233 } else {
234 p->tokenizer_escape = 0;
235 }
236 }
237 }
238
239 if (ttype != CX_JSON_NO_TOKEN) {
240 // uncompleted token
241 size_t uncompeted_len = p->size - token_start;
242 if (p->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
243 // current token is uncompleted
244 // save current token content in p->uncompleted
245 CxJsonToken uncompleted;
246 uncompleted.tokentype = ttype;
247 uncompleted.length = uncompeted_len;
248 uncompleted.alloc = uncompeted_len + 16;
249 char *tmp = malloc(uncompleted.alloc);
250 if (tmp) {
251 memcpy(tmp, p->buffer + token_start, uncompeted_len);
252 uncompleted.content = tmp;
253 p->uncompleted = uncompleted;
254 } else {
255 p->error = 1;
256 }
257 } else {
258 // previously we also had an uncompleted token
259 // combine the uncompleted token with the current token
260 if (token_append(&p->uncompleted, p->buffer + token_start, uncompeted_len)) {
261 p->error = 1;
262 }
263 }
264 }
265
266 CxJsonToken ret = {CX_JSON_NO_TOKEN, NULL, 0, 0};
267 return ret;
268 }
269
270 static cxmutstr unescape_string(const char *str, size_t len) {
271 // TODO: support more escape sequences
272 // we know that the unescaped string will be shorter by at least 2 chars
273 cxmutstr result;
274 result.length = 0;
275 result.ptr = malloc(len - 1);
276 if (result.ptr == NULL) {
277 // TODO: check if this actually leads to correct error handling
278 return result;
279 }
280
281 bool u = false;
282 for (size_t i = 1; i < len - 1; i++) {
283 char c = str[i];
284 if (u) {
285 u = false;
286 if (c == 'n') {
287 c = '\n';
288 } else if (c == 't') {
289 c = '\t';
290 }
291 result.ptr[result.length++] = c;
292 } else {
293 if (c == '\\') {
294 u = true;
295 } else {
296 result.ptr[result.length++] = c;
297 }
298 }
299 }
300 result.ptr[result.length] = 0;
301
302 return result;
303 }
304
305 static int parse_integer(const char *str, size_t len, int64_t *value) {
306 char *endptr = NULL;
307 char buf[32];
308 if (len > 30) {
309 return 1;
310 }
311 memcpy(buf, str, len);
312 buf[len] = 0;
313
314 long long v = strtoll(buf, &endptr, 10);
315 if (endptr != &buf[len]) {
316 return 1;
317 }
318 *value = (int64_t) v;
319
320 return 0;
321 }
322
323 static int parse_number(const char *str, size_t len, double *value) {
324 char *endptr = NULL;
325 char buf[32];
326 if (len > 30) {
327 return 1;
328 }
329 memcpy(buf, str, len);
330 buf[len] = 0;
331
332 double v = strtod(buf, &endptr);
333 if (endptr != &buf[len]) {
334 return 1;
335 }
336 *value = v;
337
338 return 0;
339 }
340
341 static int add_state(CxJson *p, int state) {
342 if (p->nstates >= p->states_alloc) {
343 p->states_alloc += PARSER_STATES_ALLOC;
344 if (cx_reallocate(&p->states, p->states_alloc * sizeof(int))) {
345 return 1;
346 }
347 }
348 p->states[++p->nstates] = state;
349 return 0;
350 }
351
352 static void end_elm(CxJson *p, CxJsonReaderType type) {
353 p->reader_type = type;
354 p->nstates--;
355 }
356
357 #define JP_STATE_VALUE_BEGIN 0
358 #define JP_STATE_VALUE_BEGIN_OBJ 1
359 #define JP_STATE_VALUE_BEGIN_AR 2
360 #define JP_STATE_ARRAY_SEP_OR_CLOSE 3
361 #define JP_STATE_OBJ_NAME_OR_CLOSE 4
362 #define JP_STATE_OBJ_NAME 5
363 #define JP_STATE_OBJ_COLON 6
364 #define JP_STATE_OBJ_SEP_OR_CLOSE 7
365
366 static int next_state_after_value(int current) {
367 switch (current) {
368 default:
369 return -1;
370 // after value JSON complete, expect nothing
371 case JP_STATE_VALUE_BEGIN:
372 return -1;
373 // after obj value, expect ',' or '}'
374 case JP_STATE_VALUE_BEGIN_OBJ:
375 return JP_STATE_OBJ_SEP_OR_CLOSE;
376 // after array value, expect ',' or ']'
377 case JP_STATE_VALUE_BEGIN_AR:
378 return JP_STATE_ARRAY_SEP_OR_CLOSE;
379 }
380 }
381
382 static void clear_valuename(CxJson *p) {
383 free(p->value_name);
384 p->value_name = NULL;
385 p->value_name_len = 0;
386 }
387
388 static void clear_values(CxJson *p) {
389 free(p->value_str);
390 p->value_str = NULL;
391 p->value_str_len = 0;
392 p->value_int = 0;
393 p->value_double = 0;
394 }
395
396 static int json_read(CxJson *p) {
397 int state = p->states[p->nstates];
398 clear_values(p);
399 CxJsonToken token = json_parser_next_token(p);
400 p->reader_token = token;
401
402 p->value_ready = 0;
403
404 if (token.tokentype == CX_JSON_NO_TOKEN) {
405 return 0;
406 }
407
408 int ret = 1;
409
410 // 0 JP_STATE_VALUE_BEGIN value begin
411 // 1 JP_STATE_VALUE_BEGIN_OBJ value begin (inside object)
412 // 2 JP_STATE_VALUE_BEGIN_AR value begin (inside array)
413 // 3 JP_STATE_ARRAY_SEP_OR_CLOSE array, expect separator or arrayclose
414 // 4 JP_STATE_OBJ_NAME_OR_CLOSE object, expect name or objclose
415 // 5 JP_STATE_OBJ_NAME object, expect name
416 // 6 JP_STATE_OBJ_COLON object, expect ':'
417 // 7 JP_STATE_OBJ_SEP_OR_CLOSE object, expect separator, objclose
418
419 if (state == JP_STATE_VALUE_BEGIN_AR || state == JP_STATE_OBJ_SEP_OR_CLOSE) {
420 clear_valuename(p);
421 }
422
423 if (state < 3) {
424 // expect value
425 p->states[p->nstates] = next_state_after_value(state);
426 p->value_ready = 1;
427 switch (token.tokentype) {
428 case CX_JSON_TOKEN_BEGIN_ARRAY: {
429 p->reader_type = CX_JSON_READER_ARRAY_BEGIN;
430 if (add_state(p, JP_STATE_VALUE_BEGIN_AR)) return -1;
431 return 1;
432 //return json_read(p);
433 }
434 case CX_JSON_TOKEN_BEGIN_OBJECT: {
435 p->reader_type = CX_JSON_READER_OBJECT_BEGIN;
436 if (add_state(p, JP_STATE_OBJ_NAME_OR_CLOSE)) return -1;
437 return 1;
438 //return json_read(p);
439 }
440 case CX_JSON_TOKEN_END_ARRAY: {
441 p->value_ready = 0;
442 end_elm(p, CX_JSON_READER_ARRAY_END);
443 break;
444 }
445 case CX_JSON_TOKEN_END_OBJECT: {
446 p->value_ready = 0;
447 end_elm(p, CX_JSON_READER_OBJECT_END);
448 break;
449 }
450 case CX_JSON_TOKEN_STRING: {
451 p->reader_type = CX_JSON_READER_STRING;
452 cxmutstr str = unescape_string(token.content, token.length);
453 if (str.ptr) {
454 p->value_str = str.ptr;
455 p->value_str_len = str.length;
456 } else {
457 return -1;
458 }
459 break;
460 }
461 case CX_JSON_TOKEN_INTEGER: {
462 p->reader_type = CX_JSON_READER_INTEGER;
463 int64_t value;
464 if (parse_integer(token.content, token.length, &value)) {
465 return -1;
466 }
467 p->value_int = value;
468 p->value_double = (double) value;
469 break;
470 }
471 case CX_JSON_TOKEN_NUMBER: {
472 p->reader_type = CX_JSON_READER_NUMBER;
473 double value;
474 if (parse_number(token.content, token.length, &value)) {
475 return -1;
476 }
477 p->value_double = value;
478 p->value_int = (int64_t) value;
479 break;
480 }
481 case CX_JSON_TOKEN_LITERAL: {
482 p->reader_type = CX_JSON_READER_LITERAL;
483 break;
484 }
485 default:
486 return -1;
487 }
488 } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) {
489 // expect ',' or ']'
490 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) {
491 p->states[p->nstates] = JP_STATE_VALUE_BEGIN_AR;
492 return json_read(p);
493 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) {
494 end_elm(p, CX_JSON_READER_ARRAY_END);
495 } else {
496 return -1;
497 }
498 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) {
499 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
500 clear_valuename(p);
501 end_elm(p, CX_JSON_READER_OBJECT_END);
502 } else {
503 // expect string
504 if (token.tokentype != CX_JSON_TOKEN_STRING) return -1;
505
506 if (p->value_name) free(p->value_name);
507 cxmutstr valname = unescape_string(token.content, token.length);
508 p->value_name = valname.ptr;
509 p->value_name_len = valname.length;
510
511 // next state
512 p->states[p->nstates] = JP_STATE_OBJ_COLON;
513 return json_read(p);
514 }
515 } else if (state == JP_STATE_OBJ_COLON) {
516 // expect ':'
517 if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) return -1;
518 // next state
519 p->states[p->nstates] = 1;
520 return json_read(p);
521 } else if (state == 7) {
522 // expect ',' or '}]'
523 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) {
524 p->states[p->nstates] = JP_STATE_OBJ_NAME;
525 return json_read(p);
526 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) {
527 end_elm(p, CX_JSON_READER_OBJECT_END);
528 } else {
529 return -1;
530 }
531 }
532
533 return ret;
534 }
535
536 static CxJsonLiteralType json_reader_literal(CxJson *p) {
537 const char *l = p->reader_token.content;
538 size_t token_len = p->reader_token.length;
539 if (token_len == 4 && !memcmp(l, "true", 4)) {
540 return CX_JSON_TRUE;
541 } else if (token_len == 5 && !memcmp(l, "false", 5)) {
542 return CX_JSON_FALSE;
543 }
544 return CX_JSON_NULL;
545 }
546
547 /* -------------------- read value functions -------------------- */
548
549 static int setup_read_value(CxJson *p) {
550 p->readvalue_alloc = PARSER_STATES_ALLOC;
551 p->readvalue_nelm = 0;
552 p->readvalue_stack = calloc(PARSER_STATES_ALLOC, sizeof(CxJsonValue *));
553 if (!p->readvalue_stack) return -1;
554
555 p->read_value = NULL;
556 p->readvalue_stack[0] = NULL;
557
558 return 0;
559 }
560
561 static int obj_init_values(CxJson *p, CxJsonValue *v) {
562 v->value.object.values = calloc(p->reader_array_alloc, sizeof(CxJsonObjValue));
563 if (!v->value.object.values) {
564 return -1;
565 }
566 v->value.object.alloc = p->reader_array_alloc;
567 v->value.object.size = 0;
568
569 return 0;
570 }
571
572 static int obj_add_value(CxJson *p, CxJsonValue *parent, CxJsonObjValue v) {
573 if (!parent->value.object.values) {
574 if (obj_init_values(p, parent)) {
575 return -1;
576 }
577 }
578
579 if (parent->value.object.size == parent->value.object.alloc) {
580 parent->value.object.alloc *= 2;
581 if (cx_reallocate(&parent->value.object.values,
582 sizeof(CxJsonObjValue) * parent->value.object.alloc)) {
583 return -1;
584 }
585 }
586
587 parent->value.object.values[parent->value.object.size++] = v;
588
589 return 0;
590 }
591
592 static int array_init(CxJson *p, CxJsonValue *v) {
593 v->value.array.array = calloc(p->reader_array_alloc, sizeof(CxJsonValue *));
594 if (!v->value.array.array) {
595 return -1;
596 }
597 v->value.array.alloc = p->reader_array_alloc;
598 v->value.array.size = 0;
599
600 return 0;
601 }
602
603 static int array_add_value(CxJson *p, CxJsonValue *parent, CxJsonValue *v) {
604 if (!parent->value.array.array) {
605 if (array_init(p, parent)) {
606 return -1;
607 }
608 }
609
610 if (parent->value.array.size == parent->value.array.alloc) {
611 parent->value.array.alloc *= 2;
612 if (cx_reallocate(parent->value.array.array,
613 sizeof(CxJsonValue *) * parent->value.array.alloc)) {
614 return -1;
615 }
616 }
617
618 parent->value.array.array[parent->value.array.size++] = v;
619
620 return 0;
621 }
622
623 static int add_to_parent(CxJson *p, CxJsonValue *parent, CxJsonValue *v) {
624 if (!parent) {
625 return -1; // shouldn't happen but who knows
626 }
627
628 int ret = 0;
629 if (parent->type == CX_JSON_OBJECT) {
630 if (!p->value_name || p->value_name_len == 0) {
631 return -1;
632 }
633 char *valuename = p->value_name;
634 p->value_name = NULL;
635
636 CxJsonObjValue newvalue;
637 newvalue.name = valuename;
638 newvalue.value = v;
639
640 ret = obj_add_value(p, parent, newvalue);
641 } else if (parent->type == CX_JSON_ARRAY) {
642 ret = array_add_value(p, parent, v);
643 } else {
644 ret = -1; // should also never happen
645 }
646
647 return ret;
648 }
649
650
651 static int readvaluestack_add(CxJson *p, CxJsonValue *v) {
652 if (p->readvalue_nelm == p->readvalue_alloc) {
653 p->readvalue_alloc *= 2;
654 if (cx_reallocate(&p->readvalue_stack, sizeof(CxJsonValue *) * p->readvalue_alloc)) {
655 return -1;
656 }
657 }
658 p->readvalue_stack[p->readvalue_nelm++] = v;
659 return 0;
660 }
661
662 void cxJsonInit(CxJson *json) {
663 memset(json, 0, sizeof(CxJson));
664 // TODO: do not allocate states right away
665 json->states_alloc = PARSER_STATES_ALLOC;
666 json->states = calloc(PARSER_STATES_ALLOC, sizeof(int));
667 // TODO: find better way to configure the initial allocation size for arrays and objects
668 json->reader_array_alloc = 8;
669 }
670
671 void cxJsonDestroy(CxJson *p) {
672 free(p->states);
673 free(p->readvalue_stack);
674 }
675
676 void cxJsonFill(CxJson *p, const char *buf, size_t size) {
677 // TODO: implement rescue buffer like in CxProperties to allow subsequent fills
678 p->buffer = buf;
679 p->size = size;
680 p->pos = 0;
681 }
682
683 int cxJsonNext(CxJson *p, CxJsonValue **value) {
684 // TODO: replace int with a status enum like in CxProperties
685
686 *value = NULL;
687 if (!p->readvalue_stack) {
688 if (setup_read_value(p)) return -1;
689 }
690
691 while (p->readvalue_nelm > 0 || !p->read_value) {
692 if (p->value_ready) {
693 // value available without another read
694 CxJsonValue *v = calloc(1, sizeof(CxJsonValue));
695 if (!v) return -1;
696
697 if (p->readvalue_nelm > 0) {
698 if (add_to_parent(p, p->readvalue_stack[p->readvalue_nelm - 1], v)) {
699 return -1;
700 }
701 } else {
702 // set this value as root
703 p->read_value = v;
704 }
705
706 switch (p->reader_type) {
707 case CX_JSON_READER_OBJECT_BEGIN: {
708 v->type = CX_JSON_OBJECT;
709 if (readvaluestack_add(p, v)) {
710 return -1;
711 }
712 break;
713 }
714 case CX_JSON_READER_OBJECT_END:
715 return -1; // should not happen
716 case CX_JSON_READER_ARRAY_BEGIN: {
717 v->type = CX_JSON_ARRAY;
718 if (readvaluestack_add(p, v)) {
719 return -1;
720 }
721 break;
722 }
723 case CX_JSON_READER_ARRAY_END:
724 return -1; // should not happen
725 case CX_JSON_READER_STRING: {
726 v->type = CX_JSON_STRING;
727 if (p->value_str) {
728 v->value.string.ptr = p->value_str;
729 v->value.string.length = p->value_str_len;
730 p->value_str = NULL;
731 }
732 break;
733 }
734 case CX_JSON_READER_INTEGER: {
735 v->type = CX_JSON_INTEGER;
736 v->value.integer.value = p->value_int;
737 break;
738 }
739 case CX_JSON_READER_NUMBER: {
740 v->type = CX_JSON_NUMBER;
741 v->value.number.value = p->value_double;
742 break;
743 }
744 case CX_JSON_READER_LITERAL: {
745 v->type = CX_JSON_LITERAL;
746 v->value.literal.literal = json_reader_literal(p);
747 break;
748 }
749 }
750 } else if (p->readvalue_initialized) {
751 CxJsonReaderType rt = p->reader_type;
752 if (rt == CX_JSON_READER_OBJECT_END || rt == CX_JSON_READER_ARRAY_END) {
753 p->readvalue_nelm--;
754 }
755 // else: p->value_ready is 1, this will be handled in the next run
756 }
757
758 if (p->readvalue_nelm > 0 || !p->read_value) {
759 int r = json_read(p);
760 if (r != 1) {
761 p->readvalue_initialized = 0;
762 return r;
763 }
764 p->readvalue_initialized = 1;
765 }
766 }
767
768 *value = p->read_value;
769 p->readvalue_initialized = 0;
770 p->read_value = NULL;
771
772 return 1;
773 }
774
775 void cxJsonValueFree(CxJsonValue *value) {
776 if (value == NULL || value == &cx_json_value_nothing) return;
777
778 // TODO: discuss if we should keep freeing the stuff recursively
779 switch (value->type) {
780 case CX_JSON_OBJECT: {
781 CxJsonObject obj = value->value.object;
782 for (size_t i = 0; i < obj.size; i++) {
783 cxJsonValueFree(obj.values[i].value);
784 free(obj.values[i].name);
785 }
786 free(obj.values);
787 break;
788 }
789 case CX_JSON_ARRAY: {
790 CxJsonArray array = value->value.array;
791 for (size_t i = 0; i < array.size; i++) {
792 cxJsonValueFree(array.array[i]);
793 }
794 free(array.array);
795 break;
796 }
797 case CX_JSON_STRING: {
798 free(value->value.string.ptr);
799 break;
800 }
801 default: {
802 break;
803 }
804 }
805 free(value);
806 }
807
808 CxJsonValue *cxJsonArrGet(CxJsonValue *value, size_t index) {
809 if (index >= value->value.array.size) {
810 return &cx_json_value_nothing;
811 }
812 return value->value.array.array[index];
813 }
814
815 CxJsonValue *cxJsonObjGet(CxJsonValue *value, const char *name) {
816 CxJsonObject *obj = &(value->value.object);
817 // TODO: think about sorting the object so that we can use binary search here
818 for (size_t i = 0; i < obj->size; i++) {
819 // TODO: we might want to store names as cxmutstr
820 if (0 == strcmp(name, obj->values[i].name)) {
821 return obj->values[i].value;
822 }
823 }
824 return &cx_json_value_nothing;
825 }

mercurial