1 /***********************************************************************************************************************************
2 Convert JSON to/from KeyValue
3 ***********************************************************************************************************************************/
4 #include "build.auto.h"
5 
6 #include <ctype.h>
7 #include <string.h>
8 
9 #include "common/debug.h"
10 #include "common/log.h"
11 #include "common/type/json.h"
12 
13 /***********************************************************************************************************************************
14 Prototypes
15 ***********************************************************************************************************************************/
16 static Variant *jsonToVarInternal(const char *json, unsigned int *jsonPos);
17 
18 /***********************************************************************************************************************************
19 Consume whitespace
20 ***********************************************************************************************************************************/
21 static void
jsonConsumeWhiteSpace(const char * json,unsigned int * jsonPos)22 jsonConsumeWhiteSpace(const char *json, unsigned int *jsonPos)
23 {
24     FUNCTION_TEST_BEGIN();
25         FUNCTION_TEST_PARAM(STRINGZ, json);
26         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
27     FUNCTION_TEST_END();
28 
29     // Consume whitespace
30     while (json[*jsonPos] == ' ' || json[*jsonPos] == '\t' || json[*jsonPos] == '\n'  || json[*jsonPos] == '\r')
31         (*jsonPos)++;
32 
33     FUNCTION_TEST_RETURN_VOID();
34 }
35 
36 /**********************************************************************************************************************************/
37 static bool
jsonToBoolInternal(const char * json,unsigned int * jsonPos)38 jsonToBoolInternal(const char *json, unsigned int *jsonPos)
39 {
40     FUNCTION_TEST_BEGIN();
41         FUNCTION_TEST_PARAM(STRINGZ, json);
42         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
43     FUNCTION_TEST_END();
44 
45     bool result;
46 
47     if (strncmp(json + *jsonPos, TRUE_Z, 4) == 0)
48     {
49         result = true;
50         *jsonPos += 4;
51     }
52     else if (strncmp(json + *jsonPos, FALSE_Z, 5) == 0)
53     {
54         result = false;
55         *jsonPos += 5;
56     }
57     else
58         THROW_FMT(JsonFormatError, "expected boolean at '%s'", json + *jsonPos);
59 
60     FUNCTION_TEST_RETURN(result);
61 }
62 
63 bool
jsonToBool(const String * json)64 jsonToBool(const String *json)
65 {
66     FUNCTION_TEST_BEGIN();
67         FUNCTION_TEST_PARAM(STRING, json);
68     FUNCTION_TEST_END();
69 
70     unsigned int jsonPos = 0;
71     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
72 
73     bool result = jsonToBoolInternal(strZ(json), &jsonPos);
74 
75     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
76 
77     if (jsonPos != strSize(json))
78         THROW_FMT(JsonFormatError, "unexpected characters after boolean at '%s'", strZ(json) + jsonPos);
79 
80     FUNCTION_TEST_RETURN(result);
81 }
82 
83 /**********************************************************************************************************************************/
84 static Variant *
jsonToNumberInternal(const char * json,unsigned int * jsonPos)85 jsonToNumberInternal(const char *json, unsigned int *jsonPos)
86 {
87     FUNCTION_TEST_BEGIN();
88         FUNCTION_TEST_PARAM(STRINGZ, json);
89         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
90     FUNCTION_TEST_END();
91 
92     Variant *result = NULL;
93     unsigned int beginPos = *jsonPos;
94     bool intSigned = false;
95 
96     // Consume the -
97     if (json[*jsonPos] == '-')
98     {
99         (*jsonPos)++;
100         intSigned = true;
101     }
102 
103     // Consume all digits
104     while (isdigit(json[*jsonPos]))
105         (*jsonPos)++;
106 
107     // Invalid if only a - was found
108     if (json[*jsonPos - 1] == '-')
109         THROW_FMT(JsonFormatError, "found '-' with no integer at '%s'", json + beginPos);
110 
111     MEM_CONTEXT_TEMP_BEGIN()
112     {
113         // Extract the numeric as a string
114         String *resultStr = strNewN(json + beginPos, *jsonPos - beginPos);
115 
116         // Convert the string to a integer variant
117         MEM_CONTEXT_PRIOR_BEGIN()
118         {
119             if (intSigned)
120                 result = varNewInt64(cvtZToInt64(strZ(resultStr)));
121             else
122                 result = varNewUInt64(cvtZToUInt64(strZ(resultStr)));
123         }
124         MEM_CONTEXT_PRIOR_END();
125     }
126     MEM_CONTEXT_TEMP_END();
127 
128     FUNCTION_TEST_RETURN(result);
129 }
130 
131 static Variant *
jsonToNumber(const String * json)132 jsonToNumber(const String *json)
133 {
134     FUNCTION_TEST_BEGIN();
135         FUNCTION_TEST_PARAM(STRING, json);
136     FUNCTION_TEST_END();
137 
138     unsigned int jsonPos = 0;
139     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
140 
141     Variant *result = jsonToNumberInternal(strZ(json), &jsonPos);
142 
143     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
144 
145     if (jsonPos != strSize(json))
146         THROW_FMT(JsonFormatError, "unexpected characters after number at '%s'", strZ(json) + jsonPos);
147 
148     FUNCTION_TEST_RETURN(result);
149 }
150 
151 int
jsonToInt(const String * json)152 jsonToInt(const String *json)
153 {
154     FUNCTION_TEST_BEGIN();
155         FUNCTION_TEST_PARAM(STRING, json);
156     FUNCTION_TEST_END();
157 
158     int result = 0;
159 
160     MEM_CONTEXT_TEMP_BEGIN()
161     {
162         result = varIntForce(jsonToNumber(json));
163     }
164     MEM_CONTEXT_TEMP_END();
165 
166     FUNCTION_TEST_RETURN(result);
167 }
168 
169 int64_t
jsonToInt64(const String * json)170 jsonToInt64(const String *json)
171 {
172     FUNCTION_TEST_BEGIN();
173         FUNCTION_TEST_PARAM(STRING, json);
174     FUNCTION_TEST_END();
175 
176     int64_t result = 0;
177 
178     MEM_CONTEXT_TEMP_BEGIN()
179     {
180         result = varInt64Force(jsonToNumber(json));
181     }
182     MEM_CONTEXT_TEMP_END();
183 
184     FUNCTION_TEST_RETURN(result);
185 }
186 
187 unsigned int
jsonToUInt(const String * json)188 jsonToUInt(const String *json)
189 {
190     FUNCTION_TEST_BEGIN();
191         FUNCTION_TEST_PARAM(STRING, json);
192     FUNCTION_TEST_END();
193 
194     unsigned int result = 0;
195 
196     MEM_CONTEXT_TEMP_BEGIN()
197     {
198         result = varUIntForce(jsonToNumber(json));
199     }
200     MEM_CONTEXT_TEMP_END();
201 
202     FUNCTION_TEST_RETURN(result);
203 }
204 
205 uint64_t
jsonToUInt64(const String * json)206 jsonToUInt64(const String *json)
207 {
208     FUNCTION_TEST_BEGIN();
209         FUNCTION_TEST_PARAM(STRING, json);
210     FUNCTION_TEST_END();
211 
212     uint64_t result = 0;
213 
214     MEM_CONTEXT_TEMP_BEGIN()
215     {
216         result = varUInt64Force(jsonToNumber(json));
217     }
218     MEM_CONTEXT_TEMP_END();
219 
220     FUNCTION_TEST_RETURN(result);
221 }
222 
223 /**********************************************************************************************************************************/
224 static String *
jsonToStrInternal(const char * json,unsigned int * jsonPos)225 jsonToStrInternal(const char *json, unsigned int *jsonPos)
226 {
227     FUNCTION_TEST_BEGIN();
228         FUNCTION_TEST_PARAM(STRINGZ, json);
229         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
230     FUNCTION_TEST_END();
231 
232     String *result = strNew();
233 
234     if (json[*jsonPos] != '"')
235         THROW_FMT(JsonFormatError, "expected '\"' at '%s'", json + *jsonPos);
236 
237     MEM_CONTEXT_TEMP_BEGIN()
238     {
239         // Skip the beginning "
240         (*jsonPos)++;
241 
242         // Track portion of string with no escapes
243         const char *noEscape = NULL;
244         size_t noEscapeSize = 0;
245 
246         while (json[*jsonPos] != '"')
247         {
248             if (json[*jsonPos] == '\\')
249             {
250                 // Copy portion of string without escapes
251                 if (noEscapeSize > 0)
252                 {
253                     strCatZN(result, noEscape, noEscapeSize);
254                     noEscapeSize = 0;
255                 }
256 
257                 (*jsonPos)++;
258 
259                 switch (json[*jsonPos])
260                 {
261                     case '"':
262                         strCatChr(result, '"');
263                         break;
264 
265                     case '\\':
266                         strCatChr(result, '\\');
267                         break;
268 
269                     case '/':
270                         strCatChr(result, '/');
271                         break;
272 
273                     case 'n':
274                         strCatChr(result, '\n');
275                         break;
276 
277                     case 'r':
278                         strCatChr(result, '\r');
279                         break;
280 
281                     case 't':
282                         strCatChr(result, '\t');
283                         break;
284 
285                     case 'b':
286                         strCatChr(result, '\b');
287                         break;
288 
289                     case 'f':
290                         strCatChr(result, '\f');
291                         break;
292 
293                     case 'u':
294                     {
295                         (*jsonPos)++;
296 
297                         // We don't know how to decode anything except ASCII so fail if it looks like Unicode
298                         if (strncmp(json + *jsonPos, "00", 2) != 0)
299                             THROW_FMT(JsonFormatError, "unable to decode '%.4s'", json + *jsonPos);
300 
301                         // Decode char
302                         (*jsonPos) += 2;
303                         strCatChr(result, (char)cvtZToUIntBase(strZ(strNewN(json + *jsonPos, 2)), 16));
304                         (*jsonPos) += 1;
305 
306                         break;
307                     }
308 
309                     default:
310                         THROW_FMT(JsonFormatError, "invalid escape character '%c'", json[*jsonPos]);
311                 }
312             }
313             else
314             {
315                 if (json[*jsonPos] == '\0')
316                     THROW(JsonFormatError, "expected '\"' but found null delimiter");
317 
318                 // If escape string is zero size then start it
319                 if (noEscapeSize == 0)
320                     noEscape = json + *jsonPos;
321 
322                 noEscapeSize++;
323             }
324 
325             (*jsonPos)++;;
326         };
327 
328         // Copy portion of string without escapes
329         if (noEscapeSize > 0)
330             strCatZN(result, noEscape, noEscapeSize);
331 
332         // Advance the character array pointer to the next element after the string
333         (*jsonPos)++;;
334     }
335     MEM_CONTEXT_TEMP_END();
336 
337     FUNCTION_TEST_RETURN(result);
338 }
339 
340 String *
jsonToStr(const String * json)341 jsonToStr(const String *json)
342 {
343     FUNCTION_TEST_BEGIN();
344         FUNCTION_TEST_PARAM(STRING, json);
345     FUNCTION_TEST_END();
346 
347     unsigned int jsonPos = 0;
348     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
349 
350     String *result = NULL;
351 
352     if (strncmp(strZ(json), NULL_Z, 4) == 0)
353         jsonPos += 4;
354     else
355         result = jsonToStrInternal(strZ(json), &jsonPos);
356 
357     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
358 
359     if (jsonPos != strSize(json))
360         THROW_FMT(JsonFormatError, "unexpected characters after string at '%s'", strZ(json) + jsonPos);
361 
362     FUNCTION_TEST_RETURN(result);
363 }
364 
365 /**********************************************************************************************************************************/
366 static KeyValue *
jsonToKvInternal(const char * json,unsigned int * jsonPos)367 jsonToKvInternal(const char *json, unsigned int *jsonPos)
368 {
369     FUNCTION_TEST_BEGIN();
370         FUNCTION_TEST_PARAM(STRINGZ, json);
371         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
372     FUNCTION_TEST_END();
373 
374     KeyValue *result = kvNew();
375 
376     MEM_CONTEXT_TEMP_BEGIN()
377     {
378         // Move position to the first key/value in the object
379         (*jsonPos)++;
380         jsonConsumeWhiteSpace(json, jsonPos);
381 
382         // Only proceed if the array is not empty
383         if (json[*jsonPos] != '}')
384         {
385             do
386             {
387                 if (json[*jsonPos] == ',')
388                 {
389                     (*jsonPos)++;
390                     jsonConsumeWhiteSpace(json, jsonPos);
391                 }
392 
393                 Variant *key = varNewStr(jsonToStrInternal(json, jsonPos));
394 
395                 jsonConsumeWhiteSpace(json, jsonPos);
396 
397                 if (json[*jsonPos] != ':')
398                     THROW_FMT(JsonFormatError, "expected ':' at '%s'", json + *jsonPos);
399                 (*jsonPos)++;
400 
401                 jsonConsumeWhiteSpace(json, jsonPos);
402 
403                 kvPut(result, key, jsonToVarInternal(json, jsonPos));
404             }
405             while (json[*jsonPos] == ',');
406         }
407 
408         if (json[*jsonPos] != '}')
409             THROW_FMT(JsonFormatError, "expected '}' at '%s'", json + *jsonPos);
410         (*jsonPos)++;
411     }
412     MEM_CONTEXT_TEMP_END();
413 
414     FUNCTION_TEST_RETURN(result);
415 }
416 
417 KeyValue *
jsonToKv(const String * json)418 jsonToKv(const String *json)
419 {
420     FUNCTION_TEST_BEGIN();
421         FUNCTION_TEST_PARAM(STRING, json);
422     FUNCTION_TEST_END();
423 
424     unsigned int jsonPos = 0;
425     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
426 
427     if (strZ(json)[jsonPos] != '{')
428         THROW_FMT(JsonFormatError, "expected '{' at '%s'", strZ(json) + jsonPos);
429 
430     KeyValue *result = jsonToKvInternal(strZ(json), &jsonPos);
431 
432     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
433 
434     if (jsonPos != strSize(json))
435         THROW_FMT(JsonFormatError, "unexpected characters after object at '%s'", strZ(json) + jsonPos);
436 
437     FUNCTION_TEST_RETURN(result);
438 }
439 
440 /**********************************************************************************************************************************/
441 static VariantList *
jsonToVarLstInternal(const char * json,unsigned int * jsonPos)442 jsonToVarLstInternal(const char *json, unsigned int *jsonPos)
443 {
444     FUNCTION_TEST_BEGIN();
445         FUNCTION_TEST_PARAM(STRINGZ, json);
446         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
447     FUNCTION_TEST_END();
448 
449     VariantList *result = varLstNew();
450 
451     if (json[*jsonPos] != '[')
452         THROW_FMT(JsonFormatError, "expected '[' at '%s'", json + *jsonPos);
453 
454     MEM_CONTEXT_TEMP_BEGIN()
455     {
456         // Move position to the first element in the array
457         (*jsonPos)++;
458         jsonConsumeWhiteSpace(json, jsonPos);
459 
460         // Only proceed if the array is not empty
461         if (json[*jsonPos] != ']')
462         {
463             do
464             {
465                 if (json[*jsonPos] == ',')
466                 {
467                     (*jsonPos)++;
468                     jsonConsumeWhiteSpace(json, jsonPos);
469                 }
470 
471                 MEM_CONTEXT_PRIOR_BEGIN()
472                 {
473                     varLstAdd(result, jsonToVarInternal(json, jsonPos));
474                 }
475                 MEM_CONTEXT_PRIOR_END();
476 
477                 jsonConsumeWhiteSpace(json, jsonPos);
478             }
479             while (json[*jsonPos] == ',');
480         }
481 
482         if (json[*jsonPos] != ']')
483             THROW_FMT(JsonFormatError, "expected ']' at '%s'", json + *jsonPos);
484 
485         (*jsonPos)++;
486     }
487     MEM_CONTEXT_TEMP_END();
488 
489     FUNCTION_TEST_RETURN(result);
490 }
491 
492 VariantList *
jsonToVarLst(const String * json)493 jsonToVarLst(const String *json)
494 {
495     FUNCTION_TEST_BEGIN();
496         FUNCTION_TEST_PARAM(STRING, json);
497     FUNCTION_TEST_END();
498 
499     unsigned int jsonPos = 0;
500     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
501 
502     VariantList *result = jsonToVarLstInternal(strZ(json), &jsonPos);
503 
504     jsonConsumeWhiteSpace(strZ(json), &jsonPos);
505 
506     if (jsonPos != strSize(json))
507         THROW_FMT(JsonFormatError, "unexpected characters after array at '%s'", strZ(json) + jsonPos);
508 
509     FUNCTION_TEST_RETURN(result);
510 }
511 
512 /**********************************************************************************************************************************/
513 static Variant *
jsonToVarInternal(const char * json,unsigned int * jsonPos)514 jsonToVarInternal(const char *json, unsigned int *jsonPos)
515 {
516     FUNCTION_TEST_BEGIN();
517         FUNCTION_TEST_PARAM(STRINGZ, json);
518         FUNCTION_TEST_PARAM_P(UINT, jsonPos);
519     FUNCTION_TEST_END();
520 
521     Variant *result = NULL;
522 
523     jsonConsumeWhiteSpace(json, jsonPos);
524 
525     // There should be some data
526     if (json[*jsonPos] == '\0')
527         THROW(JsonFormatError, "expected data");
528 
529     // Determine data type
530     switch (json[*jsonPos])
531     {
532         // String
533         case '"':
534             result = varNewStr(jsonToStrInternal(json, jsonPos));
535             break;
536 
537         // Integer
538         case '-':
539         case '0' ... '9':
540             result = jsonToNumberInternal(json, jsonPos);
541             break;
542 
543         // Boolean
544         case 't':
545         case 'f':
546             result = varNewBool(jsonToBoolInternal(json, jsonPos));
547             break;
548 
549         // Null
550         case 'n':
551         {
552             if (strncmp(json + *jsonPos, NULL_Z, 4) == 0)
553                 *jsonPos += 4;
554             else
555                 THROW_FMT(JsonFormatError, "expected null at '%s'", json + *jsonPos);
556 
557             break;
558         }
559 
560         // Array
561         case '[':
562             result = varNewVarLst(jsonToVarLstInternal(json, jsonPos));
563             break;
564 
565         // Object
566         case '{':
567             result = varNewKv(jsonToKvInternal(json, jsonPos));
568             break;
569 
570         // Object
571         default:
572             THROW_FMT(JsonFormatError, "invalid type at '%s'", json + *jsonPos);
573             break;
574     }
575 
576     jsonConsumeWhiteSpace(json, jsonPos);
577 
578     FUNCTION_TEST_RETURN(result);
579 }
580 
581 Variant *
jsonToVar(const String * json)582 jsonToVar(const String *json)
583 {
584     FUNCTION_LOG_BEGIN(logLevelTrace);
585         FUNCTION_LOG_PARAM(STRING, json);
586     FUNCTION_LOG_END();
587 
588     const char *jsonPtr = strZ(json);
589     unsigned int jsonPos = 0;
590 
591     Variant *result = jsonToVarInternal(jsonPtr, &jsonPos);
592 
593     if (jsonPos != strSize(json))
594         THROW_FMT(JsonFormatError, "unexpected characters after JSON at '%s'", strZ(json) + jsonPos);
595 
596     FUNCTION_LOG_RETURN(VARIANT, result);
597 }
598 
599 /**********************************************************************************************************************************/
600 const String *
jsonFromBool(bool value)601 jsonFromBool(bool value)
602 {
603     FUNCTION_TEST_BEGIN();
604         FUNCTION_TEST_PARAM(BOOL, value);
605     FUNCTION_TEST_END();
606 
607     FUNCTION_TEST_RETURN(value ? TRUE_STR : FALSE_STR);
608 }
609 
610 /**********************************************************************************************************************************/
611 String *
jsonFromInt(int number)612 jsonFromInt(int number)
613 {
614     FUNCTION_TEST_BEGIN();
615         FUNCTION_TEST_PARAM(INT, number);
616     FUNCTION_TEST_END();
617 
618     char working[CVT_BASE10_BUFFER_SIZE];
619     cvtIntToZ(number, working, sizeof(working));
620 
621     FUNCTION_TEST_RETURN(strNewZ(working));
622 }
623 
624 String *
jsonFromInt64(int64_t number)625 jsonFromInt64(int64_t number)
626 {
627     FUNCTION_TEST_BEGIN();
628         FUNCTION_TEST_PARAM(INT64, number);
629     FUNCTION_TEST_END();
630 
631     char working[CVT_BASE10_BUFFER_SIZE];
632     cvtInt64ToZ(number, working, sizeof(working));
633 
634     FUNCTION_TEST_RETURN(strNewZ(working));
635 }
636 
637 String *
jsonFromUInt(unsigned int number)638 jsonFromUInt(unsigned int number)
639 {
640     FUNCTION_TEST_BEGIN();
641         FUNCTION_TEST_PARAM(UINT, number);
642     FUNCTION_TEST_END();
643 
644     char working[CVT_BASE10_BUFFER_SIZE];
645     cvtUIntToZ(number, working, sizeof(working));
646 
647     FUNCTION_TEST_RETURN(strNewZ(working));
648 }
649 
650 String *
jsonFromUInt64(uint64_t number)651 jsonFromUInt64(uint64_t number)
652 {
653     FUNCTION_TEST_BEGIN();
654         FUNCTION_TEST_PARAM(UINT64, number);
655     FUNCTION_TEST_END();
656 
657     char working[CVT_BASE10_BUFFER_SIZE];
658     cvtUInt64ToZ(number, working, sizeof(working));
659 
660     FUNCTION_TEST_RETURN(strNewZ(working));
661 }
662 
663 /**********************************************************************************************************************************/
664 static void
jsonFromStrInternal(String * json,const String * string)665 jsonFromStrInternal(String *json, const String *string)
666 {
667     FUNCTION_TEST_BEGIN();
668         FUNCTION_TEST_PARAM(STRING, json);
669         FUNCTION_TEST_PARAM(STRING, string);
670     FUNCTION_TEST_END();
671 
672     ASSERT(json != NULL);
673 
674     // If string is null
675     if (string == NULL)
676     {
677         strCat(json, NULL_STR);
678     }
679     // Else escape and output string
680     else
681     {
682         strCatChr(json, '"');
683 
684         // Track portion of string with no escapes
685         const char *noEscape = NULL;
686         size_t noEscapeSize = 0;
687 
688         for (unsigned int stringIdx = 0; stringIdx < strSize(string); stringIdx++)
689         {
690             char stringChr = strZ(string)[stringIdx];
691 
692             switch (stringChr)
693             {
694                 case '"':
695                 case '\\':
696                 case '\n':
697                 case '\r':
698                 case '\t':
699                 case '\b':
700                 case '\f':
701                 {
702                     // Copy portion of string without escapes
703                     if (noEscapeSize > 0)
704                     {
705                         strCatZN(json, noEscape, noEscapeSize);
706                         noEscapeSize = 0;
707                     }
708 
709                     switch (stringChr)
710                     {
711                         case '"':
712                             strCatZ(json, "\\\"");
713                             break;
714 
715                         case '\\':
716                             strCatZ(json, "\\\\");
717                             break;
718 
719                         case '\n':
720                             strCatZ(json, "\\n");
721                             break;
722 
723                         case '\r':
724                             strCatZ(json, "\\r");
725                             break;
726 
727                         case '\t':
728                             strCatZ(json, "\\t");
729                             break;
730 
731                         case '\b':
732                             strCatZ(json, "\\b");
733                             break;
734 
735                         case '\f':
736                             strCatZ(json, "\\f");
737                             break;
738                     }
739 
740                     break;
741                 }
742 
743                 default:
744                 {
745                     // If escape string is zero size then start it
746                     if (noEscapeSize == 0)
747                         noEscape = strZ(string) + stringIdx;
748 
749                     noEscapeSize++;
750                     break;
751                 }
752             }
753         }
754 
755         // Copy portion of string without escapes
756         if (noEscapeSize > 0)
757             strCatZN(json, noEscape, noEscapeSize);
758 
759         strCatChr(json, '"');
760     }
761 
762     FUNCTION_TEST_RETURN_VOID();
763 }
764 
765 String *
jsonFromStr(const String * string)766 jsonFromStr(const String *string)
767 {
768     FUNCTION_TEST_BEGIN();
769         FUNCTION_TEST_PARAM(STRING, string);
770     FUNCTION_TEST_END();
771 
772     String *json = strNew();
773     jsonFromStrInternal(json, string);
774 
775     FUNCTION_TEST_RETURN(json);
776 }
777 
778 /***********************************************************************************************************************************
779 Internal recursive function to walk a KeyValue and return a json string
780 ***********************************************************************************************************************************/
781 static String *
jsonFromKvInternal(const KeyValue * kv)782 jsonFromKvInternal(const KeyValue *kv)
783 {
784     FUNCTION_TEST_BEGIN();
785         FUNCTION_TEST_PARAM(KEY_VALUE, kv);
786     FUNCTION_TEST_END();
787 
788     ASSERT(kv != NULL);
789 
790     String *result = strNewZ("{");
791 
792     MEM_CONTEXT_TEMP_BEGIN()
793     {
794         const StringList *keyList = strLstSort(strLstNewVarLst(kvKeyList(kv)), sortOrderAsc);
795 
796         for (unsigned int keyIdx = 0; keyIdx < strLstSize(keyList); keyIdx++)
797         {
798             String *key = strLstGet(keyList, keyIdx);
799             const Variant *value = kvGet(kv, VARSTR(key));
800 
801             // If going to add another key, prepend a comma
802             if (keyIdx > 0)
803                 strCatZ(result, ",");
804 
805             // Keys are always strings in the output, so add starting quote and colon.
806             strCatFmt(result, "\"%s\":", strZ(key));
807 
808             // NULL value
809             if (value == NULL)
810                 strCat(result, NULL_STR);
811             else
812             {
813                 switch (varType(value))
814                 {
815                     case varTypeKeyValue:
816                         strCat(result, jsonFromKvInternal(kvDup(varKv(value))));
817                         break;
818 
819                     case varTypeVariantList:
820                     {
821                         // If the array is empty, then do not add formatting, else process the array.
822                         if (varVarLst(value) == NULL)
823                             strCat(result, NULL_STR);
824                         else if (varLstEmpty(varVarLst(value)))
825                             strCatZ(result, "[]");
826                         else
827                         {
828                             strCatZ(result, "[");
829 
830                             for (unsigned int arrayIdx = 0; arrayIdx < varLstSize(varVarLst(value)); arrayIdx++)
831                             {
832                                 Variant *arrayValue = varLstGet(varVarLst(value), arrayIdx);
833 
834                                 // If going to add another element, add a comma
835                                 if (arrayIdx > 0)
836                                     strCatZ(result, ",");
837 
838                                 // If array value is null
839                                 if (arrayValue == NULL)
840                                 {
841                                     strCat(result, NULL_STR);
842                                 }
843                                 // If the type is a string, add leading and trailing double quotes
844                                 else if (varType(arrayValue) == varTypeString)
845                                 {
846                                     jsonFromStrInternal(result, varStr(arrayValue));
847                                 }
848                                 else if (varType(arrayValue) == varTypeKeyValue)
849                                 {
850                                     strCat(result, jsonFromKvInternal(kvDup(varKv(arrayValue))));
851                                 }
852                                 else if (varType(arrayValue) == varTypeVariantList)
853                                 {
854                                     strCat(result, jsonFromVar(arrayValue));
855                                 }
856                                 // Numeric, Boolean or other type
857                                 else
858                                     strCat(result, varStrForce(arrayValue));
859                             }
860 
861                             strCatZ(result, "]");
862                         }
863 
864                         break;
865                     }
866 
867                     // String
868                     case varTypeString:
869                         jsonFromStrInternal(result, varStr(value));
870                         break;
871 
872                     default:
873                         strCat(result, varStrForce(value));
874                         break;
875                 }
876             }
877         }
878 
879         result = strCatZ(result, "}");
880     }
881     MEM_CONTEXT_TEMP_END();
882 
883     FUNCTION_TEST_RETURN(result);
884 }
885 
886 /***********************************************************************************************************************************
887 Currently this function is only intended to convert the limited types that are included in info files.  More types will be added as
888 needed.  Since this function is only intended to read internally-generated JSON it is assumed to be well-formed with no extraneous
889 whitespace.
890 ***********************************************************************************************************************************/
891 String *
jsonFromKv(const KeyValue * kv)892 jsonFromKv(const KeyValue *kv)
893 {
894     FUNCTION_LOG_BEGIN(logLevelTrace);
895         FUNCTION_LOG_PARAM(KEY_VALUE, kv);
896     FUNCTION_LOG_END();
897 
898     ASSERT(kv != NULL);
899 
900     String *result = NULL;
901 
902     MEM_CONTEXT_TEMP_BEGIN()
903     {
904         String *jsonStr = jsonFromKvInternal(kv);
905 
906         // Duplicate the string into the prior context
907         MEM_CONTEXT_PRIOR_BEGIN()
908         {
909             result = strDup(jsonStr);
910         }
911         MEM_CONTEXT_PRIOR_END();
912     }
913     MEM_CONTEXT_TEMP_END();
914 
915     FUNCTION_LOG_RETURN(STRING, result);
916 }
917 
918 /***********************************************************************************************************************************
919 Currently this function is only intended to convert the limited types that are included in info files.  More types will be added as
920 needed.
921 ***********************************************************************************************************************************/
922 String *
jsonFromVar(const Variant * var)923 jsonFromVar(const Variant *var)
924 {
925     FUNCTION_LOG_BEGIN(logLevelTrace);
926         FUNCTION_LOG_PARAM(VARIANT, var);
927     FUNCTION_LOG_END();
928 
929     String *result = NULL;
930 
931     MEM_CONTEXT_TEMP_BEGIN()
932     {
933         String *jsonStr = strNew();
934 
935         // If VariantList then process each item in the array. Currently the list must be KeyValue types.
936         if (var == NULL)
937         {
938             strCat(jsonStr, NULL_STR);
939         }
940         else if (varType(var) == varTypeBool)
941         {
942             strCat(jsonStr, jsonFromBool(varBool(var)));
943         }
944         else if (varType(var) == varTypeUInt)
945         {
946             strCat(jsonStr, jsonFromUInt(varUInt(var)));
947         }
948         else if (varType(var) == varTypeUInt64)
949         {
950             strCat(jsonStr, jsonFromUInt64(varUInt64(var)));
951         }
952         else if (varType(var) == varTypeString)
953         {
954             jsonFromStrInternal(jsonStr, varStr(var));
955         }
956         else if (varType(var) == varTypeVariantList)
957         {
958             const VariantList *vl = varVarLst(var);
959 
960             // If null
961             if (vl == NULL)
962             {
963                 strCat(jsonStr, NULL_STR);
964             }
965             // Else if not an empty array
966             else if (!varLstEmpty(vl))
967             {
968                 strCatZ(jsonStr, "[");
969 
970                 // Currently only KeyValue and String lists are supported
971                 for (unsigned int vlIdx = 0; vlIdx < varLstSize(vl); vlIdx++)
972                 {
973                     // If going to add another key, append a comma
974                     if (vlIdx > 0)
975                         strCatZ(jsonStr, ",");
976 
977                     Variant *varSub = varLstGet(vl, vlIdx);
978 
979                     if (varSub == NULL)
980                     {
981                         strCat(jsonStr, NULL_STR);
982                     }
983                     else if (varType(varSub) == varTypeBool)
984                     {
985                         strCat(jsonStr, jsonFromBool(varBool(varSub)));
986                     }
987                     else if (varType(varSub) == varTypeKeyValue)
988                     {
989                         strCat(jsonStr, jsonFromKvInternal(varKv(varSub)));
990                     }
991                     else if (varType(varSub) == varTypeVariantList)
992                     {
993                         strCat(jsonStr, jsonFromVar(varSub));
994                     }
995                     else if (varType(varSub) == varTypeInt)
996                     {
997                         strCat(jsonStr, jsonFromInt(varInt(varSub)));
998                     }
999                     else if (varType(varSub) == varTypeInt64)
1000                     {
1001                         strCat(jsonStr, jsonFromInt64(varInt64(varSub)));
1002                     }
1003                     else if (varType(varSub) == varTypeUInt)
1004                     {
1005                         strCat(jsonStr, jsonFromUInt(varUInt(varSub)));
1006                     }
1007                     else if (varType(varSub) == varTypeUInt64)
1008                     {
1009                         strCat(jsonStr, jsonFromUInt64(varUInt64(varSub)));
1010                     }
1011                     else
1012                         jsonFromStrInternal(jsonStr, varStr(varSub));
1013                 }
1014 
1015                 // Close the array
1016                 strCatZ(jsonStr, "]");
1017             }
1018             // Else empty array
1019             else
1020                 strCatZ(jsonStr, "[]");
1021         }
1022         else if (varType(var) == varTypeKeyValue)
1023         {
1024             strCat(jsonStr, jsonFromKvInternal(varKv(var)));
1025         }
1026         else
1027             THROW(JsonFormatError, "variant type is invalid");
1028 
1029         // Duplicate the string into the prior context
1030         MEM_CONTEXT_PRIOR_BEGIN()
1031         {
1032             result = strDup(jsonStr);
1033         }
1034         MEM_CONTEXT_PRIOR_END();
1035     }
1036     MEM_CONTEXT_TEMP_END();
1037 
1038     FUNCTION_LOG_RETURN(STRING, result);
1039 }
1040