]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/netstd/Thrift/Protocol/TJSONProtocol.cs
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / jaegertracing / thrift / lib / netstd / Thrift / Protocol / TJSONProtocol.cs
1 // Licensed to the Apache Software Foundation(ASF) under one
2 // or more contributor license agreements.See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership.The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17
18 using System;
19 using System.Collections.Generic;
20 using System.Globalization;
21 using System.IO;
22 using System.Linq;
23 using System.Text;
24 using System.Threading;
25 using System.Threading.Tasks;
26 using Thrift.Protocol.Entities;
27 using Thrift.Protocol.Utilities;
28 using Thrift.Transport;
29
30 namespace Thrift.Protocol
31 {
32 /// <summary>
33 /// JSON protocol implementation for thrift.
34 /// This is a full-featured protocol supporting Write and Read.
35 /// Please see the C++ class header for a detailed description of the
36 /// protocol's wire format.
37 /// Adapted from the Java version.
38 /// </summary>
39 // ReSharper disable once InconsistentNaming
40 public class TJsonProtocol : TProtocol
41 {
42 private const long Version = 1;
43
44 // Temporary buffer used by several methods
45 private readonly byte[] _tempBuffer = new byte[4];
46
47 // Current context that we are in
48 protected JSONBaseContext Context;
49
50 // Stack of nested contexts that we may be in
51 protected Stack<JSONBaseContext> ContextStack = new Stack<JSONBaseContext>();
52
53 // Reader that manages a 1-byte buffer
54 protected LookaheadReader Reader;
55
56 // Default encoding
57 protected Encoding Utf8Encoding = Encoding.UTF8;
58
59 /// <summary>
60 /// TJsonProtocol Constructor
61 /// </summary>
62 public TJsonProtocol(TTransport trans)
63 : base(trans)
64 {
65 Context = new JSONBaseContext(this);
66 Reader = new LookaheadReader(this);
67 }
68
69 /// <summary>
70 /// Push a new JSON context onto the stack.
71 /// </summary>
72 protected void PushContext(JSONBaseContext c)
73 {
74 ContextStack.Push(Context);
75 Context = c;
76 }
77
78 /// <summary>
79 /// Pop the last JSON context off the stack
80 /// </summary>
81 protected void PopContext()
82 {
83 Context = ContextStack.Pop();
84 }
85
86 /// <summary>
87 /// Read a byte that must match b[0]; otherwise an exception is thrown.
88 /// Marked protected to avoid synthetic accessor in JSONListContext.Read
89 /// and JSONPairContext.Read
90 /// </summary>
91 protected async Task ReadJsonSyntaxCharAsync(byte[] bytes, CancellationToken cancellationToken)
92 {
93 var ch = await Reader.ReadAsync(cancellationToken);
94 if (ch != bytes[0])
95 {
96 throw new TProtocolException(TProtocolException.INVALID_DATA, $"Unexpected character: {(char) ch}");
97 }
98 }
99
100 /// <summary>
101 /// Write the bytes in array buf as a JSON characters, escaping as needed
102 /// </summary>
103 private async Task WriteJsonStringAsync(byte[] bytes, CancellationToken cancellationToken)
104 {
105 await Context.WriteConditionalDelimiterAsync(cancellationToken);
106 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
107
108 var len = bytes.Length;
109 for (var i = 0; i < len; i++)
110 {
111 if ((bytes[i] & 0x00FF) >= 0x30)
112 {
113 if (bytes[i] == TJSONProtocolConstants.Backslash[0])
114 {
115 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
116 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
117 }
118 else
119 {
120 await Trans.WriteAsync(bytes.ToArray(), i, 1, cancellationToken);
121 }
122 }
123 else
124 {
125 _tempBuffer[0] = TJSONProtocolConstants.JsonCharTable[bytes[i]];
126 if (_tempBuffer[0] == 1)
127 {
128 await Trans.WriteAsync(bytes, i, 1, cancellationToken);
129 }
130 else if (_tempBuffer[0] > 1)
131 {
132 await Trans.WriteAsync(TJSONProtocolConstants.Backslash, cancellationToken);
133 await Trans.WriteAsync(_tempBuffer, 0, 1, cancellationToken);
134 }
135 else
136 {
137 await Trans.WriteAsync(TJSONProtocolConstants.EscSequences, cancellationToken);
138 _tempBuffer[0] = TJSONProtocolHelper.ToHexChar((byte) (bytes[i] >> 4));
139 _tempBuffer[1] = TJSONProtocolHelper.ToHexChar(bytes[i]);
140 await Trans.WriteAsync(_tempBuffer, 0, 2, cancellationToken);
141 }
142 }
143 }
144 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
145 }
146
147 /// <summary>
148 /// Write out number as a JSON value. If the context dictates so, it will be
149 /// wrapped in quotes to output as a JSON string.
150 /// </summary>
151 private async Task WriteJsonIntegerAsync(long num, CancellationToken cancellationToken)
152 {
153 await Context.WriteConditionalDelimiterAsync(cancellationToken);
154 var str = num.ToString();
155
156 var escapeNum = Context.EscapeNumbers();
157 if (escapeNum)
158 {
159 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
160 }
161
162 var bytes = Utf8Encoding.GetBytes(str);
163 await Trans.WriteAsync(bytes, cancellationToken);
164
165 if (escapeNum)
166 {
167 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
168 }
169 }
170
171 /// <summary>
172 /// Write out a double as a JSON value. If it is NaN or infinity or if the
173 /// context dictates escaping, Write out as JSON string.
174 /// </summary>
175 private async Task WriteJsonDoubleAsync(double num, CancellationToken cancellationToken)
176 {
177 await Context.WriteConditionalDelimiterAsync(cancellationToken);
178 var str = num.ToString("G17", CultureInfo.InvariantCulture);
179 var special = false;
180
181 switch (str[0])
182 {
183 case 'N': // NaN
184 case 'I': // Infinity
185 special = true;
186 break;
187 case '-':
188 if (str[1] == 'I')
189 {
190 // -Infinity
191 special = true;
192 }
193 break;
194 }
195
196 var escapeNum = special || Context.EscapeNumbers();
197
198 if (escapeNum)
199 {
200 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
201 }
202
203 await Trans.WriteAsync(Utf8Encoding.GetBytes(str), cancellationToken);
204
205 if (escapeNum)
206 {
207 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
208 }
209 }
210
211 /// <summary>
212 /// Write out contents of byte array b as a JSON string with base-64 encoded
213 /// data
214 /// </summary>
215 private async Task WriteJsonBase64Async(byte[] bytes, CancellationToken cancellationToken)
216 {
217 await Context.WriteConditionalDelimiterAsync(cancellationToken);
218 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
219
220 var len = bytes.Length;
221 var off = 0;
222
223 while (len >= 3)
224 {
225 // Encode 3 bytes at a time
226 TBase64Utils.Encode(bytes, off, 3, _tempBuffer, 0);
227 await Trans.WriteAsync(_tempBuffer, 0, 4, cancellationToken);
228 off += 3;
229 len -= 3;
230 }
231
232 if (len > 0)
233 {
234 // Encode remainder
235 TBase64Utils.Encode(bytes, off, len, _tempBuffer, 0);
236 await Trans.WriteAsync(_tempBuffer, 0, len + 1, cancellationToken);
237 }
238
239 await Trans.WriteAsync(TJSONProtocolConstants.Quote, cancellationToken);
240 }
241
242 private async Task WriteJsonObjectStartAsync(CancellationToken cancellationToken)
243 {
244 await Context.WriteConditionalDelimiterAsync(cancellationToken);
245 await Trans.WriteAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
246 PushContext(new JSONPairContext(this));
247 }
248
249 private async Task WriteJsonObjectEndAsync(CancellationToken cancellationToken)
250 {
251 PopContext();
252 await Trans.WriteAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
253 }
254
255 private async Task WriteJsonArrayStartAsync(CancellationToken cancellationToken)
256 {
257 await Context.WriteConditionalDelimiterAsync(cancellationToken);
258 await Trans.WriteAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
259 PushContext(new JSONListContext(this));
260 }
261
262 private async Task WriteJsonArrayEndAsync(CancellationToken cancellationToken)
263 {
264 PopContext();
265 await Trans.WriteAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
266 }
267
268 public override async Task WriteMessageBeginAsync(TMessage message, CancellationToken cancellationToken)
269 {
270 await WriteJsonArrayStartAsync(cancellationToken);
271 await WriteJsonIntegerAsync(Version, cancellationToken);
272
273 var b = Utf8Encoding.GetBytes(message.Name);
274 await WriteJsonStringAsync(b, cancellationToken);
275
276 await WriteJsonIntegerAsync((long) message.Type, cancellationToken);
277 await WriteJsonIntegerAsync(message.SeqID, cancellationToken);
278 }
279
280 public override async Task WriteMessageEndAsync(CancellationToken cancellationToken)
281 {
282 await WriteJsonArrayEndAsync(cancellationToken);
283 }
284
285 public override async Task WriteStructBeginAsync(TStruct @struct, CancellationToken cancellationToken)
286 {
287 await WriteJsonObjectStartAsync(cancellationToken);
288 }
289
290 public override async Task WriteStructEndAsync(CancellationToken cancellationToken)
291 {
292 await WriteJsonObjectEndAsync(cancellationToken);
293 }
294
295 public override async Task WriteFieldBeginAsync(TField field, CancellationToken cancellationToken)
296 {
297 await WriteJsonIntegerAsync(field.ID, cancellationToken);
298 await WriteJsonObjectStartAsync(cancellationToken);
299 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(field.Type), cancellationToken);
300 }
301
302 public override async Task WriteFieldEndAsync(CancellationToken cancellationToken)
303 {
304 await WriteJsonObjectEndAsync(cancellationToken);
305 }
306
307 public override async Task WriteFieldStopAsync(CancellationToken cancellationToken)
308 {
309 if (cancellationToken.IsCancellationRequested)
310 {
311 await Task.FromCanceled(cancellationToken);
312 }
313 }
314
315 public override async Task WriteMapBeginAsync(TMap map, CancellationToken cancellationToken)
316 {
317 await WriteJsonArrayStartAsync(cancellationToken);
318 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.KeyType), cancellationToken);
319 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(map.ValueType), cancellationToken);
320 await WriteJsonIntegerAsync(map.Count, cancellationToken);
321 await WriteJsonObjectStartAsync(cancellationToken);
322 }
323
324 public override async Task WriteMapEndAsync(CancellationToken cancellationToken)
325 {
326 await WriteJsonObjectEndAsync(cancellationToken);
327 await WriteJsonArrayEndAsync(cancellationToken);
328 }
329
330 public override async Task WriteListBeginAsync(TList list, CancellationToken cancellationToken)
331 {
332 await WriteJsonArrayStartAsync(cancellationToken);
333 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(list.ElementType), cancellationToken);
334 await WriteJsonIntegerAsync(list.Count, cancellationToken);
335 }
336
337 public override async Task WriteListEndAsync(CancellationToken cancellationToken)
338 {
339 await WriteJsonArrayEndAsync(cancellationToken);
340 }
341
342 public override async Task WriteSetBeginAsync(TSet set, CancellationToken cancellationToken)
343 {
344 await WriteJsonArrayStartAsync(cancellationToken);
345 await WriteJsonStringAsync(TJSONProtocolHelper.GetTypeNameForTypeId(set.ElementType), cancellationToken);
346 await WriteJsonIntegerAsync(set.Count, cancellationToken);
347 }
348
349 public override async Task WriteSetEndAsync(CancellationToken cancellationToken)
350 {
351 await WriteJsonArrayEndAsync(cancellationToken);
352 }
353
354 public override async Task WriteBoolAsync(bool b, CancellationToken cancellationToken)
355 {
356 await WriteJsonIntegerAsync(b ? 1 : 0, cancellationToken);
357 }
358
359 public override async Task WriteByteAsync(sbyte b, CancellationToken cancellationToken)
360 {
361 await WriteJsonIntegerAsync(b, cancellationToken);
362 }
363
364 public override async Task WriteI16Async(short i16, CancellationToken cancellationToken)
365 {
366 await WriteJsonIntegerAsync(i16, cancellationToken);
367 }
368
369 public override async Task WriteI32Async(int i32, CancellationToken cancellationToken)
370 {
371 await WriteJsonIntegerAsync(i32, cancellationToken);
372 }
373
374 public override async Task WriteI64Async(long i64, CancellationToken cancellationToken)
375 {
376 await WriteJsonIntegerAsync(i64, cancellationToken);
377 }
378
379 public override async Task WriteDoubleAsync(double d, CancellationToken cancellationToken)
380 {
381 await WriteJsonDoubleAsync(d, cancellationToken);
382 }
383
384 public override async Task WriteStringAsync(string s, CancellationToken cancellationToken)
385 {
386 var b = Utf8Encoding.GetBytes(s);
387 await WriteJsonStringAsync(b, cancellationToken);
388 }
389
390 public override async Task WriteBinaryAsync(byte[] bytes, CancellationToken cancellationToken)
391 {
392 await WriteJsonBase64Async(bytes, cancellationToken);
393 }
394
395 /// <summary>
396 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
397 /// context if skipContext is true.
398 /// </summary>
399 private async ValueTask<byte[]> ReadJsonStringAsync(bool skipContext, CancellationToken cancellationToken)
400 {
401 using (var buffer = new MemoryStream())
402 {
403 var codeunits = new List<char>();
404
405
406 if (!skipContext)
407 {
408 await Context.ReadConditionalDelimiterAsync(cancellationToken);
409 }
410
411 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
412
413 while (true)
414 {
415 var ch = await Reader.ReadAsync(cancellationToken);
416 if (ch == TJSONProtocolConstants.Quote[0])
417 {
418 break;
419 }
420
421 // escaped?
422 if (ch != TJSONProtocolConstants.EscSequences[0])
423 {
424 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
425 continue;
426 }
427
428 // distinguish between \uXXXX and \?
429 ch = await Reader.ReadAsync(cancellationToken);
430 if (ch != TJSONProtocolConstants.EscSequences[1]) // control chars like \n
431 {
432 var off = Array.IndexOf(TJSONProtocolConstants.EscapeChars, (char) ch);
433 if (off == -1)
434 {
435 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected control char");
436 }
437 ch = TJSONProtocolConstants.EscapeCharValues[off];
438 await buffer.WriteAsync(new[] {ch}, 0, 1, cancellationToken);
439 continue;
440 }
441
442 // it's \uXXXX
443 await Trans.ReadAllAsync(_tempBuffer, 0, 4, cancellationToken);
444
445 var wch = (short) ((TJSONProtocolHelper.ToHexVal(_tempBuffer[0]) << 12) +
446 (TJSONProtocolHelper.ToHexVal(_tempBuffer[1]) << 8) +
447 (TJSONProtocolHelper.ToHexVal(_tempBuffer[2]) << 4) +
448 TJSONProtocolHelper.ToHexVal(_tempBuffer[3]));
449
450 if (char.IsHighSurrogate((char) wch))
451 {
452 if (codeunits.Count > 0)
453 {
454 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
455 }
456 codeunits.Add((char) wch);
457 }
458 else if (char.IsLowSurrogate((char) wch))
459 {
460 if (codeunits.Count == 0)
461 {
462 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected high surrogate char");
463 }
464
465 codeunits.Add((char) wch);
466 var tmp = Utf8Encoding.GetBytes(codeunits.ToArray());
467 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
468 codeunits.Clear();
469 }
470 else
471 {
472 var tmp = Utf8Encoding.GetBytes(new[] {(char) wch});
473 await buffer.WriteAsync(tmp, 0, tmp.Length, cancellationToken);
474 }
475 }
476
477 if (codeunits.Count > 0)
478 {
479 throw new TProtocolException(TProtocolException.INVALID_DATA, "Expected low surrogate char");
480 }
481
482 return buffer.ToArray();
483 }
484 }
485
486 /// <summary>
487 /// Read in a sequence of characters that are all valid in JSON numbers. Does
488 /// not do a complete regex check to validate that this is actually a number.
489 /// </summary>
490 private async ValueTask<string> ReadJsonNumericCharsAsync(CancellationToken cancellationToken)
491 {
492 var strbld = new StringBuilder();
493 while (true)
494 {
495 //TODO: workaround for primitive types with TJsonProtocol, think - how to rewrite into more easy form without exceptions
496 try
497 {
498 var ch = await Reader.PeekAsync(cancellationToken);
499 if (!TJSONProtocolHelper.IsJsonNumeric(ch))
500 {
501 break;
502 }
503 var c = (char)await Reader.ReadAsync(cancellationToken);
504 strbld.Append(c);
505 }
506 catch (TTransportException)
507 {
508 break;
509 }
510 }
511 return strbld.ToString();
512 }
513
514 /// <summary>
515 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
516 /// </summary>
517 private async ValueTask<long> ReadJsonIntegerAsync(CancellationToken cancellationToken)
518 {
519 await Context.ReadConditionalDelimiterAsync(cancellationToken);
520 if (Context.EscapeNumbers())
521 {
522 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
523 }
524
525 var str = await ReadJsonNumericCharsAsync(cancellationToken);
526 if (Context.EscapeNumbers())
527 {
528 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
529 }
530
531 try
532 {
533 return long.Parse(str);
534 }
535 catch (FormatException)
536 {
537 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
538 }
539 }
540
541 /// <summary>
542 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
543 /// when expected or if wrapped in quotes when not expected.
544 /// </summary>
545 private async ValueTask<double> ReadJsonDoubleAsync(CancellationToken cancellationToken)
546 {
547 await Context.ReadConditionalDelimiterAsync(cancellationToken);
548 if (await Reader.PeekAsync(cancellationToken) == TJSONProtocolConstants.Quote[0])
549 {
550 var arr = await ReadJsonStringAsync(true, cancellationToken);
551 var dub = double.Parse(Utf8Encoding.GetString(arr, 0, arr.Length), CultureInfo.InvariantCulture);
552
553 if (!Context.EscapeNumbers() && !double.IsNaN(dub) && !double.IsInfinity(dub))
554 {
555 // Throw exception -- we should not be in a string in this case
556 throw new TProtocolException(TProtocolException.INVALID_DATA, "Numeric data unexpectedly quoted");
557 }
558
559 return dub;
560 }
561
562 if (Context.EscapeNumbers())
563 {
564 // This will throw - we should have had a quote if escapeNum == true
565 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Quote, cancellationToken);
566 }
567
568 try
569 {
570 return double.Parse(await ReadJsonNumericCharsAsync(cancellationToken), CultureInfo.InvariantCulture);
571 }
572 catch (FormatException)
573 {
574 throw new TProtocolException(TProtocolException.INVALID_DATA, "Bad data encounted in numeric data");
575 }
576 }
577
578 /// <summary>
579 /// Read in a JSON string containing base-64 encoded data and decode it.
580 /// </summary>
581 private async ValueTask<byte[]> ReadJsonBase64Async(CancellationToken cancellationToken)
582 {
583 var b = await ReadJsonStringAsync(false, cancellationToken);
584 var len = b.Length;
585 var off = 0;
586 var size = 0;
587
588 // reduce len to ignore fill bytes
589 while ((len > 0) && (b[len - 1] == '='))
590 {
591 --len;
592 }
593
594 // read & decode full byte triplets = 4 source bytes
595 while (len > 4)
596 {
597 // Decode 4 bytes at a time
598 TBase64Utils.Decode(b, off, 4, b, size); // NB: decoded in place
599 off += 4;
600 len -= 4;
601 size += 3;
602 }
603
604 // Don't decode if we hit the end or got a single leftover byte (invalid
605 // base64 but legal for skip of regular string exType)
606 if (len > 1)
607 {
608 // Decode remainder
609 TBase64Utils.Decode(b, off, len, b, size); // NB: decoded in place
610 size += len - 1;
611 }
612
613 // Sadly we must copy the byte[] (any way around this?)
614 var result = new byte[size];
615 Array.Copy(b, 0, result, 0, size);
616 return result;
617 }
618
619 private async Task ReadJsonObjectStartAsync(CancellationToken cancellationToken)
620 {
621 await Context.ReadConditionalDelimiterAsync(cancellationToken);
622 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBrace, cancellationToken);
623 PushContext(new JSONPairContext(this));
624 }
625
626 private async Task ReadJsonObjectEndAsync(CancellationToken cancellationToken)
627 {
628 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBrace, cancellationToken);
629 PopContext();
630 }
631
632 private async Task ReadJsonArrayStartAsync(CancellationToken cancellationToken)
633 {
634 await Context.ReadConditionalDelimiterAsync(cancellationToken);
635 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.LeftBracket, cancellationToken);
636 PushContext(new JSONListContext(this));
637 }
638
639 private async Task ReadJsonArrayEndAsync(CancellationToken cancellationToken)
640 {
641 await ReadJsonSyntaxCharAsync(TJSONProtocolConstants.RightBracket, cancellationToken);
642 PopContext();
643 }
644
645 public override async ValueTask<TMessage> ReadMessageBeginAsync(CancellationToken cancellationToken)
646 {
647 var message = new TMessage();
648 await ReadJsonArrayStartAsync(cancellationToken);
649 if (await ReadJsonIntegerAsync(cancellationToken) != Version)
650 {
651 throw new TProtocolException(TProtocolException.BAD_VERSION, "Message contained bad version.");
652 }
653
654 var buf = await ReadJsonStringAsync(false, cancellationToken);
655 message.Name = Utf8Encoding.GetString(buf, 0, buf.Length);
656 message.Type = (TMessageType) await ReadJsonIntegerAsync(cancellationToken);
657 message.SeqID = (int) await ReadJsonIntegerAsync(cancellationToken);
658 return message;
659 }
660
661 public override async Task ReadMessageEndAsync(CancellationToken cancellationToken)
662 {
663 await ReadJsonArrayEndAsync(cancellationToken);
664 }
665
666 public override async ValueTask<TStruct> ReadStructBeginAsync(CancellationToken cancellationToken)
667 {
668 await ReadJsonObjectStartAsync(cancellationToken);
669 return new TStruct();
670 }
671
672 public override async Task ReadStructEndAsync(CancellationToken cancellationToken)
673 {
674 await ReadJsonObjectEndAsync(cancellationToken);
675 }
676
677 public override async ValueTask<TField> ReadFieldBeginAsync(CancellationToken cancellationToken)
678 {
679 var field = new TField();
680 var ch = await Reader.PeekAsync(cancellationToken);
681 if (ch == TJSONProtocolConstants.RightBrace[0])
682 {
683 field.Type = TType.Stop;
684 }
685 else
686 {
687 field.ID = (short) await ReadJsonIntegerAsync(cancellationToken);
688 await ReadJsonObjectStartAsync(cancellationToken);
689 field.Type = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
690 }
691 return field;
692 }
693
694 public override async Task ReadFieldEndAsync(CancellationToken cancellationToken)
695 {
696 await ReadJsonObjectEndAsync(cancellationToken);
697 }
698
699 public override async ValueTask<TMap> ReadMapBeginAsync(CancellationToken cancellationToken)
700 {
701 var map = new TMap();
702 await ReadJsonArrayStartAsync(cancellationToken);
703 map.KeyType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
704 map.ValueType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
705 map.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
706 await ReadJsonObjectStartAsync(cancellationToken);
707 return map;
708 }
709
710 public override async Task ReadMapEndAsync(CancellationToken cancellationToken)
711 {
712 await ReadJsonObjectEndAsync(cancellationToken);
713 await ReadJsonArrayEndAsync(cancellationToken);
714 }
715
716 public override async ValueTask<TList> ReadListBeginAsync(CancellationToken cancellationToken)
717 {
718 var list = new TList();
719 await ReadJsonArrayStartAsync(cancellationToken);
720 list.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
721 list.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
722 return list;
723 }
724
725 public override async Task ReadListEndAsync(CancellationToken cancellationToken)
726 {
727 await ReadJsonArrayEndAsync(cancellationToken);
728 }
729
730 public override async ValueTask<TSet> ReadSetBeginAsync(CancellationToken cancellationToken)
731 {
732 var set = new TSet();
733 await ReadJsonArrayStartAsync(cancellationToken);
734 set.ElementType = TJSONProtocolHelper.GetTypeIdForTypeName(await ReadJsonStringAsync(false, cancellationToken));
735 set.Count = (int) await ReadJsonIntegerAsync(cancellationToken);
736 return set;
737 }
738
739 public override async Task ReadSetEndAsync(CancellationToken cancellationToken)
740 {
741 await ReadJsonArrayEndAsync(cancellationToken);
742 }
743
744 public override async ValueTask<bool> ReadBoolAsync(CancellationToken cancellationToken)
745 {
746 return await ReadJsonIntegerAsync(cancellationToken) != 0;
747 }
748
749 public override async ValueTask<sbyte> ReadByteAsync(CancellationToken cancellationToken)
750 {
751 return (sbyte) await ReadJsonIntegerAsync(cancellationToken);
752 }
753
754 public override async ValueTask<short> ReadI16Async(CancellationToken cancellationToken)
755 {
756 return (short) await ReadJsonIntegerAsync(cancellationToken);
757 }
758
759 public override async ValueTask<int> ReadI32Async(CancellationToken cancellationToken)
760 {
761 return (int) await ReadJsonIntegerAsync(cancellationToken);
762 }
763
764 public override async ValueTask<long> ReadI64Async(CancellationToken cancellationToken)
765 {
766 return await ReadJsonIntegerAsync(cancellationToken);
767 }
768
769 public override async ValueTask<double> ReadDoubleAsync(CancellationToken cancellationToken)
770 {
771 return await ReadJsonDoubleAsync(cancellationToken);
772 }
773
774 public override async ValueTask<string> ReadStringAsync(CancellationToken cancellationToken)
775 {
776 var buf = await ReadJsonStringAsync(false, cancellationToken);
777 return Utf8Encoding.GetString(buf, 0, buf.Length);
778 }
779
780 public override async ValueTask<byte[]> ReadBinaryAsync(CancellationToken cancellationToken)
781 {
782 return await ReadJsonBase64Async(cancellationToken);
783 }
784
785 /// <summary>
786 /// Factory for JSON protocol objects
787 /// </summary>
788 public class Factory : TProtocolFactory
789 {
790 public override TProtocol GetProtocol(TTransport trans)
791 {
792 return new TJsonProtocol(trans);
793 }
794 }
795
796 /// <summary>
797 /// Base class for tracking JSON contexts that may require
798 /// inserting/Reading additional JSON syntax characters
799 /// This base context does nothing.
800 /// </summary>
801 protected class JSONBaseContext
802 {
803 protected TJsonProtocol Proto;
804
805 public JSONBaseContext(TJsonProtocol proto)
806 {
807 Proto = proto;
808 }
809
810 public virtual async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
811 {
812 if (cancellationToken.IsCancellationRequested)
813 {
814 await Task.FromCanceled(cancellationToken);
815 }
816 }
817
818 public virtual async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
819 {
820 if (cancellationToken.IsCancellationRequested)
821 {
822 await Task.FromCanceled(cancellationToken);
823 }
824 }
825
826 public virtual bool EscapeNumbers()
827 {
828 return false;
829 }
830 }
831
832 /// <summary>
833 /// Context for JSON lists. Will insert/Read commas before each item except
834 /// for the first one
835 /// </summary>
836 protected class JSONListContext : JSONBaseContext
837 {
838 private bool _first = true;
839
840 public JSONListContext(TJsonProtocol protocol)
841 : base(protocol)
842 {
843 }
844
845 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
846 {
847 if (_first)
848 {
849 _first = false;
850 }
851 else
852 {
853 await Proto.Trans.WriteAsync(TJSONProtocolConstants.Comma, cancellationToken);
854 }
855 }
856
857 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
858 {
859 if (_first)
860 {
861 _first = false;
862 }
863 else
864 {
865 await Proto.ReadJsonSyntaxCharAsync(TJSONProtocolConstants.Comma, cancellationToken);
866 }
867 }
868 }
869
870 /// <summary>
871 /// Context for JSON records. Will insert/Read colons before the value portion
872 /// of each record pair, and commas before each key except the first. In
873 /// addition, will indicate that numbers in the key position need to be
874 /// escaped in quotes (since JSON keys must be strings).
875 /// </summary>
876 // ReSharper disable once InconsistentNaming
877 protected class JSONPairContext : JSONBaseContext
878 {
879 private bool _colon = true;
880
881 private bool _first = true;
882
883 public JSONPairContext(TJsonProtocol proto)
884 : base(proto)
885 {
886 }
887
888 public override async Task WriteConditionalDelimiterAsync(CancellationToken cancellationToken)
889 {
890 if (_first)
891 {
892 _first = false;
893 _colon = true;
894 }
895 else
896 {
897 await Proto.Trans.WriteAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
898 _colon = !_colon;
899 }
900 }
901
902 public override async Task ReadConditionalDelimiterAsync(CancellationToken cancellationToken)
903 {
904 if (_first)
905 {
906 _first = false;
907 _colon = true;
908 }
909 else
910 {
911 await Proto.ReadJsonSyntaxCharAsync(_colon ? TJSONProtocolConstants.Colon : TJSONProtocolConstants.Comma, cancellationToken);
912 _colon = !_colon;
913 }
914 }
915
916 public override bool EscapeNumbers()
917 {
918 return _colon;
919 }
920 }
921
922 /// <summary>
923 /// Holds up to one byte from the transport
924 /// </summary>
925 protected class LookaheadReader
926 {
927 private readonly byte[] _data = new byte[1];
928
929 private bool _hasData;
930 protected TJsonProtocol Proto;
931
932 public LookaheadReader(TJsonProtocol proto)
933 {
934 Proto = proto;
935 }
936
937 /// <summary>
938 /// Return and consume the next byte to be Read, either taking it from the
939 /// data buffer if present or getting it from the transport otherwise.
940 /// </summary>
941 public async ValueTask<byte> ReadAsync(CancellationToken cancellationToken)
942 {
943 if (cancellationToken.IsCancellationRequested)
944 {
945 return await Task.FromCanceled<byte>(cancellationToken);
946 }
947
948 if (_hasData)
949 {
950 _hasData = false;
951 }
952 else
953 {
954 // find more easy way to avoid exception on reading primitive types
955 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
956 }
957 return _data[0];
958 }
959
960 /// <summary>
961 /// Return the next byte to be Read without consuming, filling the data
962 /// buffer if it has not been filled alReady.
963 /// </summary>
964 public async ValueTask<byte> PeekAsync(CancellationToken cancellationToken)
965 {
966 if (cancellationToken.IsCancellationRequested)
967 {
968 return await Task.FromCanceled<byte>(cancellationToken);
969 }
970
971 if (!_hasData)
972 {
973 // find more easy way to avoid exception on reading primitive types
974 await Proto.Trans.ReadAllAsync(_data, 0, 1, cancellationToken);
975 _hasData = true;
976 }
977 return _data[0];
978 }
979 }
980 }
981 }