diff --git a/README.md b/README.md index 6b344cbf..1d6fb8ad 100644 --- a/README.md +++ b/README.md @@ -138,8 +138,8 @@ This is an object. We're in C. We don't have objects. But we do have structs. What's the framerate? ```c -cJSON *format = cJSON_GetObjectItem(root, "format"); -cJSON *framerate_item = cJSON_GetObjectItem(format, "frame rate"); +cJSON *format = cJSON_GetObjectItemCaseSensitive(root, "format"); +cJSON *framerate_item = cJSON_GetObjectItemCaseSensitive(format, "frame rate"); double framerate = 0; if (cJSON_IsNumber(framerate_item)) { @@ -150,7 +150,7 @@ if (cJSON_IsNumber(framerate_item)) Want to change the framerate? ```c -cJSON *framerate_item = cJSON_GetObjectItem(format, "frame rate"); +cJSON *framerate_item = cJSON_GetObjectItemCaseSensitive(format, "frame rate"); cJSON_SetNumberValue(framerate_item, 25); ``` diff --git a/cJSON.c b/cJSON.c index 3c3f10b2..9e192366 100644 --- a/cJSON.c +++ b/cJSON.c @@ -1665,16 +1665,33 @@ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) return (int)i; } -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item) +static cJSON* get_array_item(const cJSON *array, size_t index) { - cJSON *c = array ? array->child : NULL; - while (c && item > 0) + cJSON *current_child = NULL; + + if (array == NULL) { - item--; - c = c->next; + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; } - return c; + return get_array_item(array, (size_t)index); } static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) @@ -1814,37 +1831,36 @@ CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *str cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); } -static cJSON *DetachItemFromArray(cJSON *array, size_t which) +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) { - cJSON *c = array->child; - while (c && (which > 0)) + if ((parent == NULL) || (item == NULL)) { - c = c->next; - which--; - } - if (!c) - { - /* item doesn't exist */ return NULL; } - if (c->prev) + + if (item->prev != NULL) { /* not the first element */ - c->prev->next = c->next; + item->prev->next = item->next; } - if (c->next) + if (item->next != NULL) { - c->next->prev = c->prev; + /* not the last element */ + item->next->prev = item->prev; } - if (c==array->child) + + if (item == parent->child) { - array->child = c->next; + /* first element */ + parent->child = item->next; } /* make sure the detached item doesn't point anywhere anymore */ - c->prev = c->next = NULL; + item->prev = NULL; + item->next = NULL; - return c; + return item; } + CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) { if (which < 0) @@ -1852,7 +1868,7 @@ CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) return NULL; } - return DetachItemFromArray(array, (size_t)which); + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); } CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) @@ -1862,19 +1878,16 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) { - size_t i = 0; - cJSON *c = object->child; - while (c && (case_insensitive_strcmp((unsigned char*)c->string, (const unsigned char*)string) != 0)) - { - i++; - c = c->next; - } - if (c) - { - return DetachItemFromArray(object, i); - } + cJSON *to_detach = cJSON_GetObjectItem(object, string); - return NULL; + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); } CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) @@ -1882,24 +1895,32 @@ CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) cJSON_Delete(cJSON_DetachItemFromObject(object, string)); } +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + /* Replace array/object items with new ones. */ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) { - cJSON *c = array->child; - while (c && (which > 0)) + cJSON *after_inserted = NULL; + + if (which < 0) { - c = c->next; - which--; + return; } - if (!c) + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) { cJSON_AddItemToArray(array, newitem); return; } - newitem->next = c; - newitem->prev = c->prev; - c->prev = newitem; - if (c == array->child) + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) { array->child = newitem; } @@ -1909,35 +1930,41 @@ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newit } } -static void ReplaceItemInArray(cJSON *array, size_t which, cJSON *newitem) +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) { - cJSON *c = array->child; - while (c && (which > 0)) + if ((parent == NULL) || (replacement == NULL)) { - c = c->next; - which--; + return false; } - if (!c) + + if (replacement == item) { - return; + return true; } - newitem->next = c->next; - newitem->prev = c->prev; - if (newitem->next) + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) { - newitem->next->prev = newitem; + replacement->next->prev = replacement; } - if (c == array->child) + if (replacement->prev != NULL) { - array->child = newitem; + replacement->prev->next = replacement; } - else + if (parent->child == item) { - newitem->prev->next = newitem; + parent->child = replacement; } - c->next = c->prev = NULL; - cJSON_Delete(c); + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; } + CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) { if (which < 0) @@ -1945,29 +1972,17 @@ CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newi return; } - ReplaceItemInArray(array, (size_t)which, newitem); + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); } CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) { - size_t i = 0; - cJSON *c = object->child; - while(c && (case_insensitive_strcmp((unsigned char*)c->string, (const unsigned char*)string) != 0)) - { - i++; - c = c->next; - } - if(c) - { - /* free the old string if not const */ - if (!(newitem->type & cJSON_StringIsConst) && newitem->string) - { - global_hooks.deallocate(newitem->string); - } + cJSON_ReplaceItemViaPointer(object, cJSON_GetObjectItem(object, string), newitem); +} - newitem->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); - ReplaceItemInArray(object, i, newitem); - } +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + cJSON_ReplaceItemViaPointer(object, cJSON_GetObjectItemCaseSensitive(object, string), newitem); } /* Create basic types: */ diff --git a/cJSON.h b/cJSON.h index 1ef70f01..b990a3ff 100644 --- a/cJSON.h +++ b/cJSON.h @@ -153,7 +153,7 @@ CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); /* Returns the number of items in an array (or object). */ CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item); +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string); CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); @@ -203,15 +203,20 @@ CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); /* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); diff --git a/tests/misc_tests.c b/tests/misc_tests.c index 249500dc..24a390d3 100644 --- a/tests/misc_tests.c +++ b/tests/misc_tests.c @@ -218,6 +218,92 @@ static void cjson_set_number_value_should_set_numbers(void) TEST_ASSERT_EQUAL_DOUBLE(-1 + (double)INT_MIN, number->valuedouble); } +static void cjson_detach_item_via_pointer_should_detach_items(void) +{ + cJSON list[4]; + cJSON parent[1]; + + memset(list, '\0', sizeof(list)); + + /* link the list */ + list[0].next = &(list[1]); + list[1].next = &(list[2]); + list[2].next = &(list[3]); + + list[3].prev = &(list[2]); + list[2].prev = &(list[1]); + list[1].prev = &(list[0]); + + parent->child = &list[0]; + + /* detach in the middle (list[1]) */ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[1])) == &(list[1]), "Failed to detach in the middle."); + TEST_ASSERT_TRUE_MESSAGE((list[1].prev == NULL) && (list[1].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_TRUE((list[0].next == &(list[2])) && (list[2].prev == &(list[0]))); + + /* detach beginning (list[0]) */ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[0])) == &(list[0]), "Failed to detach beginning."); + TEST_ASSERT_TRUE_MESSAGE((list[0].prev == NULL) && (list[0].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_TRUE_MESSAGE((list[2].prev == NULL) && (parent->child == &(list[2])), "Didn't set the new beginning."); + + /* detach end (list[3])*/ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &(list[3])) == &(list[3]), "Failed to detach end."); + TEST_ASSERT_TRUE_MESSAGE((list[3].prev == NULL) && (list[3].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_TRUE_MESSAGE((list[2].next == NULL) && (parent->child == &(list[2])), "Didn't set the new end"); + + /* detach single item (list[2]) */ + TEST_ASSERT_TRUE_MESSAGE(cJSON_DetachItemViaPointer(parent, &list[2]) == &list[2], "Failed to detach single item."); + TEST_ASSERT_TRUE_MESSAGE((list[2].prev == NULL) && (list[2].next == NULL), "Didn't set pointers of detached item to NULL."); + TEST_ASSERT_NULL_MESSAGE(parent->child, "Child of the parent wasn't set to NULL."); +} + +static void cjson_replace_item_via_pointer_should_replace_items(void) +{ + cJSON replacements[3]; + cJSON *beginning = NULL; + cJSON *middle = NULL; + cJSON *end = NULL; + cJSON *array = NULL; + + beginning = cJSON_CreateNull(); + TEST_ASSERT_NOT_NULL(beginning); + middle = cJSON_CreateNull(); + TEST_ASSERT_NOT_NULL(middle); + end = cJSON_CreateNull(); + TEST_ASSERT_NOT_NULL(end); + + array = cJSON_CreateArray(); + TEST_ASSERT_NOT_NULL(array); + + cJSON_AddItemToArray(array, beginning); + cJSON_AddItemToArray(array, middle); + cJSON_AddItemToArray(array, end); + + + memset(replacements, '\0', sizeof(replacements)); + + /* replace beginning */ + TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, beginning, &(replacements[0]))); + TEST_ASSERT_NULL(replacements[0].prev); + TEST_ASSERT_TRUE(replacements[0].next == middle); + TEST_ASSERT_TRUE(middle->prev == &(replacements[0])); + TEST_ASSERT_TRUE(array->child == &(replacements[0])); + + /* replace middle */ + TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, middle, &(replacements[1]))); + TEST_ASSERT_TRUE(replacements[1].prev == &(replacements[0])); + TEST_ASSERT_TRUE(replacements[1].next == end); + TEST_ASSERT_TRUE(end->prev == &(replacements[1])); + + /* replace end */ + TEST_ASSERT_TRUE(cJSON_ReplaceItemViaPointer(array, end, &(replacements[2]))); + TEST_ASSERT_TRUE(replacements[2].prev == &(replacements[1])); + TEST_ASSERT_NULL(replacements[2].next); + TEST_ASSERT_TRUE(replacements[1].next == &(replacements[2])); + + cJSON_free(array); +} + int main(void) { UNITY_BEGIN(); @@ -229,6 +315,8 @@ int main(void) RUN_TEST(typecheck_functions_should_check_type); RUN_TEST(cjson_should_not_parse_to_deeply_nested_jsons); RUN_TEST(cjson_set_number_value_should_set_numbers); + RUN_TEST(cjson_detach_item_via_pointer_should_detach_items); + RUN_TEST(cjson_replace_item_via_pointer_should_replace_items); return UNITY_END(); }