--- a/src/json.c Wed Dec 24 12:13:59 2025 +0100 +++ b/src/json.c Thu Dec 25 11:10:13 2025 +0100 @@ -96,23 +96,27 @@ } static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { - cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); - bool allocated = false; - if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { + cxmutstr buf_str = cx_mutstrn(json->buffer.space + start, end - start); + cxmutstr str; + bool allocated; + if (json->uncompleted_tokentype != CX_JSON_NO_TOKEN) { allocated = true; - str = cx_strcat_m(json->uncompleted.content, 1, str); - if (str.ptr == NULL) { // LCOV_EXCL_START - return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}}; - } // LCOV_EXCL_STOP + str = json->uncompleted_content; + if (cx_strcat(&str, 1, buf_str)) { + return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}}; // LCOV_EXCL_LINE + } + json->uncompleted_content = (cxmutstr){NULL, 0}; + json->uncompleted_tokentype = CX_JSON_NO_TOKEN; + } else { + allocated = false; + str = buf_str; } - json->uncompleted = (CxJsonToken){0}; CxJsonTokenType ttype; if (isstring) { ttype = CX_JSON_TOKEN_STRING; } else { - cxstring s = cx_strcast(str); - if (!cx_strcmp(s, "true") || !cx_strcmp(s, "false") - || !cx_strcmp(s, "null")) { + if (!cx_strcmp(str, "true") || !cx_strcmp(str, "false") + || !cx_strcmp(str, "null")) { ttype = CX_JSON_TOKEN_LITERAL; } else { ttype = token_numbertype(str.ptr, str.length); @@ -162,16 +166,16 @@ static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { // check if there is data in the buffer if (cxBufferEof(&json->buffer)) { - return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ? + return json->uncompleted_tokentype == CX_JSON_NO_TOKEN ? CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; } // current token type and start index - CxJsonTokenType ttype = json->uncompleted.tokentype; + CxJsonTokenType ttype = json->uncompleted_tokentype; size_t token_part_start = json->buffer.pos; bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING - && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\'; + && json->uncompleted_content.ptr[json->uncompleted_content.length-1] == '\\'; for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { char c = json->buffer.space[i]; @@ -232,31 +236,23 @@ return CX_JSON_NO_DATA; } else { // uncompleted token - size_t uncompleted_len = json->buffer.size - token_part_start; - if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { - // current token is uncompleted - // save current token content - CxJsonToken uncompleted = { - ttype, true, - cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len)) - }; - if (uncompleted.content.ptr == NULL) { + cxstring uncompleted = cx_strn(json->buffer.space + token_part_start, json->buffer.size - token_part_start); + if (json->uncompleted_tokentype == CX_JSON_NO_TOKEN) { + assert(json->uncompleted_content.ptr == NULL); + json->uncompleted_content = cx_strdup(uncompleted); + if (json->uncompleted_content.ptr == NULL) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } - json->uncompleted = uncompleted; + json->uncompleted_tokentype = ttype; } else { // previously we also had an uncompleted token // combine the uncompleted token with the current token - assert(json->uncompleted.allocated); - cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, - cx_strn(json->buffer.space + token_part_start, uncompleted_len)); - if (str.ptr == NULL) { + if (cx_strcat(&json->uncompleted_content, 1, uncompleted)) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } - json->uncompleted.content = str; } // advance the buffer position - we saved the stuff in the uncompleted token - json->buffer.pos += uncompleted_len; + json->buffer.pos += uncompleted.length; return CX_JSON_INCOMPLETE_DATA; } } @@ -564,7 +560,8 @@ } cxJsonValueFree(json->parsed); json->parsed = NULL; - token_destroy(&json->uncompleted); + json->uncompleted_tokentype = CX_JSON_NO_TOKEN; + cx_strfree(&json->uncompleted_content); cx_strfree_a(json->allocator, &json->uncompleted_member_name); }