blob: 5f6351f7eff6d6a271465d6d89fdfaef96fd5a5c [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;
26
27namespace Thrift.Protocol
28{
29 /// <summary>
30 /// JSON protocol implementation for thrift.
31 ///
32 /// This is a full-featured protocol supporting Write and Read.
33 ///
34 /// Please see the C++ class header for a detailed description of the
35 /// protocol's wire format.
36 ///
37 /// Adapted from the Java version.
38 /// </summary>
39 public class TJSONProtocol : TProtocol
40 {
41 /// <summary>
42 /// Factory for JSON protocol objects
43 /// </summary>
44 public class Factory : TProtocolFactory
45 {
46 public TProtocol GetProtocol(TTransport trans)
47 {
48 return new TJSONProtocol(trans);
49 }
50 }
51
52 private static byte[] COMMA = new byte[] { (byte)',' };
53 private static byte[] COLON = new byte[] { (byte)':' };
54 private static byte[] LBRACE = new byte[] { (byte)'{' };
55 private static byte[] RBRACE = new byte[] { (byte)'}' };
56 private static byte[] LBRACKET = new byte[] { (byte)'[' };
57 private static byte[] RBRACKET = new byte[] { (byte)']' };
58 private static byte[] QUOTE = new byte[] { (byte)'"' };
59 private static byte[] BACKSLASH = new byte[] { (byte)'\\' };
60 private static byte[] ZERO = new byte[] { (byte)'0' };
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)'\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
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
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
191 public JSONBaseContext(TJSONProtocol proto)
192 {
193 this.proto = proto;
194 }
195
196 public virtual void Write() { }
197
198 public virtual void Read() { }
199
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 {
209 public JSONListContext(TJSONProtocol protocol)
210 : base(protocol)
211 {
212
213 }
214
215 private bool first = true;
216
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
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 {
250 public JSONPairContext(TJSONProtocol proto)
251 : base(proto)
252 {
253
254 }
255
256 private bool first = true;
257 private bool colon = true;
258
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
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
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
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>
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>
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>
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>
364 protected void PopContext()
365 {
366 context = contextStack.Pop();
367 }
368
369 ///<summary>
370 /// TJSONProtocol Constructor
371 ///</summary>
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>
Roger Meiere0cac982010-12-16 13:15:49 +0000383 /// Read a byte that must match b[0]; otherwise an exception is thrown.
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000384 /// Marked protected to avoid synthetic accessor in JSONListContext.Read
385 /// and JSONPairContext.Read
386 ///</summary>
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>
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 return (byte)((char)ch - 'a');
410 }
411 else
412 {
413 throw new TProtocolException(TProtocolException.INVALID_DATA,
414 "Expected hex character");
415 }
416 }
417
418 ///<summary>
419 /// Convert a byte containing a hex value to its corresponding hex character
420 ///</summary>
421 private static byte HexChar(byte val)
422 {
423 val &= 0x0F;
424 if (val < 10)
425 {
426 return (byte)((char)val + '0');
427 }
428 else
429 {
430 return (byte)((char)val + 'a');
431 }
432 }
433
434 ///<summary>
435 /// Write the bytes in array buf as a JSON characters, escaping as needed
436 ///</summary>
437 private void WriteJSONString(byte[] b)
438 {
439 context.Write();
440 trans.Write(QUOTE);
441 int len = b.Length;
442 for (int i = 0; i < len; i++)
443 {
444 if ((b[i] & 0x00FF) >= 0x30)
445 {
446 if (b[i] == BACKSLASH[0])
447 {
448 trans.Write(BACKSLASH);
449 trans.Write(BACKSLASH);
450 }
451 else
452 {
453 trans.Write(b, i, 1);
454 }
455 }
456 else
457 {
458 tempBuffer[0] = JSON_CHAR_TABLE[b[i]];
459 if (tempBuffer[0] == 1)
460 {
461 trans.Write(b, i, 1);
462 }
463 else if (tempBuffer[0] > 1)
464 {
465 trans.Write(BACKSLASH);
466 trans.Write(tempBuffer, 0, 1);
467 }
468 else
469 {
470 trans.Write(ESCSEQ);
471 tempBuffer[0] = HexChar((byte)(b[i] >> 4));
472 tempBuffer[1] = HexChar(b[i]);
473 trans.Write(tempBuffer, 0, 2);
474 }
475 }
476 }
477 trans.Write(QUOTE);
478 }
479
480 ///<summary>
481 /// Write out number as a JSON value. If the context dictates so, it will be
482 /// wrapped in quotes to output as a JSON string.
483 ///</summary>
484 private void WriteJSONInteger(long num)
485 {
486 context.Write();
487 String str = num.ToString();
488
489 bool escapeNum = context.EscapeNumbers();
490 if (escapeNum)
491 trans.Write(QUOTE);
492
493 trans.Write(utf8Encoding.GetBytes(str));
494
495 if (escapeNum)
496 trans.Write(QUOTE);
497 }
498
499 ///<summary>
500 /// Write out a double as a JSON value. If it is NaN or infinity or if the
501 /// context dictates escaping, Write out as JSON string.
502 ///</summary>
503 private void WriteJSONDouble(double num)
504 {
505 context.Write();
506 String str = num.ToString();
507 bool special = false;
508
509 switch (str[0])
510 {
511 case 'N': // NaN
512 case 'I': // Infinity
513 special = true;
514 break;
515 case '-':
516 if (str[1] == 'I')
517 { // -Infinity
518 special = true;
519 }
520 break;
521 }
522
523 bool escapeNum = special || context.EscapeNumbers();
524
525 if (escapeNum)
526 trans.Write(QUOTE);
527
528 trans.Write(utf8Encoding.GetBytes(str));
529
530 if (escapeNum)
531 trans.Write(QUOTE);
532 }
533 ///<summary>
534 /// Write out contents of byte array b as a JSON string with base-64 encoded
535 /// data
536 ///</summary>
537 private void WriteJSONBase64(byte[] b)
538 {
539 context.Write();
540 trans.Write(QUOTE);
541
542 int len = b.Length;
543 int off = 0;
544
545 while (len >= 3)
546 {
547 // Encode 3 bytes at a time
548 TBase64Utils.encode(b, off, 3, tempBuffer, 0);
549 trans.Write(tempBuffer, 0, 4);
550 off += 3;
551 len -= 3;
552 }
553 if (len > 0)
554 {
555 // Encode remainder
556 TBase64Utils.encode(b, off, len, tempBuffer, 0);
557 trans.Write(tempBuffer, 0, len + 1);
558 }
559
560 trans.Write(QUOTE);
561 }
562
563 private void WriteJSONObjectStart()
564 {
565 context.Write();
566 trans.Write(LBRACE);
567 PushContext(new JSONPairContext(this));
568 }
569
570 private void WriteJSONObjectEnd()
571 {
572 PopContext();
573 trans.Write(RBRACE);
574 }
575
576 private void WriteJSONArrayStart()
577 {
578 context.Write();
579 trans.Write(LBRACKET);
580 PushContext(new JSONListContext(this));
581 }
582
583 private void WriteJSONArrayEnd()
584 {
585 PopContext();
586 trans.Write(RBRACKET);
587 }
588
589 public override void WriteMessageBegin(TMessage message)
590 {
591 WriteJSONArrayStart();
592 WriteJSONInteger(VERSION);
593
594 byte[] b = utf8Encoding.GetBytes(message.Name);
595 WriteJSONString(b);
596
597 WriteJSONInteger((long)message.Type);
598 WriteJSONInteger(message.SeqID);
599 }
600
601 public override void WriteMessageEnd()
602 {
603 WriteJSONArrayEnd();
604 }
605
606 public override void WriteStructBegin(TStruct str)
607 {
608 WriteJSONObjectStart();
609 }
610
611 public override void WriteStructEnd()
612 {
613 WriteJSONObjectEnd();
614 }
615
616 public override void WriteFieldBegin(TField field)
617 {
618 WriteJSONInteger(field.ID);
619 WriteJSONObjectStart();
620 WriteJSONString(GetTypeNameForTypeID(field.Type));
621 }
622
623 public override void WriteFieldEnd()
624 {
625 WriteJSONObjectEnd();
626 }
627
628 public override void WriteFieldStop() { }
629
630 public override void WriteMapBegin(TMap map)
631 {
632 WriteJSONArrayStart();
633 WriteJSONString(GetTypeNameForTypeID(map.KeyType));
634 WriteJSONString(GetTypeNameForTypeID(map.ValueType));
635 WriteJSONInteger(map.Count);
636 WriteJSONObjectStart();
637 }
638
639 public override void WriteMapEnd()
640 {
641 WriteJSONObjectEnd();
642 WriteJSONArrayEnd();
643 }
644
645 public override void WriteListBegin(TList list)
646 {
647 WriteJSONArrayStart();
648 WriteJSONString(GetTypeNameForTypeID(list.ElementType));
649 WriteJSONInteger(list.Count);
650 }
651
652 public override void WriteListEnd()
653 {
654 WriteJSONArrayEnd();
655 }
656
657 public override void WriteSetBegin(TSet set)
658 {
659 WriteJSONArrayStart();
660 WriteJSONString(GetTypeNameForTypeID(set.ElementType));
661 WriteJSONInteger(set.Count);
662 }
663
664 public override void WriteSetEnd()
665 {
666 WriteJSONArrayEnd();
667 }
668
669 public override void WriteBool(bool b)
670 {
671 WriteJSONInteger(b ? (long)1 : (long)0);
672 }
673
674 public override void WriteByte(byte b)
675 {
676 WriteJSONInteger((long)b);
677 }
678
679 public override void WriteI16(short i16)
680 {
681 WriteJSONInteger((long)i16);
682 }
683
684 public override void WriteI32(int i32)
685 {
686 WriteJSONInteger((long)i32);
687 }
688
689 public override void WriteI64(long i64)
690 {
691 WriteJSONInteger(i64);
692 }
693
694 public override void WriteDouble(double dub)
695 {
696 WriteJSONDouble(dub);
697 }
698
699 public override void WriteString(String str)
700 {
701 byte[] b = utf8Encoding.GetBytes(str);
702 WriteJSONString(b);
703 }
704
705 public override void WriteBinary(byte[] bin)
706 {
707 WriteJSONBase64(bin);
708 }
709
710 /**
711 * Reading methods.
712 */
713
714 ///<summary>
715 /// Read in a JSON string, unescaping as appropriate.. Skip Reading from the
716 /// context if skipContext is true.
717 ///</summary>
718 private byte[] ReadJSONString(bool skipContext)
719 {
720 MemoryStream buffer = new MemoryStream();
721
722
723 if (!skipContext)
724 {
725 context.Read();
726 }
727 ReadJSONSyntaxChar(QUOTE);
728 while (true)
729 {
730 byte ch = reader.Read();
731 if (ch == QUOTE[0])
732 {
733 break;
734 }
735 if (ch == ESCSEQ[0])
736 {
737 ch = reader.Read();
738 if (ch == ESCSEQ[1])
739 {
740 ReadJSONSyntaxChar(ZERO);
741 ReadJSONSyntaxChar(ZERO);
742 trans.ReadAll(tempBuffer, 0, 2);
743 ch = (byte)((HexVal((byte)tempBuffer[0]) << 4) + HexVal(tempBuffer[1]));
744 }
745 else
746 {
Jake Farrell83693532011-04-14 14:30:25 +0000747 int off = Array.IndexOf(ESCAPE_CHARS, (char)ch);
Bryan Duxburyfd32d792010-09-18 20:51:25 +0000748 if (off == -1)
749 {
750 throw new TProtocolException(TProtocolException.INVALID_DATA,
751 "Expected control char");
752 }
753 ch = ESCAPE_CHAR_VALS[off];
754 }
755 }
756 buffer.Write(new byte[] { (byte)ch }, 0, 1);
757 }
758 return buffer.ToArray();
759 }
760
761 ///<summary>
762 /// Return true if the given byte could be a valid part of a JSON number.
763 ///</summary>
764 private bool IsJSONNumeric(byte b)
765 {
766 switch (b)
767 {
768 case (byte)'+':
769 case (byte)'-':
770 case (byte)'.':
771 case (byte)'0':
772 case (byte)'1':
773 case (byte)'2':
774 case (byte)'3':
775 case (byte)'4':
776 case (byte)'5':
777 case (byte)'6':
778 case (byte)'7':
779 case (byte)'8':
780 case (byte)'9':
781 case (byte)'E':
782 case (byte)'e':
783 return true;
784 }
785 return false;
786 }
787
788 ///<summary>
789 /// Read in a sequence of characters that are all valid in JSON numbers. Does
790 /// not do a complete regex check to validate that this is actually a number.
791 ////</summary>
792 private String ReadJSONNumericChars()
793 {
794 StringBuilder strbld = new StringBuilder();
795 while (true)
796 {
797 byte ch = reader.Peek();
798 if (!IsJSONNumeric(ch))
799 {
800 break;
801 }
802 strbld.Append((char)reader.Read());
803 }
804 return strbld.ToString();
805 }
806
807 ///<summary>
808 /// Read in a JSON number. If the context dictates, Read in enclosing quotes.
809 ///</summary>
810 private long ReadJSONInteger()
811 {
812 context.Read();
813 if (context.EscapeNumbers())
814 {
815 ReadJSONSyntaxChar(QUOTE);
816 }
817 String str = ReadJSONNumericChars();
818 if (context.EscapeNumbers())
819 {
820 ReadJSONSyntaxChar(QUOTE);
821 }
822 try
823 {
824 return Int64.Parse(str);
825 }
826 catch (FormatException)
827 {
828 throw new TProtocolException(TProtocolException.INVALID_DATA,
829 "Bad data encounted in numeric data");
830 }
831 }
832
833 ///<summary>
834 /// Read in a JSON double value. Throw if the value is not wrapped in quotes
835 /// when expected or if wrapped in quotes when not expected.
836 ///</summary>
837 private double ReadJSONDouble()
838 {
839 context.Read();
840 if (reader.Peek() == QUOTE[0])
841 {
842 byte[] arr = ReadJSONString(true);
843 double dub = Double.Parse(utf8Encoding.GetString(arr));
844
845 if (!context.EscapeNumbers() && !Double.IsNaN(dub) &&
846 !Double.IsInfinity(dub))
847 {
848 // Throw exception -- we should not be in a string in this case
849 throw new TProtocolException(TProtocolException.INVALID_DATA,
850 "Numeric data unexpectedly quoted");
851 }
852 return dub;
853 }
854 else
855 {
856 if (context.EscapeNumbers())
857 {
858 // This will throw - we should have had a quote if escapeNum == true
859 ReadJSONSyntaxChar(QUOTE);
860 }
861 try
862 {
863 return Double.Parse(ReadJSONNumericChars());
864 }
865 catch (FormatException)
866 {
867 throw new TProtocolException(TProtocolException.INVALID_DATA,
868 "Bad data encounted in numeric data");
869 }
870 }
871 }
872
873 //<summary>
874 /// Read in a JSON string containing base-64 encoded data and decode it.
875 ///</summary>
876 private byte[] ReadJSONBase64()
877 {
878 byte[] b = ReadJSONString(false);
879 int len = b.Length;
880 int off = 0;
881 int size = 0;
882 while (len >= 4)
883 {
884 // Decode 4 bytes at a time
885 TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
886 off += 4;
887 len -= 4;
888 size += 3;
889 }
890 // Don't decode if we hit the end or got a single leftover byte (invalid
891 // base64 but legal for skip of regular string type)
892 if (len > 1)
893 {
894 // Decode remainder
895 TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
896 size += len - 1;
897 }
898 // Sadly we must copy the byte[] (any way around this?)
899 byte[] result = new byte[size];
900 Array.Copy(b, 0, result, 0, size);
901 return result;
902 }
903
904 private void ReadJSONObjectStart()
905 {
906 context.Read();
907 ReadJSONSyntaxChar(LBRACE);
908 PushContext(new JSONPairContext(this));
909 }
910
911 private void ReadJSONObjectEnd()
912 {
913 ReadJSONSyntaxChar(RBRACE);
914 PopContext();
915 }
916
917 private void ReadJSONArrayStart()
918 {
919 context.Read();
920 ReadJSONSyntaxChar(LBRACKET);
921 PushContext(new JSONListContext(this));
922 }
923
924 private void ReadJSONArrayEnd()
925 {
926 ReadJSONSyntaxChar(RBRACKET);
927 PopContext();
928 }
929
930 public override TMessage ReadMessageBegin()
931 {
932 TMessage message = new TMessage();
933 ReadJSONArrayStart();
934 if (ReadJSONInteger() != VERSION)
935 {
936 throw new TProtocolException(TProtocolException.BAD_VERSION,
937 "Message contained bad version.");
938 }
939
940 message.Name = utf8Encoding.GetString(ReadJSONString(false));
941 message.Type = (TMessageType)ReadJSONInteger();
942 message.SeqID = (int)ReadJSONInteger();
943 return message;
944 }
945
946 public override void ReadMessageEnd()
947 {
948 ReadJSONArrayEnd();
949 }
950
951 public override TStruct ReadStructBegin()
952 {
953 ReadJSONObjectStart();
954 return new TStruct();
955 }
956
957 public override void ReadStructEnd()
958 {
959 ReadJSONObjectEnd();
960 }
961
962 public override TField ReadFieldBegin()
963 {
964 TField field = new TField();
965 byte ch = reader.Peek();
966 if (ch == RBRACE[0])
967 {
968 field.Type = TType.Stop;
969 }
970 else
971 {
972 field.ID = (short)ReadJSONInteger();
973 ReadJSONObjectStart();
974 field.Type = GetTypeIDForTypeName(ReadJSONString(false));
975 }
976 return field;
977 }
978
979 public override void ReadFieldEnd()
980 {
981 ReadJSONObjectEnd();
982 }
983
984 public override TMap ReadMapBegin()
985 {
986 TMap map = new TMap();
987 ReadJSONArrayStart();
988 map.KeyType = GetTypeIDForTypeName(ReadJSONString(false));
989 map.ValueType = GetTypeIDForTypeName(ReadJSONString(false));
990 map.Count = (int)ReadJSONInteger();
991 ReadJSONObjectStart();
992 return map;
993 }
994
995 public override void ReadMapEnd()
996 {
997 ReadJSONObjectEnd();
998 ReadJSONArrayEnd();
999 }
1000
1001 public override TList ReadListBegin()
1002 {
1003 TList list = new TList();
1004 ReadJSONArrayStart();
1005 list.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
1006 list.Count = (int)ReadJSONInteger();
1007 return list;
1008 }
1009
1010 public override void ReadListEnd()
1011 {
1012 ReadJSONArrayEnd();
1013 }
1014
1015 public override TSet ReadSetBegin()
1016 {
1017 TSet set = new TSet();
1018 ReadJSONArrayStart();
1019 set.ElementType = GetTypeIDForTypeName(ReadJSONString(false));
1020 set.Count = (int)ReadJSONInteger();
1021 return set;
1022 }
1023
1024 public override void ReadSetEnd()
1025 {
1026 ReadJSONArrayEnd();
1027 }
1028
1029 public override bool ReadBool()
1030 {
1031 return (ReadJSONInteger() == 0 ? false : true);
1032 }
1033
1034 public override byte ReadByte()
1035 {
1036 return (byte)ReadJSONInteger();
1037 }
1038
1039 public override short ReadI16()
1040 {
1041 return (short)ReadJSONInteger();
1042 }
1043
1044 public override int ReadI32()
1045 {
1046 return (int)ReadJSONInteger();
1047 }
1048
1049 public override long ReadI64()
1050 {
1051 return (long)ReadJSONInteger();
1052 }
1053
1054 public override double ReadDouble()
1055 {
1056 return ReadJSONDouble();
1057 }
1058
1059 public override String ReadString()
1060 {
Bryan Duxburyf02d4372011-03-28 14:56:53 +00001061 return utf8Encoding.GetString(ReadJSONString(false));
Bryan Duxburyfd32d792010-09-18 20:51:25 +00001062 }
1063
1064 public override byte[] ReadBinary()
1065 {
1066 return ReadJSONBase64();
1067 }
1068
1069 }
1070}