--- a/ucx/json.c Wed Dec 31 12:37:09 2025 +0100 +++ b/ucx/json.c Wed Dec 31 16:40:12 2025 +0100 @@ -98,21 +98,21 @@ 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) { + 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 = cx_strcat(json->uncompleted_content, 1, str); + if (str.ptr == NULL) { + return (CxJsonToken){CX_JSON_NO_TOKEN, false, CX_NULLSTR}; // LCOV_EXCL_LINE + } + json->uncompleted_content = CX_NULLSTR; + json->uncompleted_tokentype = CX_JSON_NO_TOKEN; } - 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); @@ -122,7 +122,7 @@ if (allocated) { cx_strfree(&str); } - return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {NULL, 0}}; + return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, CX_NULLSTR}; } return (CxJsonToken){ttype, allocated, str}; } @@ -162,16 +162,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] == '\\'; + && cx_strat(json->uncompleted_content, -1) == '\\'; for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { char c = json->buffer.space[i]; @@ -189,7 +189,7 @@ } else if (ctype != CX_JSON_NO_TOKEN) { // single-char token json->buffer.pos = i + 1; - *result = (CxJsonToken){ctype, false, {NULL, 0}}; + *result = (CxJsonToken){ctype, false, CX_NULLSTR}; return CX_JSON_NO_ERROR; } else { ttype = CX_JSON_TOKEN_LITERAL; // number or literal @@ -232,31 +232,25 @@ 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 + // previously we already 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) { + cxmutstr s = cx_strcat(json->uncompleted_content, 1, uncompleted); + if (s.ptr == NULL) { return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE } - json->uncompleted.content = str; + json->uncompleted_content = s; } // 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 +558,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); } @@ -1083,12 +1078,7 @@ return NULL; } CxJsonValue *ret = value->array.data[index]; - // TODO: replace with a low level cx_array_remove() - size_t count = value->array.size - index - 1; - if (count > 0) { - memmove(value->array.data + index, value->array.data + index + 1, count * sizeof(CxJsonValue*)); - } - value->array.size--; + cx_array_remove(value->array, index); return ret; } @@ -1231,7 +1221,7 @@ // the name actual += wfunc("\"", 1, 1, target); - cxstring key = cx_strn(member->key->data, member->key->len); + cxstring key = cx_hash_key_as_string(member->key); cxmutstr name = escape_string(key, settings->escape_slash); actual += wfunc(name.ptr, 1, name.length, target); actual += wfunc("\"", 1, 1, target); @@ -1437,17 +1427,17 @@ CxBuffer buffer; if (cxBufferInit(&buffer, allocator, NULL, 128, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) { - return (cxmutstr){NULL, 0}; + return CX_NULLSTR; // LCOV_EXCL_LINE } if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0) || cxBufferTerminate(&buffer)) { // LCOV_EXCL_START buffer.flags &= ~CX_BUFFER_DO_NOT_FREE; cxBufferDestroy(&buffer); - return (cxmutstr){NULL, 0}; + return CX_NULLSTR; // LCOV_EXCL_STOP } else { - cxmutstr str = cx_mutstrn(buffer.space, buffer.size); + cxmutstr str = cx_bstr_m(&buffer); cxBufferDestroy(&buffer); return str; } @@ -1495,8 +1485,7 @@ return cx_vcmp_double(json->number, cxJsonAsDouble(other)); case CX_JSON_LITERAL: return json->literal == other->literal ? 0 : -1; - default: - // LCOV_EXCL_START + default: // LCOV_EXCL_START // unreachable assert(false); return -1; @@ -1509,7 +1498,7 @@ } CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source, - const CxAllocator* allocator, cx_attr_unused void *data) { + const CxAllocator* allocator, CX_UNUSED void *data) { if (source == NULL || source->type == CX_JSON_NOTHING) { return &cx_json_value_nothing; } @@ -1521,7 +1510,8 @@ return ret; \ } else { \ *target = *ret; \ - cxFree(allocator, ret); \ + ret->type = CX_JSON_UNINITIALIZED; \ + cxJsonValueFree(ret); \ return target; \ } \ } @@ -1545,8 +1535,7 @@ arr->array.size = elem_count; for (size_t i = 0 ; i < elem_count ; i++) { CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL); - if (e == NULL) { - // LCOV_EXCL_START + if (e == NULL) { // LCOV_EXCL_START cxJsonValueFree(arr); return NULL; // LCOV_EXCL_STOP @@ -1563,8 +1552,7 @@ return_value(cxJsonCreateNumber(allocator, source->number)); case CX_JSON_LITERAL: return_value(cxJsonCreateLiteral(allocator, source->literal)); - default: - // LCOV_EXCL_START + default: // LCOV_EXCL_START // unreachable assert(false); return NULL;