xref: /third_party/vulkan-loader/loader/cJSON.c (revision 5db71995)
1/*
2  Copyright (c) 2009 Dave Gamble
3  Copyright (c) 2015-2021 The Khronos Group Inc.
4  Copyright (c) 2015-2021 Valve Corporation
5  Copyright (c) 2015-2021 LunarG, Inc.
6
7  Permission is hereby granted, free of charge, to any person obtaining a copy
8  of this software and associated documentation files (the "Software"), to deal
9  in the Software without restriction, including without limitation the rights
10  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  copies of the Software, and to permit persons to whom the Software is
12  furnished to do so, subject to the following conditions:
13
14  The above copyright notice and this permission notice shall be included in
15  all copies or substantial portions of the Software.
16
17  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  THE SOFTWARE.
24*/
25
26/* cJSON */
27/* JSON parser in C. */
28
29#include <ctype.h>
30#include <float.h>
31#include <limits.h>
32#include <math.h>
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36
37#include "cJSON.h"
38
39#include "allocation.h"
40#include "loader.h"
41#include "log.h"
42
43static void *cJSON_malloc(const VkAllocationCallbacks *pAllocator, size_t size) {
44    return loader_calloc(pAllocator, size, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
45}
46
47static void *cJSON_malloc_instance_scope(const VkAllocationCallbacks *pAllocator, size_t size) {
48    return loader_calloc(pAllocator, size, VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
49}
50
51static void cJSON_Free(const VkAllocationCallbacks *pAllocator, void *pMemory) { loader_free(pAllocator, pMemory); }
52
53/*
54// commented out as it is unused - static error code channel requires external locks to be used.
55static const char *ep;
56
57const char *cJSON_GetErrorPtr(void) { return ep; }
58*/
59
60static char *cJSON_strdup(const VkAllocationCallbacks *pAllocator, const char *str) {
61    size_t len;
62    char *copy;
63
64    len = strlen(str) + 1;
65    copy = (char *)cJSON_malloc(pAllocator, len);
66    if (!copy) return 0;
67    memcpy(copy, str, len);
68    return copy;
69}
70
71/* Internal constructor. */
72static cJSON *cJSON_New_Item(const VkAllocationCallbacks *pAllocator) {
73    cJSON *node = (cJSON *)cJSON_malloc(pAllocator, sizeof(cJSON));
74    if (node) {
75        memset(node, 0, sizeof(cJSON));
76        node->pAllocator = (VkAllocationCallbacks *)pAllocator;
77    }
78    return node;
79}
80
81/* Delete a cJSON structure. */
82void loader_cJSON_Delete(cJSON *c) {
83    cJSON *next;
84    while (c) {
85        next = c->next;
86        if (!(c->type & cJSON_IsReference) && c->child) loader_cJSON_Delete(c->child);
87        if (!(c->type & cJSON_IsReference) && c->valuestring) cJSON_Free(c->pAllocator, c->valuestring);
88        if (!(c->type & cJSON_StringIsConst) && c->string) cJSON_Free(c->pAllocator, c->string);
89        cJSON_Free(c->pAllocator, c);
90        c = next;
91    }
92}
93
94/* Parse the input text to generate a number, and populate the result into item.
95 */
96static const char *parse_number(cJSON *item, const char *num) {
97    double n = 0, sign = 1, scale = 0;
98    int subscale = 0, signsubscale = 1;
99
100    if (*num == '-') sign = -1, num++; /* Has sign? */
101    if (*num == '0') num++;            /* is zero */
102    if (*num >= '1' && *num <= '9') do
103            n = (n * 10.0) + (*num++ - '0');
104        while (*num >= '0' && *num <= '9'); /* Number? */
105    if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
106        num++;
107        do n = (n * 10.0) + (*num++ - '0'), scale--;
108        while (*num >= '0' && *num <= '9');
109    }                               /* Fractional part? */
110    if (*num == 'e' || *num == 'E') /* Exponent? */
111    {
112        num++;
113        if (*num == '+')
114            num++;
115        else if (*num == '-')
116            signsubscale = -1, num++;                                                   /* With sign? */
117        while (*num >= '0' && *num <= '9') subscale = (subscale * 10) + (*num++ - '0'); /* Number? */
118    }
119
120    n = sign * n * pow(10.0, (scale + subscale * signsubscale)); /* number = +/-
121                                                                    number.fraction *
122                                                                    10^+/- exponent */
123
124    item->valuedouble = n;
125    item->valueint = (int)n;
126    item->type = cJSON_Number;
127    return num;
128}
129
130static size_t pow2gt(size_t x) {
131    --x;
132    x |= x >> 1;
133    x |= x >> 2;
134    x |= x >> 4;
135    x |= x >> 8;
136    x |= x >> 16;
137    return x + 1;
138}
139
140typedef struct {
141    char *buffer;
142    size_t length;
143    size_t offset;
144} printbuffer;
145
146static char *ensure(const VkAllocationCallbacks *pAllocator, printbuffer *p, size_t needed) {
147    char *newbuffer;
148    size_t newsize;
149    if (!p || !p->buffer) return 0;
150    needed += p->offset;
151    if (needed <= p->length) return p->buffer + p->offset;
152
153    newsize = pow2gt(needed);
154    newbuffer = (char *)cJSON_malloc(pAllocator, newsize);
155    if (!newbuffer) {
156        cJSON_Free(pAllocator, p->buffer);
157        p->length = 0, p->buffer = 0;
158        return 0;
159    }
160    if (newbuffer) memcpy(newbuffer, p->buffer, p->length);
161    cJSON_Free(pAllocator, p->buffer);
162    p->length = newsize;
163    p->buffer = newbuffer;
164    return newbuffer + p->offset;
165}
166
167static size_t cJSON_update(printbuffer *p) {
168    char *str;
169    if (!p || !p->buffer) return 0;
170    str = p->buffer + p->offset;
171    return p->offset + strlen(str);
172}
173
174/* Render the number nicely from the given item into a string. */
175static char *print_number(cJSON *item, printbuffer *p) {
176    char *str = 0;
177    size_t str_buf_size;
178    double d = item->valuedouble;
179    if (d == 0) {
180        str_buf_size = 2; /* special case for 0. */
181        if (p)
182            str = ensure(item->pAllocator, p, str_buf_size);
183        else
184            str = (char *)cJSON_malloc(item->pAllocator, str_buf_size);
185        if (str) loader_strncpy(str, str_buf_size, "0", 2);
186    } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
187        str_buf_size = 21; /* 2^64+1 can be represented in 21 chars. */
188        if (p)
189            str = ensure(item->pAllocator, p, str_buf_size);
190        else
191            str = (char *)cJSON_malloc(item->pAllocator, str_buf_size);
192        if (str) snprintf(str, str_buf_size, "%d", item->valueint);
193    } else {
194        str_buf_size = 64; /* This is a nice tradeoff. */
195        if (p)
196            str = ensure(item->pAllocator, p, str_buf_size);
197        else
198            str = (char *)cJSON_malloc(item->pAllocator, str_buf_size);
199        if (str) {
200            if (fabs(floor(d) - d) <= DBL_EPSILON && fabs(d) < 1.0e60)
201                snprintf(str, str_buf_size, "%.0f", d);
202            else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
203                snprintf(str, str_buf_size, "%e", d);
204            else
205                snprintf(str, str_buf_size, "%f", d);
206        }
207    }
208    return str;
209}
210
211static unsigned parse_hex4(const char *str) {
212    unsigned h = 0;
213    if (*str >= '0' && *str <= '9')
214        h += (*str) - '0';
215    else if (*str >= 'A' && *str <= 'F')
216        h += 10 + (*str) - 'A';
217    else if (*str >= 'a' && *str <= 'f')
218        h += 10 + (*str) - 'a';
219    else
220        return 0;
221    h = h << 4;
222    str++;
223    if (*str >= '0' && *str <= '9')
224        h += (*str) - '0';
225    else if (*str >= 'A' && *str <= 'F')
226        h += 10 + (*str) - 'A';
227    else if (*str >= 'a' && *str <= 'f')
228        h += 10 + (*str) - 'a';
229    else
230        return 0;
231    h = h << 4;
232    str++;
233    if (*str >= '0' && *str <= '9')
234        h += (*str) - '0';
235    else if (*str >= 'A' && *str <= 'F')
236        h += 10 + (*str) - 'A';
237    else if (*str >= 'a' && *str <= 'f')
238        h += 10 + (*str) - 'a';
239    else
240        return 0;
241    h = h << 4;
242    str++;
243    if (*str >= '0' && *str <= '9')
244        h += (*str) - '0';
245    else if (*str >= 'A' && *str <= 'F')
246        h += 10 + (*str) - 'A';
247    else if (*str >= 'a' && *str <= 'f')
248        h += 10 + (*str) - 'a';
249    else
250        return 0;
251    return h;
252}
253
254/* Parse the input text into an unescaped cstring, and populate item. */
255static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
256static const char *parse_string(cJSON *item, const char *str, bool *out_of_memory) {
257    const char *ptr = str + 1;
258    char *ptr2;
259    char *out;
260    int len = 0;
261    unsigned uc, uc2;
262    if (*str != '\"') {
263        // ep = str; // commented out as it is unused
264        return 0;
265    } /* not a string! */
266
267    while (*ptr != '\"' && *ptr && ++len)
268        if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
269
270    out = (char *)cJSON_malloc(item->pAllocator, len + 1); /* This is how long we need for the string, roughly. */
271    if (!out) {
272        *out_of_memory = true;
273        return 0;
274    }
275
276    ptr = str + 1;
277    ptr2 = out;
278    while (*ptr != '\"' && *ptr) {
279        if (*ptr != '\\')
280            *ptr2++ = *ptr++;
281        else {
282            ptr++;
283            switch (*ptr) {
284                case 'b':
285                    *ptr2++ = '\b';
286                    break;
287                case 'f':
288                    *ptr2++ = '\f';
289                    break;
290                case 'n':
291                    *ptr2++ = '\n';
292                    break;
293                case 'r':
294                    *ptr2++ = '\r';
295                    break;
296                case 't':
297                    *ptr2++ = '\t';
298                    break;
299                case 'u': /* transcode utf16 to utf8. */
300                    uc = parse_hex4(ptr + 1);
301                    ptr += 4; /* get the unicode char. */
302
303                    if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid.	*/
304
305                    if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs.	*/
306                    {
307                        if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate.	*/
308                        uc2 = parse_hex4(ptr + 3);
309                        ptr += 6;
310                        if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate.	*/
311                        uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
312                    }
313
314                    len = 4;
315                    if (uc < 0x80)
316                        len = 1;
317                    else if (uc < 0x800)
318                        len = 2;
319                    else if (uc < 0x10000)
320                        len = 3;
321                    ptr2 += len;
322
323                    for (size_t i = len; i > 0; i--) {
324                        if (i == 1) {
325                            *--ptr2 = ((unsigned char)uc | firstByteMark[len]);
326                        } else if (i >= 2) {
327                            *--ptr2 = ((uc | 0x80) & 0xBF);
328                            uc >>= 6;
329                        }
330                    }
331                    ptr2 += len;
332                    break;
333                default:
334                    *ptr2++ = *ptr;
335                    break;
336            }
337            ptr++;
338        }
339    }
340    *ptr2 = 0;
341    if (*ptr == '\"') ptr++;
342    item->valuestring = out;
343    item->type = cJSON_String;
344    return ptr;
345}
346
347/* Render the cstring provided to an escaped version that can be printed. */
348static char *print_string_ptr(const VkAllocationCallbacks *pAllocator, const char *str, printbuffer *p) {
349    const char *ptr;
350    char *ptr2;
351    char *out;
352    size_t out_buf_size, len = 0, flag = 0;
353    unsigned char token;
354
355    for (ptr = str; *ptr; ptr++) flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"') || (*ptr == '\\')) ? 1 : 0;
356    if (!flag) {
357        len = ptr - str;
358        out_buf_size = len + 1;
359        // out_buf_size = len + 3; // Modified to not put quotes around the string
360        if (p)
361            out = ensure(pAllocator, p, out_buf_size);
362        else
363            out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size);
364        if (!out) return 0;
365        ptr2 = out;
366        // *ptr2++ = '\"'; // Modified to not put quotes around the string
367        loader_strncpy(ptr2, out_buf_size, str, out_buf_size);
368        // ptr2[len] = '\"'; // Modified to not put quotes around the string
369        ptr2[len] = 0;  // ptr2[len + 1] = 0; // Modified to not put quotes around the string
370        return out;
371    }
372
373    if (!str) {
374        out_buf_size = 3;
375        if (p)
376            out = ensure(pAllocator, p, out_buf_size);
377        else
378            out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size);
379        if (!out) return 0;
380        loader_strncpy(out, out_buf_size, "\"\"", 3);
381        return out;
382    }
383    ptr = str;
384    token = *ptr;
385    while (token && ++len) {
386        if (strchr("\"\\\b\f\n\r\t", token))
387            len++;
388        else if (token < 32)
389            len += 5;
390        ptr++;
391        token = *ptr;
392    }
393
394    out_buf_size = len + 1;
395    // out_buf_size = len + 3; // Modified to not put quotes around the string
396    if (p)
397        out = ensure(pAllocator, p, out_buf_size);
398    else
399        out = (char *)cJSON_malloc_instance_scope(pAllocator, out_buf_size);
400    if (!out) return 0;
401
402    ptr2 = out;
403    ptr = str;
404    // *ptr2++ = '\"'; // Modified to not put quotes around the string
405    while (*ptr) {
406        if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')
407            *ptr2++ = *ptr++;
408        else {
409            switch (token = *ptr++) {
410                case '\\':
411                    *ptr2++ = '\\';
412                    break;
413                case '\"':
414                    *ptr2++ = '\"';
415                    break;
416                case '\b':
417                    *ptr2++ = '\b';
418                    break;
419                case '\f':
420                    *ptr2++ = '\f';
421                    break;
422                case '\n':
423                    *ptr2++ = '\n';
424                    break;
425                case '\r':
426                    *ptr2++ = '\r';
427                    break;
428                case '\t':
429                    *ptr2++ = '\t';
430                    break;
431                default:
432                    snprintf(ptr2, out_buf_size - (ptr2 - out), "u%04x", token);
433                    ptr2 += 5;
434                    break; /* escape and print */
435            }
436        }
437    }
438    // *ptr2++ = '\"'; // Modified to not put quotes around the string
439    *ptr2++ = 0;
440    return out;
441}
442/* Invoke print_string_ptr (which is useful) on an item. */
443static char *print_string(cJSON *item, printbuffer *p) { return print_string_ptr(item->pAllocator, item->valuestring, p); }
444
445/* Declare these prototypes. */
446static const char *parse_value(cJSON *item, const char *value, bool *out_of_memory);
447static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p);
448static const char *parse_array(cJSON *item, const char *value, bool *out_of_memory);
449static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p);
450static const char *parse_object(cJSON *item, const char *value, bool *out_of_memory);
451static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p);
452
453/* Utility to jump whitespace and cr/lf */
454static const char *skip(const char *in) {
455    while (in && *in && (unsigned char)*in <= 32) in++;
456    return in;
457}
458
459/* Parse an object - create a new root, and populate. */
460static cJSON *cJSON_ParseWithOpts(const VkAllocationCallbacks *pAllocator, const char *value, const char **return_parse_end,
461                                  int require_null_terminated, bool *out_of_memory) {
462    const char *end = 0;
463    cJSON *c = cJSON_New_Item(pAllocator);
464    // ep = 0; // commented out as it is unused
465    if (!c) {
466        *out_of_memory = true;
467        return 0; /* memory fail */
468    }
469
470    end = parse_value(c, skip(value), out_of_memory);
471    if (!end) {
472        loader_cJSON_Delete(c);
473        return 0;
474    } /* parse failure. ep is set. */
475
476    /* if we require null-terminated JSON without appended garbage, skip and
477     * then check for a null terminator */
478    if (require_null_terminated) {
479        end = skip(end);
480        if (*end) {
481            loader_cJSON_Delete(c);
482            // ep = end; // commented out as it is unused
483            return 0;
484        }
485    }
486    if (return_parse_end) *return_parse_end = end;
487    return c;
488}
489/* Default options for cJSON_Parse */
490static cJSON *cJSON_Parse(const VkAllocationCallbacks *pAllocator, const char *value, bool *out_of_memory) {
491    return cJSON_ParseWithOpts(pAllocator, value, 0, 0, out_of_memory);
492}
493
494/* Render a cJSON item/entity/structure to text. */
495char *loader_cJSON_Print(cJSON *item) { return print_value(item, 0, 1, 0); }
496char *loader_cJSON_PrintUnformatted(cJSON *item) { return print_value(item, 0, 0, 0); }
497
498/* Parser core - when encountering text, process appropriately. */
499static const char *parse_value(cJSON *item, const char *value, bool *out_of_memory) {
500    if (!value) return 0; /* Fail on null. */
501    if (!strncmp(value, "null", 4)) {
502        item->type = cJSON_NULL;
503        return value + 4;
504    }
505    if (!strncmp(value, "false", 5)) {
506        item->type = cJSON_False;
507        return value + 5;
508    }
509    if (!strncmp(value, "true", 4)) {
510        item->type = cJSON_True;
511        item->valueint = 1;
512        return value + 4;
513    }
514    if (*value == '\"') {
515        return parse_string(item, value, out_of_memory);
516    }
517    if (*value == '-' || (*value >= '0' && *value <= '9')) {
518        return parse_number(item, value);
519    }
520    if (*value == '[') {
521        return parse_array(item, value, out_of_memory);
522    }
523    if (*value == '{') {
524        return parse_object(item, value, out_of_memory);
525    }
526
527    // ep = value; // commented out as it is unused
528    return 0; /* failure. */
529}
530
531/* Render a value to text. */
532static char *print_value(cJSON *item, int depth, int fmt, printbuffer *p) {
533    char *out = 0;
534    if (!item) return 0;
535    if (p) {
536        switch ((item->type) & 255) {
537            case cJSON_NULL: {
538                out = ensure(item->pAllocator, p, 5);
539                if (out) loader_strncpy(out, 5, "null", 5);
540                break;
541            }
542            case cJSON_False: {
543                out = ensure(item->pAllocator, p, 6);
544                if (out) loader_strncpy(out, 6, "false", 6);
545                break;
546            }
547            case cJSON_True: {
548                out = ensure(item->pAllocator, p, 5);
549                if (out) loader_strncpy(out, 5, "true", 5);
550                break;
551            }
552            case cJSON_Number:
553                out = print_number(item, p);
554                break;
555            case cJSON_String:
556                out = print_string(item, p);
557                break;
558            case cJSON_Array:
559                out = print_array(item, depth, fmt, p);
560                break;
561            case cJSON_Object:
562                out = print_object(item, depth, fmt, p);
563                break;
564        }
565    } else {
566        switch ((item->type) & 255) {
567            case cJSON_NULL:
568                out = cJSON_strdup(item->pAllocator, "null");
569                break;
570            case cJSON_False:
571                out = cJSON_strdup(item->pAllocator, "false");
572                break;
573            case cJSON_True:
574                out = cJSON_strdup(item->pAllocator, "true");
575                break;
576            case cJSON_Number:
577                out = print_number(item, 0);
578                break;
579            case cJSON_String:
580                out = print_string(item, 0);
581                break;
582            case cJSON_Array:
583                out = print_array(item, depth, fmt, 0);
584                break;
585            case cJSON_Object:
586                out = print_object(item, depth, fmt, 0);
587                break;
588        }
589    }
590    return out;
591}
592
593/* Build an array from input text. */
594static const char *parse_array(cJSON *item, const char *value, bool *out_of_memory) {
595    cJSON *child;
596    if (*value != '[') {
597        // ep = value; // commented out as it is unused
598        return 0;
599    } /* not an array! */
600
601    item->type = cJSON_Array;
602    value = skip(value + 1);
603    if (*value == ']') return value + 1; /* empty array. */
604
605    item->child = child = cJSON_New_Item(item->pAllocator);
606    if (!item->child) {
607        *out_of_memory = true;
608        return 0; /* memory fail */
609    }
610    value = skip(parse_value(child, skip(value), out_of_memory)); /* skip any spacing, get the value. */
611    if (!value) return 0;
612
613    while (*value == ',') {
614        cJSON *new_item;
615        new_item = cJSON_New_Item(item->pAllocator);
616        if (!new_item) {
617            *out_of_memory = true;
618            return 0; /* memory fail */
619        }
620        child->next = new_item;
621        new_item->prev = child;
622        child = new_item;
623        value = skip(parse_value(child, skip(value + 1), out_of_memory));
624        if (!value) return 0; /* memory fail */
625    }
626
627    if (*value == ']') return value + 1; /* end of array */
628    // ep = value; // commented out as it is unused
629    return 0; /* malformed. */
630}
631
632/* Render an array to text */
633static char *print_array(cJSON *item, int depth, int fmt, printbuffer *p) {
634    char **entries;
635    char *out = 0, *ptr, *ret;
636    size_t len = 5;
637    cJSON *child = item->child;
638    int numentries = 0, fail = 0, j = 0;
639    size_t tmplen = 0, i = 0;
640
641    /* How many entries in the array? */
642    while (child) numentries++, child = child->next;
643    /* Explicitly handle numentries==0 */
644    if (!numentries) {
645        if (p)
646            out = ensure(item->pAllocator, p, 3);
647        else
648            out = (char *)cJSON_malloc(item->pAllocator, 3);
649        if (out) loader_strncpy(out, 3, "[]", 3);
650        return out;
651    }
652
653    if (p) {
654        /* Compose the output array. */
655        i = p->offset;
656        ptr = ensure(item->pAllocator, p, 1);
657        if (!ptr) return 0;
658        *ptr = '[';
659        p->offset++;
660        child = item->child;
661        while (child && !fail) {
662            print_value(child, depth + 1, fmt, p);
663            p->offset = cJSON_update(p);
664            if (child->next) {
665                len = fmt ? 2 : 1;
666                ptr = ensure(item->pAllocator, p, len + 1);
667                if (!ptr) return 0;
668                *ptr++ = ',';
669                if (fmt) *ptr++ = ' ';
670                *ptr = 0;
671                p->offset += len;
672            }
673            child = child->next;
674        }
675        ptr = ensure(item->pAllocator, p, 2);
676        if (!ptr) return 0;
677        *ptr++ = ']';
678        *ptr = 0;
679        out = (p->buffer) + i;
680    } else {
681        /* Allocate an array to hold the values for each */
682        entries = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *));
683        if (!entries) return 0;
684        memset(entries, 0, numentries * sizeof(char *));
685        /* Retrieve all the results: */
686        child = item->child;
687        while (child && !fail) {
688            ret = print_value(child, depth + 1, fmt, 0);
689            entries[i++] = ret;
690            if (ret)
691                len += strlen(ret) + 2 + (fmt ? 1 : 0);
692            else
693                fail = 1;
694            child = child->next;
695        }
696
697        /* If we didn't fail, try to malloc the output string */
698        if (!fail) out = (char *)cJSON_malloc(item->pAllocator, len);
699        /* If that fails, we fail. */
700        if (!out) fail = 1;
701
702        /* Handle failure. */
703        if (fail) {
704            for (j = 0; j < numentries; j++)
705                if (entries[j]) cJSON_Free(item->pAllocator, entries[j]);
706            cJSON_Free(item->pAllocator, entries);
707            return 0;
708        }
709
710        /* Compose the output array. */
711        *out = '[';
712        ptr = out + 1;
713        *ptr = 0;
714        for (j = 0; j < numentries; j++) {
715            tmplen = strlen(entries[j]);
716            memcpy(ptr, entries[j], tmplen);
717            ptr += tmplen;
718            if (j != numentries - 1) {
719                *ptr++ = ',';
720                if (fmt) *ptr++ = ' ';
721                *ptr = 0;
722            }
723            cJSON_Free(item->pAllocator, entries[j]);
724        }
725        cJSON_Free(item->pAllocator, entries);
726        *ptr++ = ']';
727        *ptr++ = 0;
728    }
729    return out;
730}
731
732/* Build an object from the text. */
733static const char *parse_object(cJSON *item, const char *value, bool *out_of_memory) {
734    cJSON *child;
735    if (*value != '{') {
736        // ep = value; // commented out as it is unused
737        return 0;
738    } /* not an object! */
739
740    item->type = cJSON_Object;
741    value = skip(value + 1);
742    if (*value == '}') return value + 1; /* empty array. */
743
744    item->child = child = cJSON_New_Item(item->pAllocator);
745    if (!item->child) {
746        *out_of_memory = true;
747        return 0;
748    }
749    value = skip(parse_string(child, skip(value), out_of_memory));
750    if (!value) return 0;
751    child->string = child->valuestring;
752    child->valuestring = 0;
753    if (*value != ':') {
754        // ep = value; // commented out as it is unused
755        return 0;
756    }                                                                 /* fail! */
757    value = skip(parse_value(child, skip(value + 1), out_of_memory)); /* skip any spacing, get the value. */
758    if (!value) return 0;
759
760    while (*value == ',') {
761        cJSON *new_item;
762        new_item = cJSON_New_Item(item->pAllocator);
763        if (!new_item) {
764            *out_of_memory = true;
765            return 0; /* memory fail */
766        }
767        child->next = new_item;
768        new_item->prev = child;
769        child = new_item;
770        value = skip(parse_string(child, skip(value + 1), out_of_memory));
771        if (!value) return 0;
772        child->string = child->valuestring;
773        child->valuestring = 0;
774        if (*value != ':') {
775            // ep = value; // commented out as it is unused
776            return 0;
777        }                                                                 /* fail! */
778        value = skip(parse_value(child, skip(value + 1), out_of_memory)); /* skip any spacing, get the value. */
779        if (!value) return 0;
780    }
781
782    if (*value == '}') return value + 1; /* end of array */
783    // ep = value; // commented out as it is unused
784    return 0; /* malformed. */
785}
786
787/* Render an object to text. */
788static char *print_object(cJSON *item, int depth, int fmt, printbuffer *p) {
789    char **entries = 0, **names = 0;
790    char *out = 0, *ptr, *ret, *str;
791    int j;
792    cJSON *child = item->child;
793    int numentries = 0, fail = 0, k;
794    size_t tmplen = 0, i = 0, len = 7;
795    /* Count the number of entries. */
796    while (child) numentries++, child = child->next;
797    /* Explicitly handle empty object case */
798    if (!numentries) {
799        if (p)
800            out = ensure(item->pAllocator, p, fmt ? depth + 4 : 3);
801        else
802            out = (char *)cJSON_malloc(item->pAllocator, fmt ? depth + 4 : 3);
803        if (!out) return 0;
804        ptr = out;
805        *ptr++ = '{';
806        if (fmt) {
807            *ptr++ = '\n';
808            for (j = 0; j < depth - 1; j++) *ptr++ = '\t';
809        }
810        *ptr++ = '}';
811        *ptr++ = 0;
812        return out;
813    }
814    if (p) {
815        /* Compose the output: */
816        i = p->offset;
817        len = fmt ? 2 : 1;
818        ptr = ensure(item->pAllocator, p, len + 1);
819        if (!ptr) return 0;
820        *ptr++ = '{';
821        if (fmt) *ptr++ = '\n';
822        *ptr = 0;
823        p->offset += len;
824        child = item->child;
825        depth++;
826        while (child) {
827            if (fmt) {
828                ptr = ensure(item->pAllocator, p, depth);
829                if (!ptr) return 0;
830                for (j = 0; j < depth; j++) *ptr++ = '\t';
831                p->offset += depth;
832            }
833            print_string_ptr(item->pAllocator, child->string, p);
834            p->offset = cJSON_update(p);
835
836            len = fmt ? 2 : 1;
837            ptr = ensure(item->pAllocator, p, len);
838            if (!ptr) return 0;
839            *ptr++ = ':';
840            if (fmt) *ptr++ = '\t';
841            p->offset += len;
842
843            print_value(child, depth, fmt, p);
844            p->offset = cJSON_update(p);
845
846            len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
847            ptr = ensure(item->pAllocator, p, len + 1);
848            if (!ptr) return 0;
849            if (child->next) *ptr++ = ',';
850            if (fmt) *ptr++ = '\n';
851            *ptr = 0;
852            p->offset += len;
853            child = child->next;
854        }
855        ptr = ensure(item->pAllocator, p, fmt ? (depth + 1) : 2);
856        if (!ptr) return 0;
857        if (fmt)
858            for (j = 0; j < depth - 1; j++) *ptr++ = '\t';
859        *ptr++ = '}';
860        *ptr = 0;
861        out = (p->buffer) + i;
862    } else {
863        /* Allocate space for the names and the objects */
864        entries = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *));
865        if (!entries) return 0;
866        names = (char **)cJSON_malloc(item->pAllocator, numentries * sizeof(char *));
867        if (!names) {
868            cJSON_Free(item->pAllocator, entries);
869            return 0;
870        }
871        memset(entries, 0, sizeof(char *) * numentries);
872        memset(names, 0, sizeof(char *) * numentries);
873
874        /* Collect all the results into our arrays: */
875        child = item->child;
876        depth++;
877        if (fmt) len += depth;
878        while (child) {
879            names[i] = str = print_string_ptr(item->pAllocator, child->string, 0);
880            entries[i++] = ret = print_value(child, depth, fmt, 0);
881            if (str && ret)
882                len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0);
883            else
884                fail = 1;
885            child = child->next;
886        }
887
888        /* Try to allocate the output string */
889        if (!fail) out = (char *)cJSON_malloc(item->pAllocator, len);
890        if (!out) fail = 1;
891
892        /* Handle failure */
893        if (fail) {
894            for (j = 0; j < numentries; j++) {
895                if (names[i]) cJSON_Free(item->pAllocator, names[j]);
896                if (entries[j]) cJSON_Free(item->pAllocator, entries[j]);
897            }
898            cJSON_Free(item->pAllocator, names);
899            cJSON_Free(item->pAllocator, entries);
900            return 0;
901        }
902
903        /* Compose the output: */
904        *out = '{';
905        ptr = out + 1;
906        if (fmt) *ptr++ = '\n';
907        *ptr = 0;
908        for (j = 0; j < numentries; j++) {
909            if (fmt)
910                for (k = 0; k < depth; k++) *ptr++ = '\t';
911            tmplen = strlen(names[j]);
912            memcpy(ptr, names[j], tmplen);
913            ptr += tmplen;
914            *ptr++ = ':';
915            if (fmt) *ptr++ = '\t';
916            size_t entries_size = strlen(entries[j]);
917            loader_strncpy(ptr, len - (ptr - out), entries[j], entries_size);
918            ptr += entries_size;
919            if (j != numentries - 1) *ptr++ = ',';
920            if (fmt) *ptr++ = '\n';
921            *ptr = 0;
922            cJSON_Free(item->pAllocator, names[j]);
923            cJSON_Free(item->pAllocator, entries[j]);
924        }
925
926        cJSON_Free(item->pAllocator, names);
927        cJSON_Free(item->pAllocator, entries);
928        if (fmt)
929            for (j = 0; j < depth - 1; j++) *ptr++ = '\t';
930        *ptr++ = '}';
931        *ptr++ = 0;
932    }
933    return out;
934}
935
936/* Get Array size/item / object item. */
937int loader_cJSON_GetArraySize(cJSON *array) {
938    cJSON *c = array->child;
939    int i = 0;
940    while (c) i++, c = c->next;
941    return i;
942}
943cJSON *loader_cJSON_GetArrayItem(cJSON *array, int item) {
944    cJSON *c = array->child;
945    while (c && item > 0) item--, c = c->next;
946    return c;
947}
948cJSON *loader_cJSON_GetObjectItem(cJSON *object, const char *string) {
949    cJSON *c = object->child;
950    while (c && strcmp(c->string, string)) c = c->next;
951    return c;
952}
953
954VkResult loader_get_json(const struct loader_instance *inst, const char *filename, cJSON **json) {
955    FILE *file = NULL;
956    char *json_buf = NULL;
957    size_t len;
958    VkResult res = VK_SUCCESS;
959
960    assert(json != NULL);
961
962    *json = NULL;
963
964#if defined(_WIN32)
965    int filename_utf16_size = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
966    if (filename_utf16_size > 0) {
967        wchar_t *filename_utf16 = (wchar_t *)loader_stack_alloc(filename_utf16_size * sizeof(wchar_t));
968        if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filename_utf16, filename_utf16_size) == filename_utf16_size) {
969            errno_t wfopen_error = _wfopen_s(&file, filename_utf16, L"rb");
970            if (0 != wfopen_error) {
971                loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename);
972            }
973        }
974    }
975#elif COMMON_UNIX_PLATFORMS
976    file = fopen(filename, "rb");
977#else
978#warning fopen not available on this platform
979#endif
980
981    if (!file) {
982        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to open JSON file %s", filename);
983        res = VK_ERROR_INITIALIZATION_FAILED;
984        goto out;
985    }
986    // NOTE: We can't just use fseek(file, 0, SEEK_END) because that isn't guaranteed to be supported on all systems
987    size_t fread_ret_count = 0;
988    do {
989        char buffer[256];
990        fread_ret_count = fread(buffer, 1, 256, file);
991    } while (fread_ret_count == 256 && !feof(file));
992    len = ftell(file);
993    fseek(file, 0, SEEK_SET);
994    json_buf = (char *)loader_instance_heap_calloc(inst, len + 1, VK_SYSTEM_ALLOCATION_SCOPE_COMMAND);
995    if (json_buf == NULL) {
996        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0,
997                   "loader_get_json: Failed to allocate space for JSON file %s buffer of length %lu", filename, len);
998        res = VK_ERROR_OUT_OF_HOST_MEMORY;
999        goto out;
1000    }
1001    if (fread(json_buf, sizeof(char), len, file) != len) {
1002        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Failed to read JSON file %s.", filename);
1003        res = VK_ERROR_INITIALIZATION_FAILED;
1004        goto out;
1005    }
1006    json_buf[len] = '\0';
1007
1008    // Can't be a valid json if the string is of length zero
1009    if (len == 0) {
1010        res = VK_ERROR_INITIALIZATION_FAILED;
1011        goto out;
1012    }
1013    // Parse text from file
1014    bool out_of_memory = false;
1015    *json = cJSON_Parse(inst ? &inst->alloc_callbacks : NULL, json_buf, &out_of_memory);
1016    if (out_of_memory) {
1017        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Out of Memory error occurred while parsing JSON file %s.",
1018                   filename);
1019        res = VK_ERROR_OUT_OF_HOST_MEMORY;
1020        goto out;
1021    } else if (*json == NULL) {
1022        loader_log(inst, VULKAN_LOADER_ERROR_BIT, 0, "loader_get_json: Invalid JSON file %s.", filename);
1023        goto out;
1024    }
1025
1026out:
1027    loader_instance_heap_free(inst, json_buf);
1028    if (NULL != file) {
1029        fclose(file);
1030    }
1031    if (res != VK_SUCCESS && *json != NULL) {
1032        loader_cJSON_Delete(*json);
1033        *json = NULL;
1034    }
1035
1036    return res;
1037}
1038
1039VkResult loader_parse_json_string_to_existing_str(const struct loader_instance *inst, cJSON *object, const char *key,
1040                                                  size_t out_str_len, char *out_string) {
1041    cJSON *item = loader_cJSON_GetObjectItem(object, key);
1042    if (NULL == item) {
1043        return VK_ERROR_INITIALIZATION_FAILED;
1044    }
1045
1046    char *str = loader_cJSON_Print(item);
1047    if (str == NULL) {
1048        return VK_ERROR_OUT_OF_HOST_MEMORY;
1049    }
1050    if (NULL != out_string) {
1051        loader_strncpy(out_string, out_str_len, str, out_str_len);
1052        if (out_str_len > 0) {
1053            out_string[out_str_len - 1] = '\0';
1054        }
1055    }
1056    loader_instance_heap_free(inst, str);
1057    return VK_SUCCESS;
1058}
1059
1060VkResult loader_parse_json_string(cJSON *object, const char *key, char **out_string) {
1061    cJSON *item = loader_cJSON_GetObjectItem(object, key);
1062    if (NULL == item) {
1063        return VK_ERROR_INITIALIZATION_FAILED;
1064    }
1065
1066    char *str = loader_cJSON_Print(item);
1067    if (str == NULL) {
1068        return VK_ERROR_OUT_OF_HOST_MEMORY;
1069    }
1070    if (NULL != out_string) {
1071        *out_string = str;
1072    }
1073    return VK_SUCCESS;
1074}
1075VkResult loader_parse_json_array_of_strings(const struct loader_instance *inst, cJSON *object, const char *key,
1076                                            struct loader_string_list *string_list) {
1077    VkResult res = VK_SUCCESS;
1078    cJSON *item = loader_cJSON_GetObjectItem(object, key);
1079    if (NULL == item) {
1080        return VK_ERROR_INITIALIZATION_FAILED;
1081    }
1082
1083    uint32_t count = loader_cJSON_GetArraySize(item);
1084    if (count == 0) {
1085        return VK_SUCCESS;
1086    }
1087
1088    res = create_string_list(inst, count, string_list);
1089    if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
1090        goto out;
1091    }
1092    for (uint32_t i = 0; i < count; i++) {
1093        cJSON *element = loader_cJSON_GetArrayItem(item, i);
1094        if (element == NULL) {
1095            return VK_ERROR_INITIALIZATION_FAILED;
1096        }
1097        char *out_data = loader_cJSON_Print(element);
1098        if (out_data == NULL) {
1099            res = VK_ERROR_OUT_OF_HOST_MEMORY;
1100            goto out;
1101        }
1102        res = append_str_to_string_list(inst, string_list, out_data);
1103        if (VK_ERROR_OUT_OF_HOST_MEMORY == res) {
1104            goto out;
1105        }
1106    }
1107out:
1108    if (res == VK_ERROR_OUT_OF_HOST_MEMORY && NULL != string_list->list) {
1109        free_string_list(inst, string_list);
1110    }
1111
1112    return res;
1113}
1114