blob: 39eb1290e751c08a36de412b01fbfd65087632e6 [file] [log] [blame]
David Reissdb0ea152008-02-18 01:49:37 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
7#include "TJSONProtocol.h"
8
9#include <math.h>
10#include "TBase64Utils.h"
11#include <transport/TTransportException.h>
12
13using namespace facebook::thrift::transport;
14
15namespace facebook { namespace thrift { namespace protocol {
16
17
18// Static data
19
20static const uint8_t kJSONObjectStart = '{';
21static const uint8_t kJSONObjectEnd = '}';
22static const uint8_t kJSONArrayStart = '[';
23static const uint8_t kJSONArrayEnd = ']';
24static const uint8_t kJSONNewline = '\n';
25static const uint8_t kJSONPairSeparator = ':';
26static const uint8_t kJSONElemSeparator = ',';
27static const uint8_t kJSONBackslash = '\\';
28static const uint8_t kJSONStringDelimiter = '"';
29static const uint8_t kJSONZeroChar = '0';
30static const uint8_t kJSONEscapeChar = 'u';
31
32static const std::string kJSONEscapePrefix("\\u00");
33
34static const uint8_t kThriftVersion1 = 1;
35
36static const std::string kThriftNan("NaN");
37static const std::string kThriftInfinity("Infinity");
38static const std::string kThriftNegativeInfinity("-Infinity");
39
40static const std::string kTypeNameBool("tf");
41static const std::string kTypeNameByte("i8");
42static const std::string kTypeNameI16("i16");
43static const std::string kTypeNameI32("i32");
44static const std::string kTypeNameI64("i64");
45static const std::string kTypeNameDouble("dbl");
46static const std::string kTypeNameStruct("rec");
47static const std::string kTypeNameString("str");
48static const std::string kTypeNameMap("map");
49static const std::string kTypeNameList("lst");
50static const std::string kTypeNameSet("set");
51
52static const std::string &getTypeNameForTypeID(TType typeID) {
53 switch (typeID) {
54 case T_BOOL:
55 return kTypeNameBool;
56 case T_BYTE:
57 return kTypeNameByte;
58 case T_I16:
59 return kTypeNameI16;
60 case T_I32:
61 return kTypeNameI32;
62 case T_I64:
63 return kTypeNameI64;
64 case T_DOUBLE:
65 return kTypeNameDouble;
66 case T_STRING:
67 return kTypeNameString;
68 case T_STRUCT:
69 return kTypeNameStruct;
70 case T_MAP:
71 return kTypeNameMap;
72 case T_SET:
73 return kTypeNameSet;
74 case T_LIST:
75 return kTypeNameList;
76 default:
77 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
78 "Unrecognized type");
79 }
80}
81
82static TType getTypeIDForTypeName(const std::string &name) {
83 TType result = T_STOP; // Sentinel value
David Reiss1e62ab42008-02-21 22:37:45 +000084 if (name.length() > 0) {
85 switch (name[0]) {
86 case 'd':
87 result = T_DOUBLE;
David Reissdb0ea152008-02-18 01:49:37 +000088 break;
David Reiss1e62ab42008-02-21 22:37:45 +000089 case 'i':
90 switch (name[1]) {
91 case '8':
92 result = T_BYTE;
93 break;
94 case '1':
95 result = T_I16;
96 break;
97 case '3':
98 result = T_I32;
99 break;
100 case '6':
101 result = T_I64;
102 break;
103 }
David Reissdb0ea152008-02-18 01:49:37 +0000104 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000105 case 'l':
106 result = T_LIST;
David Reissdb0ea152008-02-18 01:49:37 +0000107 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000108 case 'm':
109 result = T_MAP;
110 break;
111 case 'r':
112 result = T_STRUCT;
113 break;
114 case 's':
115 if (name[1] == 't') {
116 result = T_STRING;
117 }
118 else if (name[1] == 'e') {
119 result = T_SET;
120 }
121 break;
122 case 't':
123 result = T_BOOL;
David Reissdb0ea152008-02-18 01:49:37 +0000124 break;
125 }
David Reissdb0ea152008-02-18 01:49:37 +0000126 }
127 if (result == T_STOP) {
128 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
129 "Unrecognized type");
130 }
131 return result;
132}
133
134
135// This table describes the handling for the first 0x30 characters
136// 0 : escape using "\u00xx" notation
137// 1 : just output index
138// <other> : escape using "\<other>" notation
139static const uint8_t kJSONCharTable[0x30] = {
140// 0 1 2 3 4 5 6 7 8 9 A B C D E F
141 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
143 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
144};
145
146
147// This string's characters must match up with the elements in kEscapeCharVals.
148// I don't have '/' on this list even though it appears on www.json.org --
149// it is not in the RFC
150const static std::string kEscapeChars("\"\\bfnrt");
151
152// The elements of this array must match up with the sequence of characters in
153// kEscapeChars
154const static uint8_t kEscapeCharVals[7] = {
155 '"', '\\', '\b', '\f', '\n', '\r', '\t',
156};
157
158
159// Static helper functions
160
161// Read 1 character from the transport trans and verify that it is the
162// expected character ch.
163// Throw a protocol exception if it is not.
David Reiss1e62ab42008-02-21 22:37:45 +0000164static uint32_t readSyntaxChar(TJSONProtocol::LookaheadReader &reader,
165 uint8_t ch) {
166 uint8_t ch2 = reader.read();
167 if (ch2 != ch) {
David Reissdb0ea152008-02-18 01:49:37 +0000168 throw TProtocolException(TProtocolException::INVALID_DATA,
169 "Expected \'" + std::string((char *)&ch, 1) +
David Reiss1e62ab42008-02-21 22:37:45 +0000170 "\'; got \'" + std::string((char *)&ch2, 1) +
David Reissdb0ea152008-02-18 01:49:37 +0000171 "\'.");
172 }
173 return 1;
174}
175
David Reissdb0ea152008-02-18 01:49:37 +0000176// Return the integer value of a hex character ch.
177// Throw a protocol exception if the character is not [0-9a-f].
178static uint8_t hexVal(uint8_t ch) {
179 if ((ch >= '0') && (ch <= '9')) {
180 return ch - '0';
181 }
182 else if ((ch >= 'a') && (ch <= 'f')) {
183 return ch - 'a';
184 }
185 else {
186 throw TProtocolException(TProtocolException::INVALID_DATA,
187 "Expected hex val ([0-9a-f]); got \'"
188 + std::string((char *)&ch, 1) + "\'.");
189 }
190}
191
192// Return the hex character representing the integer val. The value is masked
193// to make sure it is in the correct range.
194static uint8_t hexChar(uint8_t val) {
195 val &= 0x0F;
196 if (val < 10) {
197 return val + '0';
198 }
199 else {
200 return val + 'a';
201 }
202}
203
204// Return true if the character ch is in [-+0-9.Ee]; false otherwise
205static bool isJSONNumeric(uint8_t ch) {
206 switch (ch) {
207 case '+':
208 case '-':
209 case '.':
210 case '0':
211 case '1':
212 case '2':
213 case '3':
214 case '4':
215 case '5':
216 case '6':
217 case '7':
218 case '8':
219 case '9':
220 case 'E':
221 case 'e':
222 return true;
223 }
224 return false;
225}
226
227
228/**
David Reiss1e62ab42008-02-21 22:37:45 +0000229 * Class to serve as base JSON context and as base class for other context
David Reissdb0ea152008-02-18 01:49:37 +0000230 * implementations
231 */
232class TJSONContext {
233
234 public:
235
236 TJSONContext() {};
237
238 virtual ~TJSONContext() {};
239
240 /**
241 * Write context data to the transport. Default is to do nothing.
242 */
243 virtual uint32_t write(TTransport &trans) {
244 return 0;
245 };
246
247 /**
248 * Read context data from the transport. Default is to do nothing.
249 */
David Reiss1e62ab42008-02-21 22:37:45 +0000250 virtual uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000251 return 0;
252 };
253
254 /**
255 * Return true if numbers need to be escaped as strings in this context.
256 * Default behavior is to return false.
257 */
258 virtual bool escapeNum() {
259 return false;
260 }
261};
262
263// Context class for object member key-value pairs
264class JSONPairContext : public TJSONContext {
265
266public:
267
268 JSONPairContext() :
269 first_(true),
270 colon_(true) {
271 }
272
273 uint32_t write(TTransport &trans) {
274 if (first_) {
275 first_ = false;
276 colon_ = true;
277 return 0;
278 }
279 else {
280 trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1);
281 colon_ = !colon_;
282 return 1;
283 }
284 }
285
David Reiss1e62ab42008-02-21 22:37:45 +0000286 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000287 if (first_) {
288 first_ = false;
289 colon_ = true;
290 return 0;
291 }
292 else {
293 uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
294 colon_ = !colon_;
David Reiss1e62ab42008-02-21 22:37:45 +0000295 return readSyntaxChar(reader, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000296 }
297 }
298
299 // Numbers must be turned into strings if they are the key part of a pair
300 virtual bool escapeNum() {
301 return colon_;
302 }
303
304 private:
305
306 bool first_;
307 bool colon_;
308};
309
310// Context class for lists
311class JSONListContext : public TJSONContext {
312
313public:
314
315 JSONListContext() :
316 first_(true) {
317 }
318
319 uint32_t write(TTransport &trans) {
320 if (first_) {
321 first_ = false;
322 return 0;
323 }
324 else {
325 trans.write(&kJSONElemSeparator, 1);
326 return 1;
327 }
328 }
329
David Reiss1e62ab42008-02-21 22:37:45 +0000330 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000331 if (first_) {
332 first_ = false;
333 return 0;
334 }
335 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000336 return readSyntaxChar(reader, kJSONElemSeparator);
David Reissdb0ea152008-02-18 01:49:37 +0000337 }
338 }
339
340 private:
341 bool first_;
342};
343
344
345TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) :
346 TProtocol(ptrans),
David Reiss1e62ab42008-02-21 22:37:45 +0000347 context_(new TJSONContext()),
348 reader_(*ptrans) {
David Reissdb0ea152008-02-18 01:49:37 +0000349}
350
351TJSONProtocol::~TJSONProtocol() {}
352
353void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) {
354 contexts_.push(context_);
355 context_ = c;
356}
357
358void TJSONProtocol::popContext() {
359 context_ = contexts_.top();
360 contexts_.pop();
361}
362
363// Write the character ch as a JSON escape sequence ("\u00xx")
364uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
365 trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(),
366 kJSONEscapePrefix.length());
367 uint8_t outCh = hexChar(ch >> 4);
368 trans_->write(&outCh, 1);
369 outCh = hexChar(ch);
370 trans_->write(&outCh, 1);
371 return 6;
372}
373
374// Write the character ch as part of a JSON string, escaping as appropriate.
375uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
376 if (ch >= 0x30) {
377 if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
378 trans_->write(&kJSONBackslash, 1);
379 trans_->write(&kJSONBackslash, 1);
380 return 2;
381 }
382 else {
383 trans_->write(&ch, 1);
384 return 1;
385 }
386 }
387 else {
388 uint8_t outCh = kJSONCharTable[ch];
389 // Check if regular character, backslash escaped, or JSON escaped
390 if (outCh == 1) {
391 trans_->write(&ch, 1);
392 return 1;
393 }
394 else if (outCh > 1) {
395 trans_->write(&kJSONBackslash, 1);
396 trans_->write(&outCh, 1);
397 return 2;
398 }
399 else {
400 return writeJSONEscapeChar(ch);
401 }
402 }
403}
404
405// Write out the contents of the string str as a JSON string, escaping
406// characters as appropriate.
407uint32_t TJSONProtocol::writeJSONString(const std::string &str) {
408 uint32_t result = context_->write(*trans_);
409 result += 2; // For quotes
410 trans_->write(&kJSONStringDelimiter, 1);
411 std::string::const_iterator iter(str.begin());
412 std::string::const_iterator end(str.end());
413 while (iter != end) {
414 result += writeJSONChar(*iter++);
415 }
416 trans_->write(&kJSONStringDelimiter, 1);
417 return result;
418}
419
420// Write out the contents of the string as JSON string, base64-encoding
421// the string's contents, and escaping as appropriate
422uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) {
423 uint32_t result = context_->write(*trans_);
424 result += 2; // For quotes
425 trans_->write(&kJSONStringDelimiter, 1);
426 uint8_t b[4];
427 const uint8_t *bytes = (const uint8_t *)str.c_str();
428 uint32_t len = str.length();
429 while (len >= 3) {
430 // Encode 3 bytes at a time
431 base64_encode(bytes, 3, b);
432 trans_->write(b, 4);
433 result += 4;
434 bytes += 3;
435 len -=3;
436 }
437 if (len) { // Handle remainder
438 base64_encode(bytes, len, b);
439 trans_->write(b, len + 1);
440 result += len + 1;
441 }
442 trans_->write(&kJSONStringDelimiter, 1);
443 return result;
444}
445
446// Convert the given integer type to a JSON number, or a string
447// if the context requires it (eg: key in a map pair).
448template <typename NumberType>
449uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
450 uint32_t result = context_->write(*trans_);
451 std::string val(boost::lexical_cast<std::string>(num));
452 bool escapeNum = context_->escapeNum();
453 if (escapeNum) {
454 trans_->write(&kJSONStringDelimiter, 1);
455 result += 1;
456 }
457 trans_->write((const uint8_t *)val.c_str(), val.length());
458 result += val.length();
459 if (escapeNum) {
460 trans_->write(&kJSONStringDelimiter, 1);
461 result += 1;
462 }
463 return result;
464}
465
466// Convert the given double to a JSON string, which is either the number,
467// "NaN" or "Infinity" or "-Infinity".
468uint32_t TJSONProtocol::writeJSONDouble(double num) {
469 uint32_t result = context_->write(*trans_);
470 std::string val(boost::lexical_cast<std::string>(num));
471
472 // Normalize output of boost::lexical_cast for NaNs and Infinities
473 bool special = false;
474 switch (val[0]) {
475 case 'N':
476 case 'n':
477 val = kThriftNan;
478 special = true;
479 break;
480 case 'I':
481 case 'i':
482 val = kThriftInfinity;
483 special = true;
484 break;
485 case '-':
486 if ((val[1] == 'I') || (val[1] == 'i')) {
487 val = kThriftNegativeInfinity;
488 special = true;
489 }
490 break;
491 }
492
493 bool escapeNum = special || context_->escapeNum();
494 if (escapeNum) {
495 trans_->write(&kJSONStringDelimiter, 1);
496 result += 1;
497 }
498 trans_->write((const uint8_t *)val.c_str(), val.length());
499 result += val.length();
500 if (escapeNum) {
501 trans_->write(&kJSONStringDelimiter, 1);
502 result += 1;
503 }
504 return result;
505}
506
507uint32_t TJSONProtocol::writeJSONObjectStart() {
508 uint32_t result = context_->write(*trans_);
509 trans_->write(&kJSONObjectStart, 1);
510 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
511 return result + 1;
512}
513
514uint32_t TJSONProtocol::writeJSONObjectEnd() {
515 popContext();
516 trans_->write(&kJSONObjectEnd, 1);
517 return 1;
518}
519
520uint32_t TJSONProtocol::writeJSONArrayStart() {
521 uint32_t result = context_->write(*trans_);
522 trans_->write(&kJSONArrayStart, 1);
523 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
524 return result + 1;
525}
526
527uint32_t TJSONProtocol::writeJSONArrayEnd() {
528 popContext();
529 trans_->write(&kJSONArrayEnd, 1);
530 return 1;
531}
532
533uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
534 const TMessageType messageType,
535 const int32_t seqid) {
536 uint32_t result = writeJSONArrayStart();
537 result += writeJSONInteger(kThriftVersion1);
538 result += writeJSONString(name);
539 result += writeJSONInteger(messageType);
540 result += writeJSONInteger(seqid);
541 return result;
542}
543
544uint32_t TJSONProtocol::writeMessageEnd() {
545 return writeJSONArrayEnd();
546}
547
548uint32_t TJSONProtocol::writeStructBegin(const std::string& name) {
549 return writeJSONObjectStart();
550}
551
552uint32_t TJSONProtocol::writeStructEnd() {
553 return writeJSONObjectEnd();
554}
555
556uint32_t TJSONProtocol::writeFieldBegin(const std::string& name,
557 const TType fieldType,
558 const int16_t fieldId) {
559 uint32_t result = writeJSONInteger(fieldId);
560 result += writeJSONObjectStart();
561 result += writeJSONString(getTypeNameForTypeID(fieldType));
562 return result;
563}
564
565uint32_t TJSONProtocol::writeFieldEnd() {
566 return writeJSONObjectEnd();
567}
568
569uint32_t TJSONProtocol::writeFieldStop() {
570 return 0;
571}
572
573uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
574 const TType valType,
575 const uint32_t size) {
576 uint32_t result = writeJSONArrayStart();
577 result += writeJSONString(getTypeNameForTypeID(keyType));
578 result += writeJSONString(getTypeNameForTypeID(valType));
579 result += writeJSONInteger((int64_t)size);
580 result += writeJSONObjectStart();
581 return result;
582}
583
584uint32_t TJSONProtocol::writeMapEnd() {
585 return writeJSONObjectEnd() + writeJSONArrayEnd();
586}
587
588uint32_t TJSONProtocol::writeListBegin(const TType elemType,
589 const uint32_t size) {
590 uint32_t result = writeJSONArrayStart();
591 result += writeJSONString(getTypeNameForTypeID(elemType));
592 result += writeJSONInteger((int64_t)size);
593 return result;
594}
595
596uint32_t TJSONProtocol::writeListEnd() {
597 return writeJSONArrayEnd();
598}
599
600uint32_t TJSONProtocol::writeSetBegin(const TType elemType,
601 const uint32_t size) {
602 uint32_t result = writeJSONArrayStart();
603 result += writeJSONString(getTypeNameForTypeID(elemType));
604 result += writeJSONInteger((int64_t)size);
605 return result;
606}
607
608uint32_t TJSONProtocol::writeSetEnd() {
609 return writeJSONArrayEnd();
610}
611
612uint32_t TJSONProtocol::writeBool(const bool value) {
613 return writeJSONInteger(value);
614}
615
616uint32_t TJSONProtocol::writeByte(const int8_t byte) {
David Reiss1e62ab42008-02-21 22:37:45 +0000617 // writeByte() must be handled specially becuase boost::lexical cast sees
David Reissdb0ea152008-02-18 01:49:37 +0000618 // int8_t as a text type instead of an integer type
619 return writeJSONInteger((int16_t)byte);
620}
621
622uint32_t TJSONProtocol::writeI16(const int16_t i16) {
623 return writeJSONInteger(i16);
624}
625
626uint32_t TJSONProtocol::writeI32(const int32_t i32) {
627 return writeJSONInteger(i32);
628}
629
630uint32_t TJSONProtocol::writeI64(const int64_t i64) {
631 return writeJSONInteger(i64);
632}
633
634uint32_t TJSONProtocol::writeDouble(const double dub) {
635 return writeJSONDouble(dub);
636}
637
638uint32_t TJSONProtocol::writeString(const std::string& str) {
639 return writeJSONString(str);
640}
641
642uint32_t TJSONProtocol::writeBinary(const std::string& str) {
643 return writeJSONBase64(str);
644}
645
646 /**
647 * Reading functions
648 */
649
David Reiss1e62ab42008-02-21 22:37:45 +0000650// Reads 1 byte and verifies that it matches ch.
David Reissdb0ea152008-02-18 01:49:37 +0000651uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
David Reiss1e62ab42008-02-21 22:37:45 +0000652 return readSyntaxChar(reader_, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000653}
654
655// Decodes the four hex parts of a JSON escaped string character and returns
656// the character via out. The first two characters must be "00".
657uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) {
658 uint8_t b[2];
659 readJSONSyntaxChar(kJSONZeroChar);
660 readJSONSyntaxChar(kJSONZeroChar);
David Reiss1e62ab42008-02-21 22:37:45 +0000661 b[0] = reader_.read();
662 b[1] = reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000663 *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
664 return 4;
665}
666
667// Decodes a JSON string, including unescaping, and returns the string via str
668uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
David Reiss1e62ab42008-02-21 22:37:45 +0000669 uint32_t result = (skipContext ? 0 : context_->read(reader_));
David Reissdb0ea152008-02-18 01:49:37 +0000670 result += readJSONSyntaxChar(kJSONStringDelimiter);
David Reiss1e62ab42008-02-21 22:37:45 +0000671 uint8_t ch;
David Reiss91630732008-02-28 21:11:39 +0000672 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000673 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000674 ch = reader_.read();
675 ++result;
676 if (ch == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000677 break;
678 }
David Reiss1e62ab42008-02-21 22:37:45 +0000679 if (ch == kJSONBackslash) {
680 ch = reader_.read();
681 ++result;
682 if (ch == kJSONEscapeChar) {
683 result += readJSONEscapeChar(&ch);
David Reissdb0ea152008-02-18 01:49:37 +0000684 }
685 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000686 size_t pos = kEscapeChars.find(ch);
David Reissdb0ea152008-02-18 01:49:37 +0000687 if (pos == std::string::npos) {
688 throw TProtocolException(TProtocolException::INVALID_DATA,
689 "Expected control char, got '" +
David Reiss1e62ab42008-02-21 22:37:45 +0000690 std::string((const char *)&ch, 1) + "'.");
David Reissdb0ea152008-02-18 01:49:37 +0000691 }
David Reiss1e62ab42008-02-21 22:37:45 +0000692 ch = kEscapeCharVals[pos];
David Reissdb0ea152008-02-18 01:49:37 +0000693 }
694 }
David Reiss1e62ab42008-02-21 22:37:45 +0000695 str += ch;
David Reissdb0ea152008-02-18 01:49:37 +0000696 }
697 return result;
698}
699
700// Reads a block of base64 characters, decoding it, and returns via str
701uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
702 std::string tmp;
703 uint32_t result = readJSONString(tmp);
704 uint8_t *b = (uint8_t *)tmp.c_str();
705 uint32_t len = tmp.length();
David Reiss91630732008-02-28 21:11:39 +0000706 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000707 while (len >= 4) {
708 base64_decode(b, 4);
709 str.append((const char *)b, 3);
710 b += 4;
711 len -= 4;
712 }
713 // Don't decode if we hit the end or got a single leftover byte (invalid
714 // base64 but legal for skip of regular string type)
715 if (len > 1) {
716 base64_decode(b, len);
717 str.append((const char *)b, len - 1);
718 }
719 return result;
720}
721
722// Reads a sequence of characters, stopping at the first one that is not
723// a valid JSON numeric character.
724uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
725 uint32_t result = 0;
David Reiss91630732008-02-28 21:11:39 +0000726 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000727 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000728 uint8_t ch = reader_.peek();
David Reissdb0ea152008-02-18 01:49:37 +0000729 if (!isJSONNumeric(ch)) {
730 break;
731 }
David Reiss1e62ab42008-02-21 22:37:45 +0000732 reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000733 str += ch;
734 ++result;
735 }
736 return result;
737}
738
739// Reads a sequence of characters and assembles them into a number,
740// returning them via num
741template <typename NumberType>
742uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000743 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000744 if (context_->escapeNum()) {
745 result += readJSONSyntaxChar(kJSONStringDelimiter);
746 }
747 std::string str;
748 result += readJSONNumericChars(str);
749 try {
750 num = boost::lexical_cast<NumberType>(str);
751 }
752 catch (boost::bad_lexical_cast e) {
753 throw new TProtocolException(TProtocolException::INVALID_DATA,
754 "Expected numeric value; got \"" + str +
755 "\"");
756 }
757 if (context_->escapeNum()) {
758 result += readJSONSyntaxChar(kJSONStringDelimiter);
759 }
760 return result;
761}
762
763// Reads a JSON number or string and interprets it as a double.
764uint32_t TJSONProtocol::readJSONDouble(double &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000765 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000766 std::string str;
David Reiss1e62ab42008-02-21 22:37:45 +0000767 if (reader_.peek() == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000768 result += readJSONString(str, true);
769 // Check for NaN, Infinity and -Infinity
770 if (str == kThriftNan) {
771 num = HUGE_VAL/HUGE_VAL; // generates NaN
772 }
773 else if (str == kThriftInfinity) {
774 num = HUGE_VAL;
775 }
776 else if (str == kThriftNegativeInfinity) {
777 num = -HUGE_VAL;
778 }
779 else {
780 if (!context_->escapeNum()) {
781 // Throw exception -- we should not be in a string in this case
782 throw new TProtocolException(TProtocolException::INVALID_DATA,
783 "Numeric data unexpectedly quoted");
784 }
785 try {
786 num = boost::lexical_cast<double>(str);
787 }
788 catch (boost::bad_lexical_cast e) {
789 throw new TProtocolException(TProtocolException::INVALID_DATA,
790 "Expected numeric value; got \"" + str +
791 "\"");
792 }
793 }
794 }
795 else {
796 if (context_->escapeNum()) {
797 // This will throw - we should have had a quote if escapeNum == true
798 readJSONSyntaxChar(kJSONStringDelimiter);
799 }
800 result += readJSONNumericChars(str);
801 try {
802 num = boost::lexical_cast<double>(str);
803 }
804 catch (boost::bad_lexical_cast e) {
805 throw new TProtocolException(TProtocolException::INVALID_DATA,
806 "Expected numeric value; got \"" + str +
807 "\"");
808 }
809 }
810 return result;
811}
812
813uint32_t TJSONProtocol::readJSONObjectStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000814 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000815 result += readJSONSyntaxChar(kJSONObjectStart);
816 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
817 return result;
818}
819
820uint32_t TJSONProtocol::readJSONObjectEnd() {
821 uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
822 popContext();
823 return result;
824}
825
826uint32_t TJSONProtocol::readJSONArrayStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000827 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000828 result += readJSONSyntaxChar(kJSONArrayStart);
829 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
830 return result;
831}
832
833uint32_t TJSONProtocol::readJSONArrayEnd() {
834 uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
835 popContext();
836 return result;
837}
838
839uint32_t TJSONProtocol::readMessageBegin(std::string& name,
840 TMessageType& messageType,
841 int32_t& seqid) {
842 uint32_t result = readJSONArrayStart();
David Reissdb0ea152008-02-18 01:49:37 +0000843 uint64_t tmpVal = 0;
844 result += readJSONInteger(tmpVal);
845 if (tmpVal != kThriftVersion1) {
846 throw TProtocolException(TProtocolException::BAD_VERSION,
847 "Message contained bad version.");
848 }
849 result += readJSONString(name);
850 result += readJSONInteger(tmpVal);
851 messageType = (TMessageType)tmpVal;
852 result += readJSONInteger(tmpVal);
853 seqid = tmpVal;
854 return result;
855}
856
857uint32_t TJSONProtocol::readMessageEnd() {
858 return readJSONArrayEnd();
859}
860
861uint32_t TJSONProtocol::readStructBegin(std::string& name) {
862 return readJSONObjectStart();
863}
864
865uint32_t TJSONProtocol::readStructEnd() {
866 return readJSONObjectEnd();
867}
868
869uint32_t TJSONProtocol::readFieldBegin(std::string& name,
870 TType& fieldType,
871 int16_t& fieldId) {
David Reissdb0ea152008-02-18 01:49:37 +0000872 uint32_t result = 0;
David Reiss1e62ab42008-02-21 22:37:45 +0000873 // Check if we hit the end of the list
874 uint8_t ch = reader_.peek();
875 if (ch == kJSONObjectEnd) {
David Reissdb0ea152008-02-18 01:49:37 +0000876 fieldType = facebook::thrift::protocol::T_STOP;
877 }
878 else {
879 uint64_t tmpVal = 0;
880 std::string tmpStr;
881 result += readJSONInteger(tmpVal);
882 fieldId = tmpVal;
883 result += readJSONObjectStart();
884 result += readJSONString(tmpStr);
885 fieldType = getTypeIDForTypeName(tmpStr);
886 }
887 return result;
888}
889
890uint32_t TJSONProtocol::readFieldEnd() {
891 return readJSONObjectEnd();
892}
893
894uint32_t TJSONProtocol::readMapBegin(TType& keyType,
895 TType& valType,
896 uint32_t& size) {
897 uint64_t tmpVal = 0;
898 std::string tmpStr;
899 uint32_t result = readJSONArrayStart();
900 result += readJSONString(tmpStr);
901 keyType = getTypeIDForTypeName(tmpStr);
902 result += readJSONString(tmpStr);
903 valType = getTypeIDForTypeName(tmpStr);
904 result += readJSONInteger(tmpVal);
905 size = tmpVal;
906 result += readJSONObjectStart();
907 return result;
908}
909
910uint32_t TJSONProtocol::readMapEnd() {
911 return readJSONObjectEnd() + readJSONArrayEnd();
912}
913
914uint32_t TJSONProtocol::readListBegin(TType& elemType,
915 uint32_t& size) {
916 uint64_t tmpVal = 0;
917 std::string tmpStr;
918 uint32_t result = readJSONArrayStart();
919 result += readJSONString(tmpStr);
920 elemType = getTypeIDForTypeName(tmpStr);
921 result += readJSONInteger(tmpVal);
922 size = tmpVal;
923 return result;
924}
925
926uint32_t TJSONProtocol::readListEnd() {
927 return readJSONArrayEnd();
928}
929
930uint32_t TJSONProtocol::readSetBegin(TType& elemType,
931 uint32_t& size) {
932 uint64_t tmpVal = 0;
933 std::string tmpStr;
934 uint32_t result = readJSONArrayStart();
935 result += readJSONString(tmpStr);
936 elemType = getTypeIDForTypeName(tmpStr);
937 result += readJSONInteger(tmpVal);
938 size = tmpVal;
939 return result;
940}
941
942uint32_t TJSONProtocol::readSetEnd() {
943 return readJSONArrayEnd();
944}
945
946uint32_t TJSONProtocol::readBool(bool& value) {
947 return readJSONInteger(value);
948}
949
950// readByte() must be handled properly becuase boost::lexical cast sees int8_t
951// as a text type instead of an integer type
952uint32_t TJSONProtocol::readByte(int8_t& byte) {
953 int16_t tmp = (int16_t) byte;
954 uint32_t result = readJSONInteger(tmp);
955 assert(tmp < 256);
956 byte = (int8_t)tmp;
957 return result;
958}
959
960uint32_t TJSONProtocol::readI16(int16_t& i16) {
961 return readJSONInteger(i16);
962}
963
964uint32_t TJSONProtocol::readI32(int32_t& i32) {
965 return readJSONInteger(i32);
966}
967
968uint32_t TJSONProtocol::readI64(int64_t& i64) {
969 return readJSONInteger(i64);
970}
971
972uint32_t TJSONProtocol::readDouble(double& dub) {
973 return readJSONDouble(dub);
974}
975
976uint32_t TJSONProtocol::readString(std::string &str) {
977 return readJSONString(str);
978}
979
980uint32_t TJSONProtocol::readBinary(std::string &str) {
981 return readJSONBase64(str);
982}
983
984}}} // facebook::thrift::protocol