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