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