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