]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/java/src/org/apache/thrift/protocol/TJSONProtocol.java
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / lib / java / src / org / apache / thrift / protocol / TJSONProtocol.java
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 package org.apache.thrift.protocol;
21
22 import java.io.IOException;
23 import java.nio.ByteBuffer;
24 import java.nio.charset.StandardCharsets;
25 import java.util.ArrayList;
26 import java.util.Stack;
27
28 import org.apache.thrift.TByteArrayOutputStream;
29 import org.apache.thrift.TException;
30 import org.apache.thrift.transport.TTransport;
31
32 /**
33 * JSON protocol implementation for thrift.
34 *
35 * This is a full-featured protocol supporting write and read.
36 *
37 * Please see the C++ class header for a detailed description of the
38 * protocol's wire format.
39 *
40 */
41 public class TJSONProtocol extends TProtocol {
42
43 /**
44 * Factory for JSON protocol objects
45 */
46 public static class Factory implements TProtocolFactory {
47 protected boolean fieldNamesAsString_ = false;
48
49 public Factory() {}
50
51 public Factory(boolean fieldNamesAsString) {
52 fieldNamesAsString_ = fieldNamesAsString;
53 }
54
55 public TProtocol getProtocol(TTransport trans) {
56 return new TJSONProtocol(trans, fieldNamesAsString_);
57 }
58
59 }
60
61 private static final byte[] COMMA = new byte[] {','};
62 private static final byte[] COLON = new byte[] {':'};
63 private static final byte[] LBRACE = new byte[] {'{'};
64 private static final byte[] RBRACE = new byte[] {'}'};
65 private static final byte[] LBRACKET = new byte[] {'['};
66 private static final byte[] RBRACKET = new byte[] {']'};
67 private static final byte[] QUOTE = new byte[] {'"'};
68 private static final byte[] BACKSLASH = new byte[] {'\\'};
69 private static final byte[] ZERO = new byte[] {'0'};
70
71 private static final byte[] ESCSEQ = new byte[] {'\\','u','0','0'};
72
73 private static final long VERSION = 1;
74
75 private static final byte[] JSON_CHAR_TABLE = {
76 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
77 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
78 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
79 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
80 };
81
82 private static final String ESCAPE_CHARS = "\"\\/bfnrt";
83
84 private static final byte[] ESCAPE_CHAR_VALS = {
85 '"', '\\', '/', '\b', '\f', '\n', '\r', '\t',
86 };
87
88 private static final int DEF_STRING_SIZE = 16;
89
90 private static final byte[] NAME_BOOL = new byte[] {'t', 'f'};
91 private static final byte[] NAME_BYTE = new byte[] {'i','8'};
92 private static final byte[] NAME_I16 = new byte[] {'i','1','6'};
93 private static final byte[] NAME_I32 = new byte[] {'i','3','2'};
94 private static final byte[] NAME_I64 = new byte[] {'i','6','4'};
95 private static final byte[] NAME_DOUBLE = new byte[] {'d','b','l'};
96 private static final byte[] NAME_STRUCT = new byte[] {'r','e','c'};
97 private static final byte[] NAME_STRING = new byte[] {'s','t','r'};
98 private static final byte[] NAME_MAP = new byte[] {'m','a','p'};
99 private static final byte[] NAME_LIST = new byte[] {'l','s','t'};
100 private static final byte[] NAME_SET = new byte[] {'s','e','t'};
101
102 private static final TStruct ANONYMOUS_STRUCT = new TStruct();
103
104 private static final byte[] getTypeNameForTypeID(byte typeID)
105 throws TException {
106 switch (typeID) {
107 case TType.BOOL:
108 return NAME_BOOL;
109 case TType.BYTE:
110 return NAME_BYTE;
111 case TType.I16:
112 return NAME_I16;
113 case TType.I32:
114 return NAME_I32;
115 case TType.I64:
116 return NAME_I64;
117 case TType.DOUBLE:
118 return NAME_DOUBLE;
119 case TType.STRING:
120 return NAME_STRING;
121 case TType.STRUCT:
122 return NAME_STRUCT;
123 case TType.MAP:
124 return NAME_MAP;
125 case TType.SET:
126 return NAME_SET;
127 case TType.LIST:
128 return NAME_LIST;
129 default:
130 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
131 "Unrecognized type");
132 }
133 }
134
135 private static final byte getTypeIDForTypeName(byte[] name)
136 throws TException {
137 byte result = TType.STOP;
138 if (name.length > 1) {
139 switch (name[0]) {
140 case 'd':
141 result = TType.DOUBLE;
142 break;
143 case 'i':
144 switch (name[1]) {
145 case '8':
146 result = TType.BYTE;
147 break;
148 case '1':
149 result = TType.I16;
150 break;
151 case '3':
152 result = TType.I32;
153 break;
154 case '6':
155 result = TType.I64;
156 break;
157 }
158 break;
159 case 'l':
160 result = TType.LIST;
161 break;
162 case 'm':
163 result = TType.MAP;
164 break;
165 case 'r':
166 result = TType.STRUCT;
167 break;
168 case 's':
169 if (name[1] == 't') {
170 result = TType.STRING;
171 }
172 else if (name[1] == 'e') {
173 result = TType.SET;
174 }
175 break;
176 case 't':
177 result = TType.BOOL;
178 break;
179 }
180 }
181 if (result == TType.STOP) {
182 throw new TProtocolException(TProtocolException.NOT_IMPLEMENTED,
183 "Unrecognized type");
184 }
185 return result;
186 }
187
188 // Base class for tracking JSON contexts that may require inserting/reading
189 // additional JSON syntax characters
190 // This base context does nothing.
191 protected class JSONBaseContext {
192 protected void write() throws TException {}
193
194 protected void read() throws TException {}
195
196 protected boolean escapeNum() { return false; }
197 }
198
199 // Context for JSON lists. Will insert/read commas before each item except
200 // for the first one
201 protected class JSONListContext extends JSONBaseContext {
202 private boolean first_ = true;
203
204 @Override
205 protected void write() throws TException {
206 if (first_) {
207 first_ = false;
208 } else {
209 trans_.write(COMMA);
210 }
211 }
212
213 @Override
214 protected void read() throws TException {
215 if (first_) {
216 first_ = false;
217 } else {
218 readJSONSyntaxChar(COMMA);
219 }
220 }
221 }
222
223 // Context for JSON records. Will insert/read colons before the value portion
224 // of each record pair, and commas before each key except the first. In
225 // addition, will indicate that numbers in the key position need to be
226 // escaped in quotes (since JSON keys must be strings).
227 protected class JSONPairContext extends JSONBaseContext {
228 private boolean first_ = true;
229 private boolean colon_ = true;
230
231 @Override
232 protected void write() throws TException {
233 if (first_) {
234 first_ = false;
235 colon_ = true;
236 } else {
237 trans_.write(colon_ ? COLON : COMMA);
238 colon_ = !colon_;
239 }
240 }
241
242 @Override
243 protected void read() throws TException {
244 if (first_) {
245 first_ = false;
246 colon_ = true;
247 } else {
248 readJSONSyntaxChar(colon_ ? COLON : COMMA);
249 colon_ = !colon_;
250 }
251 }
252
253 @Override
254 protected boolean escapeNum() {
255 return colon_;
256 }
257 }
258
259 // Holds up to one byte from the transport
260 protected class LookaheadReader {
261
262 private boolean hasData_;
263 private byte[] data_ = new byte[1];
264
265 // Return and consume the next byte to be read, either taking it from the
266 // data buffer if present or getting it from the transport otherwise.
267 protected byte read() throws TException {
268 if (hasData_) {
269 hasData_ = false;
270 }
271 else {
272 trans_.readAll(data_, 0, 1);
273 }
274 return data_[0];
275 }
276
277 // Return the next byte to be read without consuming, filling the data
278 // buffer if it has not been filled already.
279 protected byte peek() throws TException {
280 if (!hasData_) {
281 trans_.readAll(data_, 0, 1);
282 }
283 hasData_ = true;
284 return data_[0];
285 }
286 }
287
288 // Stack of nested contexts that we may be in
289 private Stack<JSONBaseContext> contextStack_ = new Stack<JSONBaseContext>();
290
291 // Current context that we are in
292 private JSONBaseContext context_ = new JSONBaseContext();
293
294 // Reader that manages a 1-byte buffer
295 private LookaheadReader reader_ = new LookaheadReader();
296
297 // Write out the TField names as a string instead of the default integer value
298 private boolean fieldNamesAsString_ = false;
299
300 // Push a new JSON context onto the stack.
301 private void pushContext(JSONBaseContext c) {
302 contextStack_.push(context_);
303 context_ = c;
304 }
305
306 // Pop the last JSON context off the stack
307 private void popContext() {
308 context_ = contextStack_.pop();
309 }
310
311 // Reset the context stack to its initial state
312 private void resetContext() {
313 while (!contextStack_.isEmpty()) {
314 popContext();
315 }
316 }
317
318 /**
319 * Constructor
320 */
321 public TJSONProtocol(TTransport trans) {
322 super(trans);
323 }
324
325 public TJSONProtocol(TTransport trans, boolean fieldNamesAsString) {
326 super(trans);
327 fieldNamesAsString_ = fieldNamesAsString;
328 }
329
330 @Override
331 public void reset() {
332 contextStack_.clear();
333 context_ = new JSONBaseContext();
334 reader_ = new LookaheadReader();
335 }
336
337 // Temporary buffer used by several methods
338 private byte[] tmpbuf_ = new byte[4];
339
340 // Read a byte that must match b[0]; otherwise an exception is thrown.
341 // Marked protected to avoid synthetic accessor in JSONListContext.read
342 // and JSONPairContext.read
343 protected void readJSONSyntaxChar(byte[] b) throws TException {
344 byte ch = reader_.read();
345 if (ch != b[0]) {
346 throw new TProtocolException(TProtocolException.INVALID_DATA,
347 "Unexpected character:" + (char)ch);
348 }
349 }
350
351 // Convert a byte containing a hex char ('0'-'9' or 'a'-'f') into its
352 // corresponding hex value
353 private static final byte hexVal(byte ch) throws TException {
354 if ((ch >= '0') && (ch <= '9')) {
355 return (byte)((char)ch - '0');
356 }
357 else if ((ch >= 'a') && (ch <= 'f')) {
358 return (byte)((char)ch - 'a' + 10);
359 }
360 else {
361 throw new TProtocolException(TProtocolException.INVALID_DATA,
362 "Expected hex character");
363 }
364 }
365
366 // Convert a byte containing a hex value to its corresponding hex character
367 private static final byte hexChar(byte val) {
368 val &= 0x0F;
369 if (val < 10) {
370 return (byte)((char)val + '0');
371 }
372 else {
373 return (byte)((char)(val - 10) + 'a');
374 }
375 }
376
377 // Write the bytes in array buf as a JSON characters, escaping as needed
378 private void writeJSONString(byte[] b) throws TException {
379 context_.write();
380 trans_.write(QUOTE);
381 int len = b.length;
382 for (int i = 0; i < len; i++) {
383 if ((b[i] & 0x00FF) >= 0x30) {
384 if (b[i] == BACKSLASH[0]) {
385 trans_.write(BACKSLASH);
386 trans_.write(BACKSLASH);
387 }
388 else {
389 trans_.write(b, i, 1);
390 }
391 }
392 else {
393 tmpbuf_[0] = JSON_CHAR_TABLE[b[i]];
394 if (tmpbuf_[0] == 1) {
395 trans_.write(b, i, 1);
396 }
397 else if (tmpbuf_[0] > 1) {
398 trans_.write(BACKSLASH);
399 trans_.write(tmpbuf_, 0, 1);
400 }
401 else {
402 trans_.write(ESCSEQ);
403 tmpbuf_[0] = hexChar((byte)(b[i] >> 4));
404 tmpbuf_[1] = hexChar(b[i]);
405 trans_.write(tmpbuf_, 0, 2);
406 }
407 }
408 }
409 trans_.write(QUOTE);
410 }
411
412 // Write out number as a JSON value. If the context dictates so, it will be
413 // wrapped in quotes to output as a JSON string.
414 private void writeJSONInteger(long num) throws TException {
415 context_.write();
416 String str = Long.toString(num);
417 boolean escapeNum = context_.escapeNum();
418 if (escapeNum) {
419 trans_.write(QUOTE);
420 }
421 byte[] buf = str.getBytes(StandardCharsets.UTF_8);
422 trans_.write(buf);
423 if (escapeNum) {
424 trans_.write(QUOTE);
425 }
426 }
427
428 // Write out a double as a JSON value. If it is NaN or infinity or if the
429 // context dictates escaping, write out as JSON string.
430 private void writeJSONDouble(double num) throws TException {
431 context_.write();
432 String str = Double.toString(num);
433 boolean special = false;
434 switch (str.charAt(0)) {
435 case 'N': // NaN
436 case 'I': // Infinity
437 special = true;
438 break;
439 case '-':
440 if (str.charAt(1) == 'I') { // -Infinity
441 special = true;
442 }
443 break;
444 default:
445 break;
446 }
447
448 boolean escapeNum = special || context_.escapeNum();
449 if (escapeNum) {
450 trans_.write(QUOTE);
451 }
452 byte[] b = str.getBytes(StandardCharsets.UTF_8);
453 trans_.write(b, 0, b.length);
454 if (escapeNum) {
455 trans_.write(QUOTE);
456 }
457 }
458
459 // Write out contents of byte array b as a JSON string with base-64 encoded
460 // data
461 private void writeJSONBase64(byte[] b, int offset, int length) throws TException {
462 context_.write();
463 trans_.write(QUOTE);
464 int len = length;
465 int off = offset;
466 while (len >= 3) {
467 // Encode 3 bytes at a time
468 TBase64Utils.encode(b, off, 3, tmpbuf_, 0);
469 trans_.write(tmpbuf_, 0, 4);
470 off += 3;
471 len -= 3;
472 }
473 if (len > 0) {
474 // Encode remainder
475 TBase64Utils.encode(b, off, len, tmpbuf_, 0);
476 trans_.write(tmpbuf_, 0, len + 1);
477 }
478 trans_.write(QUOTE);
479 }
480
481 private void writeJSONObjectStart() throws TException {
482 context_.write();
483 trans_.write(LBRACE);
484 pushContext(new JSONPairContext());
485 }
486
487 private void writeJSONObjectEnd() throws TException {
488 popContext();
489 trans_.write(RBRACE);
490 }
491
492 private void writeJSONArrayStart() throws TException {
493 context_.write();
494 trans_.write(LBRACKET);
495 pushContext(new JSONListContext());
496 }
497
498 private void writeJSONArrayEnd() throws TException {
499 popContext();
500 trans_.write(RBRACKET);
501 }
502
503 @Override
504 public void writeMessageBegin(TMessage message) throws TException {
505 resetContext(); // THRIFT-3743
506 writeJSONArrayStart();
507 writeJSONInteger(VERSION);
508 byte[] b = message.name.getBytes(StandardCharsets.UTF_8);
509 writeJSONString(b);
510 writeJSONInteger(message.type);
511 writeJSONInteger(message.seqid);
512 }
513
514 @Override
515 public void writeMessageEnd() throws TException {
516 writeJSONArrayEnd();
517 }
518
519 @Override
520 public void writeStructBegin(TStruct struct) throws TException {
521 writeJSONObjectStart();
522 }
523
524 @Override
525 public void writeStructEnd() throws TException {
526 writeJSONObjectEnd();
527 }
528
529 @Override
530 public void writeFieldBegin(TField field) throws TException {
531 if (fieldNamesAsString_) {
532 writeString(field.name);
533 } else {
534 writeJSONInteger(field.id);
535 }
536 writeJSONObjectStart();
537 writeJSONString(getTypeNameForTypeID(field.type));
538 }
539
540 @Override
541 public void writeFieldEnd() throws TException {
542 writeJSONObjectEnd();
543 }
544
545 @Override
546 public void writeFieldStop() {}
547
548 @Override
549 public void writeMapBegin(TMap map) throws TException {
550 writeJSONArrayStart();
551 writeJSONString(getTypeNameForTypeID(map.keyType));
552 writeJSONString(getTypeNameForTypeID(map.valueType));
553 writeJSONInteger(map.size);
554 writeJSONObjectStart();
555 }
556
557 @Override
558 public void writeMapEnd() throws TException {
559 writeJSONObjectEnd();
560 writeJSONArrayEnd();
561 }
562
563 @Override
564 public void writeListBegin(TList list) throws TException {
565 writeJSONArrayStart();
566 writeJSONString(getTypeNameForTypeID(list.elemType));
567 writeJSONInteger(list.size);
568 }
569
570 @Override
571 public void writeListEnd() throws TException {
572 writeJSONArrayEnd();
573 }
574
575 @Override
576 public void writeSetBegin(TSet set) throws TException {
577 writeJSONArrayStart();
578 writeJSONString(getTypeNameForTypeID(set.elemType));
579 writeJSONInteger(set.size);
580 }
581
582 @Override
583 public void writeSetEnd() throws TException {
584 writeJSONArrayEnd();
585 }
586
587 @Override
588 public void writeBool(boolean b) throws TException {
589 writeJSONInteger(b ? (long)1 : (long)0);
590 }
591
592 @Override
593 public void writeByte(byte b) throws TException {
594 writeJSONInteger((long)b);
595 }
596
597 @Override
598 public void writeI16(short i16) throws TException {
599 writeJSONInteger((long)i16);
600 }
601
602 @Override
603 public void writeI32(int i32) throws TException {
604 writeJSONInteger((long)i32);
605 }
606
607 @Override
608 public void writeI64(long i64) throws TException {
609 writeJSONInteger(i64);
610 }
611
612 @Override
613 public void writeDouble(double dub) throws TException {
614 writeJSONDouble(dub);
615 }
616
617 @Override
618 public void writeString(String str) throws TException {
619 byte[] b = str.getBytes(StandardCharsets.UTF_8);
620 writeJSONString(b);
621 }
622
623 @Override
624 public void writeBinary(ByteBuffer bin) throws TException {
625 writeJSONBase64(bin.array(), bin.position() + bin.arrayOffset(), bin.limit() - bin.position() - bin.arrayOffset());
626 }
627
628 /**
629 * Reading methods.
630 */
631
632 // Read in a JSON string, unescaping as appropriate.. Skip reading from the
633 // context if skipContext is true.
634 private TByteArrayOutputStream readJSONString(boolean skipContext)
635 throws TException {
636 TByteArrayOutputStream arr = new TByteArrayOutputStream(DEF_STRING_SIZE);
637 ArrayList<Character> codeunits = new ArrayList<Character>();
638 if (!skipContext) {
639 context_.read();
640 }
641 readJSONSyntaxChar(QUOTE);
642 while (true) {
643 byte ch = reader_.read();
644 if (ch == QUOTE[0]) {
645 break;
646 }
647 if (ch == ESCSEQ[0]) {
648 ch = reader_.read();
649 if (ch == ESCSEQ[1]) {
650 trans_.readAll(tmpbuf_, 0, 4);
651 short cu = (short)(
652 ((short)hexVal(tmpbuf_[0]) << 12) +
653 ((short)hexVal(tmpbuf_[1]) << 8) +
654 ((short)hexVal(tmpbuf_[2]) << 4) +
655 (short)hexVal(tmpbuf_[3]));
656 try {
657 if (Character.isHighSurrogate((char)cu)) {
658 if (codeunits.size() > 0) {
659 throw new TProtocolException(TProtocolException.INVALID_DATA,
660 "Expected low surrogate char");
661 }
662 codeunits.add((char)cu);
663 }
664 else if (Character.isLowSurrogate((char)cu)) {
665 if (codeunits.size() == 0) {
666 throw new TProtocolException(TProtocolException.INVALID_DATA,
667 "Expected high surrogate char");
668 }
669
670 codeunits.add((char)cu);
671 arr.write(
672 (new String(new int[] { codeunits.get(0), codeunits.get(1) },
673 0, 2)).getBytes(StandardCharsets.UTF_8));
674 codeunits.clear();
675 }
676 else {
677 arr.write((new String(new int[] { cu }, 0, 1))
678 .getBytes(StandardCharsets.UTF_8));
679 }
680 continue;
681 } catch (IOException ex) {
682 throw new TProtocolException(TProtocolException.INVALID_DATA,
683 "Invalid unicode sequence");
684 }
685 }
686 else {
687 int off = ESCAPE_CHARS.indexOf(ch);
688 if (off == -1) {
689 throw new TProtocolException(TProtocolException.INVALID_DATA,
690 "Expected control char");
691 }
692 ch = ESCAPE_CHAR_VALS[off];
693 }
694 }
695 arr.write(ch);
696 }
697 return arr;
698 }
699
700 // Return true if the given byte could be a valid part of a JSON number.
701 private boolean isJSONNumeric(byte b) {
702 switch (b) {
703 case '+':
704 case '-':
705 case '.':
706 case '0':
707 case '1':
708 case '2':
709 case '3':
710 case '4':
711 case '5':
712 case '6':
713 case '7':
714 case '8':
715 case '9':
716 case 'E':
717 case 'e':
718 return true;
719 }
720 return false;
721 }
722
723 // Read in a sequence of characters that are all valid in JSON numbers. Does
724 // not do a complete regex check to validate that this is actually a number.
725 private String readJSONNumericChars() throws TException {
726 StringBuilder strbld = new StringBuilder();
727 while (true) {
728 byte ch = reader_.peek();
729 if (!isJSONNumeric(ch)) {
730 break;
731 }
732 strbld.append((char)reader_.read());
733 }
734 return strbld.toString();
735 }
736
737 // Read in a JSON number. If the context dictates, read in enclosing quotes.
738 private long readJSONInteger() throws TException {
739 context_.read();
740 if (context_.escapeNum()) {
741 readJSONSyntaxChar(QUOTE);
742 }
743 String str = readJSONNumericChars();
744 if (context_.escapeNum()) {
745 readJSONSyntaxChar(QUOTE);
746 }
747 try {
748 return Long.valueOf(str);
749 }
750 catch (NumberFormatException ex) {
751 throw new TProtocolException(TProtocolException.INVALID_DATA,
752 "Bad data encounted in numeric data");
753 }
754 }
755
756 // Read in a JSON double value. Throw if the value is not wrapped in quotes
757 // when expected or if wrapped in quotes when not expected.
758 private double readJSONDouble() throws TException {
759 context_.read();
760 if (reader_.peek() == QUOTE[0]) {
761 TByteArrayOutputStream arr = readJSONString(true);
762 double dub = Double.valueOf(arr.toString(StandardCharsets.UTF_8));
763 if (!context_.escapeNum() && !Double.isNaN(dub)
764 && !Double.isInfinite(dub)) {
765 // Throw exception -- we should not be in a string in this case
766 throw new TProtocolException(TProtocolException.INVALID_DATA,
767 "Numeric data unexpectedly quoted");
768 }
769 return dub;
770 }
771 else {
772 if (context_.escapeNum()) {
773 // This will throw - we should have had a quote if escapeNum == true
774 readJSONSyntaxChar(QUOTE);
775 }
776 try {
777 return Double.valueOf(readJSONNumericChars());
778 }
779 catch (NumberFormatException ex) {
780 throw new TProtocolException(TProtocolException.INVALID_DATA,
781 "Bad data encounted in numeric data");
782 }
783 }
784 }
785
786 // Read in a JSON string containing base-64 encoded data and decode it.
787 private byte[] readJSONBase64() throws TException {
788 TByteArrayOutputStream arr = readJSONString(false);
789 byte[] b = arr.get();
790 int len = arr.len();
791 int off = 0;
792 int size = 0;
793 // Ignore padding
794 int bound = len >= 2 ? len - 2 : 0;
795 for (int i = len - 1; i >= bound && b[i] == '='; --i) {
796 --len;
797 }
798 while (len >= 4) {
799 // Decode 4 bytes at a time
800 TBase64Utils.decode(b, off, 4, b, size); // NB: decoded in place
801 off += 4;
802 len -= 4;
803 size += 3;
804 }
805 // Don't decode if we hit the end or got a single leftover byte (invalid
806 // base64 but legal for skip of regular string type)
807 if (len > 1) {
808 // Decode remainder
809 TBase64Utils.decode(b, off, len, b, size); // NB: decoded in place
810 size += len - 1;
811 }
812 // Sadly we must copy the byte[] (any way around this?)
813 byte [] result = new byte[size];
814 System.arraycopy(b, 0, result, 0, size);
815 return result;
816 }
817
818 private void readJSONObjectStart() throws TException {
819 context_.read();
820 readJSONSyntaxChar(LBRACE);
821 pushContext(new JSONPairContext());
822 }
823
824 private void readJSONObjectEnd() throws TException {
825 readJSONSyntaxChar(RBRACE);
826 popContext();
827 }
828
829 private void readJSONArrayStart() throws TException {
830 context_.read();
831 readJSONSyntaxChar(LBRACKET);
832 pushContext(new JSONListContext());
833 }
834
835 private void readJSONArrayEnd() throws TException {
836 readJSONSyntaxChar(RBRACKET);
837 popContext();
838 }
839
840 @Override
841 public TMessage readMessageBegin() throws TException {
842 resetContext(); // THRIFT-3743
843 readJSONArrayStart();
844 if (readJSONInteger() != VERSION) {
845 throw new TProtocolException(TProtocolException.BAD_VERSION,
846 "Message contained bad version.");
847 }
848 String name = readJSONString(false).toString(StandardCharsets.UTF_8);
849 byte type = (byte) readJSONInteger();
850 int seqid = (int) readJSONInteger();
851 return new TMessage(name, type, seqid);
852 }
853
854 @Override
855 public void readMessageEnd() throws TException {
856 readJSONArrayEnd();
857 }
858
859 @Override
860 public TStruct readStructBegin() throws TException {
861 readJSONObjectStart();
862 return ANONYMOUS_STRUCT;
863 }
864
865 @Override
866 public void readStructEnd() throws TException {
867 readJSONObjectEnd();
868 }
869
870 @Override
871 public TField readFieldBegin() throws TException {
872 byte ch = reader_.peek();
873 byte type;
874 short id = 0;
875 if (ch == RBRACE[0]) {
876 type = TType.STOP;
877 }
878 else {
879 id = (short) readJSONInteger();
880 readJSONObjectStart();
881 type = getTypeIDForTypeName(readJSONString(false).get());
882 }
883 return new TField("", type, id);
884 }
885
886 @Override
887 public void readFieldEnd() throws TException {
888 readJSONObjectEnd();
889 }
890
891 @Override
892 public TMap readMapBegin() throws TException {
893 readJSONArrayStart();
894 byte keyType = getTypeIDForTypeName(readJSONString(false).get());
895 byte valueType = getTypeIDForTypeName(readJSONString(false).get());
896 int size = (int)readJSONInteger();
897 readJSONObjectStart();
898 return new TMap(keyType, valueType, size);
899 }
900
901 @Override
902 public void readMapEnd() throws TException {
903 readJSONObjectEnd();
904 readJSONArrayEnd();
905 }
906
907 @Override
908 public TList readListBegin() throws TException {
909 readJSONArrayStart();
910 byte elemType = getTypeIDForTypeName(readJSONString(false).get());
911 int size = (int)readJSONInteger();
912 return new TList(elemType, size);
913 }
914
915 @Override
916 public void readListEnd() throws TException {
917 readJSONArrayEnd();
918 }
919
920 @Override
921 public TSet readSetBegin() throws TException {
922 readJSONArrayStart();
923 byte elemType = getTypeIDForTypeName(readJSONString(false).get());
924 int size = (int)readJSONInteger();
925 return new TSet(elemType, size);
926 }
927
928 @Override
929 public void readSetEnd() throws TException {
930 readJSONArrayEnd();
931 }
932
933 @Override
934 public boolean readBool() throws TException {
935 return (readJSONInteger() == 0 ? false : true);
936 }
937
938 @Override
939 public byte readByte() throws TException {
940 return (byte) readJSONInteger();
941 }
942
943 @Override
944 public short readI16() throws TException {
945 return (short) readJSONInteger();
946 }
947
948 @Override
949 public int readI32() throws TException {
950 return (int) readJSONInteger();
951 }
952
953 @Override
954 public long readI64() throws TException {
955 return (long) readJSONInteger();
956 }
957
958 @Override
959 public double readDouble() throws TException {
960 return readJSONDouble();
961 }
962
963 @Override
964 public String readString() throws TException {
965 return readJSONString(false).toString(StandardCharsets.UTF_8);
966 }
967
968 @Override
969 public ByteBuffer readBinary() throws TException {
970 return ByteBuffer.wrap(readJSONBase64());
971 }
972
973 }