aboutsummaryrefslogtreecommitdiff
path: root/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/jsonutil.cpp')
-rw-r--r--src/libs/dutil/WixToolset.DUtil/jsonutil.cpp687
1 files changed, 687 insertions, 0 deletions
diff --git a/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp b/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp
new file mode 100644
index 00000000..3450ba59
--- /dev/null
+++ b/src/libs/dutil/WixToolset.DUtil/jsonutil.cpp
@@ -0,0 +1,687 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6// Exit macros
7#define JsonExitOnLastError(x, s, ...) ExitOnLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
8#define JsonExitOnLastErrorDebugTrace(x, s, ...) ExitOnLastErrorDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
9#define JsonExitWithLastError(x, s, ...) ExitWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
10#define JsonExitOnFailure(x, s, ...) ExitOnFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
11#define JsonExitOnRootFailure(x, s, ...) ExitOnRootFailureSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
12#define JsonExitOnFailureDebugTrace(x, s, ...) ExitOnFailureDebugTraceSource(DUTIL_SOURCE_JSONUTIL, x, s, __VA_ARGS__)
13#define JsonExitOnNull(p, x, e, s, ...) ExitOnNullSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__)
14#define JsonExitOnNullWithLastError(p, x, s, ...) ExitOnNullWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__)
15#define JsonExitOnNullDebugTrace(p, x, e, s, ...) ExitOnNullDebugTraceSource(DUTIL_SOURCE_JSONUTIL, p, x, e, s, __VA_ARGS__)
16#define JsonExitOnInvalidHandleWithLastError(p, x, s, ...) ExitOnInvalidHandleWithLastErrorSource(DUTIL_SOURCE_JSONUTIL, p, x, s, __VA_ARGS__)
17#define JsonExitOnWin32Error(e, x, s, ...) ExitOnWin32ErrorSource(DUTIL_SOURCE_JSONUTIL, e, x, s, __VA_ARGS__)
18#define JsonExitOnGdipFailure(g, x, s, ...) ExitOnGdipFailureSource(DUTIL_SOURCE_JSONUTIL, g, x, s, __VA_ARGS__)
19
20const DWORD JSON_STACK_INCREMENT = 5;
21
22// Prototypes
23static HRESULT DoStart(
24 __in JSON_WRITER* pWriter,
25 __in JSON_TOKEN tokenStart,
26 __in_z LPCWSTR wzStartString
27 );
28static HRESULT DoEnd(
29 __in JSON_WRITER* pWriter,
30 __in JSON_TOKEN tokenEnd,
31 __in_z LPCWSTR wzEndString
32 );
33static HRESULT DoKey(
34 __in JSON_WRITER* pWriter,
35 __in_z LPCWSTR wzKey
36 );
37static HRESULT DoValue(
38 __in JSON_WRITER* pWriter,
39 __in_z_opt LPCWSTR wzValue
40 );
41static HRESULT EnsureTokenStack(
42 __in JSON_WRITER* pWriter
43 );
44static HRESULT SerializeJsonString(
45 __out_z LPWSTR* psczJsonString,
46 __in_z LPCWSTR wzString
47 );
48
49
50
51DAPI_(HRESULT) JsonInitializeReader(
52 __in_z LPCWSTR wzJson,
53 __in JSON_READER* pReader
54 )
55{
56 HRESULT hr = S_OK;
57
58 memset(pReader, 0, sizeof(JSON_READER));
59 ::InitializeCriticalSection(&pReader->cs);
60
61 hr = StrAllocString(&pReader->sczJson, wzJson, 0);
62 JsonExitOnFailure(hr, "Failed to allocate json string.");
63
64 pReader->pwz = pReader->sczJson;
65
66LExit:
67 return hr;
68}
69
70
71DAPI_(void) JsonUninitializeReader(
72 __in JSON_READER* pReader
73 )
74{
75 ReleaseStr(pReader->sczJson);
76
77 ::DeleteCriticalSection(&pReader->cs);
78 memset(pReader, 0, sizeof(JSON_READER));
79}
80
81
82static HRESULT NextToken(
83 __in JSON_READER* pReader,
84 __out JSON_TOKEN* pToken
85 )
86{
87 HRESULT hr = S_OK;
88
89 // Skip whitespace.
90 while (L' ' == *pReader->pwz ||
91 L'\t' == *pReader->pwz ||
92 L'\r' == *pReader->pwz ||
93 L'\n' == *pReader->pwz ||
94 L',' == *pReader->pwz)
95 {
96 ++pReader->pwz;
97 }
98
99 switch (*pReader->pwz)
100 {
101 case L'{':
102 *pToken = JSON_TOKEN_OBJECT_START;
103 break;
104
105 case L':': // begin object value
106 *pToken = JSON_TOKEN_OBJECT_VALUE;
107 break;
108
109 case L'}': // end object
110 *pToken = JSON_TOKEN_OBJECT_END;
111 break;
112
113 case L'[': // begin array
114 *pToken = JSON_TOKEN_ARRAY_START;
115 break;
116
117 case L']': // end array
118 *pToken = JSON_TOKEN_ARRAY_END;
119 break;
120
121 case L'"': // string
122 *pToken = JSON_TOKEN_OBJECT_START == *pToken ? JSON_TOKEN_VALUE : JSON_TOKEN_OBJECT_KEY;
123 break;
124
125 case L't': // true
126 case L'f': // false
127 case L'n': // null
128 case L'-': // number
129 case L'0':
130 case L'1':
131 case L'2':
132 case L'3':
133 case L'4':
134 case L'5':
135 case L'6':
136 case L'7':
137 case L'8':
138 case L'9':
139 *pToken = JSON_TOKEN_VALUE;
140 break;
141
142 case L'\0':
143 hr = E_NOMOREITEMS;
144 break;
145
146 default:
147 hr = E_INVALIDSTATE;
148 }
149
150 return hr;
151}
152
153
154DAPI_(HRESULT) JsonReadNext(
155 __in JSON_READER* pReader,
156 __out JSON_TOKEN* pToken,
157 __out JSON_VALUE* pValue
158 )
159{
160 HRESULT hr = S_OK;
161 //WCHAR wz;
162 //JSON_TOKEN token = JSON_TOKEN_NONE;
163
164 ::EnterCriticalSection(&pReader->cs);
165
166 hr = NextToken(pReader, pToken);
167 if (E_NOMOREITEMS == hr)
168 {
169 ExitFunction();
170 }
171 JsonExitOnFailure(hr, "Failed to get next token.");
172
173 if (JSON_TOKEN_VALUE == *pToken)
174 {
175 hr = JsonReadValue(pReader, pValue);
176 }
177 else
178 {
179 ++pReader->pwz;
180 }
181
182LExit:
183 ::LeaveCriticalSection(&pReader->cs);
184 return hr;
185}
186
187
188DAPI_(HRESULT) JsonReadValue(
189 __in JSON_READER* /*pReader*/,
190 __in JSON_VALUE* /*pValue*/
191 )
192{
193 HRESULT hr = S_OK;
194
195//LExit:
196 return hr;
197}
198
199
200DAPI_(HRESULT) JsonInitializeWriter(
201 __in JSON_WRITER* pWriter
202 )
203{
204 memset(pWriter, 0, sizeof(JSON_WRITER));
205 ::InitializeCriticalSection(&pWriter->cs);
206
207 return S_OK;
208}
209
210
211DAPI_(void) JsonUninitializeWriter(
212 __in JSON_WRITER* pWriter
213 )
214{
215 ReleaseMem(pWriter->rgTokenStack);
216 ReleaseStr(pWriter->sczJson);
217
218 ::DeleteCriticalSection(&pWriter->cs);
219 memset(pWriter, 0, sizeof(JSON_WRITER));
220}
221
222
223DAPI_(HRESULT) JsonWriteBool(
224 __in JSON_WRITER* pWriter,
225 __in BOOL fValue
226 )
227{
228 HRESULT hr = S_OK;
229 LPWSTR sczValue = NULL;
230
231 hr = StrAllocString(&sczValue, fValue ? L"true" : L"false", 0);
232 JsonExitOnFailure(hr, "Failed to convert boolean to string.");
233
234 hr = DoValue(pWriter, sczValue);
235 JsonExitOnFailure(hr, "Failed to add boolean to JSON.");
236
237LExit:
238 ReleaseStr(sczValue);
239 return hr;
240}
241
242
243DAPI_(HRESULT) JsonWriteNumber(
244 __in JSON_WRITER* pWriter,
245 __in DWORD dwValue
246 )
247{
248 HRESULT hr = S_OK;
249 LPWSTR sczValue = NULL;
250
251 hr = StrAllocFormatted(&sczValue, L"%u", dwValue);
252 JsonExitOnFailure(hr, "Failed to convert number to string.");
253
254 hr = DoValue(pWriter, sczValue);
255 JsonExitOnFailure(hr, "Failed to add number to JSON.");
256
257LExit:
258 ReleaseStr(sczValue);
259 return hr;
260}
261
262
263DAPI_(HRESULT) JsonWriteString(
264 __in JSON_WRITER* pWriter,
265 __in_z LPCWSTR wzValue
266 )
267{
268 HRESULT hr = S_OK;
269 LPWSTR sczJsonString = NULL;
270
271 hr = SerializeJsonString(&sczJsonString, wzValue);
272 JsonExitOnFailure(hr, "Failed to allocate string JSON.");
273
274 hr = DoValue(pWriter, sczJsonString);
275 JsonExitOnFailure(hr, "Failed to add string to JSON.");
276
277LExit:
278 ReleaseStr(sczJsonString);
279 return hr;
280}
281
282
283DAPI_(HRESULT) JsonWriteArrayStart(
284 __in JSON_WRITER* pWriter
285 )
286{
287 HRESULT hr = S_OK;
288
289 hr = DoStart(pWriter, JSON_TOKEN_ARRAY_START, L"[");
290 JsonExitOnFailure(hr, "Failed to start JSON array.");
291
292LExit:
293 return hr;
294}
295
296
297DAPI_(HRESULT) JsonWriteArrayEnd(
298 __in JSON_WRITER* pWriter
299 )
300{
301 HRESULT hr = S_OK;
302
303 hr = DoEnd(pWriter, JSON_TOKEN_ARRAY_END, L"]");
304 JsonExitOnFailure(hr, "Failed to end JSON array.");
305
306LExit:
307 return hr;
308}
309
310
311DAPI_(HRESULT) JsonWriteObjectStart(
312 __in JSON_WRITER* pWriter
313 )
314{
315 HRESULT hr = S_OK;
316
317 hr = DoStart(pWriter, JSON_TOKEN_OBJECT_START, L"{");
318 JsonExitOnFailure(hr, "Failed to start JSON object.");
319
320LExit:
321 return hr;
322}
323
324
325DAPI_(HRESULT) JsonWriteObjectKey(
326 __in JSON_WRITER* pWriter,
327 __in_z LPCWSTR wzKey
328 )
329{
330 HRESULT hr = S_OK;
331 LPWSTR sczObjectKey = NULL;
332
333 hr = StrAllocFormatted(&sczObjectKey, L"\"%ls\":", wzKey);
334 JsonExitOnFailure(hr, "Failed to allocate JSON object key.");
335
336 hr = DoKey(pWriter, sczObjectKey);
337 JsonExitOnFailure(hr, "Failed to add object key to JSON.");
338
339LExit:
340 ReleaseStr(sczObjectKey);
341 return hr;
342}
343
344
345DAPI_(HRESULT) JsonWriteObjectEnd(
346 __in JSON_WRITER* pWriter
347 )
348{
349 HRESULT hr = S_OK;
350
351 hr = DoEnd(pWriter, JSON_TOKEN_OBJECT_END, L"}");
352 JsonExitOnFailure(hr, "Failed to end JSON object.");
353
354LExit:
355 return hr;
356}
357
358
359static HRESULT DoStart(
360 __in JSON_WRITER* pWriter,
361 __in JSON_TOKEN tokenStart,
362 __in_z LPCWSTR wzStartString
363 )
364{
365 Assert(JSON_TOKEN_ARRAY_START == tokenStart || JSON_TOKEN_OBJECT_START == tokenStart);
366
367 HRESULT hr = S_OK;
368 JSON_TOKEN token = JSON_TOKEN_NONE;
369 BOOL fNeedComma = FALSE;
370 BOOL fPushToken = TRUE;
371
372 ::EnterCriticalSection(&pWriter->cs);
373
374 hr = EnsureTokenStack(pWriter);
375 JsonExitOnFailure(hr, "Failed to ensure token stack for start.");
376
377 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
378 switch (token)
379 {
380 case JSON_TOKEN_NONE:
381 token = tokenStart;
382 fPushToken = FALSE;
383 break;
384
385 case JSON_TOKEN_ARRAY_START: // array start changes to array value.
386 token = JSON_TOKEN_ARRAY_VALUE;
387 break;
388
389 case JSON_TOKEN_ARRAY_VALUE:
390 case JSON_TOKEN_ARRAY_END:
391 case JSON_TOKEN_OBJECT_END:
392 fNeedComma = TRUE;
393 break;
394
395 default: // everything else is not allowed.
396 hr = E_UNEXPECTED;
397 break;
398 }
399 JsonExitOnRootFailure(hr, "Cannot start array or object to JSON serializer now.");
400
401 if (fNeedComma)
402 {
403 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
404 JsonExitOnFailure(hr, "Failed to add comma for start array or object to JSON.");
405 }
406
407 hr = StrAllocConcat(&pWriter->sczJson, wzStartString, 0);
408 JsonExitOnFailure(hr, "Failed to start JSON array or object.");
409
410 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
411 if (fPushToken)
412 {
413 pWriter->rgTokenStack[pWriter->cTokens] = tokenStart;
414 ++pWriter->cTokens;
415 }
416
417LExit:
418 ::LeaveCriticalSection(&pWriter->cs);
419 return hr;
420}
421
422
423static HRESULT DoEnd(
424 __in JSON_WRITER* pWriter,
425 __in JSON_TOKEN tokenEnd,
426 __in_z LPCWSTR wzEndString
427 )
428{
429 HRESULT hr = S_OK;
430
431 ::EnterCriticalSection(&pWriter->cs);
432
433 if (!pWriter->rgTokenStack || 0 == pWriter->cTokens)
434 {
435 hr = E_UNEXPECTED;
436 JsonExitOnRootFailure(hr, "Failure to pop token because the stack is empty.");
437 }
438 else
439 {
440 JSON_TOKEN token = pWriter->rgTokenStack[pWriter->cTokens - 1];
441 if ((JSON_TOKEN_ARRAY_END == tokenEnd && JSON_TOKEN_ARRAY_START != token && JSON_TOKEN_ARRAY_VALUE != token) ||
442 (JSON_TOKEN_OBJECT_END == tokenEnd && JSON_TOKEN_OBJECT_START != token && JSON_TOKEN_OBJECT_VALUE != token))
443 {
444 hr = E_UNEXPECTED;
445 JsonExitOnRootFailure(hr, "Failure to pop token because the stack did not match the expected token: %d", tokenEnd);
446 }
447 }
448
449 hr = StrAllocConcat(&pWriter->sczJson, wzEndString, 0);
450 JsonExitOnFailure(hr, "Failed to end JSON array or object.");
451
452 --pWriter->cTokens;
453
454LExit:
455 ::LeaveCriticalSection(&pWriter->cs);
456 return hr;
457}
458
459
460static HRESULT DoKey(
461 __in JSON_WRITER* pWriter,
462 __in_z LPCWSTR wzKey
463 )
464{
465 HRESULT hr = S_OK;
466 JSON_TOKEN token = JSON_TOKEN_NONE;
467 BOOL fNeedComma = FALSE;
468
469 ::EnterCriticalSection(&pWriter->cs);
470
471 hr = EnsureTokenStack(pWriter);
472 JsonExitOnFailure(hr, "Failed to ensure token stack for key.");
473
474 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
475 switch (token)
476 {
477 case JSON_TOKEN_OBJECT_START:
478 token = JSON_TOKEN_OBJECT_KEY;
479 break;
480
481 case JSON_TOKEN_OBJECT_VALUE:
482 token = JSON_TOKEN_OBJECT_KEY;
483 fNeedComma = TRUE;
484 break;
485
486 default: // everything else is not allowed.
487 hr = E_UNEXPECTED;
488 break;
489 }
490 JsonExitOnRootFailure(hr, "Cannot add key to JSON serializer now.");
491
492 if (fNeedComma)
493 {
494 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
495 JsonExitOnFailure(hr, "Failed to add comma for key to JSON.");
496 }
497
498 hr = StrAllocConcat(&pWriter->sczJson, wzKey, 0);
499 JsonExitOnFailure(hr, "Failed to add key to JSON.");
500
501 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
502
503LExit:
504 ::LeaveCriticalSection(&pWriter->cs);
505 return hr;
506}
507
508
509static HRESULT DoValue(
510 __in JSON_WRITER* pWriter,
511 __in_z_opt LPCWSTR wzValue
512 )
513{
514 HRESULT hr = S_OK;
515 JSON_TOKEN token = JSON_TOKEN_NONE;
516 BOOL fNeedComma = FALSE;
517
518 ::EnterCriticalSection(&pWriter->cs);
519
520 hr = EnsureTokenStack(pWriter);
521 JsonExitOnFailure(hr, "Failed to ensure token stack for value.");
522
523 token = pWriter->rgTokenStack[pWriter->cTokens - 1];
524 switch (token)
525 {
526 case JSON_TOKEN_ARRAY_START:
527 token = JSON_TOKEN_ARRAY_VALUE;
528 break;
529
530 case JSON_TOKEN_ARRAY_VALUE:
531 fNeedComma = TRUE;
532 break;
533
534 case JSON_TOKEN_OBJECT_KEY:
535 token = JSON_TOKEN_OBJECT_VALUE;
536 break;
537
538 case JSON_TOKEN_NONE:
539 token = JSON_TOKEN_VALUE;
540 break;
541
542 default: // everything else is not allowed.
543 hr = E_UNEXPECTED;
544 break;
545 }
546 JsonExitOnRootFailure(hr, "Cannot add value to JSON serializer now.");
547
548 if (fNeedComma)
549 {
550 hr = StrAllocConcat(&pWriter->sczJson, L",", 0);
551 JsonExitOnFailure(hr, "Failed to add comma for value to JSON.");
552 }
553
554 if (wzValue)
555 {
556 hr = StrAllocConcat(&pWriter->sczJson, wzValue, 0);
557 JsonExitOnFailure(hr, "Failed to add value to JSON.");
558 }
559 else
560 {
561 hr = StrAllocConcat(&pWriter->sczJson, L"null", 0);
562 JsonExitOnFailure(hr, "Failed to add null value to JSON.");
563 }
564
565 pWriter->rgTokenStack[pWriter->cTokens - 1] = token;
566
567LExit:
568 ::LeaveCriticalSection(&pWriter->cs);
569 return hr;
570}
571
572
573static HRESULT EnsureTokenStack(
574 __in JSON_WRITER* pWriter
575 )
576{
577 HRESULT hr = S_OK;
578 DWORD cNumAlloc = pWriter->cTokens != 0 ? pWriter->cTokens : 0;
579
580 hr = MemEnsureArraySize(reinterpret_cast<LPVOID*>(&pWriter->rgTokenStack), cNumAlloc, sizeof(JSON_TOKEN), JSON_STACK_INCREMENT);
581 JsonExitOnFailure(hr, "Failed to allocate JSON token stack.");
582
583 if (0 == pWriter->cTokens)
584 {
585 pWriter->rgTokenStack[0] = JSON_TOKEN_NONE;
586 ++pWriter->cTokens;
587 }
588
589LExit:
590 return hr;
591}
592
593
594static HRESULT SerializeJsonString(
595 __out_z LPWSTR* psczJsonString,
596 __in_z LPCWSTR wzString
597 )
598{
599 HRESULT hr = S_OK;
600 DWORD cchRequired = 3; // start with enough space for null terminated empty quoted string (aka: ""\0)
601
602 for (LPCWSTR pch = wzString; *pch; ++pch)
603 {
604 // If it is a special JSON character, add space for the escape backslash.
605 if (L'"' == *pch || L'\\' == *pch || L'/' == *pch || L'\b' == *pch || L'\f' == *pch || L'\n' == *pch || L'\r' == *pch || L'\t' == *pch)
606 {
607 ++cchRequired;
608 }
609
610 ++cchRequired;
611 }
612
613 hr = StrAlloc(psczJsonString, cchRequired);
614 JsonExitOnFailure(hr, "Failed to allocate space for JSON string.");
615
616 LPWSTR pchTarget = *psczJsonString;
617
618 *pchTarget = L'\"';
619 ++pchTarget;
620
621 for (LPCWSTR pch = wzString; *pch; ++pch, ++pchTarget)
622 {
623 // If it is a special JSON character, handle it or just add the character as is.
624 switch (*pch)
625 {
626 case L'"':
627 *pchTarget = L'\\';
628 ++pchTarget;
629 *pchTarget = L'"';
630 break;
631
632 case L'\\':
633 *pchTarget = L'\\';
634 ++pchTarget;
635 *pchTarget = L'\\';
636 break;
637
638 case L'/':
639 *pchTarget = L'\\';
640 ++pchTarget;
641 *pchTarget = L'/';
642 break;
643
644 case L'\b':
645 *pchTarget = L'\\';
646 ++pchTarget;
647 *pchTarget = L'b';
648 break;
649
650 case L'\f':
651 *pchTarget = L'\\';
652 ++pchTarget;
653 *pchTarget = L'f';
654 break;
655
656 case L'\n':
657 *pchTarget = L'\\';
658 ++pchTarget;
659 *pchTarget = L'n';
660 break;
661
662 case L'\r':
663 *pchTarget = L'\\';
664 ++pchTarget;
665 *pchTarget = L'r';
666 break;
667
668 case L'\t':
669 *pchTarget = L'\\';
670 ++pchTarget;
671 *pchTarget = L't';
672 break;
673
674 default:
675 *pchTarget = *pch;
676 break;
677 }
678
679 }
680
681 *pchTarget = L'\"';
682 ++pchTarget;
683 *pchTarget = L'\0';
684
685LExit:
686 return hr;
687}