VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/json.cpp@ 74170

Last change on this file since 74170 was 74170, checked in by vboxsync, 6 years ago

IPRT/rest: Added testcase for RTCRestDouble, fixing issues found. bugref:9167

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 53.8 KB
Line 
1/* $Id: json.cpp 74170 2018-09-10 07:26:17Z vboxsync $ */
2/** @file
3 * IPRT JSON parser API (JSON).
4 */
5
6/*
7 * Copyright (C) 2016-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/json.h>
32#include "internal/iprt.h"
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/ctype.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/stream.h>
40#include <iprt/string.h>
41
42#include <stdlib.h> /* strtod() */
43#include <errno.h> /* errno */
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49
50/**
51 * JSON parser position information.
52 */
53typedef struct RTJSONPOS
54{
55 /** Line in the source. */
56 size_t iLine;
57 /** Current start character .*/
58 size_t iChStart;
59 /** Current end character. */
60 size_t iChEnd;
61} RTJSONPOS;
62/** Pointer to a position. */
63typedef RTJSONPOS *PRTJSONPOS;
64
65/**
66 * JSON token class.
67 */
68typedef enum RTJSONTOKENCLASS
69{
70 /** Invalid. */
71 RTJSONTOKENCLASS_INVALID = 0,
72 /** Array begin. */
73 RTJSONTOKENCLASS_BEGIN_ARRAY,
74 /** Object begin. */
75 RTJSONTOKENCLASS_BEGIN_OBJECT,
76 /** Array end. */
77 RTJSONTOKENCLASS_END_ARRAY,
78 /** Object end. */
79 RTJSONTOKENCLASS_END_OBJECT,
80 /** Separator for name/value pairs. */
81 RTJSONTOKENCLASS_NAME_SEPARATOR,
82 /** Value separator. */
83 RTJSONTOKENCLASS_VALUE_SEPARATOR,
84 /** String */
85 RTJSONTOKENCLASS_STRING,
86 /** Integer number. */
87 RTJSONTOKENCLASS_INTEGER,
88 /** Floating point number. */
89 RTJSONTOKENCLASS_NUMBER,
90 /** null keyword. */
91 RTJSONTOKENCLASS_NULL,
92 /** false keyword. */
93 RTJSONTOKENCLASS_FALSE,
94 /** true keyword. */
95 RTJSONTOKENCLASS_TRUE,
96 /** End of stream */
97 RTJSONTOKENCLASS_EOS,
98 /** 32bit hack. */
99 RTJSONTOKENCLASS_32BIT_HACK = 0x7fffffff
100} RTJSONTOKENCLASS;
101/** Pointer to a token class. */
102typedef RTJSONTOKENCLASS *PRTJSONTOKENCLASS;
103
104/**
105 * JSON token.
106 */
107typedef struct RTJSONTOKEN
108{
109 /** Token class. */
110 RTJSONTOKENCLASS enmClass;
111 /** Token position in the source buffer. */
112 RTJSONPOS Pos;
113 /** Data based on the token class. */
114 union
115 {
116 /** String. */
117 struct
118 {
119 /** Pointer to the start of the string. */
120 char *pszStr;
121 } String;
122 /** Number. */
123 struct
124 {
125 int64_t i64Num;
126 } Integer;
127 /** Floating point number. */
128 double rdNum;
129 } Class;
130} RTJSONTOKEN;
131/** Pointer to a JSON token. */
132typedef RTJSONTOKEN *PRTJSONTOKEN;
133/** Pointer to a const script token. */
134typedef const RTJSONTOKEN *PCRTJSONTOKEN;
135
136/**
137 * Tokenizer read input callback.
138 *
139 * @returns IPRT status code.
140 * @param pvUser Opaque user data for the callee.
141 * @param offInput Start offset from the start of the input stream to read from.
142 * @param pvBuf Where to store the read data.
143 * @param cbBuf How much to read.
144 * @param pcbRead Where to store the amount of data read on succcess.
145 */
146typedef DECLCALLBACK(int) FNRTJSONTOKENIZERREAD(void *pvUser, size_t offInput, void *pvBuf, size_t cbBuf,
147 size_t *pcbRead);
148/** Pointer to a tokenizer read buffer callback. */
149typedef FNRTJSONTOKENIZERREAD *PFNRTJSONTOKENIZERREAD;
150
151/**
152 * Tokenizer state.
153 */
154typedef struct RTJSONTOKENIZER
155{
156 /** Read callback. */
157 PFNRTJSONTOKENIZERREAD pfnRead;
158 /** Opaque user data. */
159 void *pvUser;
160 /** Current offset into the input stream. */
161 size_t offInput;
162 /** Number of valid bytes in the input buffer. */
163 size_t cbBuf;
164 /** Current offset into the input buffer. */
165 size_t offBuf;
166 /** Input cache buffer. */
167 char achBuf[512];
168 /** Current position into the input stream. */
169 RTJSONPOS Pos;
170 /** Token 1. */
171 RTJSONTOKEN Token1;
172 /** Token 2. */
173 RTJSONTOKEN Token2;
174 /** Pointer to the current active token. */
175 PRTJSONTOKEN pTokenCurr;
176 /** The next token in the input stream (used for peeking). */
177 PRTJSONTOKEN pTokenNext;
178 /** The tokenizer error state. */
179 int rcTok;
180 /** Where to return extended error information.*/
181 PRTERRINFO pErrInfo;
182} RTJSONTOKENIZER;
183/** Pointer to a JSON tokenizer. */
184typedef RTJSONTOKENIZER *PRTJSONTOKENIZER;
185
186/** Pointer to the internal JSON value instance. */
187typedef struct RTJSONVALINT *PRTJSONVALINT;
188
189/**
190 * A JSON value.
191 */
192typedef struct RTJSONVALINT
193{
194 /** Type of the JSON value. */
195 RTJSONVALTYPE enmType;
196 /** Reference count for this JSON value. */
197 volatile uint32_t cRefs;
198 /** Type dependent data. */
199 union
200 {
201 /** String type*/
202 struct
203 {
204 /** Pointer to the string. */
205 char *pszStr;
206 } String;
207 /** Number type. */
208 struct
209 {
210 /** Signed 64-bit integer. */
211 int64_t i64Num;
212 } Integer;
213 /** Floating point number . */
214 double rdNum;
215 /** Array type. */
216 struct
217 {
218 /** Number of elements in the array. */
219 unsigned cItems;
220 /** Pointer to the array of items. */
221 PRTJSONVALINT *papItems;
222 } Array;
223 /** Object type. */
224 struct
225 {
226 /** Number of members. */
227 unsigned cMembers;
228 /** Pointer to the array holding the member names. */
229 char **papszNames;
230 /** Pointer to the array holding the values. */
231 PRTJSONVALINT *papValues;
232 } Object;
233 } Type;
234} RTJSONVALINT;
235
236/**
237 * A JSON iterator.
238 */
239typedef struct RTJSONITINT
240{
241 /** Referenced JSON value. */
242 PRTJSONVALINT pJsonVal;
243 /** Current index. */
244 unsigned idxCur;
245} RTJSONITINT;
246/** Pointer to the internal JSON iterator instance. */
247typedef RTJSONITINT *PRTJSONITINT;
248
249/**
250 * Passing arguments for the read callbacks.
251 */
252typedef struct RTJSONREADERARGS
253{
254 /** Buffer/File size */
255 size_t cbData;
256 /** Data specific for one callback. */
257 union
258 {
259 PRTSTREAM hStream;
260 const uint8_t *pbBuf;
261 } u;
262} RTJSONREADERARGS;
263/** Pointer to a readers argument. */
264typedef RTJSONREADERARGS *PRTJSONREADERARGS;
265
266
267/*********************************************************************************************************************************
268* Internal Functions *
269*********************************************************************************************************************************/
270static int rtJsonParseValue(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken, PRTJSONVALINT *ppJsonVal);
271
272
273/**
274 * Fill the input buffer from the input stream.
275 *
276 * @returns IPRT status code.
277 * @param pTokenizer The tokenizer state.
278 */
279static int rtJsonTokenizerRead(PRTJSONTOKENIZER pTokenizer)
280{
281 size_t cbRead = 0;
282 int rc = pTokenizer->pfnRead(pTokenizer->pvUser, pTokenizer->offInput, &pTokenizer->achBuf[0],
283 sizeof(pTokenizer->achBuf), &cbRead);
284 if (RT_SUCCESS(rc))
285 {
286 pTokenizer->cbBuf = cbRead;
287 pTokenizer->offInput += cbRead;
288 pTokenizer->offBuf = 0;
289 /* Validate UTF-8 encoding. */
290 rc = RTStrValidateEncodingEx(&pTokenizer->achBuf[0], cbRead, 0 /* fFlags */);
291 /* If we read less than requested we reached the end and fill the remainder with terminators. */
292 if (cbRead < sizeof(pTokenizer->achBuf))
293 memset(&pTokenizer->achBuf[cbRead], 0, sizeof(pTokenizer->achBuf) - cbRead);
294 }
295
296 return rc;
297}
298
299/**
300 * Skips the given amount of characters in the input stream.
301 *
302 * @returns IPRT status code.
303 * @param pTokenizer The tokenizer state.
304 * @param cchSkip The amount of characters to skip.
305 */
306static int rtJsonTokenizerSkip(PRTJSONTOKENIZER pTokenizer, size_t cchSkip)
307{
308 int rc = VINF_SUCCESS;
309
310 /*
311 * In case we reached the end of the stream don't even attempt to read new data.
312 * Safety precaution for possible bugs in the parser causing out of bounds reads
313 */
314 if (pTokenizer->achBuf[pTokenizer->offBuf] == '\0')
315 return rc;
316
317 while ( cchSkip > 0
318 && pTokenizer->offBuf < pTokenizer->cbBuf
319 && RT_SUCCESS(rc))
320 {
321 size_t cchThisSkip = RT_MIN(cchSkip, pTokenizer->cbBuf - pTokenizer->offBuf);
322
323 pTokenizer->offBuf += cchThisSkip;
324 /* Read new data if required and we didn't reach the end yet. */
325 if ( pTokenizer->offBuf == pTokenizer->cbBuf
326 && pTokenizer->cbBuf == sizeof(pTokenizer->achBuf))
327 rc = rtJsonTokenizerRead(pTokenizer);
328
329 cchSkip -= cchThisSkip;
330 }
331
332 return rc;
333}
334
335
336/**
337 * Returns whether the tokenizer reached the end of the stream.
338 *
339 * @returns true if the tokenizer reached the end of stream marker
340 * false otherwise.
341 * @param pTokenizer The tokenizer state.
342 */
343DECLINLINE(bool) rtJsonTokenizerIsEos(PRTJSONTOKENIZER pTokenizer)
344{
345 return pTokenizer->achBuf[pTokenizer->offBuf] == '\0';
346}
347
348/**
349 * Skip one character in the input stream.
350 *
351 * @returns nothing.
352 * @param pTokenizer The tokenizer state.
353 */
354DECLINLINE(void) rtJsonTokenizerSkipCh(PRTJSONTOKENIZER pTokenizer)
355{
356 rtJsonTokenizerSkip(pTokenizer, 1);
357 pTokenizer->Pos.iChStart++;
358 pTokenizer->Pos.iChEnd++;
359}
360
361/**
362 * Returns the next char in the input buffer without advancing it.
363 *
364 * @returns Next character in the input buffer.
365 * @param pTokenizer The tokenizer state.
366 */
367DECLINLINE(char) rtJsonTokenizerPeekCh(PRTJSONTOKENIZER pTokenizer)
368{
369 return rtJsonTokenizerIsEos(pTokenizer)
370 ? '\0'
371 : pTokenizer->achBuf[pTokenizer->offBuf + 1]; /** @todo Read out of bounds */
372}
373
374/**
375 * Returns the next character in the input buffer advancing the internal
376 * position.
377 *
378 * @returns Next character in the stream.
379 * @param pTokenizer The tokenizer state.
380 */
381DECLINLINE(char) rtJsonTokenizerGetCh(PRTJSONTOKENIZER pTokenizer)
382{
383 char ch;
384
385 if (rtJsonTokenizerIsEos(pTokenizer))
386 ch = '\0';
387 else
388 ch = pTokenizer->achBuf[pTokenizer->offBuf];
389
390 return ch;
391}
392
393/**
394 * Sets a new line for the tokenizer.
395 *
396 * @returns nothing.
397 * @param pTokenizer The tokenizer state.
398 * @param cSkip Amount of characters to skip making up the new line.
399 */
400DECLINLINE(void) rtJsonTokenizerNewLine(PRTJSONTOKENIZER pTokenizer, unsigned cSkip)
401{
402 rtJsonTokenizerSkip(pTokenizer, cSkip);
403 pTokenizer->Pos.iLine++;
404 pTokenizer->Pos.iChStart = 1;
405 pTokenizer->Pos.iChEnd = 1;
406}
407
408/**
409 * Checks whether the current position in the input stream is a new line
410 * and skips it.
411 *
412 * @returns Flag whether there was a new line at the current position
413 * in the input buffer.
414 * @param pTokenizer The tokenizer state.
415 */
416DECLINLINE(bool) rtJsonTokenizerIsSkipNewLine(PRTJSONTOKENIZER pTokenizer)
417{
418 bool fNewline = true;
419
420 if ( rtJsonTokenizerGetCh(pTokenizer) == '\r'
421 && rtJsonTokenizerPeekCh(pTokenizer) == '\n')
422 rtJsonTokenizerNewLine(pTokenizer, 2);
423 else if (rtJsonTokenizerGetCh(pTokenizer) == '\n')
424 rtJsonTokenizerNewLine(pTokenizer, 1);
425 else
426 fNewline = false;
427
428 return fNewline;
429}
430
431/**
432 * Skip all whitespace starting from the current input buffer position.
433 * Skips all present comments too.
434 *
435 * @returns nothing.
436 * @param pTokenizer The tokenizer state.
437 */
438DECLINLINE(void) rtJsonTokenizerSkipWhitespace(PRTJSONTOKENIZER pTokenizer)
439{
440 while (!rtJsonTokenizerIsEos(pTokenizer))
441 {
442 while ( rtJsonTokenizerGetCh(pTokenizer) == ' '
443 || rtJsonTokenizerGetCh(pTokenizer) == '\t')
444 rtJsonTokenizerSkipCh(pTokenizer);
445
446 if ( !rtJsonTokenizerIsEos(pTokenizer)
447 && !rtJsonTokenizerIsSkipNewLine(pTokenizer))
448 break; /* Skipped everything, next is some real content. */
449 }
450}
451
452/**
453 * Get an literal token from the tokenizer.
454 *
455 * @returns IPRT status code.
456 * @param pTokenizer The tokenizer state.
457 * @param pToken The uninitialized token.
458 */
459static int rtJsonTokenizerGetLiteral(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken)
460{
461 int rc = VINF_SUCCESS;
462 char ch = rtJsonTokenizerGetCh(pTokenizer);
463 size_t cchLiteral = 0;
464 char szLiteral[6]; /* false + 0 terminator as the lingest possible literal. */
465 RT_ZERO(szLiteral);
466
467 pToken->Pos = pTokenizer->Pos;
468
469 Assert(RT_C_IS_ALPHA(ch));
470
471 while ( RT_C_IS_ALPHA(ch)
472 && cchLiteral < RT_ELEMENTS(szLiteral) - 1)
473 {
474 szLiteral[cchLiteral] = ch;
475 cchLiteral++;
476 rtJsonTokenizerSkipCh(pTokenizer);
477 ch = rtJsonTokenizerGetCh(pTokenizer);
478 }
479
480 if (!RTStrNCmp(&szLiteral[0], "false", RT_ELEMENTS(szLiteral)))
481 pToken->enmClass = RTJSONTOKENCLASS_FALSE;
482 else if (!RTStrNCmp(&szLiteral[0], "true", RT_ELEMENTS(szLiteral)))
483 pToken->enmClass = RTJSONTOKENCLASS_TRUE;
484 else if (!RTStrNCmp(&szLiteral[0], "null", RT_ELEMENTS(szLiteral)))
485 pToken->enmClass = RTJSONTOKENCLASS_NULL;
486 else
487 {
488 pToken->enmClass = RTJSONTOKENCLASS_INVALID;
489 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "malformed literal '%.6s' (line %zu col %zu)",
490 &szLiteral[0], pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
491 }
492
493 pToken->Pos.iChEnd += cchLiteral;
494 return rc;
495}
496
497/**
498 * Get a numerical constant from the tokenizer.
499 *
500 * @returns IPRT status code.
501 * @param pTokenizer The tokenizer state.
502 * @param pToken The uninitialized token.
503 */
504static int rtJsonTokenizerGetNumber(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken)
505{
506 size_t cchNum = 0;
507 char szTmp[128]; /* Everything larger is not possible to display in signed 64bit. */
508
509 pToken->enmClass = RTJSONTOKENCLASS_INTEGER;
510
511 char ch = rtJsonTokenizerGetCh(pTokenizer);
512 if (ch == '-')
513 {
514 szTmp[cchNum++] = '-';
515 rtJsonTokenizerSkipCh(pTokenizer);
516 ch = rtJsonTokenizerGetCh(pTokenizer);
517 }
518
519 while ( RT_C_IS_DIGIT(ch)
520 && cchNum < sizeof(szTmp) - 1)
521 {
522 szTmp[cchNum] = ch;
523 cchNum++;
524 rtJsonTokenizerSkipCh(pTokenizer);
525 ch = rtJsonTokenizerGetCh(pTokenizer);
526 }
527
528 int rc = VINF_SUCCESS;
529 if (RT_C_IS_DIGIT(ch) && cchNum >= sizeof(szTmp) - 1)
530 rc = VERR_NUMBER_TOO_BIG;
531 else if (ch != '.')
532 {
533 szTmp[cchNum] = '\0';
534 rc = RTStrToInt64Ex(&szTmp[0], NULL, 10, &pToken->Class.Integer.i64Num);
535 Assert(RT_SUCCESS(rc) || rc == VWRN_NUMBER_TOO_BIG);
536 if (rc == VWRN_NUMBER_TOO_BIG)
537 rc = VERR_NUMBER_TOO_BIG;
538 }
539 else
540 {
541 /*
542 * A floating point value.
543 */
544 pToken->enmClass = RTJSONTOKENCLASS_NUMBER;
545 rtJsonTokenizerSkipCh(pTokenizer);
546 szTmp[cchNum++] = '.';
547
548 ch = rtJsonTokenizerGetCh(pTokenizer);
549 while ( RT_C_IS_DIGIT(ch)
550 && cchNum < sizeof(szTmp) - 1)
551 {
552 szTmp[cchNum++] = ch;
553 rtJsonTokenizerSkipCh(pTokenizer);
554 ch = rtJsonTokenizerGetCh(pTokenizer);
555 }
556 if ( (ch == 'e' || ch == 'E')
557 && cchNum < sizeof(szTmp) - 2)
558 {
559 szTmp[cchNum++] = 'e';
560 rtJsonTokenizerSkipCh(pTokenizer);
561 ch = rtJsonTokenizerGetCh(pTokenizer);
562 if (ch == '+' || ch == '-')
563 {
564 szTmp[cchNum++] = ch;
565 rtJsonTokenizerSkipCh(pTokenizer);
566 ch = rtJsonTokenizerGetCh(pTokenizer);
567 }
568 while ( RT_C_IS_DIGIT(ch)
569 && cchNum < sizeof(szTmp) - 1)
570 {
571 szTmp[cchNum++] = ch;
572 rtJsonTokenizerSkipCh(pTokenizer);
573 ch = rtJsonTokenizerGetCh(pTokenizer);
574 }
575 }
576 if (cchNum < sizeof(szTmp) - 1)
577 {
578 szTmp[cchNum] = '\0';
579
580 /** @todo Not sure if strtod does the 100% right thing here... */
581 errno = 0;
582 char *pszNext = NULL;
583 pToken->Class.rdNum = strtod(szTmp, &pszNext);
584 if (errno == 0)
585 {
586 rc = VINF_SUCCESS;
587 Assert(!pszNext || *pszNext == '\0');
588 }
589 else
590 rc = RTErrConvertFromErrno(errno);
591 }
592 }
593
594 return rc;
595}
596
597/**
598 * Parses a string constant.
599 *
600 * @returns IPRT status code.
601 * @param pTokenizer The tokenizer state.
602 * @param pToken The uninitialized token.
603 */
604static int rtJsonTokenizerGetString(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken)
605{
606 int rc = VINF_SUCCESS;
607 size_t cchStr = 0;
608 size_t cchStrMax = _4K;
609 char *pszTmp = (char *)RTMemAllocZ(cchStrMax * sizeof(char));
610 if (RT_UNLIKELY(!pszTmp))
611 return VERR_NO_STR_MEMORY;
612
613 RT_BZERO(pszTmp, cchStrMax);
614
615 Assert(rtJsonTokenizerGetCh(pTokenizer) == '\"');
616 rtJsonTokenizerSkipCh(pTokenizer); /* Skip " */
617
618 pToken->enmClass = RTJSONTOKENCLASS_STRING;
619 pToken->Pos = pTokenizer->Pos;
620
621 char ch = rtJsonTokenizerGetCh(pTokenizer);
622 while ( ch != '\"'
623 && ch != '\0'
624 && RT_SUCCESS(rc))
625 {
626 if (ch == '\\')
627 {
628 /* Escape sequence, check the next character */
629 rtJsonTokenizerSkipCh(pTokenizer);
630 char chNext = rtJsonTokenizerGetCh(pTokenizer);
631 switch (chNext)
632 {
633 case '\"':
634 pszTmp[cchStr] = '\"';
635 break;
636 case '\\':
637 pszTmp[cchStr] = '\\';
638 break;
639 case '/':
640 pszTmp[cchStr] = '/';
641 break;
642 case '\b':
643 pszTmp[cchStr] = '\b';
644 break;
645 case '\n':
646 pszTmp[cchStr] = '\n';
647 break;
648 case '\f':
649 pszTmp[cchStr] = '\f';
650 break;
651 case '\r':
652 pszTmp[cchStr] = '\r';
653 break;
654 case '\t':
655 pszTmp[cchStr] = '\t';
656 break;
657 case 'u':
658 rc = VERR_NOT_SUPPORTED;
659 break;
660 default:
661 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "bad escape sequence (line %zu col %zu)",
662 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
663 break;
664 }
665 }
666 else
667 pszTmp[cchStr] = ch;
668
669 if (RT_FAILURE(rc))
670 break;
671
672 cchStr++;
673 rtJsonTokenizerSkipCh(pTokenizer);
674 if (cchStr == cchStrMax - 1)
675 {
676 /* Increase string space. */
677 size_t cchStrMaxNew = cchStrMax + _4K;
678 char *pszTmpNew = (char *)RTMemRealloc(pszTmp, cchStrMaxNew * sizeof(char));
679 if (RT_UNLIKELY(!pszTmpNew))
680 {
681 rc = VERR_NO_STR_MEMORY;
682 break;
683 }
684
685 RT_BZERO(&pszTmpNew[cchStr], _4K);
686 pszTmp = pszTmpNew;
687 cchStrMax = cchStrMaxNew;
688 }
689 ch = rtJsonTokenizerGetCh(pTokenizer);
690 }
691
692 if (RT_SUCCESS(rc))
693 {
694 if (rtJsonTokenizerGetCh(pTokenizer) == '\"')
695 rtJsonTokenizerSkipCh(pTokenizer); /* Skip closing " */
696
697 pToken->Class.String.pszStr = RTStrDupN(pszTmp, cchStr);
698 if (pToken->Class.String.pszStr)
699 pToken->Pos.iChEnd += cchStr;
700 else
701 rc = VERR_NO_STR_MEMORY;
702 }
703
704 if (pszTmp)
705 RTMemFree(pszTmp);
706
707 return rc;
708}
709
710/**
711 * Get the end of stream token.
712 *
713 * @returns IPRT status code.
714 * @param pTokenizer The tokenizer state.
715 * @param pToken The uninitialized token.
716 */
717static int rtJsonTokenizerGetEos(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken)
718{
719 Assert(rtJsonTokenizerGetCh(pTokenizer) == '\0');
720
721 pToken->enmClass = RTJSONTOKENCLASS_EOS;
722 pToken->Pos = pTokenizer->Pos;
723 return VINF_SUCCESS;
724}
725
726/**
727 * Read the next token from the tokenizer stream.
728 *
729 * @returns IPRT status code.
730 * @param pTokenizer The tokenizer to read from.
731 * @param pToken Uninitialized token to fill the token data into.
732 */
733static int rtJsonTokenizerReadNextToken(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken)
734{
735 int rc = VINF_SUCCESS;
736
737 /* Skip all eventually existing whitespace and newlines first. */
738 rtJsonTokenizerSkipWhitespace(pTokenizer);
739
740 char ch = rtJsonTokenizerGetCh(pTokenizer);
741 if (RT_C_IS_ALPHA(ch))
742 rc = rtJsonTokenizerGetLiteral(pTokenizer, pToken);
743 else if (RT_C_IS_DIGIT(ch) || ch == '-')
744 rc = rtJsonTokenizerGetNumber(pTokenizer, pToken);
745 else if (ch == '\"')
746 rc = rtJsonTokenizerGetString(pTokenizer, pToken);
747 else if (ch == '\0')
748 rc = rtJsonTokenizerGetEos(pTokenizer, pToken);
749 else if (ch == '{')
750 {
751 pToken->enmClass = RTJSONTOKENCLASS_BEGIN_OBJECT;
752 rtJsonTokenizerSkipCh(pTokenizer);
753 }
754 else if (ch == '}')
755 {
756 pToken->enmClass = RTJSONTOKENCLASS_END_OBJECT;
757 rtJsonTokenizerSkipCh(pTokenizer);
758 }
759 else if (ch == '[')
760 {
761 pToken->enmClass = RTJSONTOKENCLASS_BEGIN_ARRAY;
762 rtJsonTokenizerSkipCh(pTokenizer);
763 }
764 else if (ch == ']')
765 {
766 pToken->enmClass = RTJSONTOKENCLASS_END_ARRAY;
767 rtJsonTokenizerSkipCh(pTokenizer);
768 }
769 else if (ch == ':')
770 {
771 pToken->enmClass = RTJSONTOKENCLASS_NAME_SEPARATOR;
772 rtJsonTokenizerSkipCh(pTokenizer);
773 }
774 else if (ch == ',')
775 {
776 pToken->enmClass = RTJSONTOKENCLASS_VALUE_SEPARATOR;
777 rtJsonTokenizerSkipCh(pTokenizer);
778 }
779 else
780 {
781 pToken->enmClass = RTJSONTOKENCLASS_INVALID;
782 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "bad token '%c' (line %zu col %zu)",
783 ch, pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
784 }
785
786 if (RT_FAILURE(rc))
787 pTokenizer->rcTok = rc;
788
789 return rc;
790}
791
792/**
793 * Create a new tokenizer.
794 *
795 * @returns IPRT status code.
796 * @param pTokenizer The tokenizer state to initialize.
797 * @param pfnRead Read callback for the input stream.
798 * @param pvUser Opaque user data to pass to the callback.
799 * @param pErrInfo Where to return extended error info.
800 */
801static int rtJsonTokenizerInit(PRTJSONTOKENIZER pTokenizer, PFNRTJSONTOKENIZERREAD pfnRead, void *pvUser, PRTERRINFO pErrInfo)
802{
803 pTokenizer->pfnRead = pfnRead;
804 pTokenizer->pvUser = pvUser;
805 pTokenizer->offInput = 0;
806 pTokenizer->cbBuf = 0;
807 pTokenizer->offBuf = 0;
808 pTokenizer->Pos.iLine = 1;
809 pTokenizer->Pos.iChStart = 1;
810 pTokenizer->Pos.iChEnd = 1;
811 pTokenizer->pTokenCurr = &pTokenizer->Token1;
812 pTokenizer->pTokenNext = &pTokenizer->Token2;
813 pTokenizer->rcTok = VINF_SUCCESS;
814 pTokenizer->pErrInfo = pErrInfo;
815
816 RT_ZERO(pTokenizer->achBuf);
817
818 /* Fill the input buffer. */
819 int rc = rtJsonTokenizerRead(pTokenizer);
820
821 /* Fill the tokenizer with two first tokens. */
822 if (RT_SUCCESS(rc))
823 rc = rtJsonTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenCurr);
824 if (RT_SUCCESS(rc))
825 rc = rtJsonTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
826
827 return rc;
828}
829
830/**
831 * Destroys a given tokenizer state.
832 *
833 * @returns nothing.
834 * @param pTokenizer The tokenizer to destroy.
835 */
836static void rtJsonTokenizerDestroy(PRTJSONTOKENIZER pTokenizer)
837{
838 RT_NOREF_PV(pTokenizer);
839}
840
841/**
842 * Get the current token in the input stream.
843 *
844 * @returns Pointer to the next token in the stream.
845 * @param pTokenizer The tokenizer state.
846 * @param ppToken Where to store the pointer to the current token on success.
847 */
848DECLINLINE(int) rtJsonTokenizerGetToken(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN *ppToken)
849{
850 if (RT_SUCCESS(pTokenizer->rcTok))
851 {
852 *ppToken = pTokenizer->pTokenCurr;
853 return VINF_SUCCESS;
854 }
855
856 return pTokenizer->rcTok;
857}
858
859/**
860 * Consume the current token advancing to the next in the stream.
861 *
862 * @returns nothing.
863 * @param pTokenizer The tokenizer state.
864 */
865static void rtJsonTokenizerConsume(PRTJSONTOKENIZER pTokenizer)
866{
867 PRTJSONTOKEN pTokenTmp = pTokenizer->pTokenCurr;
868
869 /* Switch next token to current token and read in the next token. */
870 pTokenizer->pTokenCurr = pTokenizer->pTokenNext;
871 pTokenizer->pTokenNext = pTokenTmp;
872 rtJsonTokenizerReadNextToken(pTokenizer, pTokenizer->pTokenNext);
873}
874
875/**
876 * Consumes the current token if it matches the given class returning an indicator.
877 *
878 * @returns true if the class matched and the token was consumed.
879 * @retval false otherwise.
880 * @param pTokenizer The tokenizer state.
881 * @param enmClass The token class to match against.
882 */
883static bool rtJsonTokenizerConsumeIfMatched(PRTJSONTOKENIZER pTokenizer, RTJSONTOKENCLASS enmClass)
884{
885 PRTJSONTOKEN pToken = NULL;
886 int rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
887 if (RT_SUCCESS(rc))
888 {
889 if (pToken->enmClass == enmClass)
890 {
891 rtJsonTokenizerConsume(pTokenizer);
892 return true;
893 }
894 }
895
896 return false;
897}
898
899/**
900 * Destroys a given JSON value releasing the reference to all child values.
901 *
902 * @returns nothing.
903 * @param pThis The JSON value to destroy.
904 */
905static void rtJsonValDestroy(PRTJSONVALINT pThis)
906{
907 switch (pThis->enmType)
908 {
909 case RTJSONVALTYPE_OBJECT:
910 for (unsigned i = 0; i < pThis->Type.Object.cMembers; i++)
911 {
912 RTStrFree(pThis->Type.Object.papszNames[i]);
913 RTJsonValueRelease(pThis->Type.Object.papValues[i]);
914 }
915 RTMemFree(pThis->Type.Object.papszNames);
916 RTMemFree(pThis->Type.Object.papValues);
917 break;
918 case RTJSONVALTYPE_ARRAY:
919 for (unsigned i = 0; i < pThis->Type.Array.cItems; i++)
920 RTJsonValueRelease(pThis->Type.Array.papItems[i]);
921 RTMemFree(pThis->Type.Array.papItems);
922 break;
923 case RTJSONVALTYPE_STRING:
924 RTStrFree(pThis->Type.String.pszStr);
925 break;
926 case RTJSONVALTYPE_INTEGER:
927 case RTJSONVALTYPE_NUMBER:
928 case RTJSONVALTYPE_NULL:
929 case RTJSONVALTYPE_TRUE:
930 case RTJSONVALTYPE_FALSE:
931 /* nothing to do. */
932 break;
933 default:
934 AssertMsgFailed(("Invalid JSON value type: %u\n", pThis->enmType));
935 }
936 RTMemFree(pThis);
937}
938
939/**
940 * Creates a new JSON value with the given type.
941 *
942 * @returns Pointer to JSON value on success, NULL if out of memory.
943 * @param enmType The JSON value type.
944 */
945static PRTJSONVALINT rtJsonValueCreate(RTJSONVALTYPE enmType)
946{
947 PRTJSONVALINT pThis = (PRTJSONVALINT)RTMemAllocZ(sizeof(RTJSONVALINT));
948 if (RT_LIKELY(pThis))
949 {
950 pThis->enmType = enmType;
951 pThis->cRefs = 1;
952 }
953
954 return pThis;
955}
956
957/**
958 * Parses an JSON array.
959 *
960 * @returns IPRT status code.
961 * @param pTokenizer The tokenizer to use.
962 * @param pJsonVal The JSON array value to fill in.
963 */
964static int rtJsonParseArray(PRTJSONTOKENIZER pTokenizer, PRTJSONVALINT pJsonVal)
965{
966 int rc = VINF_SUCCESS;
967 PRTJSONTOKEN pToken = NULL;
968 uint32_t cItems = 0;
969 uint32_t cItemsMax = 0;
970 PRTJSONVALINT *papItems = NULL;
971
972 rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
973 while ( RT_SUCCESS(rc)
974 && pToken->enmClass != RTJSONTOKENCLASS_END_ARRAY
975 && pToken->enmClass != RTJSONTOKENCLASS_EOS)
976 {
977 PRTJSONVALINT pVal = NULL;
978 rc = rtJsonParseValue(pTokenizer, pToken, &pVal);
979 if (RT_SUCCESS(rc))
980 {
981 if (cItems == cItemsMax)
982 {
983 cItemsMax += 10;
984 PRTJSONVALINT *papItemsNew = (PRTJSONVALINT *)RTMemRealloc(papItems, cItemsMax * sizeof(PRTJSONVALINT));
985 if (RT_UNLIKELY(!papItemsNew))
986 {
987 rc = VERR_NO_MEMORY;
988 break;
989 }
990 papItems = papItemsNew;
991 }
992
993 Assert(cItems < cItemsMax);
994 papItems[cItems] = pVal;
995 cItems++;
996 }
997
998 /* Skip value separator and continue with next token. */
999 bool fSkippedSep = rtJsonTokenizerConsumeIfMatched(pTokenizer, RTJSONTOKENCLASS_VALUE_SEPARATOR);
1000 rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
1001
1002 if ( RT_SUCCESS(rc)
1003 && !fSkippedSep
1004 && pToken->enmClass != RTJSONTOKENCLASS_END_ARRAY)
1005 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "expected end of array (#1) (line %zu col %zu)",
1006 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1007 }
1008
1009 if (RT_SUCCESS(rc))
1010 {
1011 if (pToken->enmClass == RTJSONTOKENCLASS_END_ARRAY)
1012 {
1013 rtJsonTokenizerConsume(pTokenizer);
1014 pJsonVal->Type.Array.cItems = cItems;
1015 pJsonVal->Type.Array.papItems = papItems;
1016 }
1017 else
1018 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "expected end of array (#2) (line %zu col %zu)",
1019 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1020 }
1021
1022 if (RT_FAILURE(rc))
1023 {
1024 for (uint32_t i = 0; i < cItems; i++)
1025 RTJsonValueRelease(papItems[i]);
1026 RTMemFree(papItems);
1027 }
1028
1029 return rc;
1030}
1031
1032/**
1033 * Parses an JSON object.
1034 *
1035 * @returns IPRT status code.
1036 * @param pTokenizer The tokenizer to use.
1037 * @param pJsonVal The JSON object value to fill in.
1038 */
1039static int rtJsonParseObject(PRTJSONTOKENIZER pTokenizer, PRTJSONVALINT pJsonVal)
1040{
1041 int rc = VINF_SUCCESS;
1042 PRTJSONTOKEN pToken = NULL;
1043 uint32_t cMembers = 0;
1044 uint32_t cMembersMax = 0;
1045 PRTJSONVALINT *papValues = NULL;
1046 char **papszNames = NULL;
1047
1048 rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
1049 while ( RT_SUCCESS(rc)
1050 && pToken->enmClass == RTJSONTOKENCLASS_STRING)
1051 {
1052 char *pszName = pToken->Class.String.pszStr; /* We can consume this string as it was allocated. */
1053
1054 rtJsonTokenizerConsume(pTokenizer);
1055 if (rtJsonTokenizerConsumeIfMatched(pTokenizer, RTJSONTOKENCLASS_NAME_SEPARATOR))
1056 {
1057 PRTJSONVALINT pVal = NULL;
1058 rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
1059 if (RT_SUCCESS(rc))
1060 rc = rtJsonParseValue(pTokenizer, pToken, &pVal);
1061 if (RT_SUCCESS(rc))
1062 {
1063 if (cMembers == cMembersMax)
1064 {
1065 cMembersMax += 10;
1066 PRTJSONVALINT *papValuesNew = (PRTJSONVALINT *)RTMemRealloc(papValues, cMembersMax * sizeof(PRTJSONVALINT));
1067 char **papszNamesNew = (char **)RTMemRealloc(papszNames, cMembersMax * sizeof(char *));
1068 if (RT_UNLIKELY(!papValuesNew || !papszNamesNew))
1069 {
1070 if (papValuesNew)
1071 RTMemFree(papValuesNew);
1072 if (papszNamesNew)
1073 RTMemFree(papszNamesNew);
1074 rc = VERR_NO_MEMORY;
1075 break;
1076 }
1077
1078 papValues = papValuesNew;
1079 papszNames = papszNamesNew;
1080 }
1081
1082 Assert(cMembers < cMembersMax);
1083 papszNames[cMembers] = pszName;
1084 papValues[cMembers] = pVal;
1085 cMembers++;
1086
1087 /* Skip value separator and continue with next token. */
1088 bool fSkippedSep = rtJsonTokenizerConsumeIfMatched(pTokenizer, RTJSONTOKENCLASS_VALUE_SEPARATOR);
1089 rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
1090
1091 if ( RT_SUCCESS(rc)
1092 && !fSkippedSep
1093 && pToken->enmClass != RTJSONTOKENCLASS_END_OBJECT)
1094 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "expected end of object (#1) (line %zu col %zu)",
1095 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1096 }
1097 }
1098 else
1099 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "expected name separator (line %zu col %zu)",
1100 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1101 }
1102
1103 if (RT_SUCCESS(rc))
1104 {
1105 if (pToken->enmClass == RTJSONTOKENCLASS_END_OBJECT)
1106 {
1107 rtJsonTokenizerConsume(pTokenizer);
1108 pJsonVal->Type.Object.cMembers = cMembers;
1109 pJsonVal->Type.Object.papValues = papValues;
1110 pJsonVal->Type.Object.papszNames = papszNames;
1111 }
1112 else
1113 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "expected end of object (#2) (line %zu col %zu)",
1114 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1115 }
1116
1117 if (RT_FAILURE(rc))
1118 {
1119 for (uint32_t i = 0; i < cMembers; i++)
1120 {
1121 RTJsonValueRelease(papValues[i]);
1122 RTStrFree(papszNames[i]);
1123 }
1124 RTMemFree(papValues);
1125 RTMemFree(papszNames);
1126 }
1127
1128 return rc;
1129}
1130
1131/**
1132 * Parses a single JSON value and returns it on success.
1133 *
1134 * @returns IPRT status code.
1135 * @param pTokenizer The tokenizer to use.
1136 * @param pToken The token to parse.
1137 * @param ppJsonVal Where to store the pointer to the JSON value on success.
1138 */
1139static int rtJsonParseValue(PRTJSONTOKENIZER pTokenizer, PRTJSONTOKEN pToken, PRTJSONVALINT *ppJsonVal)
1140{
1141 int rc = VINF_SUCCESS;
1142 PRTJSONVALINT pVal = NULL;
1143
1144 switch (pToken->enmClass)
1145 {
1146 case RTJSONTOKENCLASS_BEGIN_ARRAY:
1147 rtJsonTokenizerConsume(pTokenizer);
1148 pVal = rtJsonValueCreate(RTJSONVALTYPE_ARRAY);
1149 if (RT_LIKELY(pVal))
1150 rc = rtJsonParseArray(pTokenizer, pVal);
1151 break;
1152 case RTJSONTOKENCLASS_BEGIN_OBJECT:
1153 rtJsonTokenizerConsume(pTokenizer);
1154 pVal = rtJsonValueCreate(RTJSONVALTYPE_OBJECT);
1155 if (RT_LIKELY(pVal))
1156 rc = rtJsonParseObject(pTokenizer, pVal);
1157 break;
1158 case RTJSONTOKENCLASS_STRING:
1159 pVal = rtJsonValueCreate(RTJSONVALTYPE_STRING);
1160 if (RT_LIKELY(pVal))
1161 pVal->Type.String.pszStr = pToken->Class.String.pszStr;
1162 rtJsonTokenizerConsume(pTokenizer);
1163 break;
1164 case RTJSONTOKENCLASS_INTEGER:
1165 pVal = rtJsonValueCreate(RTJSONVALTYPE_INTEGER);
1166 if (RT_LIKELY(pVal))
1167 pVal->Type.Integer.i64Num = pToken->Class.Integer.i64Num;
1168 rtJsonTokenizerConsume(pTokenizer);
1169 break;
1170 case RTJSONTOKENCLASS_NUMBER:
1171 pVal = rtJsonValueCreate(RTJSONVALTYPE_NUMBER);
1172 if (RT_LIKELY(pVal))
1173 pVal->Type.rdNum = pToken->Class.rdNum;
1174 rtJsonTokenizerConsume(pTokenizer);
1175 break;
1176 case RTJSONTOKENCLASS_NULL:
1177 rtJsonTokenizerConsume(pTokenizer);
1178 pVal = rtJsonValueCreate(RTJSONVALTYPE_NULL);
1179 break;
1180 case RTJSONTOKENCLASS_FALSE:
1181 rtJsonTokenizerConsume(pTokenizer);
1182 pVal = rtJsonValueCreate(RTJSONVALTYPE_FALSE);
1183 break;
1184 case RTJSONTOKENCLASS_TRUE:
1185 rtJsonTokenizerConsume(pTokenizer);
1186 pVal = rtJsonValueCreate(RTJSONVALTYPE_TRUE);
1187 break;
1188
1189 case RTJSONTOKENCLASS_INVALID:
1190 Assert(!pTokenizer->pErrInfo || RTErrInfoIsSet(pTokenizer->pErrInfo));
1191 rc = VERR_JSON_MALFORMED;
1192 break;
1193 case RTJSONTOKENCLASS_END_ARRAY:
1194 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "unexpected '}' (line %zu col %zu)",
1195 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1196 break;
1197 case RTJSONTOKENCLASS_END_OBJECT:
1198 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "unexpected ']' (line %zu col %zu)",
1199 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1200 break;
1201 case RTJSONTOKENCLASS_NAME_SEPARATOR:
1202 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "unexpected ':' (line %zu col %zu)",
1203 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1204 break;
1205 case RTJSONTOKENCLASS_VALUE_SEPARATOR:
1206 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "unexpected ',' (line %zu col %zu)",
1207 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1208 break;
1209 case RTJSONTOKENCLASS_EOS:
1210 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "expected end of object (#1) (line %zu col %zu)",
1211 pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1212 break;
1213 default:
1214 rc = RTErrInfoSetF(pTokenizer->pErrInfo, VERR_JSON_MALFORMED, "Unexpected token class %d (line %zu col %zu)",
1215 pToken->enmClass, pTokenizer->Pos.iLine, pTokenizer->Pos.iChStart);
1216 break;
1217 }
1218
1219 if (RT_SUCCESS(rc))
1220 {
1221 if (pVal)
1222 *ppJsonVal = pVal;
1223 else
1224 rc = VERR_NO_MEMORY;
1225 }
1226 else if (pVal)
1227 rtJsonValDestroy(pVal);
1228
1229 return rc;
1230}
1231
1232/**
1233 * Entry point to parse a JSON document.
1234 *
1235 * @returns IPRT status code.
1236 * @param pTokenizer The tokenizer state.
1237 * @param ppJsonVal Where to store the root JSON value on success.
1238 */
1239static int rtJsonParse(PRTJSONTOKENIZER pTokenizer, PRTJSONVALINT *ppJsonVal)
1240{
1241 PRTJSONTOKEN pToken = NULL;
1242 int rc = rtJsonTokenizerGetToken(pTokenizer, &pToken);
1243 if (RT_SUCCESS(rc))
1244 rc = rtJsonParseValue(pTokenizer, pToken, ppJsonVal);
1245
1246 return rc;
1247}
1248
1249/**
1250 * Read callback for RTJsonParseFromBuf().
1251 */
1252static DECLCALLBACK(int) rtJsonTokenizerParseFromBuf(void *pvUser, size_t offInput,
1253 void *pvBuf, size_t cbBuf,
1254 size_t *pcbRead)
1255{
1256 PRTJSONREADERARGS pArgs = (PRTJSONREADERARGS)pvUser;
1257 size_t cbLeft = offInput < pArgs->cbData ? pArgs->cbData - offInput : 0;
1258
1259 if (cbLeft)
1260 memcpy(pvBuf, &pArgs->u.pbBuf[offInput], RT_MIN(cbLeft, cbBuf));
1261
1262 *pcbRead = RT_MIN(cbLeft, cbBuf);
1263
1264 return VINF_SUCCESS;
1265}
1266
1267/**
1268 * Read callback for RTJsonParseFromString().
1269 */
1270static DECLCALLBACK(int) rtJsonTokenizerParseFromString(void *pvUser, size_t offInput,
1271 void *pvBuf, size_t cbBuf,
1272 size_t *pcbRead)
1273{
1274 const char *pszStr = (const char *)pvUser;
1275 size_t cchStr = strlen(pszStr) + 1; /* Include zero terminator. */
1276 size_t cbLeft = offInput < cchStr ? cchStr - offInput : 0;
1277
1278 if (cbLeft)
1279 memcpy(pvBuf, &pszStr[offInput], RT_MIN(cbLeft, cbBuf));
1280
1281 *pcbRead = RT_MIN(cbLeft, cbBuf);
1282
1283 return VINF_SUCCESS;
1284}
1285
1286/**
1287 * Read callback for RTJsonParseFromFile().
1288 */
1289static DECLCALLBACK(int) rtJsonTokenizerParseFromFile(void *pvUser, size_t offInput,
1290 void *pvBuf, size_t cbBuf,
1291 size_t *pcbRead)
1292{
1293 PRTJSONREADERARGS pArgs = (PRTJSONREADERARGS)pvUser;
1294
1295 RT_NOREF_PV(offInput);
1296
1297 size_t cbRead = 0;
1298 int rc = RTStrmReadEx(pArgs->u.hStream, pvBuf, cbBuf, &cbRead);
1299 if (RT_SUCCESS(rc))
1300 *pcbRead = cbRead;
1301
1302 return rc;
1303}
1304
1305RTDECL(int) RTJsonParseFromBuf(PRTJSONVAL phJsonVal, const uint8_t *pbBuf, size_t cbBuf, PRTERRINFO pErrInfo)
1306{
1307 AssertPtrReturn(phJsonVal, VERR_INVALID_POINTER);
1308 AssertPtrReturn(pbBuf, VERR_INVALID_POINTER);
1309 AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
1310
1311 RTJSONTOKENIZER Tokenizer;
1312 RTJSONREADERARGS Args;
1313 Args.cbData = cbBuf;
1314 Args.u.pbBuf = pbBuf;
1315
1316 int rc = rtJsonTokenizerInit(&Tokenizer, rtJsonTokenizerParseFromBuf, &Args, pErrInfo);
1317 if (RT_SUCCESS(rc))
1318 {
1319 rc = rtJsonParse(&Tokenizer, phJsonVal);
1320 rtJsonTokenizerDestroy(&Tokenizer);
1321 }
1322
1323 return rc;
1324}
1325
1326RTDECL(int) RTJsonParseFromString(PRTJSONVAL phJsonVal, const char *pszStr, PRTERRINFO pErrInfo)
1327{
1328 AssertPtrReturn(phJsonVal, VERR_INVALID_POINTER);
1329 AssertPtrReturn(pszStr, VERR_INVALID_POINTER);
1330
1331 /** @todo r=bird: The rtJsonTokenizerParseFromString function does
1332 * strlen() on the whole pszStr for each read. For larger strings (
1333 * longer than sizeof(Tokenizer.achBuf)) it would be good to join
1334 * forces with RTJsonParseFromBuf. */
1335 RTJSONTOKENIZER Tokenizer;
1336 int rc = rtJsonTokenizerInit(&Tokenizer, rtJsonTokenizerParseFromString, (void *)pszStr, pErrInfo);
1337 if (RT_SUCCESS(rc))
1338 {
1339 rc = rtJsonParse(&Tokenizer, phJsonVal);
1340 rtJsonTokenizerDestroy(&Tokenizer);
1341 }
1342
1343 return rc;
1344}
1345
1346RTDECL(int) RTJsonParseFromFile(PRTJSONVAL phJsonVal, const char *pszFilename, PRTERRINFO pErrInfo)
1347{
1348 AssertPtrReturn(phJsonVal, VERR_INVALID_POINTER);
1349 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
1350
1351 int rc = VINF_SUCCESS;
1352 RTJSONREADERARGS Args;
1353
1354 Args.cbData = 0;
1355 rc = RTStrmOpen(pszFilename, "r", &Args.u.hStream);
1356 if (RT_SUCCESS(rc))
1357 {
1358 RTJSONTOKENIZER Tokenizer;
1359
1360 rc = rtJsonTokenizerInit(&Tokenizer, rtJsonTokenizerParseFromFile, &Args, pErrInfo);
1361 if (RT_SUCCESS(rc))
1362 {
1363 rc = rtJsonParse(&Tokenizer, phJsonVal);
1364 rtJsonTokenizerDestroy(&Tokenizer);
1365 }
1366 RTStrmClose(Args.u.hStream);
1367 }
1368
1369 return rc;
1370}
1371
1372RTDECL(uint32_t) RTJsonValueRetain(RTJSONVAL hJsonVal)
1373{
1374 PRTJSONVALINT pThis = hJsonVal;
1375 AssertPtrReturn(pThis, UINT32_MAX);
1376
1377 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
1378 AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
1379 return cRefs;
1380}
1381
1382RTDECL(uint32_t) RTJsonValueRelease(RTJSONVAL hJsonVal)
1383{
1384 PRTJSONVALINT pThis = hJsonVal;
1385 if (pThis == NIL_RTJSONVAL)
1386 return 0;
1387 AssertPtrReturn(pThis, UINT32_MAX);
1388
1389 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
1390 AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
1391 if (cRefs == 0)
1392 rtJsonValDestroy(pThis);
1393 return cRefs;
1394}
1395
1396RTDECL(RTJSONVALTYPE) RTJsonValueGetType(RTJSONVAL hJsonVal)
1397{
1398 PRTJSONVALINT pThis = hJsonVal;
1399 AssertPtrReturn(pThis, RTJSONVALTYPE_INVALID);
1400
1401 if (pThis == NIL_RTJSONVAL)
1402 return RTJSONVALTYPE_INVALID;
1403
1404 return pThis->enmType;
1405}
1406
1407
1408RTDECL(const char *) RTJsonValueTypeName(RTJSONVALTYPE enmType)
1409{
1410 switch (enmType)
1411 {
1412 case RTJSONVALTYPE_INVALID: return "invalid";
1413 case RTJSONVALTYPE_OBJECT: return "object";
1414 case RTJSONVALTYPE_ARRAY: return "array";
1415 case RTJSONVALTYPE_STRING: return "string";
1416 case RTJSONVALTYPE_INTEGER: return "integer";
1417 case RTJSONVALTYPE_NUMBER: return "number";
1418 case RTJSONVALTYPE_NULL: return "null";
1419 case RTJSONVALTYPE_TRUE: return "true";
1420 case RTJSONVALTYPE_FALSE: return "false";
1421 default: return "???";
1422 }
1423}
1424
1425
1426#define RTJSON_ASSERT_VALID_HANDLE_AND_TYPE_RETURN(pJson, enmExpectedType, ret) do { \
1427 AssertPtrReturn((pJson), (ret)); \
1428 AssertReturn((pJson) != NIL_RTJSONVAL, (ret)); \
1429 AssertReturn((pJson)->enmType == (enmExpectedType), (ret)); \
1430 } while (0)
1431
1432#define RTJSON_TYPECHECK_RETURN(pJson, enmExpectedType) do {\
1433 if ((pJson)->enmType == (enmExpectedType)) { /*likely*/ } \
1434 else { /*AssertFailed();*/ return VERR_JSON_VALUE_INVALID_TYPE; } \
1435 } while (0)
1436
1437
1438#define RTJSON_TYPECHECK_CONTAINER_RETURN(pJson) do { \
1439 if ( (pJson)->enmType == RTJSONVALTYPE_ARRAY \
1440 || (pJson)->enmType == RTJSONVALTYPE_OBJECT) \
1441 { /* likely */ } \
1442 else { /*AssertFailed();*/ return VERR_JSON_VALUE_INVALID_TYPE;} \
1443 } while (0)
1444
1445
1446RTDECL(const char *) RTJsonValueGetString(RTJSONVAL hJsonVal)
1447{
1448 PRTJSONVALINT pThis = hJsonVal;
1449 RTJSON_ASSERT_VALID_HANDLE_AND_TYPE_RETURN(pThis, RTJSONVALTYPE_STRING, NULL);
1450
1451 return pThis->Type.String.pszStr;
1452}
1453
1454
1455RTDECL(int) RTJsonValueQueryString(RTJSONVAL hJsonVal, const char **ppszStr)
1456{
1457 PRTJSONVALINT pThis = hJsonVal;
1458 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1459 AssertPtrReturn(ppszStr, VERR_INVALID_POINTER);
1460
1461 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_STRING);
1462 *ppszStr = pThis->Type.String.pszStr;
1463 return VINF_SUCCESS;
1464}
1465
1466RTDECL(int) RTJsonValueQueryInteger(RTJSONVAL hJsonVal, int64_t *pi64Num)
1467{
1468 PRTJSONVALINT pThis = hJsonVal;
1469 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1470 AssertPtrReturn(pi64Num, VERR_INVALID_POINTER);
1471
1472 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_INTEGER);
1473 *pi64Num = pThis->Type.Integer.i64Num;
1474 return VINF_SUCCESS;
1475}
1476
1477RTDECL(int) RTJsonValueQueryNumber(RTJSONVAL hJsonVal, double *prdNum)
1478{
1479 PRTJSONVALINT pThis = hJsonVal;
1480 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1481 AssertPtrReturn(prdNum, VERR_INVALID_POINTER);
1482
1483 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_NUMBER);
1484 *prdNum = pThis->Type.rdNum;
1485 return VINF_SUCCESS;
1486}
1487
1488RTDECL(int) RTJsonValueQueryByName(RTJSONVAL hJsonVal, const char *pszName, PRTJSONVAL phJsonVal)
1489{
1490 PRTJSONVALINT pThis = hJsonVal;
1491 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1492 AssertPtrReturn(pszName, VERR_INVALID_POINTER);
1493 AssertPtrReturn(phJsonVal, VERR_INVALID_POINTER);
1494
1495 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_OBJECT);
1496
1497 int rc = VERR_NOT_FOUND;
1498 for (unsigned i = 0; i < pThis->Type.Object.cMembers; i++)
1499 {
1500 if (!RTStrCmp(pThis->Type.Object.papszNames[i], pszName))
1501 {
1502 RTJsonValueRetain(pThis->Type.Object.papValues[i]);
1503 *phJsonVal = pThis->Type.Object.papValues[i];
1504 rc = VINF_SUCCESS;
1505 break;
1506 }
1507 }
1508
1509 return rc;
1510}
1511
1512RTDECL(int) RTJsonValueQueryIntegerByName(RTJSONVAL hJsonVal, const char *pszName, int64_t *pi64Num)
1513{
1514 RTJSONVAL hJsonValNum = NIL_RTJSONVAL;
1515 int rc = RTJsonValueQueryByName(hJsonVal, pszName, &hJsonValNum);
1516 if (RT_SUCCESS(rc))
1517 {
1518 rc = RTJsonValueQueryInteger(hJsonValNum, pi64Num);
1519 RTJsonValueRelease(hJsonValNum);
1520 }
1521
1522 return rc;
1523}
1524
1525RTDECL(int) RTJsonValueQueryNumberByName(RTJSONVAL hJsonVal, const char *pszName, double *prdNum)
1526{
1527 RTJSONVAL hJsonValNum = NIL_RTJSONVAL;
1528 int rc = RTJsonValueQueryByName(hJsonVal, pszName, &hJsonValNum);
1529 if (RT_SUCCESS(rc))
1530 {
1531 rc = RTJsonValueQueryNumber(hJsonValNum, prdNum);
1532 RTJsonValueRelease(hJsonValNum);
1533 }
1534
1535 return rc;
1536}
1537
1538RTDECL(int) RTJsonValueQueryStringByName(RTJSONVAL hJsonVal, const char *pszName, char **ppszStr)
1539{
1540 RTJSONVAL hJsonValStr = NIL_RTJSONVAL;
1541 int rc = RTJsonValueQueryByName(hJsonVal, pszName, &hJsonValStr);
1542 if (RT_SUCCESS(rc))
1543 {
1544 const char *pszStr = NULL;
1545 rc = RTJsonValueQueryString(hJsonValStr, &pszStr);
1546 if (RT_SUCCESS(rc))
1547 {
1548 *ppszStr = RTStrDup(pszStr);
1549 if (!*ppszStr)
1550 rc = VERR_NO_STR_MEMORY;
1551 }
1552 RTJsonValueRelease(hJsonValStr);
1553 }
1554
1555 return rc;
1556}
1557
1558RTDECL(int) RTJsonValueQueryBooleanByName(RTJSONVAL hJsonVal, const char *pszName, bool *pfBoolean)
1559{
1560 AssertPtrReturn(pfBoolean, VERR_INVALID_POINTER);
1561
1562 RTJSONVAL hJsonValBool = NIL_RTJSONVAL;
1563 int rc = RTJsonValueQueryByName(hJsonVal, pszName, &hJsonValBool);
1564 if (RT_SUCCESS(rc))
1565 {
1566 RTJSONVALTYPE enmType = RTJsonValueGetType(hJsonValBool);
1567 if (enmType == RTJSONVALTYPE_TRUE)
1568 *pfBoolean = true;
1569 else if (enmType == RTJSONVALTYPE_FALSE)
1570 *pfBoolean = false;
1571 else
1572 rc = VERR_JSON_VALUE_INVALID_TYPE;
1573 RTJsonValueRelease(hJsonValBool);
1574 }
1575
1576 return rc;
1577}
1578
1579RTDECL(unsigned) RTJsonValueGetArraySize(RTJSONVAL hJsonVal)
1580{
1581 PRTJSONVALINT pThis = hJsonVal;
1582 RTJSON_ASSERT_VALID_HANDLE_AND_TYPE_RETURN(pThis, RTJSONVALTYPE_ARRAY, 0);
1583
1584 return pThis->Type.Array.cItems;
1585}
1586
1587RTDECL(int) RTJsonValueQueryArraySize(RTJSONVAL hJsonVal, unsigned *pcItems)
1588{
1589 PRTJSONVALINT pThis = hJsonVal;
1590 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1591 AssertPtrReturn(pcItems, VERR_INVALID_POINTER);
1592
1593 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_ARRAY);
1594 *pcItems = pThis->Type.Array.cItems;
1595 return VINF_SUCCESS;
1596}
1597
1598RTDECL(int) RTJsonValueQueryByIndex(RTJSONVAL hJsonVal, unsigned idx, PRTJSONVAL phJsonVal)
1599{
1600 PRTJSONVALINT pThis = hJsonVal;
1601 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1602 AssertPtrReturn(phJsonVal, VERR_INVALID_POINTER);
1603
1604 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_ARRAY);
1605 if (RT_UNLIKELY(idx >= pThis->Type.Array.cItems))
1606 return VERR_OUT_OF_RANGE;
1607
1608 RTJsonValueRetain(pThis->Type.Array.papItems[idx]);
1609 *phJsonVal = pThis->Type.Array.papItems[idx];
1610 return VINF_SUCCESS;
1611}
1612
1613static int rtJsonIteratorBeginWorker(PRTJSONVALINT pThis, PRTJSONIT phJsonIt)
1614{
1615 PRTJSONITINT pIt = (PRTJSONITINT)RTMemTmpAllocZ(sizeof(RTJSONITINT));
1616 if (pIt)
1617 {
1618 RTJsonValueRetain(pThis);
1619 pIt->pJsonVal = pThis;
1620 pIt->idxCur = 0;
1621
1622 *phJsonIt = pIt;
1623 return VINF_SUCCESS;
1624 }
1625 return VERR_NO_MEMORY;
1626}
1627
1628RTDECL(int) RTJsonIteratorBegin(RTJSONVAL hJsonVal, PRTJSONIT phJsonIt)
1629{
1630 PRTJSONVALINT pThis = hJsonVal;
1631 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1632 AssertPtrReturn(phJsonIt, VERR_INVALID_POINTER);
1633 RTJSON_TYPECHECK_CONTAINER_RETURN(pThis);
1634
1635 return rtJsonIteratorBeginWorker(pThis, phJsonIt);
1636}
1637
1638RTDECL(int) RTJsonIteratorBeginArray(RTJSONVAL hJsonVal, PRTJSONIT phJsonIt)
1639{
1640 PRTJSONVALINT pThis = hJsonVal;
1641 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1642 AssertPtrReturn(phJsonIt, VERR_INVALID_POINTER);
1643 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_ARRAY);
1644
1645 if (pThis->Type.Array.cItems > 0)
1646 return rtJsonIteratorBeginWorker(pThis, phJsonIt);
1647 return VERR_JSON_IS_EMPTY;
1648}
1649
1650RTDECL(int) RTJsonIteratorBeginObject(RTJSONVAL hJsonVal, PRTJSONIT phJsonIt)
1651{
1652 PRTJSONVALINT pThis = hJsonVal;
1653 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1654 AssertPtrReturn(phJsonIt, VERR_INVALID_POINTER);
1655 RTJSON_TYPECHECK_RETURN(pThis, RTJSONVALTYPE_OBJECT);
1656
1657 if (pThis->Type.Object.cMembers > 0)
1658 return rtJsonIteratorBeginWorker(pThis, phJsonIt);
1659 return VERR_JSON_IS_EMPTY;
1660}
1661
1662RTDECL(int) RTJsonIteratorQueryValue(RTJSONIT hJsonIt, PRTJSONVAL phJsonVal, const char **ppszName)
1663{
1664 PRTJSONITINT pIt = hJsonIt;
1665 AssertPtrReturn(pIt, VERR_INVALID_HANDLE);
1666 AssertReturn(pIt != NIL_RTJSONIT, VERR_INVALID_HANDLE);
1667 AssertPtrReturn(phJsonVal, VERR_INVALID_POINTER);
1668
1669 int rc = VINF_SUCCESS;
1670 PRTJSONVALINT pThis = pIt->pJsonVal;
1671 if (pThis->enmType == RTJSONVALTYPE_ARRAY)
1672 {
1673 if (pIt->idxCur < pThis->Type.Array.cItems)
1674 {
1675 if (ppszName)
1676 *ppszName = NULL;
1677
1678 RTJsonValueRetain(pThis->Type.Array.papItems[pIt->idxCur]);
1679 *phJsonVal = pThis->Type.Array.papItems[pIt->idxCur];
1680 }
1681 else
1682 rc = VERR_JSON_ITERATOR_END;
1683 }
1684 else
1685 {
1686 Assert(pThis->enmType == RTJSONVALTYPE_OBJECT);
1687
1688 if (pIt->idxCur < pThis->Type.Object.cMembers)
1689 {
1690 if (ppszName)
1691 *ppszName = pThis->Type.Object.papszNames[pIt->idxCur];
1692
1693 RTJsonValueRetain(pThis->Type.Object.papValues[pIt->idxCur]);
1694 *phJsonVal = pThis->Type.Object.papValues[pIt->idxCur];
1695 }
1696 else
1697 rc = VERR_JSON_ITERATOR_END;
1698 }
1699
1700 return rc;
1701}
1702
1703RTDECL(int) RTJsonIteratorNext(RTJSONIT hJsonIt)
1704{
1705 PRTJSONITINT pIt = hJsonIt;
1706 AssertPtrReturn(pIt, VERR_INVALID_HANDLE);
1707 AssertReturn(pIt != NIL_RTJSONIT, VERR_INVALID_HANDLE);
1708
1709 int rc = VINF_SUCCESS;
1710 PRTJSONVALINT pThis = pIt->pJsonVal;
1711 if (pThis->enmType == RTJSONVALTYPE_ARRAY)
1712 {
1713 if (pIt->idxCur < pThis->Type.Array.cItems)
1714 pIt->idxCur++;
1715
1716 if (pIt->idxCur == pThis->Type.Object.cMembers)
1717 rc = VERR_JSON_ITERATOR_END;
1718 }
1719 else
1720 {
1721 Assert(pThis->enmType == RTJSONVALTYPE_OBJECT);
1722
1723 if (pIt->idxCur < pThis->Type.Object.cMembers)
1724 pIt->idxCur++;
1725
1726 if (pIt->idxCur == pThis->Type.Object.cMembers)
1727 rc = VERR_JSON_ITERATOR_END;
1728 }
1729
1730 return rc;
1731}
1732
1733RTDECL(void) RTJsonIteratorFree(RTJSONIT hJsonIt)
1734{
1735 PRTJSONITINT pThis = hJsonIt;
1736 AssertPtrReturnVoid(pThis);
1737
1738 if (pThis == NIL_RTJSONIT)
1739 return;
1740
1741 RTJsonValueRelease(pThis->pJsonVal);
1742 RTMemTmpFree(pThis);
1743}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette