cJSON_Utils.c 30.4 KB
Newer Older
M
Max Bruckner 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
  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.
*/

23
#pragma GCC visibility push(default)
24
#include <ctype.h>
25 26
#include <string.h>
#include <stdlib.h>
27
#include <stdio.h>
M
Max Bruckner 已提交
28
#include <limits.h>
29
#pragma GCC visibility pop
M
Max Bruckner 已提交
30

31 32
#include "cJSON_Utils.h"

M
Max Bruckner 已提交
33
static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
34
{
M
Max Bruckner 已提交
35
    size_t length = 0;
36
    unsigned char *copy = NULL;
37

M
Max Bruckner 已提交
38 39 40
    length = strlen((const char*)string) + sizeof("");
    copy = (unsigned char*) cJSON_malloc(length);
    if (copy == NULL)
41
    {
42
        return NULL;
43
    }
M
Max Bruckner 已提交
44
    memcpy(copy, string, length);
45 46 47 48

    return copy;
}

M
Max Bruckner 已提交
49 50
/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
static int cJSONUtils_strcasecmp(const unsigned char *string1, const unsigned char *string2)
51
{
M
Max Bruckner 已提交
52
    if ((string1 == NULL) || (string2 == NULL))
M
Max Bruckner 已提交
53
    {
M
Max Bruckner 已提交
54
        return 1;
M
Max Bruckner 已提交
55
    }
M
Max Bruckner 已提交
56 57

    if (string1 == string2)
M
Max Bruckner 已提交
58
    {
M
Max Bruckner 已提交
59
        return 0;
M
Max Bruckner 已提交
60
    }
M
Max Bruckner 已提交
61 62

    for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
M
Max Bruckner 已提交
63
    {
M
Max Bruckner 已提交
64
        if (*string1 == '\0')
M
Max Bruckner 已提交
65 66 67 68 69
        {
            return 0;
        }
    }

M
Max Bruckner 已提交
70
    return tolower(*string1) - tolower(*string2);
71 72
}

M
Max Bruckner 已提交
73 74
/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
static int cJSONUtils_Pstrcasecmp(const unsigned char *name, const unsigned char *pointer)
75
{
M
Max Bruckner 已提交
76
    if ((name == NULL) || (pointer == NULL))
77
    {
M
Max Bruckner 已提交
78
        return 1;
79
    }
M
Max Bruckner 已提交
80 81

    for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
82
    {
M
Max Bruckner 已提交
83
        if (*pointer == '~')
84 85
        {
            /* check for escaped '~' (~0) and '/' (~1) */
M
Max Bruckner 已提交
86
            if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
87
            {
M
Max Bruckner 已提交
88
                /* invalid escape sequence or wrong character in *name */
89 90 91 92
                return 1;
            }
            else
            {
M
Max Bruckner 已提交
93
                pointer++;
94 95
            }
        }
M
Max Bruckner 已提交
96
        else if (tolower(*name) != tolower(*pointer))
97 98 99 100
        {
            return 1;
        }
    }
M
Max Bruckner 已提交
101
    if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
102 103 104 105 106 107
    {
        /* one string has ended, the other not */
        return 1;
    }

    return 0;
108 109
}

110 111
/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
static size_t cJSONUtils_PointerEncodedstrlen(const unsigned char *string)
112
{
113 114
    size_t length;
    for (length = 0; *string != '\0'; (void)string++, length++)
115
    {
116 117
        /* character needs to be escaped? */
        if ((*string == '~') || (*string == '/'))
118
        {
119
            length++;
120 121 122
        }
    }

123
    return length;
124
}
125

126 127
/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
static void cJSONUtils_PointerEncodedstrcpy(unsigned char *destination, const unsigned char *source)
128
{
129
    for (; source[0] != '\0'; (void)source++, destination++)
130
    {
131
        if (source[0] == '/')
132
        {
133 134
            destination[1] = '1';
            destination++;
135
        }
136
        else if (source[0] == '~')
137
        {
138 139 140
            destination[0] = '~';
            destination[1] = '1';
            destination++;
141 142 143
        }
        else
        {
144
            destination[0] = source[0];
145 146 147
        }
    }

148
    destination[0] = '\0';
149 150
}

151
CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
152
{
153
    size_t c = 0;
154
    cJSON *obj = 0;
155

156 157 158
    if (object == target)
    {
        /* found */
159
        return (char*)cJSONUtils_strdup((const unsigned char*)"");
160
    }
161

162
    /* recursively search all children of the object */
M
Max Bruckner 已提交
163
    for (obj = object->child; obj; (void)(obj = obj->next), c++)
164
    {
165
        unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
166 167
        if (found)
        {
168
            if (cJSON_IsArray(object))
169 170
            {
                /* reserve enough memory for a 64 bit integer + '/' and '\0' */
171
                unsigned char *ret = (unsigned char*)cJSON_malloc(strlen((char*)found) + 23);
172 173 174 175 176
                /* 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)
                {
177
                    cJSON_free(found);
178 179
                    return NULL;
                }
180
                sprintf((char*)ret, "/%lu%s", (unsigned long)c, found); /* /<array_index><path> */
181
                cJSON_free(found);
182

183
                return (char*)ret;
184
            }
185
            else if (cJSON_IsObject(object))
186
            {
187
                unsigned char *ret = (unsigned char*)cJSON_malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
188
                *ret = '/';
189 190
                cJSONUtils_PointerEncodedstrcpy(ret + 1, (unsigned char*)obj->string);
                strcat((char*)ret, (char*)found);
191
                cJSON_free(found);
192

193
                return (char*)ret;
194 195 196
            }

            /* reached leaf of the tree, found nothing */
197
            cJSON_free(found);
198
            return NULL;
199 200 201 202
        }
    }

    /* not found */
203
    return NULL;
204 205
}

206 207 208 209 210 211 212 213 214 215 216 217 218
/* non broken version of cJSON_GetArrayItem */
static cJSON *get_array_item(const cJSON *array, size_t item)
{
    cJSON *child = array ? array->child : NULL;
    while ((child != NULL) && (item > 0))
    {
        item--;
        child = child->next;
    }

    return child;
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
{
    size_t parsed_index = 0;
    size_t position = 0;

    if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
    {
        /* leading zeroes are not permitted */
        return 0;
    }

    for (position = 0; (pointer[position] >= '0') && (*pointer <= '9'); position++)
    {
        parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');

    }

    if ((pointer[position] != '\0') && (pointer[position] != '/'))
    {
        return 0;
    }

    *index = parsed_index;

    return 1;
}

246
CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer)
247
{
248 249 250
    /* follow path of the pointer */
    while ((*pointer++ == '/') && object)
    {
251
        if (cJSON_IsArray(object))
252
        {
253
            size_t index = 0;
254
            if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
255
            {
256
                return NULL;
257
            }
258

259
            object = get_array_item(object, index);
260
        }
261
        else if (cJSON_IsObject(object))
262 263 264
        {
            object = object->child;
            /* GetObjectItem. */
265
            while (object && cJSONUtils_Pstrcasecmp((unsigned char*)object->string, (const unsigned char*)pointer))
266 267 268 269 270 271 272 273 274 275 276
            {
                object = object->next;
            }
            /* skip to the next path token or end of string */
            while (*pointer && (*pointer != '/'))
            {
                pointer++;
            }
        }
        else
        {
277
            return NULL;
278 279 280 281
        }
    }

    return object;
282 283
}

284
/* JSON Patch implementation. */
285
static void cJSONUtils_InplaceDecodePointerString(unsigned char *string)
286
{
287
    unsigned char *s2 = string;
288 289 290 291 292

    if (string == NULL) {
        return;
    }

M
Max Bruckner 已提交
293
    for (; *string; (void)s2++, string++)
294
    {
295
        *s2 = (unsigned char) ((*string != '~')
296 297 298
            ? (*string)
            : ((*(++string) == '0')
                    ? '~'
299
                    : '/'));
300 301 302
    }

    *s2 = '\0';
303 304
}

305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
/* non-broken cJSON_DetachItemFromArray */
static cJSON *detach_item_from_array(cJSON *array, size_t which)
{
    cJSON *c = array->child;
    while (c && (which > 0))
    {
        c = c->next;
        which--;
    }
    if (!c)
    {
        /* item doesn't exist */
        return NULL;
    }
    if (c->prev)
    {
        /* not the first element */
        c->prev->next = c->next;
    }
    if (c->next)
    {
        c->next->prev = c->prev;
    }
    if (c==array->child)
    {
        array->child = c->next;
    }
    /* make sure the detached item doesn't point anywhere anymore */
    c->prev = c->next = NULL;

    return c;
}

338
static cJSON *cJSONUtils_PatchDetach(cJSON *object, const unsigned char *path)
339
{
340 341
    unsigned char *parentptr = NULL;
    unsigned char *childptr = NULL;
342 343
    cJSON *parent = NULL;
    cJSON *ret = NULL;
344 345

    /* copy path and split it in parent and child */
346
    parentptr = cJSONUtils_strdup(path);
347 348 349 350
    if (parentptr == NULL) {
        return NULL;
    }

351
    childptr = (unsigned char*)strrchr((char*)parentptr, '/'); /* last '/' */
352
    if (childptr == NULL)
353
    {
354
        cJSON_free(parentptr);
355
        return NULL;
356
    }
357 358 359
    /* split strings */
    *childptr++ = '\0';

360
    parent = cJSONUtils_GetPointer(object, (char*)parentptr);
361
    cJSONUtils_InplaceDecodePointerString(childptr);
362

363 364 365
    if (!parent)
    {
        /* Couldn't find object to remove child from. */
366
        ret = NULL;
367
    }
368
    else if (cJSON_IsArray(parent))
369
    {
370 371 372
        size_t index = 0;
        if (!decode_array_index_from_pointer(childptr, &index))
        {
373
            cJSON_free(parentptr);
374 375 376
            return NULL;
        }
        ret = detach_item_from_array(parent, index);
377
    }
378
    else if (cJSON_IsObject(parent))
379
    {
380
        ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
381
    }
382
    cJSON_free(parentptr);
383

384 385
    /* return the detachted item */
    return ret;
386 387
}

M
Max Bruckner 已提交
388
static int cJSONUtils_Compare(cJSON *a, cJSON *b)
389
{
390
    if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
M
Max Bruckner 已提交
391 392 393 394
    {
        /* mismatched type. */
        return -1;
    }
395
    switch (a->type & 0xFF)
M
Max Bruckner 已提交
396 397 398 399 400 401 402 403
    {
        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:
M
Max Bruckner 已提交
404
            for ((void)(a = a->child), b = b->child; a && b; (void)(a = a->next), b = b->next)
M
Max Bruckner 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
            {
                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)
            {
M
Max Bruckner 已提交
421
                int err = 0;
M
Max Bruckner 已提交
422
                /* compare object keys */
423
                if (cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string))
M
Max Bruckner 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437
                {
                    /* 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;
438

M
Max Bruckner 已提交
439 440 441 442 443
        default:
            break;
    }
    /* null, true or false */
    return 0;
444 445
}

446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
/* non broken version of cJSON_InsertItemInArray */
static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
{
    cJSON *child = array->child;
    while (child && (which > 0))
    {
        child = child->next;
        which--;
    }
    if (which > 0)
    {
        /* item is after the end of the array */
        return 0;
    }
    if (child == NULL)
    {
        cJSON_AddItemToArray(array, newitem);
        return 1;
    }

    /* insert into the linked list */
    newitem->next = child;
    newitem->prev = child->prev;
    child->prev = newitem;

    /* was it at the beginning */
    if (child == array->child)
    {
        array->child = newitem;
    }
    else
    {
        newitem->prev->next = newitem;
    }

    return 1;
}

M
Max Bruckner 已提交
484 485
enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };

M
Max Bruckner 已提交
486
static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
487
{
488 489 490 491
    cJSON *op = NULL;
    cJSON *path = NULL;
    cJSON *value = NULL;
    cJSON *parent = NULL;
M
Max Bruckner 已提交
492
    enum patch_operation opcode = INVALID;
493 494
    unsigned char *parentptr = NULL;
    unsigned char *childptr = NULL;
495

M
Max Bruckner 已提交
496 497
    op = cJSON_GetObjectItem(patch, "op");
    path = cJSON_GetObjectItem(patch, "path");
498
    if (!cJSON_IsString(op) || !cJSON_IsString(path))
M
Max Bruckner 已提交
499 500 501 502
    {
        /* malformed patch. */
        return 2;
    }
503

M
Max Bruckner 已提交
504 505 506
    /* decode operation */
    if (!strcmp(op->valuestring, "add"))
    {
M
Max Bruckner 已提交
507
        opcode = ADD;
M
Max Bruckner 已提交
508 509 510
    }
    else if (!strcmp(op->valuestring, "remove"))
    {
M
Max Bruckner 已提交
511
        opcode = REMOVE;
M
Max Bruckner 已提交
512 513 514
    }
    else if (!strcmp(op->valuestring, "replace"))
    {
M
Max Bruckner 已提交
515
        opcode = REPLACE;
M
Max Bruckner 已提交
516 517 518
    }
    else if (!strcmp(op->valuestring, "move"))
    {
M
Max Bruckner 已提交
519
        opcode = MOVE;
M
Max Bruckner 已提交
520 521 522
    }
    else if (!strcmp(op->valuestring, "copy"))
    {
M
Max Bruckner 已提交
523
        opcode = COPY;
M
Max Bruckner 已提交
524 525 526 527 528 529 530 531 532 533 534
    }
    else if (!strcmp(op->valuestring, "test"))
    {
        /* compare value: {...} with the given path */
        return cJSONUtils_Compare(cJSONUtils_GetPointer(object, path->valuestring), cJSON_GetObjectItem(patch, "value"));
    }
    else
    {
        /* unknown opcode. */
        return 3;
    }
535

536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
    /* special case for replacing the root */
    if (path->valuestring[0] == '\0')
    {
        if (opcode == REMOVE)
        {
            /* remove possible children */
            if (object->child != NULL)
            {
                cJSON_Delete(object->child);
            }

            /* remove other allocated resources */
            if (object->string != NULL)
            {
                cJSON_free(object->string);
            }
            if (object->valuestring != NULL)
            {
                cJSON_free(object->valuestring);
            }

            /* make it invalid */
            memset(object, '\0', sizeof(cJSON));

            return 0;
        }

        if ((opcode == REPLACE) || (opcode == ADD))
        {
            /* remove possible children */
            if (object->child != NULL)
            {
                cJSON_Delete(object->child);
            }

            /* remove other allocated resources */
            if (object->string != NULL)
            {
                cJSON_free(object->string);
            }
            if (object->valuestring != NULL)
            {
                cJSON_free(object->valuestring);
            }

            value = cJSON_GetObjectItem(patch, "value");
            if (value == NULL)
            {
                /* missing "value" for add/replace. */
                return 7;
            }

            value = cJSON_Duplicate(value, 1);
            if (value == NULL)
            {
                /* out of memory for add/replace. */
                return 8;
            }
            /* the string "value" isn't needed */
            if (value->string != NULL)
            {
                cJSON_free(value->string);
                value->string = NULL;
            }

            /* copy over the value object */
            memcpy(object, value, sizeof(cJSON));

            /* delete the duplicated value */
            cJSON_free(value);

            return 0;
        }
    }

M
Max Bruckner 已提交
611
    if ((opcode == REMOVE) || (opcode == REPLACE))
M
Max Bruckner 已提交
612 613
    {
        /* Get rid of old. */
614 615 616 617 618 619
        cJSON *old_item = cJSONUtils_PatchDetach(object, (unsigned char*)path->valuestring);
        if (old_item == NULL)
        {
            return 13;
        }
        cJSON_Delete(old_item);
M
Max Bruckner 已提交
620
        if (opcode == REMOVE)
M
Max Bruckner 已提交
621
        {
622
            /* For Remove, this job is done. */
M
Max Bruckner 已提交
623 624 625
            return 0;
        }
    }
626

M
Max Bruckner 已提交
627
    /* Copy/Move uses "from". */
M
Max Bruckner 已提交
628
    if ((opcode == MOVE) || (opcode == COPY))
M
Max Bruckner 已提交
629 630 631 632 633 634 635
    {
        cJSON *from = cJSON_GetObjectItem(patch, "from");
        if (!from)
        {
            /* missing "from" for copy/move. */
            return 4;
        }
636

M
Max Bruckner 已提交
637
        if (opcode == MOVE)
M
Max Bruckner 已提交
638
        {
639
            value = cJSONUtils_PatchDetach(object, (unsigned char*)from->valuestring);
M
Max Bruckner 已提交
640
        }
M
Max Bruckner 已提交
641
        if (opcode == COPY)
M
Max Bruckner 已提交
642 643 644 645 646 647 648 649
        {
            value = cJSONUtils_GetPointer(object, from->valuestring);
        }
        if (!value)
        {
            /* missing "from" for copy/move. */
            return 5;
        }
M
Max Bruckner 已提交
650
        if (opcode == COPY)
M
Max Bruckner 已提交
651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
        {
            value = cJSON_Duplicate(value, 1);
        }
        if (!value)
        {
            /* out of memory for copy/move. */
            return 6;
        }
    }
    else /* Add/Replace uses "value". */
    {
        value = cJSON_GetObjectItem(patch, "value");
        if (!value)
        {
            /* missing "value" for add/replace. */
            return 7;
        }
        value = cJSON_Duplicate(value, 1);
        if (!value)
        {
            /* out of memory for add/replace. */
            return 8;
        }
    }
675

M
Max Bruckner 已提交
676
    /* Now, just add "value" to "path". */
677

M
Max Bruckner 已提交
678
    /* split pointer in parent and child */
679 680
    parentptr = cJSONUtils_strdup((unsigned char*)path->valuestring);
    childptr = (unsigned char*)strrchr((char*)parentptr, '/');
M
Max Bruckner 已提交
681 682 683 684
    if (childptr)
    {
        *childptr++ = '\0';
    }
685
    parent = cJSONUtils_GetPointer(object, (char*)parentptr);
M
Max Bruckner 已提交
686
    cJSONUtils_InplaceDecodePointerString(childptr);
687

M
Max Bruckner 已提交
688
    /* add, remove, replace, move, copy, test. */
689
    if ((parent == NULL) || (childptr == NULL))
M
Max Bruckner 已提交
690 691 692 693 694 695
    {
        /* Couldn't find object to add to. */
        free(parentptr);
        cJSON_Delete(value);
        return 9;
    }
696
    else if (cJSON_IsArray(parent))
M
Max Bruckner 已提交
697
    {
698
        if (!strcmp((char*)childptr, "-"))
M
Max Bruckner 已提交
699 700 701 702 703
        {
            cJSON_AddItemToArray(parent, value);
        }
        else
        {
704 705
            size_t index = 0;
            if (!decode_array_index_from_pointer(childptr, &index))
706 707 708 709 710 711
            {
                free(parentptr);
                cJSON_Delete(value);
                return 11;
            }

712
            if (!insert_item_in_array(parent, index, value))
713 714 715 716 717
            {
                free(parentptr);
                cJSON_Delete(value);
                return 10;
            }
M
Max Bruckner 已提交
718 719
        }
    }
720
    else if (cJSON_IsObject(parent))
M
Max Bruckner 已提交
721
    {
722 723
        cJSON_DeleteItemFromObject(parent, (char*)childptr);
        cJSON_AddItemToObject(parent, (char*)childptr, value);
M
Max Bruckner 已提交
724 725 726 727 728 729 730 731 732
    }
    else
    {
        cJSON_Delete(value);
    }
    free(parentptr);

    return 0;
}
733

734
CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
735
{
M
Max Bruckner 已提交
736
    int err = 0;
737

738
    if (!cJSON_IsArray(patches))
739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
    {
        /* malformed patches. */
        return 1;
    }
    if (patches)
    {
        patches = patches->child;
    }
    while (patches)
    {
        if ((err = cJSONUtils_ApplyPatch(object, patches)))
        {
            return err;
        }
        patches = patches->next;
    }

    return 0;
757
}
758

759
static void cJSONUtils_GeneratePatch(cJSON *patches, const unsigned char *op, const unsigned char *path, const unsigned char *suffix, cJSON *val)
760
{
761
    cJSON *patch = cJSON_CreateObject();
762
    cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)op));
763 764
    if (suffix)
    {
765
        unsigned char *newpath = (unsigned char*)cJSON_malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen(suffix) + 2);
766 767
        cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", (const char*)path), suffix);
        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)newpath));
768 769 770 771
        free(newpath);
    }
    else
    {
772
        cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
773 774 775 776 777 778
    }
    if (val)
    {
        cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(val, 1));
    }
    cJSON_AddItemToArray(patches, patch);
779 780
}

781
CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
M
Max Bruckner 已提交
782
{
783
    cJSONUtils_GeneratePatch(array, (const unsigned char*)op, (const unsigned char*)path, 0, val);
M
Max Bruckner 已提交
784
}
785

786
static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path, cJSON *from, cJSON *to)
787
{
788 789 790 791 792
    if ((from == NULL) || (to == NULL))
    {
        return;
    }

793
    if ((from->type & 0xFF) != (to->type & 0xFF))
794
    {
795
        cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
796 797 798
        return;
    }

799
    switch ((from->type & 0xFF))
800 801 802 803
    {
        case cJSON_Number:
            if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble))
            {
804
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
805 806 807 808 809 810
            }
            return;

        case cJSON_String:
            if (strcmp(from->valuestring, to->valuestring) != 0)
            {
811
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"replace", path, 0, to);
812 813
            }
            return;
814

815 816
        case cJSON_Array:
        {
817
            size_t c = 0;
818
            unsigned char *newpath = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */
819
            /* generate patches for all array elements that exist in "from" and "to" */
M
Max Bruckner 已提交
820
            for ((void)(c = 0), (void)(from = from->child), to = to->child; from && to; (void)(from = from->next), (void)(to = to->next), c++)
821
            {
822 823 824 825 826 827 828 829
                /* 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;
                }
830
                sprintf((char*)newpath, "%s/%lu", path, (unsigned long)c); /* path of the current array element */
831 832 833
                cJSONUtils_CompareToPatch(patches, newpath, from, to);
            }
            /* remove leftover elements from 'from' that are not in 'to' */
834
            for (; from; (void)(from = from->next))
835
            {
836 837 838 839 840 841 842 843
                /* 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;
                }
844
                sprintf((char*)newpath, "%lu", (unsigned long)c);
845
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0);
846 847
            }
            /* add new elements in 'to' that were not in 'from' */
M
Max Bruckner 已提交
848
            for (; to; (void)(to = to->next), c++)
849
            {
850
                cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
851 852 853 854 855 856 857
            }
            free(newpath);
            return;
        }

        case cJSON_Object:
        {
M
Max Bruckner 已提交
858 859
            cJSON *a = NULL;
            cJSON *b = NULL;
860 861 862 863 864 865 866 867
            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)
            {
868
                int diff = (!a) ? 1 : ((!b) ? -1 : cJSONUtils_strcasecmp((unsigned char*)a->string, (unsigned char*)b->string));
869 870 871
                if (!diff)
                {
                    /* both object keys are the same */
872
                    unsigned char *newpath = (unsigned char*)cJSON_malloc(strlen((const char*)path) + cJSONUtils_PointerEncodedstrlen((unsigned char*)a->string) + 2);
873
                    cJSONUtils_PointerEncodedstrcpy(newpath + sprintf((char*)newpath, "%s/", path), (unsigned char*)a->string);
874 875 876 877 878 879 880 881 882
                    /* 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 */
883
                    cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, (unsigned char*)a->string, 0);
884 885 886 887 888
                    a = a->next;
                }
                else
                {
                    /* object element doesn't exist in 'from' --> add it */
889
                    cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (unsigned char*)b->string, b);
890 891 892 893 894 895 896 897 898 899
                    b = b->next;
                }
            }
            return;
        }

        default:
            break;
    }
}
900

901
CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
902
{
903
    cJSON *patches = cJSON_CreateArray();
904
    cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
905

906 907
    return patches;
}
908

M
Max Bruckner 已提交
909
/* sort lists using mergesort */
910 911
static cJSON *cJSONUtils_SortList(cJSON *list)
{
M
Max Bruckner 已提交
912 913 914
    cJSON *first = list;
    cJSON *second = list;
    cJSON *ptr = list;
915

M
Max Bruckner 已提交
916 917 918 919 920
    if (!list || !list->next)
    {
        /* One entry is sorted already. */
        return list;
    }
921

922
    while (ptr && ptr->next && (cJSONUtils_strcasecmp((unsigned char*)ptr->string, (unsigned char*)ptr->next->string) < 0))
M
Max Bruckner 已提交
923 924 925 926 927 928 929 930 931
    {
        /* Test for list sorted. */
        ptr = ptr->next;
    }
    if (!ptr || !ptr->next)
    {
        /* Leave sorted lists unmodified. */
        return list;
    }
932

M
Max Bruckner 已提交
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
    /* reset ptr to the beginning */
    ptr = list;
    while (ptr)
    {
        /* Walk two pointers to find the middle. */
        second = second->next;
        ptr = ptr->next;
        /* advances ptr two steps at a time */
        if (ptr)
        {
            ptr = ptr->next;
        }
    }
    if (second && second->prev)
    {
        /* Split the lists */
949
        second->prev->next = NULL;
M
Max Bruckner 已提交
950
    }
951

M
Max Bruckner 已提交
952 953 954
    /* Recursively sort the sub-lists. */
    first = cJSONUtils_SortList(first);
    second = cJSONUtils_SortList(second);
955
    list = ptr = NULL;
M
Max Bruckner 已提交
956 957 958

    while (first && second) /* Merge the sub-lists */
    {
959
        if (cJSONUtils_strcasecmp((unsigned char*)first->string, (unsigned char*)second->string) < 0)
M
Max Bruckner 已提交
960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
        {
            if (!list)
            {
                /* start merged list with the first element of the first list */
                list = ptr = first;
            }
            else
            {
                /* add first element of first list to merged list */
                ptr->next = first;
                first->prev = ptr;
                ptr = first;
            }
            first = first->next;
        }
        else
        {
            if (!list)
            {
                /* start merged list with the first element of the second list */
                list = ptr = second;
            }
            else
            {
                /* add first element of second list to merged list */
                ptr->next = second;
                second->prev = ptr;
                ptr = second;
            }
            second = second->next;
        }
    }
    if (first)
    {
        /* Append rest of first list. */
        if (!list)
        {
            return first;
        }
        ptr->next = first;
        first->prev = ptr;
    }
    if (second)
    {
        /* Append rest of second list */
        if (!list)
        {
            return second;
        }
        ptr->next = second;
        second->prev = ptr;
    }

    return list;
1014 1015
}

1016
CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object)
M
Max Bruckner 已提交
1017 1018 1019
{
    object->child = cJSONUtils_SortList(object->child);
}
1020

1021
CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
1022
{
1023
    if (!cJSON_IsObject(patch))
M
Max Bruckner 已提交
1024 1025 1026 1027 1028
    {
        /* scalar value, array or NULL, just duplicate */
        cJSON_Delete(target);
        return cJSON_Duplicate(patch, 1);
    }
1029

1030
    if (!cJSON_IsObject(target))
M
Max Bruckner 已提交
1031 1032 1033 1034 1035 1036 1037 1038
    {
        cJSON_Delete(target);
        target = cJSON_CreateObject();
    }

    patch = patch->child;
    while (patch)
    {
1039
        if (cJSON_IsNull(patch))
M
Max Bruckner 已提交
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051
        {
            /* NULL is the indicator to remove a value, see RFC7396 */
            cJSON_DeleteItemFromObject(target, patch->string);
        }
        else
        {
            cJSON *replaceme = cJSON_DetachItemFromObject(target, patch->string);
            cJSON_AddItemToObject(target, patch->string, cJSONUtils_MergePatch(replaceme, patch));
        }
        patch = patch->next;
    }
    return target;
1052
}
1053

1054
CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
1055
{
1056
    cJSON *patch = NULL;
1057 1058 1059 1060 1061
    if (!to)
    {
        /* patch to delete everything */
        return cJSON_CreateNull();
    }
1062
    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
    {
        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);
1104
        return NULL;
1105 1106 1107
    }

    return patch;
M
Max Bruckner 已提交
1108
}