blob: 5b0b18daddc10beec34fe70b335c8c808f42082d [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')) {
Bryan Duxbury4a2bc1b2010-11-03 17:57:38 +0000197 return ch - 'a' + 10;
David Reissdb0ea152008-02-18 01:49:37 +0000198 }
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 {
Bryan Duxbury4a2bc1b2010-11-03 17:57:38 +0000214 return val - 10 + 'a';
David Reissdb0ea152008-02-18 01:49:37 +0000215 }
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) :
David Reiss6806fb82010-10-06 17:09:52 +0000360 TVirtualProtocol<TJSONProtocol>(ptrans),
David Reisse71115b2010-10-06 17:09:56 +0000361 trans_(ptrans.get()),
David Reiss1e62ab42008-02-21 22:37:45 +0000362 context_(new TJSONContext()),
363 reader_(*ptrans) {
David Reissdb0ea152008-02-18 01:49:37 +0000364}
365
366TJSONProtocol::~TJSONProtocol() {}
367
368void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) {
369 contexts_.push(context_);
370 context_ = c;
371}
372
373void TJSONProtocol::popContext() {
374 context_ = contexts_.top();
375 contexts_.pop();
376}
377
378// Write the character ch as a JSON escape sequence ("\u00xx")
379uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
380 trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(),
381 kJSONEscapePrefix.length());
382 uint8_t outCh = hexChar(ch >> 4);
383 trans_->write(&outCh, 1);
384 outCh = hexChar(ch);
385 trans_->write(&outCh, 1);
386 return 6;
387}
388
389// Write the character ch as part of a JSON string, escaping as appropriate.
390uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
391 if (ch >= 0x30) {
392 if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
393 trans_->write(&kJSONBackslash, 1);
394 trans_->write(&kJSONBackslash, 1);
395 return 2;
396 }
397 else {
398 trans_->write(&ch, 1);
399 return 1;
400 }
401 }
402 else {
403 uint8_t outCh = kJSONCharTable[ch];
404 // Check if regular character, backslash escaped, or JSON escaped
405 if (outCh == 1) {
406 trans_->write(&ch, 1);
407 return 1;
408 }
409 else if (outCh > 1) {
410 trans_->write(&kJSONBackslash, 1);
411 trans_->write(&outCh, 1);
412 return 2;
413 }
414 else {
415 return writeJSONEscapeChar(ch);
416 }
417 }
418}
419
420// Write out the contents of the string str as a JSON string, escaping
421// characters as appropriate.
422uint32_t TJSONProtocol::writeJSONString(const std::string &str) {
423 uint32_t result = context_->write(*trans_);
424 result += 2; // For quotes
425 trans_->write(&kJSONStringDelimiter, 1);
426 std::string::const_iterator iter(str.begin());
427 std::string::const_iterator end(str.end());
428 while (iter != end) {
429 result += writeJSONChar(*iter++);
430 }
431 trans_->write(&kJSONStringDelimiter, 1);
432 return result;
433}
434
435// Write out the contents of the string as JSON string, base64-encoding
436// the string's contents, and escaping as appropriate
437uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) {
438 uint32_t result = context_->write(*trans_);
439 result += 2; // For quotes
440 trans_->write(&kJSONStringDelimiter, 1);
441 uint8_t b[4];
442 const uint8_t *bytes = (const uint8_t *)str.c_str();
443 uint32_t len = str.length();
444 while (len >= 3) {
445 // Encode 3 bytes at a time
446 base64_encode(bytes, 3, b);
447 trans_->write(b, 4);
448 result += 4;
449 bytes += 3;
450 len -=3;
451 }
452 if (len) { // Handle remainder
453 base64_encode(bytes, len, b);
454 trans_->write(b, len + 1);
455 result += len + 1;
456 }
457 trans_->write(&kJSONStringDelimiter, 1);
458 return result;
459}
460
461// Convert the given integer type to a JSON number, or a string
462// if the context requires it (eg: key in a map pair).
463template <typename NumberType>
464uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
465 uint32_t result = context_->write(*trans_);
466 std::string val(boost::lexical_cast<std::string>(num));
467 bool escapeNum = context_->escapeNum();
468 if (escapeNum) {
469 trans_->write(&kJSONStringDelimiter, 1);
470 result += 1;
471 }
472 trans_->write((const uint8_t *)val.c_str(), val.length());
473 result += val.length();
474 if (escapeNum) {
475 trans_->write(&kJSONStringDelimiter, 1);
476 result += 1;
477 }
478 return result;
479}
480
481// Convert the given double to a JSON string, which is either the number,
482// "NaN" or "Infinity" or "-Infinity".
483uint32_t TJSONProtocol::writeJSONDouble(double num) {
484 uint32_t result = context_->write(*trans_);
485 std::string val(boost::lexical_cast<std::string>(num));
486
487 // Normalize output of boost::lexical_cast for NaNs and Infinities
488 bool special = false;
489 switch (val[0]) {
490 case 'N':
491 case 'n':
492 val = kThriftNan;
493 special = true;
494 break;
495 case 'I':
496 case 'i':
497 val = kThriftInfinity;
498 special = true;
499 break;
500 case '-':
501 if ((val[1] == 'I') || (val[1] == 'i')) {
502 val = kThriftNegativeInfinity;
503 special = true;
504 }
505 break;
506 }
507
508 bool escapeNum = special || context_->escapeNum();
509 if (escapeNum) {
510 trans_->write(&kJSONStringDelimiter, 1);
511 result += 1;
512 }
513 trans_->write((const uint8_t *)val.c_str(), val.length());
514 result += val.length();
515 if (escapeNum) {
516 trans_->write(&kJSONStringDelimiter, 1);
517 result += 1;
518 }
519 return result;
520}
521
522uint32_t TJSONProtocol::writeJSONObjectStart() {
523 uint32_t result = context_->write(*trans_);
524 trans_->write(&kJSONObjectStart, 1);
525 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
526 return result + 1;
527}
528
529uint32_t TJSONProtocol::writeJSONObjectEnd() {
530 popContext();
531 trans_->write(&kJSONObjectEnd, 1);
532 return 1;
533}
534
535uint32_t TJSONProtocol::writeJSONArrayStart() {
536 uint32_t result = context_->write(*trans_);
537 trans_->write(&kJSONArrayStart, 1);
538 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
539 return result + 1;
540}
541
542uint32_t TJSONProtocol::writeJSONArrayEnd() {
543 popContext();
544 trans_->write(&kJSONArrayEnd, 1);
545 return 1;
546}
547
548uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
549 const TMessageType messageType,
550 const int32_t seqid) {
551 uint32_t result = writeJSONArrayStart();
552 result += writeJSONInteger(kThriftVersion1);
553 result += writeJSONString(name);
554 result += writeJSONInteger(messageType);
555 result += writeJSONInteger(seqid);
556 return result;
557}
558
559uint32_t TJSONProtocol::writeMessageEnd() {
560 return writeJSONArrayEnd();
561}
562
David Reiss64120002008-04-29 23:12:24 +0000563uint32_t TJSONProtocol::writeStructBegin(const char* name) {
David Reissdb0ea152008-02-18 01:49:37 +0000564 return writeJSONObjectStart();
565}
566
567uint32_t TJSONProtocol::writeStructEnd() {
568 return writeJSONObjectEnd();
569}
570
David Reiss64120002008-04-29 23:12:24 +0000571uint32_t TJSONProtocol::writeFieldBegin(const char* name,
David Reissdb0ea152008-02-18 01:49:37 +0000572 const TType fieldType,
573 const int16_t fieldId) {
574 uint32_t result = writeJSONInteger(fieldId);
575 result += writeJSONObjectStart();
576 result += writeJSONString(getTypeNameForTypeID(fieldType));
577 return result;
578}
579
580uint32_t TJSONProtocol::writeFieldEnd() {
581 return writeJSONObjectEnd();
582}
583
584uint32_t TJSONProtocol::writeFieldStop() {
585 return 0;
586}
587
588uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
589 const TType valType,
590 const uint32_t size) {
591 uint32_t result = writeJSONArrayStart();
592 result += writeJSONString(getTypeNameForTypeID(keyType));
593 result += writeJSONString(getTypeNameForTypeID(valType));
594 result += writeJSONInteger((int64_t)size);
595 result += writeJSONObjectStart();
596 return result;
597}
598
599uint32_t TJSONProtocol::writeMapEnd() {
600 return writeJSONObjectEnd() + writeJSONArrayEnd();
601}
602
603uint32_t TJSONProtocol::writeListBegin(const TType elemType,
604 const uint32_t size) {
605 uint32_t result = writeJSONArrayStart();
606 result += writeJSONString(getTypeNameForTypeID(elemType));
607 result += writeJSONInteger((int64_t)size);
608 return result;
609}
610
611uint32_t TJSONProtocol::writeListEnd() {
612 return writeJSONArrayEnd();
613}
614
615uint32_t TJSONProtocol::writeSetBegin(const TType elemType,
616 const uint32_t size) {
617 uint32_t result = writeJSONArrayStart();
618 result += writeJSONString(getTypeNameForTypeID(elemType));
619 result += writeJSONInteger((int64_t)size);
620 return result;
621}
622
623uint32_t TJSONProtocol::writeSetEnd() {
624 return writeJSONArrayEnd();
625}
626
627uint32_t TJSONProtocol::writeBool(const bool value) {
628 return writeJSONInteger(value);
629}
630
631uint32_t TJSONProtocol::writeByte(const int8_t byte) {
David Reiss1e62ab42008-02-21 22:37:45 +0000632 // writeByte() must be handled specially becuase boost::lexical cast sees
David Reissdb0ea152008-02-18 01:49:37 +0000633 // int8_t as a text type instead of an integer type
634 return writeJSONInteger((int16_t)byte);
635}
636
637uint32_t TJSONProtocol::writeI16(const int16_t i16) {
638 return writeJSONInteger(i16);
639}
640
641uint32_t TJSONProtocol::writeI32(const int32_t i32) {
642 return writeJSONInteger(i32);
643}
644
645uint32_t TJSONProtocol::writeI64(const int64_t i64) {
646 return writeJSONInteger(i64);
647}
648
649uint32_t TJSONProtocol::writeDouble(const double dub) {
650 return writeJSONDouble(dub);
651}
652
653uint32_t TJSONProtocol::writeString(const std::string& str) {
654 return writeJSONString(str);
655}
656
657uint32_t TJSONProtocol::writeBinary(const std::string& str) {
658 return writeJSONBase64(str);
659}
660
661 /**
662 * Reading functions
663 */
664
David Reiss1e62ab42008-02-21 22:37:45 +0000665// Reads 1 byte and verifies that it matches ch.
David Reissdb0ea152008-02-18 01:49:37 +0000666uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
David Reiss1e62ab42008-02-21 22:37:45 +0000667 return readSyntaxChar(reader_, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000668}
669
670// Decodes the four hex parts of a JSON escaped string character and returns
671// the character via out. The first two characters must be "00".
672uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) {
673 uint8_t b[2];
674 readJSONSyntaxChar(kJSONZeroChar);
675 readJSONSyntaxChar(kJSONZeroChar);
David Reiss1e62ab42008-02-21 22:37:45 +0000676 b[0] = reader_.read();
677 b[1] = reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000678 *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
679 return 4;
680}
681
682// Decodes a JSON string, including unescaping, and returns the string via str
683uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
David Reiss1e62ab42008-02-21 22:37:45 +0000684 uint32_t result = (skipContext ? 0 : context_->read(reader_));
David Reissdb0ea152008-02-18 01:49:37 +0000685 result += readJSONSyntaxChar(kJSONStringDelimiter);
David Reiss1e62ab42008-02-21 22:37:45 +0000686 uint8_t ch;
David Reiss91630732008-02-28 21:11:39 +0000687 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000688 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000689 ch = reader_.read();
690 ++result;
691 if (ch == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000692 break;
693 }
David Reiss1e62ab42008-02-21 22:37:45 +0000694 if (ch == kJSONBackslash) {
695 ch = reader_.read();
696 ++result;
697 if (ch == kJSONEscapeChar) {
698 result += readJSONEscapeChar(&ch);
David Reissdb0ea152008-02-18 01:49:37 +0000699 }
700 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000701 size_t pos = kEscapeChars.find(ch);
David Reissdb0ea152008-02-18 01:49:37 +0000702 if (pos == std::string::npos) {
703 throw TProtocolException(TProtocolException::INVALID_DATA,
704 "Expected control char, got '" +
David Reiss1e62ab42008-02-21 22:37:45 +0000705 std::string((const char *)&ch, 1) + "'.");
David Reissdb0ea152008-02-18 01:49:37 +0000706 }
David Reiss1e62ab42008-02-21 22:37:45 +0000707 ch = kEscapeCharVals[pos];
David Reissdb0ea152008-02-18 01:49:37 +0000708 }
709 }
David Reiss1e62ab42008-02-21 22:37:45 +0000710 str += ch;
David Reissdb0ea152008-02-18 01:49:37 +0000711 }
712 return result;
713}
714
715// Reads a block of base64 characters, decoding it, and returns via str
716uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
717 std::string tmp;
718 uint32_t result = readJSONString(tmp);
719 uint8_t *b = (uint8_t *)tmp.c_str();
720 uint32_t len = tmp.length();
David Reiss91630732008-02-28 21:11:39 +0000721 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000722 while (len >= 4) {
723 base64_decode(b, 4);
724 str.append((const char *)b, 3);
725 b += 4;
726 len -= 4;
727 }
728 // Don't decode if we hit the end or got a single leftover byte (invalid
729 // base64 but legal for skip of regular string type)
730 if (len > 1) {
731 base64_decode(b, len);
732 str.append((const char *)b, len - 1);
733 }
734 return result;
735}
736
737// Reads a sequence of characters, stopping at the first one that is not
738// a valid JSON numeric character.
739uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
740 uint32_t result = 0;
David Reiss91630732008-02-28 21:11:39 +0000741 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000742 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000743 uint8_t ch = reader_.peek();
David Reissdb0ea152008-02-18 01:49:37 +0000744 if (!isJSONNumeric(ch)) {
745 break;
746 }
David Reiss1e62ab42008-02-21 22:37:45 +0000747 reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000748 str += ch;
749 ++result;
750 }
751 return result;
752}
753
754// Reads a sequence of characters and assembles them into a number,
755// returning them via num
756template <typename NumberType>
757uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000758 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000759 if (context_->escapeNum()) {
760 result += readJSONSyntaxChar(kJSONStringDelimiter);
761 }
762 std::string str;
763 result += readJSONNumericChars(str);
764 try {
765 num = boost::lexical_cast<NumberType>(str);
766 }
767 catch (boost::bad_lexical_cast e) {
768 throw new TProtocolException(TProtocolException::INVALID_DATA,
769 "Expected numeric value; got \"" + str +
770 "\"");
771 }
772 if (context_->escapeNum()) {
773 result += readJSONSyntaxChar(kJSONStringDelimiter);
774 }
775 return result;
776}
777
778// Reads a JSON number or string and interprets it as a double.
779uint32_t TJSONProtocol::readJSONDouble(double &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000780 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000781 std::string str;
David Reiss1e62ab42008-02-21 22:37:45 +0000782 if (reader_.peek() == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000783 result += readJSONString(str, true);
784 // Check for NaN, Infinity and -Infinity
785 if (str == kThriftNan) {
786 num = HUGE_VAL/HUGE_VAL; // generates NaN
787 }
788 else if (str == kThriftInfinity) {
789 num = HUGE_VAL;
790 }
791 else if (str == kThriftNegativeInfinity) {
792 num = -HUGE_VAL;
793 }
794 else {
795 if (!context_->escapeNum()) {
796 // Throw exception -- we should not be in a string in this case
797 throw new TProtocolException(TProtocolException::INVALID_DATA,
798 "Numeric data unexpectedly quoted");
799 }
800 try {
801 num = boost::lexical_cast<double>(str);
802 }
803 catch (boost::bad_lexical_cast e) {
804 throw new TProtocolException(TProtocolException::INVALID_DATA,
805 "Expected numeric value; got \"" + str +
806 "\"");
807 }
808 }
809 }
810 else {
811 if (context_->escapeNum()) {
812 // This will throw - we should have had a quote if escapeNum == true
813 readJSONSyntaxChar(kJSONStringDelimiter);
814 }
815 result += readJSONNumericChars(str);
816 try {
817 num = boost::lexical_cast<double>(str);
818 }
819 catch (boost::bad_lexical_cast e) {
820 throw new TProtocolException(TProtocolException::INVALID_DATA,
821 "Expected numeric value; got \"" + str +
822 "\"");
823 }
824 }
825 return result;
826}
827
828uint32_t TJSONProtocol::readJSONObjectStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000829 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000830 result += readJSONSyntaxChar(kJSONObjectStart);
831 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
832 return result;
833}
834
835uint32_t TJSONProtocol::readJSONObjectEnd() {
836 uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
837 popContext();
838 return result;
839}
840
841uint32_t TJSONProtocol::readJSONArrayStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000842 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000843 result += readJSONSyntaxChar(kJSONArrayStart);
844 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
845 return result;
846}
847
848uint32_t TJSONProtocol::readJSONArrayEnd() {
849 uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
850 popContext();
851 return result;
852}
853
854uint32_t TJSONProtocol::readMessageBegin(std::string& name,
855 TMessageType& messageType,
856 int32_t& seqid) {
857 uint32_t result = readJSONArrayStart();
David Reissdb0ea152008-02-18 01:49:37 +0000858 uint64_t tmpVal = 0;
859 result += readJSONInteger(tmpVal);
860 if (tmpVal != kThriftVersion1) {
861 throw TProtocolException(TProtocolException::BAD_VERSION,
862 "Message contained bad version.");
863 }
864 result += readJSONString(name);
865 result += readJSONInteger(tmpVal);
866 messageType = (TMessageType)tmpVal;
867 result += readJSONInteger(tmpVal);
868 seqid = tmpVal;
869 return result;
870}
871
872uint32_t TJSONProtocol::readMessageEnd() {
873 return readJSONArrayEnd();
874}
875
876uint32_t TJSONProtocol::readStructBegin(std::string& name) {
877 return readJSONObjectStart();
878}
879
880uint32_t TJSONProtocol::readStructEnd() {
881 return readJSONObjectEnd();
882}
883
884uint32_t TJSONProtocol::readFieldBegin(std::string& name,
885 TType& fieldType,
886 int16_t& fieldId) {
David Reissdb0ea152008-02-18 01:49:37 +0000887 uint32_t result = 0;
David Reiss1e62ab42008-02-21 22:37:45 +0000888 // Check if we hit the end of the list
889 uint8_t ch = reader_.peek();
890 if (ch == kJSONObjectEnd) {
T Jake Lucianib5e62212009-01-31 22:36:20 +0000891 fieldType = apache::thrift::protocol::T_STOP;
David Reissdb0ea152008-02-18 01:49:37 +0000892 }
893 else {
894 uint64_t tmpVal = 0;
895 std::string tmpStr;
896 result += readJSONInteger(tmpVal);
897 fieldId = tmpVal;
898 result += readJSONObjectStart();
899 result += readJSONString(tmpStr);
900 fieldType = getTypeIDForTypeName(tmpStr);
901 }
902 return result;
903}
904
905uint32_t TJSONProtocol::readFieldEnd() {
906 return readJSONObjectEnd();
907}
908
909uint32_t TJSONProtocol::readMapBegin(TType& keyType,
910 TType& valType,
911 uint32_t& size) {
912 uint64_t tmpVal = 0;
913 std::string tmpStr;
914 uint32_t result = readJSONArrayStart();
915 result += readJSONString(tmpStr);
916 keyType = getTypeIDForTypeName(tmpStr);
917 result += readJSONString(tmpStr);
918 valType = getTypeIDForTypeName(tmpStr);
919 result += readJSONInteger(tmpVal);
920 size = tmpVal;
921 result += readJSONObjectStart();
922 return result;
923}
924
925uint32_t TJSONProtocol::readMapEnd() {
926 return readJSONObjectEnd() + readJSONArrayEnd();
927}
928
929uint32_t TJSONProtocol::readListBegin(TType& elemType,
930 uint32_t& size) {
931 uint64_t tmpVal = 0;
932 std::string tmpStr;
933 uint32_t result = readJSONArrayStart();
934 result += readJSONString(tmpStr);
935 elemType = getTypeIDForTypeName(tmpStr);
936 result += readJSONInteger(tmpVal);
937 size = tmpVal;
938 return result;
939}
940
941uint32_t TJSONProtocol::readListEnd() {
942 return readJSONArrayEnd();
943}
944
945uint32_t TJSONProtocol::readSetBegin(TType& elemType,
946 uint32_t& size) {
947 uint64_t tmpVal = 0;
948 std::string tmpStr;
949 uint32_t result = readJSONArrayStart();
950 result += readJSONString(tmpStr);
951 elemType = getTypeIDForTypeName(tmpStr);
952 result += readJSONInteger(tmpVal);
953 size = tmpVal;
954 return result;
955}
956
957uint32_t TJSONProtocol::readSetEnd() {
958 return readJSONArrayEnd();
959}
960
961uint32_t TJSONProtocol::readBool(bool& value) {
962 return readJSONInteger(value);
963}
964
965// readByte() must be handled properly becuase boost::lexical cast sees int8_t
966// as a text type instead of an integer type
967uint32_t TJSONProtocol::readByte(int8_t& byte) {
968 int16_t tmp = (int16_t) byte;
969 uint32_t result = readJSONInteger(tmp);
970 assert(tmp < 256);
971 byte = (int8_t)tmp;
972 return result;
973}
974
975uint32_t TJSONProtocol::readI16(int16_t& i16) {
976 return readJSONInteger(i16);
977}
978
979uint32_t TJSONProtocol::readI32(int32_t& i32) {
980 return readJSONInteger(i32);
981}
982
983uint32_t TJSONProtocol::readI64(int64_t& i64) {
984 return readJSONInteger(i64);
985}
986
987uint32_t TJSONProtocol::readDouble(double& dub) {
988 return readJSONDouble(dub);
989}
990
991uint32_t TJSONProtocol::readString(std::string &str) {
992 return readJSONString(str);
993}
994
995uint32_t TJSONProtocol::readBinary(std::string &str) {
996 return readJSONBase64(str);
997}
998
T Jake Lucianib5e62212009-01-31 22:36:20 +0000999}}} // apache::thrift::protocol