1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include <klib/extern.h>
28 
29 #include <klib/json.h>
30 
31 #include <limits.h>
32 #include <errno.h>
33 
34 #include <strtol.h>
35 
36 #include <klib/rc.h>
37 #include <klib/text.h>
38 #include <klib/container.h>
39 #include <klib/namelist.h>
40 #include <klib/vector.h>
41 #include <klib/data-buffer.h>
42 #include <klib/printf.h>
43 
44 #include "json-lex.h"
45 #include "json-tokens.h"
46 #include "json-priv.h"
47 
48 /* copy, convert the escapes and NUL-terminate */
49 static rc_t CopyAndUnescape ( const char * p_value, size_t p_size, char * p_target, size_t p_targetSize );
50 
51 /* NameValue */
52 
53 typedef struct NameValue NameValue;
54 struct NameValue
55 {
56     BSTNode node;
57     char * name;
58     KJsonValue * value;
59 };
60 
MakeNameValue(NameValue ** p_val,const char * p_name,size_t p_name_size,KJsonValue * p_value)61 static rc_t MakeNameValue ( NameValue ** p_val, const char * p_name, size_t p_name_size, KJsonValue * p_value )
62 {
63     rc_t rc;
64     NameValue * ret = calloc ( 1, sizeof * ret );
65     if ( ret == NULL )
66         rc = RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
67     else
68     {
69         ret -> name = malloc ( p_name_size + 1 );
70         if ( ret -> name == NULL )
71             rc = RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
72         else
73         {
74             rc = CopyAndUnescape ( p_name, p_name_size, ret -> name, p_name_size + 1 );
75             if ( rc == 0 )
76             {
77                 ret -> value = p_value;
78                 * p_val = ret;
79                 return 0;
80             }
81             free ( ret -> name );
82         }
83         free ( ret );
84     }
85     return rc;
86 }
87 
NameValueWhack(BSTNode * p_n,void * p_data)88 static void CC NameValueWhack ( BSTNode * p_n, void * p_data )
89 {
90     NameValue * self = ( NameValue * ) p_n;
91     free ( self -> name );
92     KJsonValueWhack ( self -> value );
93     free ( self );
94 }
95 
96 static
StringCmp(const char * a,const char * b)97 int StringCmp( const char * a, const char * b )
98 {
99     size_t sizeA = string_size ( a );
100     size_t sizeB = string_size ( b );
101     return string_cmp ( a, sizeA,
102                         b, sizeB,
103                         sizeA < sizeB ? sizeB : sizeA );
104 }
105 
NameValueSort(const BSTNode * p_item,const BSTNode * p_n)106 int64_t CC NameValueSort ( const BSTNode * p_item, const BSTNode * p_n )
107 {
108     const NameValue *a = ( const NameValue* ) p_item;
109     const NameValue *b = ( const NameValue* ) p_n;
110     return StringCmp ( a -> name, b -> name );
111 }
112 
NameValueCompare(const void * p_item,const BSTNode * p_n)113 int64_t CC NameValueCompare ( const void * p_item, const BSTNode * p_n )
114 {
115     const char * a = ( const char * ) p_item;
116     const NameValue * b = ( const NameValue* ) p_n;
117     return StringCmp ( a, b -> name );
118 }
119 
120 typedef struct AddKeyBlock AddKeyBlock;
121 struct AddKeyBlock
122 {
123     VNamelist * names;
124     rc_t rc;
125 };
126 
NameValueAddKey(BSTNode * p_n,void * p_data)127 bool CC NameValueAddKey ( BSTNode * p_n, void * p_data )
128 {
129     const NameValue *a = ( const NameValue* ) p_n;
130     AddKeyBlock * bl = ( AddKeyBlock * ) p_data;
131     bl -> rc = VNamelistAppend ( bl -> names, a -> name );
132     return bl -> rc != 0;
133 }
134 
135 struct KJsonValue
136 {
137     uint32_t    type; /* enum jsType */
138     union
139     {
140         char *      str;
141         bool        boolean;
142     } u;
143 };
144 
145 struct KJsonObject
146 {
147     KJsonValue dad;
148     BSTree members; /* NameValue */
149 };
150 
151 struct KJsonArray
152 {
153     KJsonValue dad;
154     Vector elements; /* KJsonValue* */
155 };
156 
157 /* Public API, read only */
158 
159 LIB_EXPORT
160 rc_t CC
KJsonValueMake(KJsonValue ** p_root,const char * p_input,char * p_error,size_t p_error_size)161 KJsonValueMake ( KJsonValue ** p_root, const char * p_input, char * p_error, size_t p_error_size )
162 {
163     rc_t rc;
164     if ( p_root == NULL )
165     {
166         rc = RC ( rcCont, rcNode, rcParsing, rcSelf, rcNull );
167     }
168     else if ( p_input == NULL )
169     {
170         rc = RC ( rcCont, rcNode, rcParsing, rcParam, rcNull );
171     }
172     else
173     {
174         JsonScanBlock sb;
175         JsonScan_yylex_init ( & sb, p_input, string_size ( p_input ) );
176 
177         if ( Json_parse ( p_root, & sb ) == 0 )
178         {
179             rc = 0;
180         }
181         else
182         {
183             if ( p_error != NULL )
184             {
185                 if ( string_copy ( p_error, p_error_size, sb . error, string_size ( sb . error ) ) == p_error_size )
186                 {
187                     p_error [ p_error_size - 1 ] = 0;
188                 }
189             }
190 
191             // sb . error is NUL-terminated, so strstr is safe
192             if ( strstr ( sb . error, "unexpected end of source" ) != NULL )
193             {
194                 rc = RC ( rcCont, rcNode, rcParsing, rcDoc, rcIncomplete );
195             }
196             else
197             {
198                 rc = RC ( rcCont, rcNode, rcParsing, rcFormat, rcUnrecognized );
199             }
200         }
201         JsonScan_yylex_destroy ( & sb );
202     }
203     return rc;
204 }
205 
KJsonObjectWhack(KJsonObject * p_root)206 void CC KJsonObjectWhack ( KJsonObject * p_root )
207 {
208     if ( p_root != NULL )
209     {
210         BSTreeWhack ( & p_root -> members, NameValueWhack, NULL);
211         free ( p_root );
212     }
213 }
214 
215 static
216 void CC
WhackValue(void * item,void * data)217 WhackValue( void * item, void * data )
218 {
219     KJsonValueWhack ( ( KJsonValue * ) item );
220 }
221 
KJsonArrayWhack(KJsonArray * p_arr)222 void KJsonArrayWhack ( KJsonArray * p_arr )
223 {
224     if ( p_arr != NULL )
225     {
226         VectorWhack ( & p_arr -> elements, WhackValue, NULL );
227         free ( p_arr );
228     }
229 }
230 
231 LIB_EXPORT
232 rc_t CC
KJsonGetString(const KJsonValue * p_node,const char ** p_value)233 KJsonGetString ( const KJsonValue * p_node, const char ** p_value )
234 {
235     rc_t rc;
236     if ( p_node == NULL )
237     {
238         rc = RC ( rcCont, rcNode, rcAccessing, rcSelf, rcNull );
239     }
240     else if ( p_value == NULL )
241     {
242         rc = RC ( rcCont, rcNode, rcAccessing, rcParam, rcNull );
243     }
244     else
245     {
246         switch ( p_node -> type )
247         {
248         case jsString:
249         case jsNumber:
250             * p_value = p_node -> u . str;
251             rc = 0;
252             break;
253         default:
254             rc = RC ( rcCont, rcNode, rcAccessing, rcType, rcIncorrect );
255             break;
256         }
257     }
258     return rc;
259 }
260 
261 LIB_EXPORT
KJsonValueToObject(const KJsonValue * p_value)262 const KJsonObject * CC  KJsonValueToObject ( const KJsonValue * p_value )
263 {
264     if ( p_value == NULL || p_value -> type != jsObject )
265     {
266         return NULL;
267     }
268 
269     return ( const KJsonObject * ) p_value;
270 }
271 
272 LIB_EXPORT
273 const KJsonValue * CC
KJsonObjectToValue(const KJsonObject * p_object)274 KJsonObjectToValue ( const KJsonObject * p_object )
275 {
276     if ( p_object == NULL )
277     {
278         return NULL;
279     }
280 
281     return & p_object -> dad;
282 }
283 
284 LIB_EXPORT
285 rc_t CC
KJsonObjectGetNames(const KJsonObject * p_node,VNamelist * p_names)286 KJsonObjectGetNames ( const KJsonObject * p_node, VNamelist * p_names )
287 {
288     rc_t rc;
289     if ( p_node == NULL )
290     {
291         rc = RC ( rcCont, rcNode, rcReading, rcSelf, rcNull );
292     }
293     else if ( p_names == NULL )
294     {
295         rc = RC ( rcCont, rcNode, rcReading, rcParam, rcNull );
296     }
297     else
298     {
299         AddKeyBlock bl;
300         bl . names = p_names;
301         bl . rc = 0;
302         BSTreeDoUntil ( & p_node -> members, false, NameValueAddKey, & bl );
303         rc = bl . rc;
304     }
305     return rc;
306 }
307 
308 LIB_EXPORT
309 const KJsonValue * CC
KJsonObjectGetMember(const KJsonObject * p_node,const char * p_name)310 KJsonObjectGetMember ( const KJsonObject * p_node, const char * p_name )
311 {
312     if ( p_node == NULL || p_name == 0 )
313     {
314         return 0;
315     }
316 	else
317 	{
318 		const BSTNode * node = BSTreeFind ( & p_node -> members, p_name, NameValueCompare );
319 		if ( node == NULL )
320 		{
321 			return NULL;
322 		}
323 		return ( ( const NameValue * ) node ) -> value;
324 	}
325 }
326 
327 LIB_EXPORT
328 const KJsonArray * CC
KJsonValueToArray(const KJsonValue * p_value)329 KJsonValueToArray ( const KJsonValue * p_value )
330 {
331     if ( p_value == NULL || p_value -> type != jsArray )
332     {
333         return NULL;
334     }
335 
336     return ( const KJsonArray * ) p_value;
337 }
338 
339 LIB_EXPORT
340 const KJsonValue * CC
KJsonArrayToValue(const KJsonArray * p_array)341 KJsonArrayToValue ( const KJsonArray * p_array )
342 {
343     if ( p_array == NULL )
344     {
345         return NULL;
346     }
347 
348     return & p_array -> dad;
349 }
350 
351 /* Construction methods */
352 
KJsonMakeObject(KJsonObject ** obj)353 rc_t KJsonMakeObject ( KJsonObject ** obj )
354 {
355     KJsonObject * ret;
356     assert ( obj != NULL );
357     ret = calloc ( 1, sizeof * ret );
358     if ( ret != NULL )
359     {
360         ret -> dad . type = jsObject;
361         BSTreeInit ( & ret -> members );
362         * obj = ret;
363         return 0;
364     }
365     return RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
366 }
367 
KJsonObjectAddMember(KJsonObject * p_obj,const char * p_name,size_t p_name_size,KJsonValue * p_value)368 rc_t KJsonObjectAddMember ( KJsonObject * p_obj, const char * p_name, size_t p_name_size, KJsonValue * p_value )
369 {
370     rc_t rc;
371     NameValue * nv;
372 
373     assert ( p_obj != NULL && p_name != NULL && p_value != NULL );
374 
375     rc = MakeNameValue ( & nv, p_name, p_name_size, p_value );
376     if ( rc == 0 )
377     {
378         rc = BSTreeInsertUnique ( & p_obj -> members, & nv -> node, NULL, NameValueSort );
379         if ( rc != 0 )
380         {
381             NameValueWhack ( & nv -> node, NULL );
382         }
383     }
384     else
385     {
386         KJsonValueWhack ( p_value );
387     }
388     return rc;
389 }
390 
KJsonMakeArray(KJsonArray ** obj)391 rc_t KJsonMakeArray ( KJsonArray ** obj )
392 {
393     KJsonArray * ret;
394     assert ( obj != NULL );
395     ret = calloc ( 1, sizeof * ret );
396     if ( ret != NULL )
397     {
398         ret -> dad . type = jsArray;
399         VectorInit ( & ret -> elements, 0, 16 );
400         * obj = ret;
401         return 0;
402     }
403     return RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
404 }
405 
406 LIB_EXPORT
407 uint32_t CC
KJsonArrayGetLength(const KJsonArray * p_node)408 KJsonArrayGetLength ( const KJsonArray * p_node )
409 {
410     if ( p_node == NULL )
411     {
412         return 0;
413     }
414     return VectorLength ( & p_node -> elements );
415 }
416 
KJsonArrayAddElement(KJsonArray * p_arr,KJsonValue * p_element)417 rc_t KJsonArrayAddElement ( KJsonArray * p_arr, KJsonValue * p_element )
418 {
419     assert ( p_arr != NULL && p_element != NULL );
420     return VectorAppend ( & p_arr -> elements, NULL, p_element );
421 }
422 
423 LIB_EXPORT
424 const KJsonValue * CC
KJsonArrayGetElement(const KJsonArray * p_node,uint32_t p_index)425 KJsonArrayGetElement ( const KJsonArray * p_node, uint32_t p_index )
426 {
427     if ( p_node == NULL )
428     {
429         return NULL;
430     }
431     return ( const KJsonValue * ) VectorGet( & p_node -> elements, p_index );
432 }
433 
434 /* hex_to_int
435  *  where 'c' is known to be hex
436  */
437 static
438 unsigned int
hex_to_int(char c)439 hex_to_int ( char c )
440 {
441     int i = c - '0';
442     if ( c > '9' )
443     {
444         if ( c < 'a' )
445             i = c - 'A' + 10;
446         else
447             i = c - 'a' + 10;
448     }
449 
450     assert ( i >= 0 && i < 16 );
451     return i;
452 }
453 
454 static
455 rc_t
CopyAndUnescape(const char * p_value,size_t p_size,char * p_target,size_t p_targetSize)456 CopyAndUnescape ( const char * p_value, size_t p_size, char * p_target, size_t p_targetSize )
457 {   /* copy, convert the escapes and NUL-terminate */
458     uint32_t out = 0;
459     uint32_t i = 0;
460     assert ( p_size < p_targetSize );
461     while ( i < p_size )
462     {
463         if ( p_value [ i ] == '\\' )
464         {
465             ++i;
466             switch ( p_value [ i ] )
467             {
468             case 'u':
469                 assert ( i + 4 < p_size );
470                 {   /* treat 4-digit hex code as UTF16 */
471                     uint64_t u64 = hex_to_int ( p_value [ i + 1 ]);
472                     u64 <<= 4;
473                     u64 += hex_to_int ( p_value [ i + 2 ]);
474                     u64 <<= 4;
475                     u64 += hex_to_int ( p_value [ i + 3 ]);
476                     u64 <<= 4;
477                     u64 += hex_to_int ( p_value [ i + 4 ]);
478 
479                     if ( u64 >= 0xD800 && u64 <= 0xDFFF )
480                     {   /* require a valid surrogate pair */
481                         if ( i + 10 < p_size && p_value [ i + 5 ] == '\\' && p_value [ i + 6 ] == 'u' )
482                         {
483                             uint64_t high = u64;
484                             uint64_t low = hex_to_int ( p_value [ i + 7 ]);
485                             low <<= 4;
486                             low += hex_to_int ( p_value [ i + 8 ]);
487                             low <<= 4;
488                             low += hex_to_int ( p_value [ i + 9 ]);
489                             low <<= 4;
490                             low += hex_to_int ( p_value [ i + 10 ]);
491                             if ( low >= 0xDC00 && low <= 0xDFFF )
492                             {
493                                 u64 = 0x10000;
494                                 u64 += ( high & 0x03FF ) << 10;
495                                 u64 += ( low & 0x03FF );
496                                 i += 6;
497                             }
498                             else
499                             {
500                                 return RC ( rcCont, rcNode, rcParsing, rcString, rcInvalid );
501                             }
502                         }
503                         else
504                         {
505                             return RC ( rcCont, rcNode, rcParsing, rcString, rcInvalid );
506                         }
507                     }
508 
509                     {
510                         int ch_len = utf32_utf8 ( p_target + out, p_target + p_size, (uint32_t)u64 );
511                         assert ( ch_len > 0 );
512                         i += 4;
513                         out += ch_len - 1;
514                     }
515                 }
516                 break;
517             case '\\':
518             case '/':
519             case '"':
520                 p_target [ out ] = p_value [ i ];
521                 break;
522             case 'b':
523                 p_target[ out ] = '\b';
524                 break;
525             case 'f':
526                 p_target [ out ] = '\f';
527                 break;
528             case 'n':
529                 p_target [ out ] = '\n';
530                 break;
531             case 'r':
532                 p_target [ out ] = '\r';
533                 break;
534             case 't':
535                 p_target [ out ] = '\t';
536                 break;
537             }
538         }
539         else
540         {
541             p_target [ out ] = p_value [ i ];
542         }
543         ++i;
544         ++ out;
545     }
546     p_target [ out ] = 0;
547     return 0;
548 }
549 
KJsonMakeString(KJsonValue ** p_val,const char * p_value,size_t p_size)550 rc_t KJsonMakeString ( KJsonValue ** p_val, const char * p_value, size_t p_size )
551 {
552     KJsonValue * ret;
553     assert ( p_val != NULL && p_value != NULL );
554     ret = malloc ( sizeof * ret );
555     if ( ret != NULL )
556     {
557         ret -> type = jsString;
558         ret -> u . str = malloc ( p_size + 1 );
559         if ( ret -> u . str != NULL )
560         {
561             rc_t rc = CopyAndUnescape ( p_value, p_size, ret -> u . str, p_size + 1 );
562             if ( rc == 0 )
563             {
564                 * p_val = ret;
565                 return 0;
566             }
567             KJsonValueWhack ( ret );
568             return rc;
569         }
570         free ( ret );
571     }
572     return RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
573 }
574 
575 /*
576 rc_t KJsonMakeBool ( KJsonValue ** obj, bool value );
577 */
578 
KJsonMakeNull(KJsonValue ** p_val)579 rc_t KJsonMakeNull ( KJsonValue ** p_val )
580 {
581     KJsonValue * ret;
582     assert ( p_val != NULL );
583     ret = calloc ( 1, sizeof * ret );
584     if ( ret != NULL )
585     {
586         ret -> type = jsNull;
587         * p_val = ret;
588         return 0;
589     }
590     return RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
591 }
592 
KJsonMakeBool(KJsonValue ** p_val,bool p_bool)593 rc_t KJsonMakeBool ( KJsonValue ** p_val, bool p_bool )
594 {
595     KJsonValue * ret;
596     assert ( p_val != NULL );
597     ret = calloc ( 1, sizeof * ret );
598     if ( ret != NULL )
599     {
600         ret -> type = jsBool;
601         ret -> u . boolean = p_bool;
602         * p_val = ret;
603         return 0;
604     }
605     return RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
606 }
607 
KJsonValueWhack(KJsonValue * p_value)608 void KJsonValueWhack ( KJsonValue * p_value )
609 {
610     if ( p_value != NULL )
611     {
612         switch ( p_value -> type )
613         {
614         case jsString:
615         case jsNumber:
616         {
617             free ( p_value -> u . str );
618             free ( p_value );
619             break;
620         }
621         case jsObject:
622         {
623             KJsonObjectWhack ( ( KJsonObject * ) p_value );
624             break;
625         }
626         case jsArray:
627         {
628             KJsonArrayWhack ( ( KJsonArray * ) p_value );
629             break;
630         }
631         default:
632             free ( p_value );
633             break;
634         }
635     }
636 }
637 
KJsonMakeNumber(KJsonValue ** p_val,const char * p_value,size_t p_size)638 rc_t KJsonMakeNumber ( KJsonValue ** p_val, const char * p_value, size_t p_size )
639 {
640     KJsonValue * ret;
641     assert ( p_val != NULL && p_value != NULL );
642     ret = malloc ( sizeof * ret );
643     if ( ret != NULL )
644     {
645         ret -> type = jsNumber;
646         ret -> u . str = string_dup ( p_value, p_size );
647         if ( ret -> u . str != NULL )
648         {
649             * p_val = ret;
650             return 0;
651         }
652         free ( ret );
653     }
654     return RC ( rcCont, rcNode, rcAllocating, rcMemory, rcExhausted );
655 }
656 
657 LIB_EXPORT
658 rc_t CC
KJsonGetNumber(const KJsonValue * p_node,int64_t * p_value)659 KJsonGetNumber ( const KJsonValue * p_node, int64_t * p_value )
660 {
661     rc_t rc;
662     if ( p_node == NULL )
663     {
664         rc = RC ( rcCont, rcNode, rcReading, rcSelf, rcNull );
665     }
666     else if ( p_value == NULL )
667     {
668         rc = RC ( rcCont, rcNode, rcReading, rcParam, rcNull );
669     }
670     else if ( p_node -> type != jsNumber )
671     {
672         rc = RC ( rcCont, rcNode, rcAccessing, rcType, rcIncorrect );
673     }
674     else
675     {
676         char * endptr;
677         int64_t value;
678         errno = 0;
679         value = strtoi64 ( p_node -> u . str, & endptr, 10 );
680         if ( errno == ERANGE )
681         {
682             rc = RC ( rcCont, rcNode, rcAccessing, rcSize, rcExcessive );
683         }
684         else if ( *endptr != 0 )
685         {
686             rc = RC ( rcCont, rcNode, rcAccessing, rcFormat, rcIncorrect );
687         }
688         else
689         {
690             * p_value = value;
691             rc = 0;
692         }
693     }
694     return rc;
695 }
696 
697 LIB_EXPORT
698 rc_t CC
KJsonGetDouble(const KJsonValue * p_node,double * p_value)699 KJsonGetDouble ( const KJsonValue * p_node, double * p_value )
700 {
701     rc_t rc;
702     if ( p_node == NULL )
703     {
704         rc = RC ( rcCont, rcNode, rcReading, rcSelf, rcNull );
705     }
706     else if ( p_value == NULL )
707     {
708         rc = RC ( rcCont, rcNode, rcReading, rcParam, rcNull );
709     }
710     else if ( p_node -> type != jsNumber )
711     {
712         rc = RC ( rcCont, rcNode, rcAccessing, rcType, rcIncorrect );
713     }
714     else
715     {
716         char * endptr;
717         double value;
718         errno = 0;
719         value = strtod ( p_node -> u . str, & endptr );
720         if ( errno == ERANGE )
721         {
722             rc = RC ( rcCont, rcNode, rcAccessing, rcSize, rcExcessive );
723         }
724         else
725         {
726             * p_value = value;
727             rc = 0;
728         }
729     }
730     return rc;
731 }
732 
733 LIB_EXPORT
734 rc_t CC
KJsonGetBool(const KJsonValue * p_node,bool * p_value)735 KJsonGetBool ( const KJsonValue * p_node, bool * p_value )
736 {
737     rc_t rc;
738     if ( p_node == NULL )
739     {
740         rc = RC ( rcCont, rcNode, rcReading, rcSelf, rcNull );
741     }
742     else if ( p_value == NULL )
743     {
744         rc = RC ( rcCont, rcNode, rcReading, rcParam, rcNull );
745     }
746     else if ( p_node -> type != jsBool )
747     {
748         rc = RC ( rcCont, rcNode, rcAccessing, rcType, rcIncorrect );
749     }
750     else
751     {
752         * p_value = p_node -> u . boolean;
753         rc = 0;
754     }
755     return rc;
756 }
757 
758 LIB_EXPORT
759 enum jsType CC
KJsonGetValueType(const KJsonValue * p_value)760 KJsonGetValueType ( const KJsonValue * p_value )
761 {
762     if ( p_value == NULL )
763     {
764         return jsInvalid;
765     }
766     return p_value -> type;
767 }
768 
769 // Conversion to a JSON formatted string
770 
771 typedef struct PrintData PrintData;
772 struct PrintData
773 {
774     KDataBuffer *   output;
775     size_t          increment;
776     size_t          offset;
777     rc_t            rc;
778     const void *    last; /* last element in a container */
779     bool            pretty;
780     uint32_t        indentTabs;
781 };
782 
783 static rc_t ObjectToJson ( const KJsonObject * root, PrintData * pd );
784 static rc_t ArrayToJson ( const KJsonArray * node, PrintData * pd );
785 
786 static
IncreaseBuffer(PrintData * p_data)787 rc_t IncreaseBuffer ( PrintData * p_data )
788 {
789     assert ( p_data != NULL );
790     return KDataBufferResize ( p_data -> output, KDataBufferBytes ( p_data -> output ) + p_data -> increment );
791 }
792 
793 static
794 rc_t
PrintWithSize(PrintData * p_pd,const char * p_text,size_t p_size)795 PrintWithSize ( PrintData * p_pd, const char * p_text, size_t p_size )
796 {
797     /* grow the buffer if necessary */
798     rc_t rc = 0;
799     while ( rc == 0 && p_pd -> offset + p_size >= KDataBufferBytes ( p_pd -> output ) )
800     {
801         rc = IncreaseBuffer ( p_pd );
802     }
803 
804     if ( rc == 0 )
805     {
806         size_t num_writ;
807         rc = string_printf ( ( char * ) p_pd -> output -> base + p_pd -> offset, KDataBufferBytes ( p_pd -> output ) - p_pd -> offset, & num_writ, "%.*s", p_size, p_text );
808         if ( rc == 0 )
809         {
810             p_pd -> offset += num_writ;
811         }
812     }
813     return rc;
814 }
815 
816 static
817 rc_t
Print(PrintData * p_pd,const char * p_text)818 Print( PrintData * p_pd, const char * p_text )
819 {
820     return PrintWithSize ( p_pd, p_text, string_size ( p_text ) );
821 }
822 
823 static
824 rc_t
PrintNewLine(PrintData * p_pd)825 PrintNewLine( PrintData * p_pd )
826 {
827     rc_t rc = Print ( p_pd, "\n" );
828 
829     uint32_t i = p_pd -> indentTabs;
830     while ( rc == 0 && i > 0 )
831     {
832         rc = Print ( p_pd, "\t" );
833         --i;
834     }
835 
836     return rc;
837 }
838 
839 static
840 rc_t
PrintString(PrintData * p_pd,const char * p_str)841 PrintString ( PrintData * p_pd, const char * p_str )
842 {
843     const char * begin = p_str;
844     const char * end = p_str + string_size ( p_str );
845     rc_t rc = 0;
846     while ( rc == 0 && begin < end )
847     {
848         uint32_t ch;
849         int bytes = utf8_utf32 ( & ch, begin, end );
850         assert ( bytes > 0 );
851         if ( ch < 32 )
852         {
853             switch (ch)
854             {
855             case 8:
856                 rc = Print ( p_pd, "\\b");
857                 break;
858             case 9:
859                 rc = Print ( p_pd, "\\t");
860                 break;
861             case 10:
862                 rc = Print ( p_pd, "\\n");
863                 break;
864             case 13:
865                 rc = Print ( p_pd, "\\r");
866                 break;
867             default:
868                 {
869                     const char to_hex[16] = "0123456789abcdef";
870                     char hex [7] = { '\\', '\\', 'u' };
871                     hex [3] = to_hex [ ( ch >> 24 ) & 0xff ];
872                     hex [4] = to_hex [ ( ch >> 16 ) & 0xff ];
873                     hex [5] = to_hex [ ( ch >> 8 ) & 0xff ];
874                     hex [6] = to_hex [ ch & 0xff ];
875                     rc = PrintWithSize ( p_pd, hex, 7);
876                     break;
877                 }
878             }
879         }
880         else if ( ch > 255 )
881         {   /* UTF-8 encoding; copy bytes from input */
882             rc = PrintWithSize ( p_pd, begin, bytes );
883         }
884         else
885         {   /* non-control ASCII */
886             switch (ch)
887             {
888             case '\\': rc = Print ( p_pd, "\\\\" ); break;
889             case '/':  rc = Print ( p_pd, "\\/" ); break;
890             case '"':  rc = Print ( p_pd, "\\\"" ); break;
891             default:
892                 rc = PrintWithSize ( p_pd, begin, 1 );
893                 break;
894             }
895         }
896 
897         begin += bytes;
898     }
899     return rc;
900 }
901 
902 static
903 rc_t
ValueToJson(const KJsonValue * p_value,PrintData * p_pd)904 ValueToJson ( const KJsonValue * p_value, PrintData * p_pd )
905 {
906     size_t saved_offset = p_pd -> offset;
907     rc_t rc;
908     switch ( p_value -> type )
909     {
910     case jsString:
911         rc = Print ( p_pd, "\"" );
912         if ( rc == 0 ) rc = PrintString ( p_pd, p_value -> u . str );
913         if ( rc == 0 ) rc = Print ( p_pd, "\"" );
914         break;
915     case jsNumber:
916         rc = Print ( p_pd, p_value -> u . str );
917         break;
918     case jsBool:
919         rc = Print ( p_pd, p_value -> u . boolean ? "true" : "false" );
920         break;
921     case jsNull:
922         rc = Print ( p_pd, "null");
923         break;
924     case jsObject:
925         {
926             const KJsonObject * obj = KJsonValueToObject ( p_value );
927             assert ( obj != 0 );
928             rc = ObjectToJson ( obj, p_pd );
929         }
930         break;
931     case jsArray:
932         {
933             const KJsonArray * arr = KJsonValueToArray ( p_value );
934             assert ( arr != 0 );
935             rc = ArrayToJson ( arr, p_pd );
936         }
937         break;
938     default:
939         assert ( false );
940         break;
941     }
942 
943     if ( rc != 0 )
944     {
945         p_pd -> offset = saved_offset;
946     }
947     return rc;
948 }
949 
950 static
951 void CC
NameValueToJson(BSTNode * n,void * data)952 NameValueToJson ( BSTNode *n, void *data )
953 {
954     const NameValue * node = (const NameValue *) n;
955     PrintData * pd = (PrintData *) data;
956     size_t saved_offset = pd -> offset;
957 
958     rc_t rc = Print ( pd, "\"" );
959     if ( rc == 0 ) rc = Print ( pd, node -> name );
960     if ( rc == 0 ) rc = Print ( pd, "\"" );
961     if ( rc == 0 ) rc = Print ( pd, pd -> pretty ? " : " : ":" );
962     if ( rc == 0 ) rc = ValueToJson ( node -> value, pd );
963     if ( rc == 0 )
964     {
965         if ( n == pd -> last )
966         {   /* restore indent before printing the closing '}', so that it aligns with the member name */
967             -- pd -> indentTabs;
968         }
969         else
970         {
971             rc = Print ( pd, "," );
972         }
973     }
974     if ( rc == 0 && pd -> pretty ) rc = PrintNewLine ( pd );
975 
976     pd -> rc = rc;
977     if ( rc != 0 )
978     {
979         pd -> offset = saved_offset;
980     }
981 }
982 
983 static
984 rc_t
ObjectToJson(const KJsonObject * p_root,PrintData * pd)985 ObjectToJson ( const KJsonObject * p_root, PrintData * pd )
986 {
987     rc_t rc;
988     size_t saved_offset;
989     const void * saved_last;
990 
991     assert ( p_root != NULL && pd != NULL );
992     saved_last = pd -> last;
993     saved_offset = pd -> offset;
994 
995     rc = Print ( pd, "{" );
996     ++ pd -> indentTabs;
997     if ( rc == 0 && pd -> pretty ) rc = PrintNewLine ( pd );
998     if ( rc == 0 )
999     {
1000         pd -> last = BSTreeLast ( & p_root -> members );
1001         BSTreeForEach ( & p_root -> members, false, NameValueToJson, pd);
1002     }
1003     if ( rc == 0 ) rc = Print ( pd, "}" );
1004 
1005     pd -> last = saved_last;
1006     if ( rc != 0 )
1007     {
1008         pd -> offset = saved_offset;
1009     }
1010     return rc;
1011 }
1012 
1013 static
1014 bool CC
ArrayElementToJson(void * item,void * data)1015 ArrayElementToJson ( void * item, void *data )
1016 {
1017     const KJsonValue * value =  (const KJsonValue *) item;
1018     PrintData * pd = (PrintData *) data;
1019 
1020     pd -> rc = ValueToJson ( value, pd );
1021     if ( pd -> rc == 0 )
1022     {
1023         if ( value == pd -> last )
1024         {   /* restore indent before printing the closing ']' */
1025             -- pd -> indentTabs;
1026         }
1027         else
1028         {
1029             pd -> rc = Print ( pd, "," );
1030         }
1031     }
1032     if ( pd -> pretty ) pd -> rc = PrintNewLine ( pd );
1033     return pd -> rc != 0;
1034 }
1035 
1036 static
1037 rc_t
ArrayToJson(const KJsonArray * p_node,PrintData * pd)1038 ArrayToJson ( const KJsonArray * p_node, PrintData * pd )
1039 {
1040     rc_t rc;
1041     size_t saved_offset;
1042     const void * saved_last;
1043     assert ( p_node != NULL && pd != NULL );
1044     saved_last = pd -> last;
1045     saved_offset = pd -> offset;
1046 
1047     rc = Print ( pd, "[" );
1048     ++ pd -> indentTabs;
1049     if ( pd -> pretty ) rc = PrintNewLine ( pd );
1050     if ( rc == 0 )
1051     {
1052         pd -> last = VectorLast ( & p_node -> elements );
1053         VectorDoUntil ( & p_node -> elements, false, ArrayElementToJson, pd );
1054     }
1055     if ( rc == 0 ) rc = Print ( pd, "]" );
1056 
1057     pd -> last = saved_last;
1058     if ( rc != 0 )
1059     {
1060         pd -> offset = saved_offset;
1061     }
1062     return rc;
1063 }
1064 
1065 LIB_EXPORT
1066 rc_t CC
KJsonToJsonString(const KJsonValue * p_root,struct KDataBuffer * p_output,size_t p_increment,bool p_pretty)1067 KJsonToJsonString ( const KJsonValue * p_root, struct KDataBuffer * p_output, size_t p_increment, bool p_pretty )
1068 {
1069     rc_t rc;
1070     if ( p_root == NULL )
1071     {
1072         rc = RC ( rcCont, rcNode, rcProcessing, rcSelf, rcNull );
1073     }
1074     else if ( p_output == NULL )
1075     {
1076         rc = RC ( rcCont, rcNode, rcReading, rcParam, rcNull );
1077     }
1078     else
1079     {
1080         rc = KDataBufferMake ( p_output, 8, p_increment == 0 ? 256 : p_increment );
1081         if ( rc == 0 )
1082         {
1083             PrintData pd;
1084             pd . output = p_output;
1085             pd . increment = p_increment == 0 ? 1024 : p_increment;
1086             pd . offset = 0;
1087             pd . rc = 0;
1088             pd . last = NULL;
1089             pd . pretty = p_pretty;
1090             pd . indentTabs = 0;
1091             rc = ValueToJson ( p_root, & pd );
1092         }
1093     }
1094     return rc;
1095 }
1096