blob: cad9134ab519926b4d9925162aa2fbd2c2c0aa8a [file] [log] [blame]
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20using System;
21using System.IO;
22using System.Text;
23using System.Collections.Generic;
24
25using Thrift.Transport;
Roger Meier3da317b2011-07-28 18:35:51 +000026using System.Globalization;
Bryan Duxburyfd32d792010-09-18 20:51:25 +000027
28namespace Thrift.Protocol
29{
30 /// <summary>
31 /// JSON protocol implementation for thrift.
32 ///
33 /// This is a full-featured protocol supporting Write and Read.
34 ///
35 /// Please see the C++ class header for a detailed description of the
36 /// protocol's wire format.
37 ///
38 /// Adapted from the Java version.
39 /// </summary>
40 public class TJSONProtocol : TProtocol
41 {
42 /// <summary>
43 /// Factory for JSON protocol objects
44 /// </summary>
45 public class Factory : TProtocolFactory
46 {
47 public TProtocol GetProtocol(TTransport trans)
48 {
49 return new TJSONProtocol(trans);
50 }
51 }
52
53 private static byte[] COMMA = new byte[] { (byte)',' };
54 private static byte[] COLON = new byte[] { (byte)':' };
55 private static byte[] LBRACE = new byte[] { (byte)'{' };
56 private static byte[] RBRACE = new byte[] { (byte)'}' };
57 private static byte[] LBRACKET = new byte[] { (byte)'[' };
58 private static byte[] RBRACKET = new byte[] { (byte)']' };
59 private static byte[] QUOTE = new byte[] { (byte)'"' };
60 private static byte[] BACKSLASH = new byte[] { (byte)'\\' };
61 private static byte[] ZERO = new byte[] { (byte)'0' };
62
63 private byte[] ESCSEQ = new byte[] { (byte)'\\', (byte)'u', (byte)'0', (byte)'0' };
64
65 private const long VERSION = 1;
66 private byte[] JSON_CHAR_TABLE = {
67 0, 0, 0, 0, 0, 0, 0, 0,(byte)'b',(byte)'t',(byte)'n', 0,(byte)'f',(byte)'r', 0, 0,
68 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
69 1, 1,(byte)'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
70 };
71
72 private char[] ESCAPE_CHARS = "\"\\bfnrt".ToCharArray();
73
74 private byte[] ESCAPE_CHAR_VALS = {
75 (byte)'"', (byte)'\\', (byte)'\b', (byte)'\f', (byte)'\n', (byte)'\r', (byte)'\t',
76 };
77
78 private const int DEF_STRING_SIZE = 16;
79
80 private static byte[] NAME_BOOL = new byte[] { (byte)'t', (byte)'f' };
81 private static byte[] NAME_BYTE = new byte[] { (byte)'i', (byte)'8' };
82 private static byte[] NAME_I16 = new byte[] { (byte)'i', (byte)'1', (byte)'6' };
83 private static byte[] NAME_I32 = new byte[] { (byte)'i', (byte)'3', (byte)'2' };
84 private static byte[] NAME_I64 = new byte[] { (byte)'i', (byte)'6', (byte)'4' };
85 private static byte[] NAME_DOUBLE = new byte[] { (byte)'d', (byte)'b', (byte)'l' };
86 private static byte[] NAME_STRUCT = new byte[] { (byte)'r', (byte)'e', (byte)'c' };
87 private static byte[] NAME_STRING = new byte[] { (byte)'s', (byte)'t', (byte)'r' };
88 private static byte[] NAME_MAP = new byte[] { (byte)'m', (byte)'a', (byte)'p' };
89 private static byte[] NAME_LIST = new byte[] { (byte)'l', (byte)'s', (byte)'t' };
90 private static byte[] NAME_SET = new byte[] { (byte)'s', (byte)'e', (byte)'t' };
91
92 private static byte[] GetTypeNameForTypeID(TType typeID)
93 {
94 switch (typeID)
95 {
96 case TType.Bool:
97 return NAME_BOOL;
98 case TType.Byte:
99 return NAME_BYTE;
100 case TType.I16:
101 return NAME_I16;
102 case TType.I32:
103 return NAME_I32;
104 case TType.I64:
105 return NAME_I64;
106 case TType.Double:
107 return NAME_DOUBLE;
108 case TType.String:
109 return NAME_STRING;
110 case TType.Struct:
111 return NAME_STRUCT;
112 case TType.Map:
113 return NAME_MAP;
114 case TType.Set:
115 return NAME_SET;
116 case TType.List:
117 return NAME_LIST;
118 default:
119 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
120 "Unrecognized type");
121 }
122 }
123
124 private static TType GetTypeIDForTypeName(byte[] name)
125 {
126 TType result = TType.Stop;
127 if (name.Length > 1)
128 {
129 switch (name[0])
130 {
131 case (byte)'d':
132 result = TType.Double;
133 break;
134 case (byte)'i':
135 switch (name[1])
136 {
137 case (byte)'8':
138 result = TType.Byte;
139 break;
140 case (byte)'1':
141 result = TType.I16;
142 break;
143 case (byte)'3':
144 result = TType.I32;
145 break;
146 case (byte)'6':
147 result = TType.I64;
148 break;
149 }
150 break;
151 case (byte)'l':
152 result = TType.List;
153 break;
154 case (byte)'m':
155 result = TType.Map;
156 break;
157 case (byte)'r':
158 result = TType.Struct;
159 break;
160 case (byte)'s':
161 if (name[1] == (byte)'t')
162 {
163 result = TType.String;
164 }
165 else if (name[1] == (byte)'e')
166 {
167 result = TType.Set;
168 }
169 break;
170 case (byte)'t':
171 result = TType.Bool;
172 break;
173 }
174 }
175 if (result == TType.Stop)
176 {
177 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
178 "Unrecognized type");
179 }
180 return result;
181 }
182
183 ///<summary>
184 /// Base class for tracking JSON contexts that may require
185 /// inserting/Reading additional JSON syntax characters
186 /// This base context does nothing.
187 ///</summary>
188 protected class JSONBaseContext
189 {
190 protected TJSONProtocol proto;
191
192 public JSONBaseContext(TJSONProtocol proto)
193 {
194 this.proto = proto;
195 }
196
197 public virtual void Write() { }
198
199 public virtual void Read() { }
200
201 public virtual bool EscapeNumbers() { return false; }
202 }
203
204 ///<summary>
205 /// Context for JSON lists. Will insert/Read commas before each item except
206 /// for the first one
207 ///</summary>
208 protected class JSONListContext : JSONBaseContext
209 {
210 public JSONListContext(TJSONProtocol protocol)
211 : base(protocol)
212 {
213
214 }
215
216 private bool first = true;
217
218 public override void Write()
219 {
220 if (first)
221 {
222 first = false;
223 }
224 else
225 {
226 proto.trans.Write(COMMA);
227 }
228 }
229
230 public override void Read()
231 {
232 if (first)
233 {
234 first = false;
235 }
236 else
237 {
238 proto.ReadJSONSyntaxChar(COMMA);
239 }
240 }
241 }
242
243 ///<summary>
244 /// Context for JSON records. Will insert/Read colons before the value portion
245 /// of each record pair, and commas before each key except the first. In
246 /// addition, will indicate that numbers in the key position need to be
247 /// escaped in quotes (since JSON keys must be strings).
248 ///</summary>
249 protected class JSONPairContext : JSONBaseContext
250 {
251 public JSONPairContext(TJSONProtocol proto)
252 : base(proto)
253 {
254
255 }
256
257 private bool first = true;
258 private bool colon = true;
259
260 public override void Write()
261 {
262 if (first)
263 {
264 first = false;
265 colon = true;
266 }
267 else
268 {
269 proto.trans.Write(colon ? COLON : COMMA);
270 colon = !colon;
271 }
272 }
273
274 public override void Read()
275 {
276 if (first)
277 {
278 first = false;
279 colon = true;
280 }
281 else
282 {
283 proto.ReadJSONSyntaxChar(colon ? COLON : COMMA);
284 colon = !colon;
285 }
286 }
287
288 public override bool EscapeNumbers()
289 {
290 return colon;
291 }
292 }
293
294 ///<summary>
295 /// Holds up to one byte from the transport
296 ///</summary>
297 protected class LookaheadReader
298 {
299 protected TJSONProtocol proto;
300
301 public LookaheadReader(TJSONProtocol proto)
302 {
303 this.proto = proto;
304 }
305
306 private bool hasData;
307 private byte[] data = new byte[1];
308
309 ///<summary>
310 /// Return and consume the next byte to be Read, either taking it from the
311 /// data buffer if present or getting it from the transport otherwise.
312 ///</summary>
313 public byte Read()
314 {
315 if (hasData)
316 {
317 hasData = false;
318 }
319 else
320 {
321 proto.trans.ReadAll(data, 0, 1);
322 }
323 return data[0];
324 }
325
326 ///<summary>
327 /// Return the next byte to be Read without consuming, filling the data
328 /// buffer if it has not been filled alReady.
329 ///</summary>
330 public byte Peek()
331 {
332 if (!hasData)
333 {
334 proto.trans.ReadAll(data, 0, 1);
335 }
336 hasData = true;
337 return data[0];
338 }
339 }
340
341 // Default encoding
342 protected Encoding utf8Encoding = UTF8Encoding.UTF8;
343
344 // Stack of nested contexts that we may be in
345 protected Stack<JSONBaseContext> contextStack = new Stack<JSONBaseContext>();
346
347 // Current context that we are in
348 protected JSONBaseContext context;
349
350 // Reader that manages a 1-byte buffer
351 protected LookaheadReader reader;
352
353 ///<summary>
354 /// Push a new JSON context onto the stack.
355 ///</summary>
356 protected void PushContext(JSONBaseContext c)
357 {
358 contextStack.Push(context);
359 context = c;
360 }
361
362 ///<summary>
363 /// Pop the last JSON context off the stack
364 ///</summary>
365 protected void PopContext()
366 {
367 context = contextStack.Pop();
368 }
369
370 ///<summary>
371 /// TJSONProtocol Constructor
372 ///</summary>
373 public TJSONProtocol(TTransport trans)
374 : base(trans)
375 {
376 context = new JSONBaseContext(this);
377 reader = new LookaheadReader(this);
378 }
379
380 // Temporary buffer used by several methods
381 private byte[] tempBuffer = new byte[4];
382
383 ///<summary>
Roger Meiere0cac982010-12-16 13:15:49 +0000384 /// Read a byte that must match b[0]; otherwise an exception is thrown.
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000385 /// Marked protected to avoid synthetic accessor in JSONListContext.Read
386 /// and JSONPairContext.Read
387 ///</summary>
388 protected void ReadJSONSyntaxChar(byte[] b)
389 {
390 byte ch = reader.Read();
391 if (ch != b[0])
392 {
393 throw new TProtocolException(TProtocolException.INVALID_DATA,
394 "Unexpected character:" + (char)ch);
395 }
396 }
397
398 ///<summary>
399 /// Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
400 /// corresponding hex value
401 ///</summary>
402 private static byte HexVal(byte ch)
403 {
404 if ((ch >= '0') && (ch <= '9'))
405 {
406 return (byte)((char)ch - '0');
407 }
408 else if ((ch >= 'a') && (ch <= 'f'))
409 {
Jens Geyere5bfd4c2013-06-28 21:48:02 +0200410 ch += 10;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000411 return (byte)((char)ch - 'a');
412 }
413 else
414 {
415 throw new TProtocolException(TProtocolException.INVALID_DATA,
416 "Expected hex character");
417 }
418 }
419
420 ///<summary>
421 /// Convert a byte containing a hex value to its corresponding hex character
422 ///</summary>
423 private static byte HexChar(byte val)
424 {
425 val &= 0x0F;
426 if (val < 10)
427 {
428 return (byte)((char)val + '0');
429 }
430 else
431 {
Jens Geyere5bfd4c2013-06-28 21:48:02 +0200432 val -= 10;
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000433 return (byte)((char)val + 'a');
434 }
435 }
436
437 ///<summary>
438 /// Write the bytes in array buf as a JSON characters, escaping as needed
439 ///</summary>
440 private void WriteJSONString(byte[] b)
441 {
442 context.Write();
443 trans.Write(QUOTE);
444 int len = b.Length;
445 for (int i = 0; i < len; i++)
446 {
447 if ((b[i] & 0x00FF) >= 0x30)
448 {
449 if (b[i] == BACKSLASH[0])
450 {
451 trans.Write(BACKSLASH);
452 trans.Write(BACKSLASH);
453 }
454 else
455 {
456 trans.Write(b, i, 1);
457 }
458 }
459 else
460 {
461 tempBuffer[0] = JSON_CHAR_TABLE[b[i]];
462 if (tempBuffer[0] == 1)
463 {
464 trans.Write(b, i, 1);
465 }
466 else if (tempBuffer[0] > 1)
467 {
468 trans.Write(BACKSLASH);
469 trans.Write(tempBuffer, 0, 1);
470 }
471 else
472 {
473 trans.Write(ESCSEQ);
474 tempBuffer[0] = HexChar((byte)(b[i] >> 4));
475 tempBuffer[1] = HexChar(b[i]);
476 trans.Write(tempBuffer, 0, 2);
477 }
478 }
479 }
480 trans.Write(QUOTE);
481 }
482
483 ///<summary>
484 /// Write out number as a JSON value. If the context dictates so, it will be
485 /// wrapped in quotes to output as a JSON string.
486 ///</summary>
487 private void WriteJSONInteger(long num)
488 {
489 context.Write();
490 String str = num.ToString();
491
492 bool escapeNum = context.EscapeNumbers();
493 if (escapeNum)
494 trans.Write(QUOTE);
495
496 trans.Write(utf8Encoding.GetBytes(str));
497
498 if (escapeNum)
499 trans.Write(QUOTE);
500 }
501
502 ///<summary>
503 /// Write out a double as a JSON value. If it is NaN or infinity or if the
504 /// context dictates escaping, Write out as JSON string.
505 ///</summary>
506 private void WriteJSONDouble(double num)
507 {
508 context.Write();
Roger Meier3da317b2011-07-28 18:35:51 +0000509 String str = num.ToString(CultureInfo.InvariantCulture);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000510 bool special = false;
511
512 switch (str[0])
513 {
514 case 'N': // NaN
515 case 'I': // Infinity
516 special = true;
517 break;
518 case '-':
519 if (str[1] == 'I')
520 { // -Infinity
521 special = true;
522 }
523 break;
524 }
525
526 bool escapeNum = special || context.EscapeNumbers();
527
528 if (escapeNum)
529 trans.Write(QUOTE);
530
531 trans.Write(utf8Encoding.GetBytes(str));
532
533 if (escapeNum)
534 trans.Write(QUOTE);
535 }
536 ///<summary>
537 /// Write out contents of byte array b as a JSON string with base-64 encoded
538 /// data
539 ///</summary>
540 private void WriteJSONBase64(byte[] b)
541 {
542 context.Write();
543 trans.Write(QUOTE);
544
545 int len = b.Length;
546 int off = 0;
547
548 while (len >= 3)
549 {
550 // Encode 3 bytes at a time
551 TBase64Utils.encode(b, off, 3, tempBuffer, 0);
552 trans.Write(tempBuffer, 0, 4);
553 off += 3;
554 len -= 3;
555 }
556 if (len > 0)
557 {
558 // Encode remainder
559 TBase64Utils.encode(b, off, len, tempBuffer, 0);
560 trans.Write(tempBuffer, 0, len + 1);
561 }
562
563 trans.Write(QUOTE);
564 }
565
566 private void WriteJSONObjectStart()
567 {
568 context.Write();
569 trans.Write(LBRACE);
570 PushContext(new JSONPairContext(this));
571 }
572
573 private void WriteJSONObjectEnd()
574 {
575 PopContext();
576 trans.Write(RBRACE);
577 }
578
579 private void WriteJSONArrayStart()
580 {
581 context.Write();
582 trans.Write(LBRACKET);
583 PushContext(new JSONListContext(this));
584 }
585
586 private void WriteJSONArrayEnd()
587 {
588 PopContext();
589 trans.Write(RBRACKET);
590 }
591
592 public override void WriteMessageBegin(TMessage message)
593 {
594 WriteJSONArrayStart();
595 WriteJSONInteger(VERSION);
596
597 byte[] b = utf8Encoding.GetBytes(message.Name);
598 WriteJSONString(b);
599
600 WriteJSONInteger((long)message.Type);
601 WriteJSONInteger(message.SeqID);
602 }
603
604 public override void WriteMessageEnd()
605 {
606 WriteJSONArrayEnd();
607 }
608
609 public override void WriteStructBegin(TStruct str)
610 {
611 WriteJSONObjectStart();
612 }
613
614 public override void WriteStructEnd()
615 {
616 WriteJSONObjectEnd();
617 }
618
619 public override void WriteFieldBegin(TField field)
620 {
621 WriteJSONInteger(field.ID);
622 WriteJSONObjectStart();
623 WriteJSONString(GetTypeNameForTypeID(field.Type));
624 }
625
626 public override void WriteFieldEnd()
627 {
628 WriteJSONObjectEnd();
629 }
630
631 public override void WriteFieldStop() { }
632
633 public override void WriteMapBegin(TMap map)
634 {
635 WriteJSONArrayStart();
636 WriteJSONString(GetTypeNameForTypeID(map.KeyType));
637 WriteJSONString(GetTypeNameForTypeID(map.ValueType));
638 WriteJSONInteger(map.Count);
639 WriteJSONObjectStart();
640 }
641
642 public override void WriteMapEnd()
643 {
644 WriteJSONObjectEnd();
645 WriteJSONArrayEnd();
646 }
647
648 public override void WriteListBegin(TList list)
649 {
650 WriteJSONArrayStart();
651 WriteJSONString(GetTypeNameForTypeID(list.ElementType));
652 WriteJSONInteger(list.Count);
653 }
654
655 public override void WriteListEnd()
656 {
657 WriteJSONArrayEnd();
658 }
659
660 public override void WriteSetBegin(TSet set)
661 {
662 WriteJSONArrayStart();
663 WriteJSONString(GetTypeNameForTypeID(set.ElementType));
664 WriteJSONInteger(set.Count);
665 }
666
667 public override void WriteSetEnd()
668 {
669 WriteJSONArrayEnd();
670 }
671
672 public override void WriteBool(bool b)
673 {
674 WriteJSONInteger(b ? (long)1 : (long)0);
675 }
676
Jens Geyerf509df92013-04-25 20:38:55 +0200677 public override void WriteByte(sbyte b)
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000678 {
679 WriteJSONInteger((long)b);
680 }
681
682 public override void WriteI16(short i16)
683 {
684 WriteJSONInteger((long)i16);
685 }
686
687 public override void WriteI32(int i32)
688 {
689 WriteJSONInteger((long)i32);
690 }
691
692 public override void WriteI64(long i64)
693 {
694 WriteJSONInteger(i64);
695 }
696
697 public override void WriteDouble(double dub)
698 {
699 WriteJSONDouble(dub);
700 }
701
702 public override void WriteString(String str)
703 {
704 byte[] b = utf8Encoding.GetBytes(str);
705 WriteJSONString(b);
706 }
707
708 public override void WriteBinary(byte[] bin)
709 {
710 WriteJSONBase64(bin);
711 }
712
713 /**
714 * Reading methods.
715 */
716
717 ///<summary>
718 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
719 /// context if skipContext is true.
720 ///</summary>
721 private byte[] ReadJSONString(bool skipContext)
722 {
723 MemoryStream buffer = new MemoryStream();
724
725
726 if (!skipContext)
727 {
728 context.Read();
729 }
730 ReadJSONSyntaxChar(QUOTE);
731 while (true)
732 {
733 byte ch = reader.Read();
734 if (ch == QUOTE[0])
735 {
736 break;
737 }
738 if (ch == ESCSEQ[0])
739 {
740 ch = reader.Read();
741 if (ch == ESCSEQ[1])
742 {
743 ReadJSONSyntaxChar(ZERO);
744 ReadJSONSyntaxChar(ZERO);
745 trans.ReadAll(tempBuffer, 0, 2);
746 ch = (byte)((HexVal((byte)tempBuffer[0]) << 4) + HexVal(tempBuffer[1]));
747 }
748 else
749 {
Jake Farrell83693532011-04-14 14:30:25 +0000750 int off = Array.IndexOf(ESCAPE_CHARS, (char)ch);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000751 if (off == -1)
752 {
753 throw new TProtocolException(TProtocolException.INVALID_DATA,
754 "Expected control char");
755 }
756 ch = ESCAPE_CHAR_VALS[off];
757 }
758 }
759 buffer.Write(new byte[] { (byte)ch }, 0, 1);
760 }
761 return buffer.ToArray();
762 }
763
764 ///<summary>
765 /// Return true if the given byte could be a valid part of a JSON number.
766 ///</summary>
767 private bool IsJSONNumeric(byte b)
768 {
769 switch (b)
770 {
771 case (byte)'+':
772 case (byte)'-':
773 case (byte)'.':
774 case (byte)'0':
775 case (byte)'1':
776 case (byte)'2':
777 case (byte)'3':
778 case (byte)'4':
779 case (byte)'5':
780 case (byte)'6':
781 case (byte)'7':
782 case (byte)'8':
783 case (byte)'9':
784 case (byte)'E':
785 case (byte)'e':
786 return true;
787 }
788 return false;
789 }
790
791 ///<summary>
792 /// Read in a sequence of characters that are all valid in JSON numbers. Does
793 /// not do a complete regex check to validate that this is actually a number.
794 ////</summary>
795 private String ReadJSONNumericChars()
796 {
797 StringBuilder strbld = new StringBuilder();
798 while (true)
799 {
800 byte ch = reader.Peek();
801 if (!IsJSONNumeric(ch))
802 {
803 break;
804 }
805 strbld.Append((char)reader.Read());
806 }
807 return strbld.ToString();
808 }
809
810 ///<summary>
811 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
812 ///</summary>
813 private long ReadJSONInteger()
814 {
815 context.Read();
816 if (context.EscapeNumbers())
817 {
818 ReadJSONSyntaxChar(QUOTE);
819 }
820 String str = ReadJSONNumericChars();
821 if (context.EscapeNumbers())
822 {
823 ReadJSONSyntaxChar(QUOTE);
824 }
825 try
826 {
827 return Int64.Parse(str);
828 }
829 catch (FormatException)
830 {
831 throw new TProtocolException(TProtocolException.INVALID_DATA,
832 "Bad data encounted in numeric data");
833 }
834 }
835
836 ///<summary>
837 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
838 /// when expected or if wrapped in quotes when not expected.
839 ///</summary>
840 private double ReadJSONDouble()
841 {
842 context.Read();
843 if (reader.Peek() == QUOTE[0])
844 {
845 byte[] arr = ReadJSONString(true);
Roger Meier284a9b52011-12-08 13:39:56 +0000846 double dub = Double.Parse(utf8Encoding.GetString(arr,0,arr.Length), CultureInfo.InvariantCulture);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000847
848 if (!context.EscapeNumbers() && !Double.IsNaN(dub) &&
849 !Double.IsInfinity(dub))
850 {
851 // Throw exception -- we should not be in a string in this case
852 throw new TProtocolException(TProtocolException.INVALID_DATA,
853 "Numeric data unexpectedly quoted");
854 }
855 return dub;
856 }
857 else
858 {
859 if (context.EscapeNumbers())
860 {
861 // This will throw - we should have had a quote if escapeNum == true
862 ReadJSONSyntaxChar(QUOTE);
863 }
864 try
865 {
Jens Geyerd73aa072013-09-18 23:30:42 +0200866 return Double.Parse(ReadJSONNumericChars(), CultureInfo.InvariantCulture);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000867 }
868 catch (FormatException)
869 {
870 throw new TProtocolException(TProtocolException.INVALID_DATA,
871 "Bad data encounted in numeric data");
872 }
873 }
874 }
875
876 //<summary>
877 /// Read in a JSON string containing base-64 encoded data and decode it.
878 ///</summary>
879 private byte[] ReadJSONBase64()
880 {
881 byte[] b = ReadJSONString(false);
882 int len = b.Length;
883 int off = 0;
884 int size = 0;
885 while (len >= 4)
886 {
887 // Decode 4 bytes at a time
888 TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
889 off += 4;
890 len -= 4;
891 size += 3;
892 }
893 // Don't decode if we hit the end or got a single leftover byte (invalid
894 // base64 but legal for skip of regular string type)
895 if (len > 1)
896 {
897 // Decode remainder
898 TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
899 size += len - 1;
900 }
901 // Sadly we must copy the byte[] (any way around this?)
902 byte[] result = new byte[size];
903 Array.Copy(b, 0, result, 0, size);
904 return result;
905 }
906
907 private void ReadJSONObjectStart()
908 {
909 context.Read();
910 ReadJSONSyntaxChar(LBRACE);
911 PushContext(new JSONPairContext(this));
912 }
913
914 private void ReadJSONObjectEnd()
915 {
916 ReadJSONSyntaxChar(RBRACE);
917 PopContext();
918 }
919
920 private void ReadJSONArrayStart()
921 {
922 context.Read();
923 ReadJSONSyntaxChar(LBRACKET);
924 PushContext(new JSONListContext(this));
925 }
926
927 private void ReadJSONArrayEnd()
928 {
929 ReadJSONSyntaxChar(RBRACKET);
930 PopContext();
931 }
932
933 public override TMessage ReadMessageBegin()
934 {
935 TMessage message = new TMessage();
936 ReadJSONArrayStart();
937 if (ReadJSONInteger() != VERSION)
938 {
939 throw new TProtocolException(TProtocolException.BAD_VERSION,
940 "Message contained bad version.");
941 }
942
Roger Meier284a9b52011-12-08 13:39:56 +0000943 var buf = ReadJSONString(false);
944 message.Name = utf8Encoding.GetString(buf,0,buf.Length);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000945 message.Type = (TMessageType)ReadJSONInteger();
946 message.SeqID = (int)ReadJSONInteger();
947 return message;
948 }
949
950 public override void ReadMessageEnd()
951 {
952 ReadJSONArrayEnd();
953 }
954
955 public override TStruct ReadStructBegin()
956 {
957 ReadJSONObjectStart();
958 return new TStruct();
959 }
960
961 public override void ReadStructEnd()
962 {
963 ReadJSONObjectEnd();
964 }
965
966 public override TField ReadFieldBegin()
967 {
968 TField field = new TField();
969 byte ch = reader.Peek();
970 if (ch == RBRACE[0])
971 {
972 field.Type = TType.Stop;
973 }
974 else
975 {
976 field.ID = (short)ReadJSONInteger();
977 ReadJSONObjectStart();
978 field.Type = GetTypeIDForTypeName(ReadJSONString(false));
979 }
980 return field;
981 }
982
983 public override void ReadFieldEnd()
984 {
985 ReadJSONObjectEnd();
986 }
987
988 public override TMap ReadMapBegin()
989 {
990 TMap map = new TMap();
991 ReadJSONArrayStart();
992 map.KeyType = GetTypeIDForTypeName(ReadJSONString(false));
993 map.ValueType = GetTypeIDForTypeName(ReadJSONString(false));
994 map.Count = (int)ReadJSONInteger();
995 ReadJSONObjectStart();
996 return map;
997 }
998
999 public override void ReadMapEnd()
1000 {
1001 ReadJSONObjectEnd();
1002 ReadJSONArrayEnd();
1003 }
1004
1005 public override TList ReadListBegin()
1006 {
1007 TList list = new TList();
1008 ReadJSONArrayStart();
1009 list.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
1010 list.Count = (int)ReadJSONInteger();
1011 return list;
1012 }
1013
1014 public override void ReadListEnd()
1015 {
1016 ReadJSONArrayEnd();
1017 }
1018
1019 public override TSet ReadSetBegin()
1020 {
1021 TSet set = new TSet();
1022 ReadJSONArrayStart();
1023 set.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
1024 set.Count = (int)ReadJSONInteger();
1025 return set;
1026 }
1027
1028 public override void ReadSetEnd()
1029 {
1030 ReadJSONArrayEnd();
1031 }
1032
1033 public override bool ReadBool()
1034 {
1035 return (ReadJSONInteger() == 0 ? false : true);
1036 }
1037
Jens Geyerf509df92013-04-25 20:38:55 +02001038 public override sbyte ReadByte()
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001039 {
Jens Geyerf509df92013-04-25 20:38:55 +02001040 return (sbyte)ReadJSONInteger();
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001041 }
1042
1043 public override short ReadI16()
1044 {
1045 return (short)ReadJSONInteger();
1046 }
1047
1048 public override int ReadI32()
1049 {
1050 return (int)ReadJSONInteger();
1051 }
1052
1053 public override long ReadI64()
1054 {
1055 return (long)ReadJSONInteger();
1056 }
1057
1058 public override double ReadDouble()
1059 {
1060 return ReadJSONDouble();
1061 }
1062
1063 public override String ReadString()
1064 {
Roger Meier284a9b52011-12-08 13:39:56 +00001065 var buf = ReadJSONString(false);
1066 return utf8Encoding.GetString(buf,0,buf.Length);
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001067 }
1068
1069 public override byte[] ReadBinary()
1070 {
1071 return ReadJSONBase64();
1072 }
1073
1074 }
1075}