2 weeks ago
add status codes to json parser - relates to #431
src/cx/json.h | file | annotate | diff | comparison | revisions | |
src/json.c | file | annotate | diff | comparison | revisions | |
tests/test_json.c | file | annotate | diff | comparison | revisions |
--- a/src/cx/json.h Sat Dec 07 23:59:54 2024 +0100 +++ b/src/cx/json.h Sun Dec 08 00:13:38 2024 +0100 @@ -123,9 +123,8 @@ struct cx_json_token_s { CxJsonTokenType tokentype; - const char *content; - size_t length; - size_t alloc; + bool allocated; + cxmutstr content; }; struct cx_json_s { @@ -167,6 +166,60 @@ bool tokenizer_escape; // TODO: check if it can be replaced with look-behind }; +/** + * Status codes for the json interface. + */ +enum cx_json_status { + /** + * Everything is fine. + */ + CX_JSON_NO_ERROR, + /** + * The input buffer does not contain more data. + */ + CX_JSON_NO_DATA, + /** + * The input ends unexpectedly. + * + * Refill the buffer with cxJsonFill() to complete the json data. + */ + CX_JSON_INCOMPLETE_DATA, + /** + * Not used as a status and never returned by any function. + * + * You can use this enumerator to check for all "good" status results + * by checking if the status is less than \c CX_JSON_OK. + * + * A "good" status means, that you can refill data and continue parsing. + */ + CX_JSON_OK, + /** + * Input buffer is \c NULL. + */ + CX_JSON_NULL_INPUT, + /** + * Allocating memory for the internal buffer failed. + */ + CX_JSON_BUFFER_ALLOC_FAILED, + /** + * Allocating memory for a json value failed. + */ + CX_JSON_VALUE_ALLOC_FAILED, + /** + * A number value is incorrectly formatted. + */ + CX_JSON_FORMAT_ERROR_NUMBER, + /** + * The tokenizer found something unexpected. + */ + CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN +}; + +/** + * Typedef for the json status enum. + */ +typedef enum cx_json_status CxJsonStatus; + cx_attr_nonnull_arg(1) void cxJsonInit(CxJson *json, const CxAllocator *allocator); @@ -243,7 +296,7 @@ void cxJsonValueFree(CxJsonValue *value); cx_attr_nonnull -int cxJsonNext(CxJson *json, CxJsonValue **value); +CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value); cx_attr_nonnull static inline bool cxJsonIsObject(CxJsonValue *value) {
--- a/src/json.c Sat Dec 07 23:59:54 2024 +0100 +++ b/src/json.c Sun Dec 08 00:13:38 2024 +0100 @@ -42,49 +42,11 @@ static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING}; static void token_destroy(CxJsonToken *token) { - if (token->alloc > 0) { - free((char*) token->content); + if (token->allocated) { + cx_strfree(&token->content); } } -static int token_append(CxJsonToken *token, const char *buf, size_t len) { - if (len == 0) { - return 0; - } - - size_t newlen = token->length + len; - if (token->alloc < newlen) { - char *newbuf = realloc( - token->alloc == 0 ? NULL : (char *) token->content, - newlen); - if (!newbuf) { - return 1; - } - token->content = newbuf; - token->alloc = newlen; - } - - memcpy((char *) token->content + token->length, buf, len); - token->length = newlen; - return 0; -} - -static CxJsonToken token_create(CxJson *json, size_t start, size_t end) { - CxJsonToken token = {0}; - size_t len = end - start; - if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { - token.content = json->buffer + start; - token.length = len; - } else { - if (token_append(&json->uncompleted, json->buffer + start, len)) { - // TODO: this does certainly not lead to correct error handling - return (CxJsonToken){0}; - } - token = json->uncompleted; - } - json->uncompleted = (CxJsonToken){0}; - return token; -} static int token_isliteral(const char *content, size_t length) { if (length == 4) { @@ -145,6 +107,36 @@ return type; } +static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { + cxmutstr str = cx_mutstrn((char*)json->buffer + start, end - start); + bool allocated = false; + if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { + allocated = true; + str = cx_strcat_m(json->uncompleted.content, 1, str); + if (str.ptr == NULL) { + return (CxJsonToken){CX_JSON_NO_TOKEN, false, 0, 0}; + } + } + json->uncompleted = (CxJsonToken){0}; + CxJsonTokenType ttype; + if (isstring) { + ttype = CX_JSON_TOKEN_STRING; + } else { + if (token_isliteral(str.ptr, str.length)) { + ttype = CX_JSON_TOKEN_LITERAL; + } else { + ttype = token_numbertype(str.ptr, str.length); + } + } + if (ttype == CX_JSON_TOKEN_ERROR) { + if (allocated) { + cx_strfree(&str); + } + return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, 0, 0}; + } + return (CxJsonToken){ttype, allocated, str}; +} + static CxJsonTokenType char2ttype(char c) { switch (c) { case '[': { @@ -177,7 +169,18 @@ return CX_JSON_NO_TOKEN; } -static CxJsonToken token_parse_next(CxJson *json) { +static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { + // check if there is data in the buffer + if (json->pos >= json->size) { + return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ? + CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; + } + + // sanity check + if (json->buffer == NULL) { + return CX_JSON_NULL_INPUT; + } + // current token type and start index CxJsonTokenType ttype = json->uncompleted.tokentype; size_t token_start = json->pos; @@ -186,9 +189,7 @@ char c = json->buffer[i]; if (ttype != CX_JSON_TOKEN_STRING) { // currently non-string token - CxJsonTokenType ctype = char2ttype(c); // start of new token? - if (ttype == CX_JSON_NO_TOKEN) { if (ctype == CX_JSON_TOKEN_SPACE) { continue; @@ -199,8 +200,8 @@ } else if (ctype != CX_JSON_NO_TOKEN) { // single-char token json->pos = i + 1; - CxJsonToken token = {ctype, NULL, 0, 0}; - return token; + *result = (CxJsonToken){ctype, NULL, 0, 0}; + return CX_JSON_NO_ERROR; } else { ttype = CX_JSON_TOKEN_LITERAL; // number or literal token_start = i; @@ -208,14 +209,15 @@ } else { // finish token if (ctype != CX_JSON_NO_TOKEN) { - CxJsonToken ret = token_create(json, token_start, i); - if (token_isliteral(ret.content, ret.length)) { - ret.tokentype = CX_JSON_TOKEN_LITERAL; - } else { - ret.tokentype = token_numbertype(ret.content, ret.length); + *result = token_create(json, false, token_start, i); + if (result->tokentype == CX_JSON_NO_TOKEN) { + return CX_JSON_BUFFER_ALLOC_FAILED; + } + if (result->tokentype == CX_JSON_TOKEN_ERROR) { + return CX_JSON_FORMAT_ERROR_NUMBER; } json->pos = i; - return ret; + return CX_JSON_NO_ERROR; } } } else { @@ -224,10 +226,12 @@ json->tokenizer_escape = false; } else { if (c == '"') { - CxJsonToken ret = token_create(json, token_start, i + 1); - ret.tokentype = CX_JSON_TOKEN_STRING; + *result = token_create(json, true, token_start, i + 1); + if (result->tokentype == CX_JSON_NO_TOKEN) { + return CX_JSON_BUFFER_ALLOC_FAILED; + } json->pos = i + 1; - return ret; + return CX_JSON_NO_ERROR; } else if (c == '\\') { json->tokenizer_escape = true; } @@ -240,45 +244,44 @@ size_t uncompeted_len = json->size - token_start; if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { // current token is uncompleted - // save current token content in p->uncompleted - CxJsonToken uncompleted; - uncompleted.tokentype = ttype; - uncompleted.length = uncompeted_len; - uncompleted.alloc = uncompeted_len + 16; - char *tmp = malloc(uncompleted.alloc); - if (tmp) { - memcpy(tmp, json->buffer + token_start, uncompeted_len); - uncompleted.content = tmp; - json->uncompleted = uncompleted; - } else { - json->error = 1; + // save current token content + CxJsonToken uncompleted = { + ttype, true, + cx_strdup(cx_strn(json->buffer + token_start, uncompeted_len)) + }; + if (uncompleted.content.ptr == NULL) { + return CX_JSON_BUFFER_ALLOC_FAILED; } + json->uncompleted = uncompleted; } else { // previously we also had an uncompleted token // combine the uncompleted token with the current token - if (token_append(&json->uncompleted, json->buffer + token_start, uncompeted_len)) { - json->error = 1; + assert(json->uncompleted.allocated); + cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, + cx_strn(json->buffer + token_start, uncompeted_len)); + if (str.ptr == NULL) { + return CX_JSON_BUFFER_ALLOC_FAILED; } + json->uncompleted.content = str; } } - CxJsonToken ret = {CX_JSON_NO_TOKEN, NULL, 0, 0}; - return ret; + return CX_JSON_INCOMPLETE_DATA; } -static cxmutstr unescape_string(const CxAllocator *a, const char *str, size_t len) { +static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { // TODO: support more escape sequences // we know that the unescaped string will be shorter by at least 2 chars cxmutstr result; result.length = 0; - result.ptr = cxMalloc(a, len - 1); + result.ptr = cxMalloc(a, str.length - 1); if (result.ptr == NULL) { return result; } bool u = false; - for (size_t i = 1; i < len - 1; i++) { - char c = str[i]; + for (size_t i = 1; i < str.length - 1; i++) { + char c = str.ptr[i]; if (u) { u = false; if (c == 'n') { @@ -300,15 +303,15 @@ return result; } -static int parse_number(const char *str, size_t len, void *value, bool asint) { +static int parse_number(cxmutstr str, void *value, bool asint) { char *endptr = NULL; char buf[32]; - if (len > 30) { + if (str.length > 30) { return 1; } // TODO: if we can guarantee that we are working on a copied string already, we can avoid this memcpy - memcpy(buf, str, len); - buf[len] = 0; + memcpy(buf, str.ptr, str.length); + buf[str.length] = 0; if (asint) { long long v = strtoll(buf, &endptr, 10); @@ -319,7 +322,7 @@ *((double*)value) = v; } - return (endptr != &buf[len]); + return (endptr != &buf[str.length]); } static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { @@ -445,15 +448,17 @@ token_destroy(&token); \ return code -static int json_parse(CxJson *json) { +static enum cx_json_status json_parse(CxJson *json) { // Reserve a pointer for a possibly read value CxJsonValue *vbuf = NULL; // grab the next token - CxJsonToken token = token_parse_next(json); - if (token.tokentype == CX_JSON_NO_TOKEN) { - // nothing found, wait for more data - return 0; + CxJsonToken token; + { + enum cx_json_status ret = token_parse_next(json, &token); + if (ret != CX_JSON_NO_ERROR) { + return ret; + } } // pop the current state @@ -463,7 +468,7 @@ // guarantee that at least two more states fit on the stack CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { - return -1; + return CX_JSON_BUFFER_ALLOC_FAILED; } @@ -483,125 +488,110 @@ switch (token.tokentype) { case CX_JSON_TOKEN_BEGIN_ARRAY: { if (create_json_value(json, CX_JSON_ARRAY) == NULL) { - // TODO: error code - no memory - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } json_add_state(json, JP_STATE_VALUE_BEGIN_AR); - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_BEGIN_OBJECT: { if (create_json_value(json, CX_JSON_OBJECT) == NULL) { - // TODO: error code - no memory - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_STRING: { if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) { - // TODO: error code - no memory - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } - cxmutstr str = unescape_string(json->allocator, token.content, token.length); + cxmutstr str = unescape_string(json->allocator, token.content); if (str.ptr == NULL) { - // TODO: error code - no memory - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } vbuf->value.string = str; - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_INTEGER: case CX_JSON_TOKEN_NUMBER: { int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; if (NULL == (vbuf = create_json_value(json, type))) { - // TODO: error code - no memory - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } - if (parse_number(token.content, token.length, &vbuf->value,type == CX_JSON_INTEGER)) { - // TODO: error code - format error - return_rec(-1); + if (parse_number(token.content, &vbuf->value,type == CX_JSON_INTEGER)) { + return_rec(CX_JSON_FORMAT_ERROR_NUMBER); } - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } case CX_JSON_TOKEN_LITERAL: { if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) { - // TODO: error code - no memory - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } - const char *l = token.content; - size_t token_len = token.length; - if (token_len == 4 && !memcmp(l, "true", 4)) { + if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { vbuf->value.literal = CX_JSON_TRUE; - } else if (token_len == 5 && !memcmp(l, "false", 5)) { + } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { vbuf->value.literal = CX_JSON_FALSE; } else { vbuf->value.literal = CX_JSON_NULL; } - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } default: { - // TODO: error code - unexpected token - return_rec(-1); + return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); } } } else if (state == JP_STATE_ARRAY_SEP_OR_CLOSE) { // expect ',' or ']' if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { json_add_state(json, JP_STATE_VALUE_BEGIN_AR); - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { // discard the array from the value buffer json->vbuf_size--; - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } else { - // TODO: error code - unexpected token - return_rec(-1); + return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); } } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { // discard the obj from the value buffer json->vbuf_size--; - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } else { // expect string if (token.tokentype != CX_JSON_TOKEN_STRING) { - // TODO: error code - unexpected token - return_rec(-1); + return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); } // add new entry - cxmutstr name = unescape_string(json->allocator, token.content, token.length); + cxmutstr name = unescape_string(json->allocator, token.content); if (name.ptr == NULL) { - // TODO: error code - no mem - return_rec(-1); + return_rec(CX_JSON_VALUE_ALLOC_FAILED); } json_obj_add_entry(json, name.ptr); // next state json_add_state(json, JP_STATE_OBJ_COLON); - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } } else if (state == JP_STATE_OBJ_COLON) { // expect ':' if (token.tokentype != CX_JSON_TOKEN_NAME_SEPARATOR) { - // TODO: error code - unexpected token - return_rec(-1); + return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); } // next state json_add_state(json, JP_STATE_VALUE_BEGIN_OBJ); - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } else if (state == JP_STATE_OBJ_SEP_OR_CLOSE) { // expect ',' or '}' if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { json_add_state(json, JP_STATE_OBJ_NAME); - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { // discard the obj from the value buffer json->vbuf_size--; - return_rec(1); + return_rec(CX_JSON_NO_ERROR); } else { - // TODO: error code - unexpected token - return_rec(-1); + return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); } } else { // should be unreachable @@ -610,17 +600,15 @@ } } -int cxJsonNext(CxJson *json, CxJsonValue **value) { - // TODO: replace int with a status enum like in CxProperties - +CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) { // initialize output value *value = &cx_json_value_nothing; // parse data - int result; + CxJsonStatus result; do { result = json_parse(json); - if (result == 1 && json->states_size == 1) { + if (result == CX_JSON_NO_ERROR && json->states_size == 1) { // final state reached assert(json->states[0] == JP_STATE_VALUE_END); assert(json->vbuf_size == 0); @@ -632,9 +620,16 @@ // re-initialize state machine json->states[0] = JP_STATE_VALUE_BEGIN; - return 1; + return CX_JSON_NO_ERROR; } - } while (result == 1); + } while (result == CX_JSON_NO_ERROR); + + // the parser might think there is no data + // but when we did not reach the final state, + // we know that there must be more to come + if (result == CX_JSON_NO_DATA && json->states_size > 1) { + return CX_JSON_INCOMPLETE_DATA; + } return result; }
--- a/tests/test_json.c Sat Dec 07 23:59:54 2024 +0100 +++ b/tests/test_json.c Sun Dec 08 00:13:38 2024 +0100 @@ -59,7 +59,7 @@ ); CX_TEST_DO { - int result; + CxJsonStatus result; CxJson json; cxJsonInit(&json, NULL); @@ -68,7 +68,7 @@ // parse the big fat object CxJsonValue *obj; result = cxJsonNext(&json, &obj); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); // check the contents CX_TEST_ASSERT(cxJsonIsObject(obj)); @@ -110,7 +110,7 @@ // we only have one object that already contained all the data result = cxJsonNext(&json, &obj); - CX_TEST_ASSERT(result == 0); + CX_TEST_ASSERT(result == CX_JSON_NO_DATA); cxJsonDestroy(&json); } @@ -126,25 +126,22 @@ } CX_TEST_DO { - int result; + CxJsonStatus result; CxJson json; cxJsonInit(&json, NULL); CxJsonValue *obj; size_t part = 0; - while(part < nparts) { + while(part < nparts - 1) { cxJsonFill(&json, parts[part]); part++; result = cxJsonNext(&json, &obj); - - if(result != 0) { - break; - } + CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); } - - CX_TEST_ASSERT(result == 1); - CX_TEST_ASSERT(part == nparts); + cxJsonFill(&json, parts[nparts - 1]); + result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(obj); CxJsonValue *message = cxJsonObjGet(obj, "message"); @@ -161,9 +158,9 @@ // this recursively frees everything else cxJsonValueFree(obj); - // we only have one object that already contained all the data + // now there is everything read result = cxJsonNext(&json, &obj); - CX_TEST_ASSERT(result == 0); + CX_TEST_ASSERT(result == CX_JSON_NO_DATA); cxJsonDestroy(&json); } @@ -187,9 +184,16 @@ cxstring text4 = cx_str("{ \"name\": \"value\" ]"); cxstring tests[] = { text0, text1, text2, text3, text4 }; + CxJsonStatus errors[] = { + CX_JSON_FORMAT_ERROR_NUMBER, + CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, + CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, + CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN, + CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN + }; CX_TEST_DO { - int result; + CxJsonStatus result; CxJson json; CxJsonValue *obj = NULL; @@ -198,7 +202,7 @@ cxJsonFill(&json, tests[i]); result = cxJsonNext(&json, &obj); - CX_TEST_ASSERT(result == -1); + CX_TEST_ASSERT(result == errors[i]); CX_TEST_ASSERT(obj != NULL && obj->type == CX_JSON_NOTHING); cxJsonDestroy(&json); } @@ -257,16 +261,16 @@ CX_TEST_DO { // TODO: find a better way to terminate values that are not arrays/objects CxJsonValue *v; - int result; + CxJsonStatus result; cxJsonFill(&json, "3.1415 "); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsNumber(v)); CX_TEST_ASSERT(cxJsonAsDouble(v) == 3.1415); cxJsonValueFree(v); cxJsonFill(&json, "-47.11e2 "); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsNumber(v)); CX_TEST_ASSERT(cxJsonAsDouble(v) == -4711.0); cxJsonValueFree(v); @@ -279,22 +283,22 @@ cxJsonInit(&json, NULL); CX_TEST_DO { CxJsonValue *v; - int result; + CxJsonStatus result; // read number cxJsonFill(&json, "10\n"); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsNumber(v)); CX_TEST_ASSERT(cxJsonAsInteger(v) == 10); cxJsonValueFree(v); // read remaining '\n' result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 0); + CX_TEST_ASSERT(result == CX_JSON_INCOMPLETE_DATA); // read string cxJsonFill(&json, "\"hello world\"\n"); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsString(v)); CX_TEST_ASSERT(!cx_strcmp(cxJsonAsCxString(v), CX_STR("hello world"))); cxJsonValueFree(v); @@ -302,7 +306,7 @@ // read obj cxJsonFill(&json, "{ \"value\": \"test\" }\n"); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsObject(v)); CxJsonValue *value = cxJsonObjGet(v, "value"); CX_TEST_ASSERT(cxJsonAsString(value)); @@ -310,7 +314,7 @@ // read array cxJsonFill(&json, "[ 0, 1, 2, 3, 4, 5 ]\n"); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsArray(v)); CxJsonValue *a0 = cxJsonArrGet(v, 0); CxJsonValue *a3 = cxJsonArrGet(v, 3); @@ -322,14 +326,14 @@ // read literal cxJsonFill(&json, "true\n"); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsLiteral(v)); CX_TEST_ASSERT(cxJsonAsBool(v)); cxJsonValueFree(v); // read null cxJsonFill(&json, "null\n"); result = cxJsonNext(&json, &v); - CX_TEST_ASSERT(result == 1); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(cxJsonIsNull(v)); cxJsonValueFree(v); } @@ -354,8 +358,8 @@ cxJsonFill(&json, text); CxJsonValue *obj; - int result = cxJsonNext(&json, &obj); - CX_TEST_ASSERT(result == 1); + CxJsonStatus result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_NO_ERROR); CX_TEST_ASSERT(obj->allocator == allocator); // this recursively frees everything @@ -385,8 +389,8 @@ cxJsonFill(&json, text); CxJsonValue *obj = NULL; - int result = cxJsonNext(&json, &obj); - CX_TEST_ASSERT(result == -1); + CxJsonStatus result = cxJsonNext(&json, &obj); + CX_TEST_ASSERT(result == CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); CX_TEST_ASSERT(obj != NULL && obj->type == CX_JSON_NOTHING); // clean-up any left-over memory