blob: 0f3b102df0ff133df0883b7d00dbe7ce534a440e [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 Reissdb0ea152008-02-18 01:49:37 +0000672 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000673 ch = reader_.read();
674 ++result;
675 if (ch == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000676 break;
677 }
David Reiss1e62ab42008-02-21 22:37:45 +0000678 if (ch == kJSONBackslash) {
679 ch = reader_.read();
680 ++result;
681 if (ch == kJSONEscapeChar) {
682 result += readJSONEscapeChar(&ch);
David Reissdb0ea152008-02-18 01:49:37 +0000683 }
684 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000685 size_t pos = kEscapeChars.find(ch);
David Reissdb0ea152008-02-18 01:49:37 +0000686 if (pos == std::string::npos) {
687 throw TProtocolException(TProtocolException::INVALID_DATA,
688 "Expected control char, got '" +
David Reiss1e62ab42008-02-21 22:37:45 +0000689 std::string((const char *)&ch, 1) + "'.");
David Reissdb0ea152008-02-18 01:49:37 +0000690 }
David Reiss1e62ab42008-02-21 22:37:45 +0000691 ch = kEscapeCharVals[pos];
David Reissdb0ea152008-02-18 01:49:37 +0000692 }
693 }
David Reiss1e62ab42008-02-21 22:37:45 +0000694 str += ch;
David Reissdb0ea152008-02-18 01:49:37 +0000695 }
696 return result;
697}
698
699// Reads a block of base64 characters, decoding it, and returns via str
700uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
701 std::string tmp;
702 uint32_t result = readJSONString(tmp);
703 uint8_t *b = (uint8_t *)tmp.c_str();
704 uint32_t len = tmp.length();
705 while (len >= 4) {
706 base64_decode(b, 4);
707 str.append((const char *)b, 3);
708 b += 4;
709 len -= 4;
710 }
711 // Don't decode if we hit the end or got a single leftover byte (invalid
712 // base64 but legal for skip of regular string type)
713 if (len > 1) {
714 base64_decode(b, len);
715 str.append((const char *)b, len - 1);
716 }
717 return result;
718}
719
720// Reads a sequence of characters, stopping at the first one that is not
721// a valid JSON numeric character.
722uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
723 uint32_t result = 0;
724 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000725 uint8_t ch = reader_.peek();
David Reissdb0ea152008-02-18 01:49:37 +0000726 if (!isJSONNumeric(ch)) {
727 break;
728 }
David Reiss1e62ab42008-02-21 22:37:45 +0000729 reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000730 str += ch;
731 ++result;
732 }
733 return result;
734}
735
736// Reads a sequence of characters and assembles them into a number,
737// returning them via num
738template <typename NumberType>
739uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000740 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000741 if (context_->escapeNum()) {
742 result += readJSONSyntaxChar(kJSONStringDelimiter);
743 }
744 std::string str;
745 result += readJSONNumericChars(str);
746 try {
747 num = boost::lexical_cast<NumberType>(str);
748 }
749 catch (boost::bad_lexical_cast e) {
750 throw new TProtocolException(TProtocolException::INVALID_DATA,
751 "Expected numeric value; got \"" + str +
752 "\"");
753 }
754 if (context_->escapeNum()) {
755 result += readJSONSyntaxChar(kJSONStringDelimiter);
756 }
757 return result;
758}
759
760// Reads a JSON number or string and interprets it as a double.
761uint32_t TJSONProtocol::readJSONDouble(double &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000762 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000763 std::string str;
David Reiss1e62ab42008-02-21 22:37:45 +0000764 if (reader_.peek() == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000765 result += readJSONString(str, true);
766 // Check for NaN, Infinity and -Infinity
767 if (str == kThriftNan) {
768 num = HUGE_VAL/HUGE_VAL; // generates NaN
769 }
770 else if (str == kThriftInfinity) {
771 num = HUGE_VAL;
772 }
773 else if (str == kThriftNegativeInfinity) {
774 num = -HUGE_VAL;
775 }
776 else {
777 if (!context_->escapeNum()) {
778 // Throw exception -- we should not be in a string in this case
779 throw new TProtocolException(TProtocolException::INVALID_DATA,
780 "Numeric data unexpectedly quoted");
781 }
782 try {
783 num = boost::lexical_cast<double>(str);
784 }
785 catch (boost::bad_lexical_cast e) {
786 throw new TProtocolException(TProtocolException::INVALID_DATA,
787 "Expected numeric value; got \"" + str +
788 "\"");
789 }
790 }
791 }
792 else {
793 if (context_->escapeNum()) {
794 // This will throw - we should have had a quote if escapeNum == true
795 readJSONSyntaxChar(kJSONStringDelimiter);
796 }
797 result += readJSONNumericChars(str);
798 try {
799 num = boost::lexical_cast<double>(str);
800 }
801 catch (boost::bad_lexical_cast e) {
802 throw new TProtocolException(TProtocolException::INVALID_DATA,
803 "Expected numeric value; got \"" + str +
804 "\"");
805 }
806 }
807 return result;
808}
809
810uint32_t TJSONProtocol::readJSONObjectStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000811 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000812 result += readJSONSyntaxChar(kJSONObjectStart);
813 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
814 return result;
815}
816
817uint32_t TJSONProtocol::readJSONObjectEnd() {
818 uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
819 popContext();
820 return result;
821}
822
823uint32_t TJSONProtocol::readJSONArrayStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000824 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000825 result += readJSONSyntaxChar(kJSONArrayStart);
826 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
827 return result;
828}
829
830uint32_t TJSONProtocol::readJSONArrayEnd() {
831 uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
832 popContext();
833 return result;
834}
835
836uint32_t TJSONProtocol::readMessageBegin(std::string& name,
837 TMessageType& messageType,
838 int32_t& seqid) {
839 uint32_t result = readJSONArrayStart();
David Reissdb0ea152008-02-18 01:49:37 +0000840 uint64_t tmpVal = 0;
841 result += readJSONInteger(tmpVal);
842 if (tmpVal != kThriftVersion1) {
843 throw TProtocolException(TProtocolException::BAD_VERSION,
844 "Message contained bad version.");
845 }
846 result += readJSONString(name);
847 result += readJSONInteger(tmpVal);
848 messageType = (TMessageType)tmpVal;
849 result += readJSONInteger(tmpVal);
850 seqid = tmpVal;
851 return result;
852}
853
854uint32_t TJSONProtocol::readMessageEnd() {
855 return readJSONArrayEnd();
856}
857
858uint32_t TJSONProtocol::readStructBegin(std::string& name) {
859 return readJSONObjectStart();
860}
861
862uint32_t TJSONProtocol::readStructEnd() {
863 return readJSONObjectEnd();
864}
865
866uint32_t TJSONProtocol::readFieldBegin(std::string& name,
867 TType& fieldType,
868 int16_t& fieldId) {
David Reissdb0ea152008-02-18 01:49:37 +0000869 uint32_t result = 0;
David Reiss1e62ab42008-02-21 22:37:45 +0000870 // Check if we hit the end of the list
871 uint8_t ch = reader_.peek();
872 if (ch == kJSONObjectEnd) {
David Reissdb0ea152008-02-18 01:49:37 +0000873 fieldType = facebook::thrift::protocol::T_STOP;
874 }
875 else {
876 uint64_t tmpVal = 0;
877 std::string tmpStr;
878 result += readJSONInteger(tmpVal);
879 fieldId = tmpVal;
880 result += readJSONObjectStart();
881 result += readJSONString(tmpStr);
882 fieldType = getTypeIDForTypeName(tmpStr);
883 }
884 return result;
885}
886
887uint32_t TJSONProtocol::readFieldEnd() {
888 return readJSONObjectEnd();
889}
890
891uint32_t TJSONProtocol::readMapBegin(TType& keyType,
892 TType& valType,
893 uint32_t& size) {
894 uint64_t tmpVal = 0;
895 std::string tmpStr;
896 uint32_t result = readJSONArrayStart();
897 result += readJSONString(tmpStr);
898 keyType = getTypeIDForTypeName(tmpStr);
899 result += readJSONString(tmpStr);
900 valType = getTypeIDForTypeName(tmpStr);
901 result += readJSONInteger(tmpVal);
902 size = tmpVal;
903 result += readJSONObjectStart();
904 return result;
905}
906
907uint32_t TJSONProtocol::readMapEnd() {
908 return readJSONObjectEnd() + readJSONArrayEnd();
909}
910
911uint32_t TJSONProtocol::readListBegin(TType& elemType,
912 uint32_t& size) {
913 uint64_t tmpVal = 0;
914 std::string tmpStr;
915 uint32_t result = readJSONArrayStart();
916 result += readJSONString(tmpStr);
917 elemType = getTypeIDForTypeName(tmpStr);
918 result += readJSONInteger(tmpVal);
919 size = tmpVal;
920 return result;
921}
922
923uint32_t TJSONProtocol::readListEnd() {
924 return readJSONArrayEnd();
925}
926
927uint32_t TJSONProtocol::readSetBegin(TType& elemType,
928 uint32_t& size) {
929 uint64_t tmpVal = 0;
930 std::string tmpStr;
931 uint32_t result = readJSONArrayStart();
932 result += readJSONString(tmpStr);
933 elemType = getTypeIDForTypeName(tmpStr);
934 result += readJSONInteger(tmpVal);
935 size = tmpVal;
936 return result;
937}
938
939uint32_t TJSONProtocol::readSetEnd() {
940 return readJSONArrayEnd();
941}
942
943uint32_t TJSONProtocol::readBool(bool& value) {
944 return readJSONInteger(value);
945}
946
947// readByte() must be handled properly becuase boost::lexical cast sees int8_t
948// as a text type instead of an integer type
949uint32_t TJSONProtocol::readByte(int8_t& byte) {
950 int16_t tmp = (int16_t) byte;
951 uint32_t result = readJSONInteger(tmp);
952 assert(tmp < 256);
953 byte = (int8_t)tmp;
954 return result;
955}
956
957uint32_t TJSONProtocol::readI16(int16_t& i16) {
958 return readJSONInteger(i16);
959}
960
961uint32_t TJSONProtocol::readI32(int32_t& i32) {
962 return readJSONInteger(i32);
963}
964
965uint32_t TJSONProtocol::readI64(int64_t& i64) {
966 return readJSONInteger(i64);
967}
968
969uint32_t TJSONProtocol::readDouble(double& dub) {
970 return readJSONDouble(dub);
971}
972
973uint32_t TJSONProtocol::readString(std::string &str) {
974 return readJSONString(str);
975}
976
977uint32_t TJSONProtocol::readBinary(std::string &str) {
978 return readJSONBase64(str);
979}
980
981}}} // facebook::thrift::protocol