diff options
Diffstat (limited to 'src/libs/dutil/WixToolset.DUtil/jsonutil.cpp')
-rw-r--r-- | src/libs/dutil/WixToolset.DUtil/jsonutil.cpp | 687 |
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 | |||
20 | const DWORD JSON_STACK_INCREMENT = 5; | ||
21 | |||
22 | // Prototypes | ||
23 | static HRESULT DoStart( | ||
24 | __in JSON_WRITER* pWriter, | ||
25 | __in JSON_TOKEN tokenStart, | ||
26 | __in_z LPCWSTR wzStartString | ||
27 | ); | ||
28 | static HRESULT DoEnd( | ||
29 | __in JSON_WRITER* pWriter, | ||
30 | __in JSON_TOKEN tokenEnd, | ||
31 | __in_z LPCWSTR wzEndString | ||
32 | ); | ||
33 | static HRESULT DoKey( | ||
34 | __in JSON_WRITER* pWriter, | ||
35 | __in_z LPCWSTR wzKey | ||
36 | ); | ||
37 | static HRESULT DoValue( | ||
38 | __in JSON_WRITER* pWriter, | ||
39 | __in_z_opt LPCWSTR wzValue | ||
40 | ); | ||
41 | static HRESULT EnsureTokenStack( | ||
42 | __in JSON_WRITER* pWriter | ||
43 | ); | ||
44 | static HRESULT SerializeJsonString( | ||
45 | __out_z LPWSTR* psczJsonString, | ||
46 | __in_z LPCWSTR wzString | ||
47 | ); | ||
48 | |||
49 | |||
50 | |||
51 | DAPI_(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 | |||
66 | LExit: | ||
67 | return hr; | ||
68 | } | ||
69 | |||
70 | |||
71 | DAPI_(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 | |||
82 | static 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 | |||
154 | DAPI_(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 | |||
182 | LExit: | ||
183 | ::LeaveCriticalSection(&pReader->cs); | ||
184 | return hr; | ||
185 | } | ||
186 | |||
187 | |||
188 | DAPI_(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 | |||
200 | DAPI_(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 | |||
211 | DAPI_(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 | |||
223 | DAPI_(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 | |||
237 | LExit: | ||
238 | ReleaseStr(sczValue); | ||
239 | return hr; | ||
240 | } | ||
241 | |||
242 | |||
243 | DAPI_(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 | |||
257 | LExit: | ||
258 | ReleaseStr(sczValue); | ||
259 | return hr; | ||
260 | } | ||
261 | |||
262 | |||
263 | DAPI_(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 | |||
277 | LExit: | ||
278 | ReleaseStr(sczJsonString); | ||
279 | return hr; | ||
280 | } | ||
281 | |||
282 | |||
283 | DAPI_(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 | |||
292 | LExit: | ||
293 | return hr; | ||
294 | } | ||
295 | |||
296 | |||
297 | DAPI_(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 | |||
306 | LExit: | ||
307 | return hr; | ||
308 | } | ||
309 | |||
310 | |||
311 | DAPI_(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 | |||
320 | LExit: | ||
321 | return hr; | ||
322 | } | ||
323 | |||
324 | |||
325 | DAPI_(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 | |||
339 | LExit: | ||
340 | ReleaseStr(sczObjectKey); | ||
341 | return hr; | ||
342 | } | ||
343 | |||
344 | |||
345 | DAPI_(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 | |||
354 | LExit: | ||
355 | return hr; | ||
356 | } | ||
357 | |||
358 | |||
359 | static 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 | |||
417 | LExit: | ||
418 | ::LeaveCriticalSection(&pWriter->cs); | ||
419 | return hr; | ||
420 | } | ||
421 | |||
422 | |||
423 | static 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 | |||
454 | LExit: | ||
455 | ::LeaveCriticalSection(&pWriter->cs); | ||
456 | return hr; | ||
457 | } | ||
458 | |||
459 | |||
460 | static 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 | |||
503 | LExit: | ||
504 | ::LeaveCriticalSection(&pWriter->cs); | ||
505 | return hr; | ||
506 | } | ||
507 | |||
508 | |||
509 | static 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 | |||
567 | LExit: | ||
568 | ::LeaveCriticalSection(&pWriter->cs); | ||
569 | return hr; | ||
570 | } | ||
571 | |||
572 | |||
573 | static 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 | |||
589 | LExit: | ||
590 | return hr; | ||
591 | } | ||
592 | |||
593 | |||
594 | static 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 | |||
685 | LExit: | ||
686 | return hr; | ||
687 | } | ||