src/json.c

changeset 1000
1aecddf7e209
parent 996
333155f234c4
child 1002
1483c47063a8
equal deleted inserted replaced
999:84fc42b04d3b 1000:1aecddf7e209
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 #include <string.h> 29 #include <string.h>
30 #include <ctype.h> 30 #include <ctype.h>
31 #include <assert.h>
31 32
32 #include "cx/json.h" 33 #include "cx/json.h"
33 34
34 /* 35 /*
35 * RFC 8259 36 * RFC 8259
38 39
39 #define PARSER_READVALUE_ALLOC 32 40 #define PARSER_READVALUE_ALLOC 32
40 41
41 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING}; 42 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING};
42 43
44 static void token_destroy(CxJsonToken *token) {
45 if (token->alloc > 0) {
46 free((char*) token->content);
47 }
48 }
43 49
44 static int token_append(CxJsonToken *token, const char *buf, size_t len) { 50 static int token_append(CxJsonToken *token, const char *buf, size_t len) {
45 if (len == 0) { 51 if (len == 0) {
46 return 0; 52 return 0;
47 } 53 }
61 memcpy((char *) token->content + token->length, buf, len); 67 memcpy((char *) token->content + token->length, buf, len);
62 token->length = newlen; 68 token->length = newlen;
63 return 0; 69 return 0;
64 } 70 }
65 71
66 static CxJsonToken get_content(CxJson *p, size_t start, size_t end) { 72 static CxJsonToken token_create(CxJson *json, size_t start, size_t end) {
67 CxJsonToken token = {0}; 73 CxJsonToken token = {0};
68 size_t part2 = end - start; 74 size_t len = end - start;
69 if (p->uncompleted.tokentype == CX_JSON_NO_TOKEN) { 75 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
70 token.content = p->buffer + start; 76 token.content = json->buffer + start;
71 token.length = part2; 77 token.length = len;
72 } else if (part2 == 0) {
73 token = p->uncompleted;
74 } else { 78 } else {
75 if (token_append(&p->uncompleted, p->buffer + start, end - start)) { 79 if (token_append(&json->uncompleted, json->buffer + start, len)) {
76 // TODO: this does certainly not lead to correct error handling 80 // TODO: this does certainly not lead to correct error handling
77 return (CxJsonToken){0}; 81 return (CxJsonToken){0};
78 } 82 }
79 token = p->uncompleted; 83 token = json->uncompleted;
80 } 84 }
81 p->uncompleted = (CxJsonToken){0}; 85 json->uncompleted = (CxJsonToken){0};
82 return token; 86 return token;
83 } 87 }
84 88
85 static int token_isliteral(const char *content, size_t length) { 89 static int token_isliteral(const char *content, size_t length) {
86 if (length == 4) { 90 if (length == 4) {
139 } 143 }
140 144
141 return type; 145 return type;
142 } 146 }
143 147
144 static CxJsonToken get_token(CxJson *p, size_t start, size_t end) {
145 CxJsonToken token = get_content(p, start, end);
146 if (token_isliteral(token.content, token.length)) {
147 token.tokentype = CX_JSON_TOKEN_LITERAL;
148 } else {
149 token.tokentype = token_numbertype(token.content, token.length);
150 }
151 p->pos = end;
152 return token;
153 }
154
155 static CxJsonTokenType char2ttype(char c) { 148 static CxJsonTokenType char2ttype(char c) {
156 switch (c) { 149 switch (c) {
157 case '[': { 150 case '[': {
158 return CX_JSON_TOKEN_BEGIN_ARRAY; 151 return CX_JSON_TOKEN_BEGIN_ARRAY;
159 } 152 }
182 } 175 }
183 } 176 }
184 return CX_JSON_NO_TOKEN; 177 return CX_JSON_NO_TOKEN;
185 } 178 }
186 179
187 static CxJsonToken json_parser_next_token(CxJson *p) { 180 static CxJsonToken token_parse_next(CxJson *json) {
188 // current token type and start index 181 // current token type and start index
189 CxJsonTokenType ttype = p->uncompleted.tokentype; 182 CxJsonTokenType ttype = json->uncompleted.tokentype;
190 size_t token_start = p->pos; 183 size_t token_start = json->pos;
191 184
192 for (size_t i = p->pos; i < p->size; i++) { 185 for (size_t i = json->pos; i < json->size; i++) {
193 char c = p->buffer[i]; 186 char c = json->buffer[i];
194 if (ttype != CX_JSON_TOKEN_STRING) { 187 if (ttype != CX_JSON_TOKEN_STRING) {
195 // currently non-string token 188 // currently non-string token
196 189
197 CxJsonTokenType ctype = char2ttype(c); // start of new token? 190 CxJsonTokenType ctype = char2ttype(c); // start of new token?
198 191
203 // begin string 196 // begin string
204 ttype = CX_JSON_TOKEN_STRING; 197 ttype = CX_JSON_TOKEN_STRING;
205 token_start = i; 198 token_start = i;
206 } else if (ctype != CX_JSON_NO_TOKEN) { 199 } else if (ctype != CX_JSON_NO_TOKEN) {
207 // single-char token 200 // single-char token
208 p->pos = i + 1; 201 json->pos = i + 1;
209 CxJsonToken token = {ctype, NULL, 0, 0}; 202 CxJsonToken token = {ctype, NULL, 0, 0};
210 return token; 203 return token;
211 } else { 204 } else {
212 ttype = CX_JSON_TOKEN_LITERAL; // number or literal 205 ttype = CX_JSON_TOKEN_LITERAL; // number or literal
213 token_start = i; 206 token_start = i;
214 } 207 }
215 } else { 208 } else {
216 // finish token 209 // finish token
217 if (ctype != CX_JSON_NO_TOKEN) { 210 if (ctype != CX_JSON_NO_TOKEN) {
218 return get_token(p, token_start, i); 211 CxJsonToken ret = token_create(json, token_start, i);
212 if (token_isliteral(ret.content, ret.length)) {
213 ret.tokentype = CX_JSON_TOKEN_LITERAL;
214 } else {
215 ret.tokentype = token_numbertype(ret.content, ret.length);
216 }
217 json->pos = i;
218 return ret;
219 } 219 }
220 } 220 }
221 } else { 221 } else {
222 // currently inside a string 222 // currently inside a string
223 if (!p->tokenizer_escape) { 223 if (json->tokenizer_escape) {
224 json->tokenizer_escape = false;
225 } else {
224 if (c == '"') { 226 if (c == '"') {
225 CxJsonToken ret = get_content(p, token_start, i + 1); 227 CxJsonToken ret = token_create(json, token_start, i + 1);
226 ret.tokentype = CX_JSON_TOKEN_STRING; 228 ret.tokentype = CX_JSON_TOKEN_STRING;
227 p->pos = i + 1; 229 json->pos = i + 1;
228 return ret; 230 return ret;
229 } else if (c == '\\') { 231 } else if (c == '\\') {
230 p->tokenizer_escape = 1; 232 json->tokenizer_escape = true;
231 } 233 }
232 } else {
233 p->tokenizer_escape = 0;
234 } 234 }
235 } 235 }
236 } 236 }
237 237
238 if (ttype != CX_JSON_NO_TOKEN) { 238 if (ttype != CX_JSON_NO_TOKEN) {
239 // uncompleted token 239 // uncompleted token
240 size_t uncompeted_len = p->size - token_start; 240 size_t uncompeted_len = json->size - token_start;
241 if (p->uncompleted.tokentype == CX_JSON_NO_TOKEN) { 241 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
242 // current token is uncompleted 242 // current token is uncompleted
243 // save current token content in p->uncompleted 243 // save current token content in p->uncompleted
244 CxJsonToken uncompleted; 244 CxJsonToken uncompleted;
245 uncompleted.tokentype = ttype; 245 uncompleted.tokentype = ttype;
246 uncompleted.length = uncompeted_len; 246 uncompleted.length = uncompeted_len;
247 uncompleted.alloc = uncompeted_len + 16; 247 uncompleted.alloc = uncompeted_len + 16;
248 char *tmp = malloc(uncompleted.alloc); 248 char *tmp = malloc(uncompleted.alloc);
249 if (tmp) { 249 if (tmp) {
250 memcpy(tmp, p->buffer + token_start, uncompeted_len); 250 memcpy(tmp, json->buffer + token_start, uncompeted_len);
251 uncompleted.content = tmp; 251 uncompleted.content = tmp;
252 p->uncompleted = uncompleted; 252 json->uncompleted = uncompleted;
253 } else { 253 } else {
254 p->error = 1; 254 json->error = 1;
255 } 255 }
256 } else { 256 } else {
257 // previously we also had an uncompleted token 257 // previously we also had an uncompleted token
258 // combine the uncompleted token with the current token 258 // combine the uncompleted token with the current token
259 if (token_append(&p->uncompleted, p->buffer + token_start, uncompeted_len)) { 259 if (token_append(&json->uncompleted, json->buffer + token_start, uncompeted_len)) {
260 p->error = 1; 260 json->error = 1;
261 } 261 }
262 } 262 }
263 } 263 }
264 264
265 CxJsonToken ret = {CX_JSON_NO_TOKEN, NULL, 0, 0}; 265 CxJsonToken ret = {CX_JSON_NO_TOKEN, NULL, 0, 0};
271 // we know that the unescaped string will be shorter by at least 2 chars 271 // we know that the unescaped string will be shorter by at least 2 chars
272 cxmutstr result; 272 cxmutstr result;
273 result.length = 0; 273 result.length = 0;
274 result.ptr = cxMalloc(a, len - 1); 274 result.ptr = cxMalloc(a, len - 1);
275 if (result.ptr == NULL) { 275 if (result.ptr == NULL) {
276 // TODO: check if this actually leads to correct error handling
277 return result; 276 return result;
278 } 277 }
279 278
280 bool u = false; 279 bool u = false;
281 for (size_t i = 1; i < len - 1; i++) { 280 for (size_t i = 1; i < len - 1; i++) {
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

mercurial