From a20701e91d782bb58c352c77a38d918fb6fc7e3f Mon Sep 17 00:00:00 2001 From: "Markus W. Weissmann" Date: Thu, 16 Apr 2020 15:54:07 +0200 Subject: [PATCH] modify sort_list to take a comparison function as 2nd argument; add two comparison helpers to make sort_object behave as before; add cJSONUtils_SortArray using the improved sort_list function --- cJSON_Utils.c | 42 ++++++++++++--- cJSON_Utils.h | 6 +++ tests/CMakeLists.txt | 3 +- tests/sort_utils_tests.c | 112 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 tests/sort_utils_tests.c diff --git a/cJSON_Utils.c b/cJSON_Utils.c index 79e052b..5cde39e 100644 --- a/cJSON_Utils.c +++ b/cJSON_Utils.c @@ -472,8 +472,23 @@ cleanup: return detached_item; } +static int compare_json_string(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + return compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive); +} + +static int compare_json_string_case_sensitive(const cJSON * const a, const cJSON * const b) +{ + return compare_json_string(a, b, true); +} + +static int compare_json_string_case_insensitive(const cJSON * const a, const cJSON * const b) +{ + return compare_json_string(a, b, false); +} + /* sort lists using mergesort */ -static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) +static cJSON *sort_list(cJSON *list, int (*compar)(const cJSON * const a, const cJSON * const b)) { cJSON *first = list; cJSON *second = list; @@ -487,7 +502,7 @@ static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) return result; } - while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0)) + while ((current_item != NULL) && (current_item->next != NULL) && (compar(current_item, current_item->next) < 0)) { /* Test for list sorted. */ current_item = current_item->next; @@ -519,15 +534,15 @@ static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) } /* Recursively sort the sub-lists. */ - first = sort_list(first, case_sensitive); - second = sort_list(second, case_sensitive); + first = sort_list(first, compar); + second = sort_list(second, compar); result = NULL; /* Merge the sub-lists */ while ((first != NULL) && (second != NULL)) { cJSON *smaller = NULL; - if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, case_sensitive) < 0) + if (compar(first, second) < 0) { smaller = first; } @@ -590,7 +605,13 @@ static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) { return; } - object->child = sort_list(object->child, case_sensitive); + if (case_sensitive) { + object->child = sort_list(object->child, compare_json_string_case_sensitive); + } + else + { + object->child = sort_list(object->child, compare_json_string_case_insensitive); + } } static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) @@ -1464,3 +1485,12 @@ CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const f { return generate_merge_patch(from, to, true); } + +CJSON_PUBLIC(void) cJSONUtils_SortArray(cJSON * const array, int (*compar)(const cJSON * const a, const cJSON * const b)) +{ + if ((array == NULL) || (array->child == NULL) || (2 > cJSON_GetArraySize(array))) { + return; + } + array->child = sort_list(array->child, compar); +} + diff --git a/cJSON_Utils.h b/cJSON_Utils.h index a970c65..4ca7348 100644 --- a/cJSON_Utils.h +++ b/cJSON_Utils.h @@ -81,6 +81,12 @@ CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const obje CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); +/* Sort the members of the array according to the provided comparison function. */ +/* The comparison function must return an integer that shows if the first */ +/* argument is considered less than (< 0), equals to (0) or greater than (> 0) */ +/* the second argument. */ +CJSON_PUBLIC(void) cJSONUtils_SortArray(cJSON * const object, int (*compar)(const cJSON * const a, const cJSON * const b)); + #ifdef __cplusplus } #endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c759221..39a5919 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -97,7 +97,8 @@ if(ENABLE_CJSON_TEST) set (cjson_utils_tests json_patch_tests old_utils_tests - misc_utils_tests) + misc_utils_tests + sort_utils_tests) foreach (cjson_utils_test ${cjson_utils_tests}) add_executable("${cjson_utils_test}" "${cjson_utils_test}.c") diff --git a/tests/sort_utils_tests.c b/tests/sort_utils_tests.c new file mode 100644 index 0000000..63ec972 --- /dev/null +++ b/tests/sort_utils_tests.c @@ -0,0 +1,112 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include +#include +#include + +#include "unity/examples/unity_config.h" +#include "unity/src/unity.h" +#include "common.h" +#include "../cJSON_Utils.h" + +static int cmp(const double *a, const double *b) { + if (*a > *b) + { + return -1; + } + if (*a < *b) + { + return 1; + } + return 0; +} + +static int jcmp_double(const cJSON *a, const cJSON *b) +{ + return cmp(&(a->valuedouble), &(b->valuedouble)); +} + +static void cjson_utils_sort_null(void) +{ + cJSON *empty = NULL; + cJSONUtils_SortArray(empty, jcmp_double); + TEST_ASSERT_NULL(empty); +} + +static void cjson_utils_sort_empty(void) +{ + cJSON *array = cJSON_CreateArray(); + cJSONUtils_SortArray(array, jcmp_double); + TEST_ASSERT_EQUAL_INT(0, cJSON_GetArraySize(array)); + cJSON_Delete(array); +} + +static void cjson_utils_sort_size1(void) +{ + double d[1] = {0.0}; + cJSON *array = cJSON_CreateDoubleArray(d, 1); + cJSONUtils_SortArray(array, jcmp_double); + TEST_ASSERT_EQUAL_INT(1, cJSON_GetArraySize(array)); + cJSON_Delete(array); +} + +static void cjson_utils_sort_double_issorted(void) +{ + size_t size = 8192; + size_t i; + cJSON *array, *elt, *prev; + double *d; + + /* generate array with rand doubles */ + d = (double*)malloc(size * sizeof(double)); + for (i = 0; i < size; i++) + { + d[i] = (double)rand(); + } + array = cJSON_CreateDoubleArray(d, (int)size); + cJSONUtils_SortArray(array, jcmp_double); + TEST_ASSERT_EQUAL_INT(size, cJSON_GetArraySize(array)); + + /* check if order is achieved */ + prev = NULL; + cJSON_ArrayForEach(elt, array) + { + if (prev != NULL) { + TEST_ASSERT_LESS_THAN_INT(1, jcmp_double(prev, elt)); + } + prev = elt; + } + cJSON_Delete(array); +} + +int main(void) +{ + UNITY_BEGIN(); + + RUN_TEST(cjson_utils_sort_null); + RUN_TEST(cjson_utils_sort_empty); + RUN_TEST(cjson_utils_sort_size1); + RUN_TEST(cjson_utils_sort_double_issorted); + + return UNITY_END(); +}