static int cJSONUtils_Compare(cJSON *a, cJSON *b) { if (a->type != b->type) { /* mismatched type. */ return -1; } switch (a->type) { case cJSON_Number: /* numeric mismatch. */ return ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) ? -2 : 0; case cJSON_String: /* string mismatch. */ return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0; case cJSON_Array: for (a = a->child, b = b->child; a && b; a = a->next, b = b->next) { int err = cJSONUtils_Compare(a, b); if (err) { return err; } } /* array size mismatch? (one of both children is not NULL) */ return (a || b) ? -4 : 0; case cJSON_Object: cJSONUtils_SortObject(a); cJSONUtils_SortObject(b); a = a->child; b = b->child; while (a && b) { int err; /* compare object keys */ if (cJSONUtils_strcasecmp(a->string, b->string)) { /* missing member */ return -6; } err = cJSONUtils_Compare(a, b); if (err) { return err; } a = a->next; b = b->next; } /* object length mismatch (one of both children is not null) */ return (a || b) ? -5 : 0; default: break; } /* null, true or false */ return 0; }
cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to) { cJSON *patch = 0; if (!to) { /* patch to delete everything */ return cJSON_CreateNull(); } if ((to->type != cJSON_Object) || !from || (from->type != cJSON_Object)) { return cJSON_Duplicate(to, 1); } cJSONUtils_SortObject(from); cJSONUtils_SortObject(to); from = from->child; to = to->child; patch = cJSON_CreateObject(); while (from || to) { int compare = from ? (to ? strcmp(from->string, to->string) : -1) : 1; if (compare < 0) { /* from has a value that to doesn't have -> remove */ cJSON_AddItemToObject(patch, from->string, cJSON_CreateNull()); from = from->next; } else if (compare > 0) { /* to has a value that from doesn't have -> add to patch */ cJSON_AddItemToObject(patch, to->string, cJSON_Duplicate(to, 1)); to = to->next; } else { /* object key exists in both objects */ if (cJSONUtils_Compare(from, to)) { /* not identical --> generate a patch */ cJSON_AddItemToObject(patch, to->string, cJSONUtils_GenerateMergePatch(from, to)); } /* next key in the object */ from = from->next; to = to->next; } } if (!patch->child) { cJSON_Delete(patch); return 0; } return patch; }
static void cjson_utils_functions_shouldnt_crash_with_null_pointers(void) { cJSON *item = cJSON_CreateString("item"); TEST_ASSERT_NOT_NULL(item); TEST_ASSERT_NULL(cJSONUtils_GetPointer(item, NULL)); TEST_ASSERT_NULL(cJSONUtils_GetPointer(NULL, "pointer")); TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(NULL, "pointer")); TEST_ASSERT_NULL(cJSONUtils_GetPointerCaseSensitive(item, NULL)); TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(item, NULL)); TEST_ASSERT_NULL(cJSONUtils_GeneratePatches(NULL, item)); TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(item, NULL)); TEST_ASSERT_NULL(cJSONUtils_GeneratePatchesCaseSensitive(NULL, item)); cJSONUtils_AddPatchToArray(item, "path", "add", NULL); cJSONUtils_AddPatchToArray(item, "path", NULL, item); cJSONUtils_AddPatchToArray(item, NULL, "add", item); cJSONUtils_AddPatchToArray(NULL, "path", "add", item); cJSONUtils_ApplyPatches(item, NULL); cJSONUtils_ApplyPatches(NULL, item); cJSONUtils_ApplyPatchesCaseSensitive(item, NULL); cJSONUtils_ApplyPatchesCaseSensitive(NULL, item); TEST_ASSERT_NULL(cJSONUtils_MergePatch(item, NULL)); item = cJSON_CreateString("item"); TEST_ASSERT_NULL(cJSONUtils_MergePatchCaseSensitive(item, NULL)); item = cJSON_CreateString("item"); /* these calls are actually valid */ /* cJSONUtils_MergePatch(NULL, item); */ /* cJSONUtils_MergePatchCaseSensitive(NULL, item);*/ /* cJSONUtils_GenerateMergePatch(item, NULL); */ /* cJSONUtils_GenerateMergePatch(NULL, item); */ /* cJSONUtils_GenerateMergePatchCaseSensitive(item, NULL); */ /* cJSONUtils_GenerateMergePatchCaseSensitive(NULL, item); */ TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(item, NULL)); TEST_ASSERT_NULL(cJSONUtils_FindPointerFromObjectTo(NULL, item)); cJSONUtils_SortObject(NULL); cJSONUtils_SortObjectCaseSensitive(NULL); cJSON_Delete(item); }
static void cJSONUtils_CompareToPatch(cJSON *patches, const char *path, cJSON *from, cJSON *to) { if (from->type != to->type) { cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); return; } switch (from->type) { case cJSON_Number: if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) { cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); } return; case cJSON_String: if (strcmp(from->valuestring, to->valuestring) != 0) { cJSONUtils_GeneratePatch(patches, "replace", path, 0, to); } return; case cJSON_Array: { int c; char *newpath = (char*)malloc(strlen(path) + 23); /* Allow space for 64bit int. */ /* generate patches for all array elements that exist in "from" and "to" */ for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++) { sprintf(newpath, "%s/%d", path, c); /* path of the current array element */ cJSONUtils_CompareToPatch(patches, newpath, from, to); } /* remove leftover elements from 'from' that are not in 'to' */ for (; from; from = from->next, c++) { sprintf(newpath, "%d", c); cJSONUtils_GeneratePatch(patches, "remove", path, newpath, 0); } /* add new elements in 'to' that were not in 'from' */ for (; to; to = to->next, c++) { cJSONUtils_GeneratePatch(patches, "add", path, "-", to); } free(newpath); return; } case cJSON_Object: { cJSON *a; cJSON *b; cJSONUtils_SortObject(from); cJSONUtils_SortObject(to); a = from->child; b = to->child; /* for all object values in the object with more of them */ while (a || b) { int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp(a->string, b->string)); if (!diff) { /* both object keys are the same */ char *newpath = (char*)malloc(strlen(path) + cJSONUtils_PointerEncodedstrlen(a->string) + 2); cJSONUtils_PointerEncodedstrcpy(newpath + sprintf(newpath, "%s/", path), a->string); /* create a patch for the element */ cJSONUtils_CompareToPatch(patches, newpath, a, b); free(newpath); a = a->next; b = b->next; } else if (diff < 0) { /* object element doesn't exist in 'to' --> remove it */ cJSONUtils_GeneratePatch(patches, "remove", path, a->string, 0); a = a->next; } else { /* object element doesn't exist in 'from' --> add it */ cJSONUtils_GeneratePatch(patches, "add", path, b->string, b); b = b->next; } } return; } default: break; } }
static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path, cJSON *from, cJSON *to) { if ((from == NULL) || (to == NULL)) { return; } if ((from->type & 0xFF) != (to->type & 0xFF)) { cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to); return; } switch ((from->type & 0xFF)) { case cJSON_Number: if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) { cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to); } return; case cJSON_String: if (strcmp(from->valuestring, to->valuestring) != 0) { cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to); } return; case cJSON_Array: { size_t c = 0; unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */ /* generate patches for all array elements that exist in "from" and "to" */ for ((void)(c = 0), (void)(from = from->child), to = to->child; from && to; (void)(from = from->next), (void)(to = to->next), c++) { /* check if conversion to unsigned long is valid * This should be eliminated at compile time by dead code elimination * if size_t is an alias of unsigned long, or if it is bigger */ if (c > ULONG_MAX) { free(newpath); return; } sprintf((char*)newpath, "%s/%lu", path, (unsigned long)c); /* path of the current array element */ cJSONUtils_CompareToPatch(patches, newpath, from, to); } /* remove leftover elements from 'from' that are not in 'to' */ for (; from; (void)(from = from->next), c++) { /* check if conversion to unsigned long is valid * This should be eliminated at compile time by dead code elimination * if size_t is an alias of unsigned long, or if it is bigger */ if (c > ULONG_MAX) { free(newpath); return; } sprintf((char*)newpath, "%lu", (unsigned long)c); cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0); } /* add new elements in 'to' that were not in 'from' */ for (; to; (void)(to = to->next), c++) { cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to); } free(newpath); return; } case cJSON_Object: { cJSON *a = NULL; cJSON *b = NULL; cJSONUtils_SortObject(from); cJSONUtils_SortObject(to); a = from->child; b = to->child; /* for all object values in the object with more of them */ while (a || b) { int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string)); if (!diff) { /* both object keys are the same */ unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen((unsigned char*)a->string) + 2); cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", path), (unsigned char*)a->string); /* create a patch for the element */ cJSONUtils_CompareToPatch(patches, newpath, a, b); free(newpath); a = a->next; b = b->next; } else if (diff < 0) { /* object element doesn't exist in 'to' --> remove it */ cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, (unsigned char*)a->string, 0); a = a->next; } else { /* object element doesn't exist in 'from' --> add it */ cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (unsigned char*)b->string, b); b = b->next; } } return; } default: break; } }