blob: 2a9c8f0b2f7b84a3cf5039ad32519174ac42f3f6 [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
David Reissdb0ea152008-02-18 01:49:37 +000019
20#include "TJSONProtocol.h"
21
22#include <math.h>
David Reissa1771092008-04-11 22:36:31 +000023#include <boost/lexical_cast.hpp>
David Reissdb0ea152008-02-18 01:49:37 +000024#include "TBase64Utils.h"
25#include <transport/TTransportException.h>
26
T Jake Lucianib5e62212009-01-31 22:36:20 +000027using namespace apache::thrift::transport;
David Reissdb0ea152008-02-18 01:49:37 +000028
T Jake Lucianib5e62212009-01-31 22:36:20 +000029namespace apache { namespace thrift { namespace protocol {
David Reissdb0ea152008-02-18 01:49:37 +000030
31
32// Static data
33
34static const uint8_t kJSONObjectStart = '{';
35static const uint8_t kJSONObjectEnd = '}';
36static const uint8_t kJSONArrayStart = '[';
37static const uint8_t kJSONArrayEnd = ']';
38static const uint8_t kJSONNewline = '\n';
39static const uint8_t kJSONPairSeparator = ':';
40static const uint8_t kJSONElemSeparator = ',';
41static const uint8_t kJSONBackslash = '\\';
42static const uint8_t kJSONStringDelimiter = '"';
43static const uint8_t kJSONZeroChar = '0';
44static const uint8_t kJSONEscapeChar = 'u';
45
46static const std::string kJSONEscapePrefix("\\u00");
47
David Reiss6713e1b2009-01-15 23:56:19 +000048static const uint32_t kThriftVersion1 = 1;
David Reissdb0ea152008-02-18 01:49:37 +000049
50static const std::string kThriftNan("NaN");
51static const std::string kThriftInfinity("Infinity");
52static const std::string kThriftNegativeInfinity("-Infinity");
53
54static const std::string kTypeNameBool("tf");
55static const std::string kTypeNameByte("i8");
56static const std::string kTypeNameI16("i16");
57static const std::string kTypeNameI32("i32");
58static const std::string kTypeNameI64("i64");
59static const std::string kTypeNameDouble("dbl");
60static const std::string kTypeNameStruct("rec");
61static const std::string kTypeNameString("str");
62static const std::string kTypeNameMap("map");
63static const std::string kTypeNameList("lst");
64static const std::string kTypeNameSet("set");
65
66static const std::string &getTypeNameForTypeID(TType typeID) {
67 switch (typeID) {
68 case T_BOOL:
69 return kTypeNameBool;
70 case T_BYTE:
71 return kTypeNameByte;
72 case T_I16:
73 return kTypeNameI16;
74 case T_I32:
75 return kTypeNameI32;
76 case T_I64:
77 return kTypeNameI64;
78 case T_DOUBLE:
79 return kTypeNameDouble;
80 case T_STRING:
81 return kTypeNameString;
82 case T_STRUCT:
83 return kTypeNameStruct;
84 case T_MAP:
85 return kTypeNameMap;
86 case T_SET:
87 return kTypeNameSet;
88 case T_LIST:
89 return kTypeNameList;
90 default:
91 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
92 "Unrecognized type");
93 }
94}
95
96static TType getTypeIDForTypeName(const std::string &name) {
97 TType result = T_STOP; // Sentinel value
David Reiss2c9824c2008-03-02 00:20:47 +000098 if (name.length() > 1) {
David Reiss1e62ab42008-02-21 22:37:45 +000099 switch (name[0]) {
100 case 'd':
101 result = T_DOUBLE;
David Reissdb0ea152008-02-18 01:49:37 +0000102 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000103 case 'i':
104 switch (name[1]) {
105 case '8':
106 result = T_BYTE;
107 break;
108 case '1':
109 result = T_I16;
110 break;
111 case '3':
112 result = T_I32;
113 break;
114 case '6':
115 result = T_I64;
116 break;
117 }
David Reissdb0ea152008-02-18 01:49:37 +0000118 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000119 case 'l':
120 result = T_LIST;
David Reissdb0ea152008-02-18 01:49:37 +0000121 break;
David Reiss1e62ab42008-02-21 22:37:45 +0000122 case 'm':
123 result = T_MAP;
124 break;
125 case 'r':
126 result = T_STRUCT;
127 break;
128 case 's':
129 if (name[1] == 't') {
130 result = T_STRING;
131 }
132 else if (name[1] == 'e') {
133 result = T_SET;
134 }
135 break;
136 case 't':
137 result = T_BOOL;
David Reissdb0ea152008-02-18 01:49:37 +0000138 break;
139 }
David Reissdb0ea152008-02-18 01:49:37 +0000140 }
141 if (result == T_STOP) {
142 throw TProtocolException(TProtocolException::NOT_IMPLEMENTED,
143 "Unrecognized type");
144 }
145 return result;
146}
147
148
149// This table describes the handling for the first 0x30 characters
150// 0 : escape using "\u00xx" notation
151// 1 : just output index
152// <other> : escape using "\<other>" notation
153static const uint8_t kJSONCharTable[0x30] = {
154// 0 1 2 3 4 5 6 7 8 9 A B C D E F
155 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
157 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2
158};
159
160
161// This string's characters must match up with the elements in kEscapeCharVals.
162// I don't have '/' on this list even though it appears on www.json.org --
163// it is not in the RFC
164const static std::string kEscapeChars("\"\\bfnrt");
165
166// The elements of this array must match up with the sequence of characters in
167// kEscapeChars
168const static uint8_t kEscapeCharVals[7] = {
169 '"', '\\', '\b', '\f', '\n', '\r', '\t',
170};
171
172
173// Static helper functions
174
175// Read 1 character from the transport trans and verify that it is the
176// expected character ch.
177// Throw a protocol exception if it is not.
David Reiss1e62ab42008-02-21 22:37:45 +0000178static uint32_t readSyntaxChar(TJSONProtocol::LookaheadReader &reader,
179 uint8_t ch) {
180 uint8_t ch2 = reader.read();
181 if (ch2 != ch) {
David Reissdb0ea152008-02-18 01:49:37 +0000182 throw TProtocolException(TProtocolException::INVALID_DATA,
183 "Expected \'" + std::string((char *)&ch, 1) +
David Reiss1e62ab42008-02-21 22:37:45 +0000184 "\'; got \'" + std::string((char *)&ch2, 1) +
David Reissdb0ea152008-02-18 01:49:37 +0000185 "\'.");
186 }
187 return 1;
188}
189
David Reissdb0ea152008-02-18 01:49:37 +0000190// Return the integer value of a hex character ch.
191// Throw a protocol exception if the character is not [0-9a-f].
192static uint8_t hexVal(uint8_t ch) {
193 if ((ch >= '0') && (ch <= '9')) {
194 return ch - '0';
195 }
196 else if ((ch >= 'a') && (ch <= 'f')) {
197 return ch - 'a';
198 }
199 else {
200 throw TProtocolException(TProtocolException::INVALID_DATA,
201 "Expected hex val ([0-9a-f]); got \'"
202 + std::string((char *)&ch, 1) + "\'.");
203 }
204}
205
206// Return the hex character representing the integer val. The value is masked
207// to make sure it is in the correct range.
208static uint8_t hexChar(uint8_t val) {
209 val &= 0x0F;
210 if (val < 10) {
211 return val + '0';
212 }
213 else {
214 return val + 'a';
215 }
216}
217
218// Return true if the character ch is in [-+0-9.Ee]; false otherwise
219static bool isJSONNumeric(uint8_t ch) {
220 switch (ch) {
221 case '+':
222 case '-':
223 case '.':
224 case '0':
225 case '1':
226 case '2':
227 case '3':
228 case '4':
229 case '5':
230 case '6':
231 case '7':
232 case '8':
233 case '9':
234 case 'E':
235 case 'e':
236 return true;
237 }
238 return false;
239}
240
241
242/**
David Reiss1e62ab42008-02-21 22:37:45 +0000243 * Class to serve as base JSON context and as base class for other context
David Reissdb0ea152008-02-18 01:49:37 +0000244 * implementations
245 */
246class TJSONContext {
247
248 public:
249
250 TJSONContext() {};
251
252 virtual ~TJSONContext() {};
253
254 /**
255 * Write context data to the transport. Default is to do nothing.
256 */
257 virtual uint32_t write(TTransport &trans) {
258 return 0;
259 };
260
261 /**
262 * Read context data from the transport. Default is to do nothing.
263 */
David Reiss1e62ab42008-02-21 22:37:45 +0000264 virtual uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000265 return 0;
266 };
267
268 /**
269 * Return true if numbers need to be escaped as strings in this context.
270 * Default behavior is to return false.
271 */
272 virtual bool escapeNum() {
273 return false;
274 }
275};
276
277// Context class for object member key-value pairs
278class JSONPairContext : public TJSONContext {
279
280public:
281
282 JSONPairContext() :
283 first_(true),
284 colon_(true) {
285 }
286
287 uint32_t write(TTransport &trans) {
288 if (first_) {
289 first_ = false;
290 colon_ = true;
291 return 0;
292 }
293 else {
294 trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1);
295 colon_ = !colon_;
296 return 1;
297 }
298 }
299
David Reiss1e62ab42008-02-21 22:37:45 +0000300 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000301 if (first_) {
302 first_ = false;
303 colon_ = true;
304 return 0;
305 }
306 else {
307 uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
308 colon_ = !colon_;
David Reiss1e62ab42008-02-21 22:37:45 +0000309 return readSyntaxChar(reader, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000310 }
311 }
312
313 // Numbers must be turned into strings if they are the key part of a pair
314 virtual bool escapeNum() {
315 return colon_;
316 }
317
318 private:
319
320 bool first_;
321 bool colon_;
322};
323
324// Context class for lists
325class JSONListContext : public TJSONContext {
326
327public:
328
329 JSONListContext() :
330 first_(true) {
331 }
332
333 uint32_t write(TTransport &trans) {
334 if (first_) {
335 first_ = false;
336 return 0;
337 }
338 else {
339 trans.write(&kJSONElemSeparator, 1);
340 return 1;
341 }
342 }
343
David Reiss1e62ab42008-02-21 22:37:45 +0000344 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000345 if (first_) {
346 first_ = false;
347 return 0;
348 }
349 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000350 return readSyntaxChar(reader, kJSONElemSeparator);
David Reissdb0ea152008-02-18 01:49:37 +0000351 }
352 }
353
354 private:
355 bool first_;
356};
357
358
359TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) :
360 TProtocol(ptrans),
David Reiss1e62ab42008-02-21 22:37:45 +0000361 context_(new TJSONContext()),
362 reader_(*ptrans) {
David Reissdb0ea152008-02-18 01:49:37 +0000363}
364
365TJSONProtocol::~TJSONProtocol() {}
366
367void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) {
368 contexts_.push(context_);
369 context_ = c;
370}
371
372void TJSONProtocol::popContext() {
373 context_ = contexts_.top();
374 contexts_.pop();
375}
376
377// Write the character ch as a JSON escape sequence ("\u00xx")
378uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
379 trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(),
380 kJSONEscapePrefix.length());
381 uint8_t outCh = hexChar(ch >> 4);
382 trans_->write(&outCh, 1);
383 outCh = hexChar(ch);
384 trans_->write(&outCh, 1);
385 return 6;
386}
387
388// Write the character ch as part of a JSON string, escaping as appropriate.
389uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
390 if (ch >= 0x30) {
391 if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
392 trans_->write(&kJSONBackslash, 1);
393 trans_->write(&kJSONBackslash, 1);
394 return 2;
395 }
396 else {
397 trans_->write(&ch, 1);
398 return 1;
399 }
400 }
401 else {
402 uint8_t outCh = kJSONCharTable[ch];
403 // Check if regular character, backslash escaped, or JSON escaped
404 if (outCh == 1) {
405 trans_->write(&ch, 1);
406 return 1;
407 }
408 else if (outCh > 1) {
409 trans_->write(&kJSONBackslash, 1);
410 trans_->write(&outCh, 1);
411 return 2;
412 }
413 else {
414 return writeJSONEscapeChar(ch);
415 }
416 }
417}
418
419// Write out the contents of the string str as a JSON string, escaping
420// characters as appropriate.
421uint32_t TJSONProtocol::writeJSONString(const std::string &str) {
422 uint32_t result = context_->write(*trans_);
423 result += 2; // For quotes
424 trans_->write(&kJSONStringDelimiter, 1);
425 std::string::const_iterator iter(str.begin());
426 std::string::const_iterator end(str.end());
427 while (iter != end) {
428 result += writeJSONChar(*iter++);
429 }
430 trans_->write(&kJSONStringDelimiter, 1);
431 return result;
432}
433
434// Write out the contents of the string as JSON string, base64-encoding
435// the string's contents, and escaping as appropriate
436uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) {
437 uint32_t result = context_->write(*trans_);
438 result += 2; // For quotes
439 trans_->write(&kJSONStringDelimiter, 1);
440 uint8_t b[4];
441 const uint8_t *bytes = (const uint8_t *)str.c_str();
442 uint32_t len = str.length();
443 while (len >= 3) {
444 // Encode 3 bytes at a time
445 base64_encode(bytes, 3, b);
446 trans_->write(b, 4);
447 result += 4;
448 bytes += 3;
449 len -=3;
450 }
451 if (len) { // Handle remainder
452 base64_encode(bytes, len, b);
453 trans_->write(b, len + 1);
454 result += len + 1;
455 }
456 trans_->write(&kJSONStringDelimiter, 1);
457 return result;
458}
459
460// Convert the given integer type to a JSON number, or a string
461// if the context requires it (eg: key in a map pair).
462template <typename NumberType>
463uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
464 uint32_t result = context_->write(*trans_);
465 std::string val(boost::lexical_cast<std::string>(num));
466 bool escapeNum = context_->escapeNum();
467 if (escapeNum) {
468 trans_->write(&kJSONStringDelimiter, 1);
469 result += 1;
470 }
471 trans_->write((const uint8_t *)val.c_str(), val.length());
472 result += val.length();
473 if (escapeNum) {
474 trans_->write(&kJSONStringDelimiter, 1);
475 result += 1;
476 }
477 return result;
478}
479
480// Convert the given double to a JSON string, which is either the number,
481// "NaN" or "Infinity" or "-Infinity".
482uint32_t TJSONProtocol::writeJSONDouble(double num) {
483 uint32_t result = context_->write(*trans_);
484 std::string val(boost::lexical_cast<std::string>(num));
485
486 // Normalize output of boost::lexical_cast for NaNs and Infinities
487 bool special = false;
488 switch (val[0]) {
489 case 'N':
490 case 'n':
491 val = kThriftNan;
492 special = true;
493 break;
494 case 'I':
495 case 'i':
496 val = kThriftInfinity;
497 special = true;
498 break;
499 case '-':
500 if ((val[1] == 'I') || (val[1] == 'i')) {
501 val = kThriftNegativeInfinity;
502 special = true;
503 }
504 break;
505 }
506
507 bool escapeNum = special || context_->escapeNum();
508 if (escapeNum) {
509 trans_->write(&kJSONStringDelimiter, 1);
510 result += 1;
511 }
512 trans_->write((const uint8_t *)val.c_str(), val.length());
513 result += val.length();
514 if (escapeNum) {
515 trans_->write(&kJSONStringDelimiter, 1);
516 result += 1;
517 }
518 return result;
519}
520
521uint32_t TJSONProtocol::writeJSONObjectStart() {
522 uint32_t result = context_->write(*trans_);
523 trans_->write(&kJSONObjectStart, 1);
524 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
525 return result + 1;
526}
527
528uint32_t TJSONProtocol::writeJSONObjectEnd() {
529 popContext();
530 trans_->write(&kJSONObjectEnd, 1);
531 return 1;
532}
533
534uint32_t TJSONProtocol::writeJSONArrayStart() {
535 uint32_t result = context_->write(*trans_);
536 trans_->write(&kJSONArrayStart, 1);
537 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
538 return result + 1;
539}
540
541uint32_t TJSONProtocol::writeJSONArrayEnd() {
542 popContext();
543 trans_->write(&kJSONArrayEnd, 1);
544 return 1;
545}
546
547uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
548 const TMessageType messageType,
549 const int32_t seqid) {
550 uint32_t result = writeJSONArrayStart();
551 result += writeJSONInteger(kThriftVersion1);
552 result += writeJSONString(name);
553 result += writeJSONInteger(messageType);
554 result += writeJSONInteger(seqid);
555 return result;
556}
557
558uint32_t TJSONProtocol::writeMessageEnd() {
559 return writeJSONArrayEnd();
560}
561
David Reiss64120002008-04-29 23:12:24 +0000562uint32_t TJSONProtocol::writeStructBegin(const char* name) {
David Reissdb0ea152008-02-18 01:49:37 +0000563 return writeJSONObjectStart();
564}
565
566uint32_t TJSONProtocol::writeStructEnd() {
567 return writeJSONObjectEnd();
568}
569
David Reiss64120002008-04-29 23:12:24 +0000570uint32_t TJSONProtocol::writeFieldBegin(const char* name,
David Reissdb0ea152008-02-18 01:49:37 +0000571 const TType fieldType,
572 const int16_t fieldId) {
573 uint32_t result = writeJSONInteger(fieldId);
574 result += writeJSONObjectStart();
575 result += writeJSONString(getTypeNameForTypeID(fieldType));
576 return result;
577}
578
579uint32_t TJSONProtocol::writeFieldEnd() {
580 return writeJSONObjectEnd();
581}
582
583uint32_t TJSONProtocol::writeFieldStop() {
584 return 0;
585}
586
587uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
588 const TType valType,
589 const uint32_t size) {
590 uint32_t result = writeJSONArrayStart();
591 result += writeJSONString(getTypeNameForTypeID(keyType));
592 result += writeJSONString(getTypeNameForTypeID(valType));
593 result += writeJSONInteger((int64_t)size);
594 result += writeJSONObjectStart();
595 return result;
596}
597
598uint32_t TJSONProtocol::writeMapEnd() {
599 return writeJSONObjectEnd() + writeJSONArrayEnd();
600}
601
602uint32_t TJSONProtocol::writeListBegin(const TType elemType,
603 const uint32_t size) {
604 uint32_t result = writeJSONArrayStart();
605 result += writeJSONString(getTypeNameForTypeID(elemType));
606 result += writeJSONInteger((int64_t)size);
607 return result;
608}
609
610uint32_t TJSONProtocol::writeListEnd() {
611 return writeJSONArrayEnd();
612}
613
614uint32_t TJSONProtocol::writeSetBegin(const TType elemType,
615 const uint32_t size) {
616 uint32_t result = writeJSONArrayStart();
617 result += writeJSONString(getTypeNameForTypeID(elemType));
618 result += writeJSONInteger((int64_t)size);
619 return result;
620}
621
622uint32_t TJSONProtocol::writeSetEnd() {
623 return writeJSONArrayEnd();
624}
625
626uint32_t TJSONProtocol::writeBool(const bool value) {
627 return writeJSONInteger(value);
628}
629
630uint32_t TJSONProtocol::writeByte(const int8_t byte) {
David Reiss1e62ab42008-02-21 22:37:45 +0000631 // writeByte() must be handled specially becuase boost::lexical cast sees
David Reissdb0ea152008-02-18 01:49:37 +0000632 // int8_t as a text type instead of an integer type
633 return writeJSONInteger((int16_t)byte);
634}
635
636uint32_t TJSONProtocol::writeI16(const int16_t i16) {
637 return writeJSONInteger(i16);
638}
639
640uint32_t TJSONProtocol::writeI32(const int32_t i32) {
641 return writeJSONInteger(i32);
642}
643
644uint32_t TJSONProtocol::writeI64(const int64_t i64) {
645 return writeJSONInteger(i64);
646}
647
648uint32_t TJSONProtocol::writeDouble(const double dub) {
649 return writeJSONDouble(dub);
650}
651
652uint32_t TJSONProtocol::writeString(const std::string& str) {
653 return writeJSONString(str);
654}
655
656uint32_t TJSONProtocol::writeBinary(const std::string& str) {
657 return writeJSONBase64(str);
658}
659
660 /**
661 * Reading functions
662 */
663
David Reiss1e62ab42008-02-21 22:37:45 +0000664// Reads 1 byte and verifies that it matches ch.
David Reissdb0ea152008-02-18 01:49:37 +0000665uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
David Reiss1e62ab42008-02-21 22:37:45 +0000666 return readSyntaxChar(reader_, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000667}
668
669// Decodes the four hex parts of a JSON escaped string character and returns
670// the character via out. The first two characters must be "00".
671uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) {
672 uint8_t b[2];
673 readJSONSyntaxChar(kJSONZeroChar);
674 readJSONSyntaxChar(kJSONZeroChar);
David Reiss1e62ab42008-02-21 22:37:45 +0000675 b[0] = reader_.read();
676 b[1] = reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000677 *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
678 return 4;
679}
680
681// Decodes a JSON string, including unescaping, and returns the string via str
682uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
David Reiss1e62ab42008-02-21 22:37:45 +0000683 uint32_t result = (skipContext ? 0 : context_->read(reader_));
David Reissdb0ea152008-02-18 01:49:37 +0000684 result += readJSONSyntaxChar(kJSONStringDelimiter);
David Reiss1e62ab42008-02-21 22:37:45 +0000685 uint8_t ch;
David Reiss91630732008-02-28 21:11:39 +0000686 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000687 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000688 ch = reader_.read();
689 ++result;
690 if (ch == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000691 break;
692 }
David Reiss1e62ab42008-02-21 22:37:45 +0000693 if (ch == kJSONBackslash) {
694 ch = reader_.read();
695 ++result;
696 if (ch == kJSONEscapeChar) {
697 result += readJSONEscapeChar(&ch);
David Reissdb0ea152008-02-18 01:49:37 +0000698 }
699 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000700 size_t pos = kEscapeChars.find(ch);
David Reissdb0ea152008-02-18 01:49:37 +0000701 if (pos == std::string::npos) {
702 throw TProtocolException(TProtocolException::INVALID_DATA,
703 "Expected control char, got '" +
David Reiss1e62ab42008-02-21 22:37:45 +0000704 std::string((const char *)&ch, 1) + "'.");
David Reissdb0ea152008-02-18 01:49:37 +0000705 }
David Reiss1e62ab42008-02-21 22:37:45 +0000706 ch = kEscapeCharVals[pos];
David Reissdb0ea152008-02-18 01:49:37 +0000707 }
708 }
David Reiss1e62ab42008-02-21 22:37:45 +0000709 str += ch;
David Reissdb0ea152008-02-18 01:49:37 +0000710 }
711 return result;
712}
713
714// Reads a block of base64 characters, decoding it, and returns via str
715uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
716 std::string tmp;
717 uint32_t result = readJSONString(tmp);
718 uint8_t *b = (uint8_t *)tmp.c_str();
719 uint32_t len = tmp.length();
David Reiss91630732008-02-28 21:11:39 +0000720 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000721 while (len >= 4) {
722 base64_decode(b, 4);
723 str.append((const char *)b, 3);
724 b += 4;
725 len -= 4;
726 }
727 // Don't decode if we hit the end or got a single leftover byte (invalid
728 // base64 but legal for skip of regular string type)
729 if (len > 1) {
730 base64_decode(b, len);
731 str.append((const char *)b, len - 1);
732 }
733 return result;
734}
735
736// Reads a sequence of characters, stopping at the first one that is not
737// a valid JSON numeric character.
738uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
739 uint32_t result = 0;
David Reiss91630732008-02-28 21:11:39 +0000740 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000741 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000742 uint8_t ch = reader_.peek();
David Reissdb0ea152008-02-18 01:49:37 +0000743 if (!isJSONNumeric(ch)) {
744 break;
745 }
David Reiss1e62ab42008-02-21 22:37:45 +0000746 reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000747 str += ch;
748 ++result;
749 }
750 return result;
751}
752
753// Reads a sequence of characters and assembles them into a number,
754// returning them via num
755template <typename NumberType>
756uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000757 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000758 if (context_->escapeNum()) {
759 result += readJSONSyntaxChar(kJSONStringDelimiter);
760 }
761 std::string str;
762 result += readJSONNumericChars(str);
763 try {
764 num = boost::lexical_cast<NumberType>(str);
765 }
766 catch (boost::bad_lexical_cast e) {
767 throw new TProtocolException(TProtocolException::INVALID_DATA,
768 "Expected numeric value; got \"" + str +
769 "\"");
770 }
771 if (context_->escapeNum()) {
772 result += readJSONSyntaxChar(kJSONStringDelimiter);
773 }
774 return result;
775}
776
777// Reads a JSON number or string and interprets it as a double.
778uint32_t TJSONProtocol::readJSONDouble(double &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000779 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000780 std::string str;
David Reiss1e62ab42008-02-21 22:37:45 +0000781 if (reader_.peek() == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000782 result += readJSONString(str, true);
783 // Check for NaN, Infinity and -Infinity
784 if (str == kThriftNan) {
785 num = HUGE_VAL/HUGE_VAL; // generates NaN
786 }
787 else if (str == kThriftInfinity) {
788 num = HUGE_VAL;
789 }
790 else if (str == kThriftNegativeInfinity) {
791 num = -HUGE_VAL;
792 }
793 else {
794 if (!context_->escapeNum()) {
795 // Throw exception -- we should not be in a string in this case
796 throw new TProtocolException(TProtocolException::INVALID_DATA,
797 "Numeric data unexpectedly quoted");
798 }
799 try {
800 num = boost::lexical_cast<double>(str);
801 }
802 catch (boost::bad_lexical_cast e) {
803 throw new TProtocolException(TProtocolException::INVALID_DATA,
804 "Expected numeric value; got \"" + str +
805 "\"");
806 }
807 }
808 }
809 else {
810 if (context_->escapeNum()) {
811 // This will throw - we should have had a quote if escapeNum == true
812 readJSONSyntaxChar(kJSONStringDelimiter);
813 }
814 result += readJSONNumericChars(str);
815 try {
816 num = boost::lexical_cast<double>(str);
817 }
818 catch (boost::bad_lexical_cast e) {
819 throw new TProtocolException(TProtocolException::INVALID_DATA,
820 "Expected numeric value; got \"" + str +
821 "\"");
822 }
823 }
824 return result;
825}
826
827uint32_t TJSONProtocol::readJSONObjectStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000828 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000829 result += readJSONSyntaxChar(kJSONObjectStart);
830 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
831 return result;
832}
833
834uint32_t TJSONProtocol::readJSONObjectEnd() {
835 uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
836 popContext();
837 return result;
838}
839
840uint32_t TJSONProtocol::readJSONArrayStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000841 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000842 result += readJSONSyntaxChar(kJSONArrayStart);
843 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
844 return result;
845}
846
847uint32_t TJSONProtocol::readJSONArrayEnd() {
848 uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
849 popContext();
850 return result;
851}
852
853uint32_t TJSONProtocol::readMessageBegin(std::string& name,
854 TMessageType& messageType,
855 int32_t& seqid) {
856 uint32_t result = readJSONArrayStart();
David Reissdb0ea152008-02-18 01:49:37 +0000857 uint64_t tmpVal = 0;
858 result += readJSONInteger(tmpVal);
859 if (tmpVal != kThriftVersion1) {
860 throw TProtocolException(TProtocolException::BAD_VERSION,
861 "Message contained bad version.");
862 }
863 result += readJSONString(name);
864 result += readJSONInteger(tmpVal);
865 messageType = (TMessageType)tmpVal;
866 result += readJSONInteger(tmpVal);
867 seqid = tmpVal;
868 return result;
869}
870
871uint32_t TJSONProtocol::readMessageEnd() {
872 return readJSONArrayEnd();
873}
874
875uint32_t TJSONProtocol::readStructBegin(std::string& name) {
876 return readJSONObjectStart();
877}
878
879uint32_t TJSONProtocol::readStructEnd() {
880 return readJSONObjectEnd();
881}
882
883uint32_t TJSONProtocol::readFieldBegin(std::string& name,
884 TType& fieldType,
885 int16_t& fieldId) {
David Reissdb0ea152008-02-18 01:49:37 +0000886 uint32_t result = 0;
David Reiss1e62ab42008-02-21 22:37:45 +0000887 // Check if we hit the end of the list
888 uint8_t ch = reader_.peek();
889 if (ch == kJSONObjectEnd) {
T Jake Lucianib5e62212009-01-31 22:36:20 +0000890 fieldType = apache::thrift::protocol::T_STOP;
David Reissdb0ea152008-02-18 01:49:37 +0000891 }
892 else {
893 uint64_t tmpVal = 0;
894 std::string tmpStr;
895 result += readJSONInteger(tmpVal);
896 fieldId = tmpVal;
897 result += readJSONObjectStart();
898 result += readJSONString(tmpStr);
899 fieldType = getTypeIDForTypeName(tmpStr);
900 }
901 return result;
902}
903
904uint32_t TJSONProtocol::readFieldEnd() {
905 return readJSONObjectEnd();
906}
907
908uint32_t TJSONProtocol::readMapBegin(TType& keyType,
909 TType& valType,
910 uint32_t& size) {
911 uint64_t tmpVal = 0;
912 std::string tmpStr;
913 uint32_t result = readJSONArrayStart();
914 result += readJSONString(tmpStr);
915 keyType = getTypeIDForTypeName(tmpStr);
916 result += readJSONString(tmpStr);
917 valType = getTypeIDForTypeName(tmpStr);
918 result += readJSONInteger(tmpVal);
919 size = tmpVal;
920 result += readJSONObjectStart();
921 return result;
922}
923
924uint32_t TJSONProtocol::readMapEnd() {
925 return readJSONObjectEnd() + readJSONArrayEnd();
926}
927
928uint32_t TJSONProtocol::readListBegin(TType& elemType,
929 uint32_t& size) {
930 uint64_t tmpVal = 0;
931 std::string tmpStr;
932 uint32_t result = readJSONArrayStart();
933 result += readJSONString(tmpStr);
934 elemType = getTypeIDForTypeName(tmpStr);
935 result += readJSONInteger(tmpVal);
936 size = tmpVal;
937 return result;
938}
939
940uint32_t TJSONProtocol::readListEnd() {
941 return readJSONArrayEnd();
942}
943
944uint32_t TJSONProtocol::readSetBegin(TType& elemType,
945 uint32_t& size) {
946 uint64_t tmpVal = 0;
947 std::string tmpStr;
948 uint32_t result = readJSONArrayStart();
949 result += readJSONString(tmpStr);
950 elemType = getTypeIDForTypeName(tmpStr);
951 result += readJSONInteger(tmpVal);
952 size = tmpVal;
953 return result;
954}
955
956uint32_t TJSONProtocol::readSetEnd() {
957 return readJSONArrayEnd();
958}
959
960uint32_t TJSONProtocol::readBool(bool& value) {
961 return readJSONInteger(value);
962}
963
964// readByte() must be handled properly becuase boost::lexical cast sees int8_t
965// as a text type instead of an integer type
966uint32_t TJSONProtocol::readByte(int8_t& byte) {
967 int16_t tmp = (int16_t) byte;
968 uint32_t result = readJSONInteger(tmp);
969 assert(tmp < 256);
970 byte = (int8_t)tmp;
971 return result;
972}
973
974uint32_t TJSONProtocol::readI16(int16_t& i16) {
975 return readJSONInteger(i16);
976}
977
978uint32_t TJSONProtocol::readI32(int32_t& i32) {
979 return readJSONInteger(i32);
980}
981
982uint32_t TJSONProtocol::readI64(int64_t& i64) {
983 return readJSONInteger(i64);
984}
985
986uint32_t TJSONProtocol::readDouble(double& dub) {
987 return readJSONDouble(dub);
988}
989
990uint32_t TJSONProtocol::readString(std::string &str) {
991 return readJSONString(str);
992}
993
994uint32_t TJSONProtocol::readBinary(std::string &str) {
995 return readJSONBase64(str);
996}
997
T Jake Lucianib5e62212009-01-31 22:36:20 +0000998}}} // apache::thrift::protocol