Thu, 14 Aug 2025 23:03:01 +0200
add cxJsonArrayRemove()
first part of issue #627
CHANGELOG | file | annotate | diff | comparison | revisions | |
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/CHANGELOG Sat Jul 19 21:09:07 2025 +0200 +++ b/CHANGELOG Thu Aug 14 23:03:01 2025 +0200 @@ -13,6 +13,7 @@ * adds cxTreeSize() * adds CX_PRIstr and CX_SFMT macros for formatting UCX strings * adds cx_strcpy() and cx_strcpy_a() + * adds cxJsonArrRemove() * adds cxStdlibAllocator and allows changes of cxDefaultAllocator * improves performance of the CxList array list implementation * changes cx_str() and cx_mutstr() to allow NULL strings
--- a/src/cx/json.h Sat Jul 19 21:09:07 2025 +0200 +++ b/src/cx/json.h Thu Aug 14 23:03:01 2025 +0200 @@ -1284,6 +1284,23 @@ CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index); /** + * Removes an element from a JSON array. + * + * If the @p value is not a JSON array, the behavior is undefined. + * + * This function, in contrast to cxJsonArrayGet(), returns @c NULL + * when the index is out of bounds. + * + * @param value the JSON value + * @param index the index in the array + * @return the removed value from the specified index or @c NULL when the index was out of bounds + * @see cxJsonIsArray() + */ +cx_attr_nonnull +cx_attr_export +CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index); + +/** * Returns an iterator over the JSON array elements. * * The iterator yields values of type @c CxJsonValue* .
--- a/src/json.c Sat Jul 19 21:09:07 2025 +0200 +++ b/src/json.c Thu Aug 14 23:03:01 2025 +0200 @@ -1126,6 +1126,20 @@ return value->value.array.array[index]; } +CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { + if (index >= value->value.array.array_size) { + return NULL; + } + CxJsonValue *ret = value->value.array.array[index]; + // TODO: replace with a low level cx_array_remove() + size_t count = value->value.array.array_size - index - 1; + if (count > 0) { + memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); + } + value->value.array.array_size--; + return ret; +} + CxIterator cxJsonArrIter(const CxJsonValue *value) { return cxIteratorPtr( value->value.array.array,
--- a/tests/test_json.c Sat Jul 19 21:09:07 2025 +0200 +++ b/tests/test_json.c Thu Aug 14 23:03:01 2025 +0200 @@ -715,6 +715,39 @@ cxJsonDestroy(&json); } +CX_TEST(test_json_array) { + CxTestingAllocator talloc; + cx_testing_allocator_init(&talloc); + const CxAllocator *allocator = &talloc.base; + CX_TEST_DO { + CxJsonValue *arr = cxJsonCreateArr(allocator); + cxJsonArrAddIntegers(arr, (int64_t[]){ 0, 3, 6, 9, 12, 15}, 6); + CX_TEST_ASSERT(cxJsonArrSize(arr) == 6); + CxJsonValue *e = cxJsonArrGet(arr, 3); + CX_TEST_ASSERT(cxJsonIsNumber(e)); + CX_TEST_ASSERT(cxJsonAsInteger(e) == 9); + CX_TEST_ASSERT(cxJsonAsDouble(e) == 9.0); + CX_TEST_ASSERT(cxJsonArrSize(arr) == 6); + e = cxJsonArrGet(arr, 6); + CX_TEST_ASSERT(!cxJsonIsNumber(e)); + // also not null, because "nothing" is not null + CX_TEST_ASSERT(!cxJsonIsNull(e)); + CX_TEST_ASSERT(e->type == CX_JSON_NOTHING); + CxJsonValue *removed = cxJsonArrRemove(arr, 2); + CX_TEST_ASSERT(cxJsonIsNumber(removed)); + CX_TEST_ASSERT(cxJsonAsInteger(removed) == 6); + CX_TEST_ASSERT(cxJsonArrSize(arr) == 5); + e = cxJsonArrRemove(arr, 5); + CX_TEST_ASSERT(e == NULL); + cxJsonValueFree(arr); + // the removed element still needs to be freed + CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); + cxJsonValueFree(removed); + CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); + } + cx_testing_allocator_destroy(&talloc); +} + CX_TEST(test_json_array_iterator) { CxJson json; cxJsonInit(&json, NULL); @@ -1199,6 +1232,7 @@ cx_test_register(suite, test_json_number); cx_test_register(suite, test_json_number_format_errors); cx_test_register(suite, test_json_multiple_values); + cx_test_register(suite, test_json_array); cx_test_register(suite, test_json_array_iterator); cx_test_register(suite, test_json_allocator); cx_test_register(suite, test_json_allocator_parse_error);