blob: d038df4ac1195f3e8ac4c4925fbe1576eb4ecaaf [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) {
Roger Meier3b771a12010-11-17 22:11:26 +0000258 (void) trans;
David Reissdb0ea152008-02-18 01:49:37 +0000259 return 0;
260 };
261
262 /**
263 * Read context data from the transport. Default is to do nothing.
264 */
David Reiss1e62ab42008-02-21 22:37:45 +0000265 virtual uint32_t read(TJSONProtocol::LookaheadReader &reader) {
Roger Meier3b771a12010-11-17 22:11:26 +0000266 (void) reader;
David Reissdb0ea152008-02-18 01:49:37 +0000267 return 0;
268 };
269
270 /**
271 * Return true if numbers need to be escaped as strings in this context.
272 * Default behavior is to return false.
273 */
274 virtual bool escapeNum() {
275 return false;
276 }
277};
278
279// Context class for object member key-value pairs
280class JSONPairContext : public TJSONContext {
281
282public:
283
284 JSONPairContext() :
285 first_(true),
286 colon_(true) {
287 }
288
289 uint32_t write(TTransport &trans) {
290 if (first_) {
291 first_ = false;
292 colon_ = true;
293 return 0;
294 }
295 else {
296 trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1);
297 colon_ = !colon_;
298 return 1;
299 }
300 }
301
David Reiss1e62ab42008-02-21 22:37:45 +0000302 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000303 if (first_) {
304 first_ = false;
305 colon_ = true;
306 return 0;
307 }
308 else {
309 uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator);
310 colon_ = !colon_;
David Reiss1e62ab42008-02-21 22:37:45 +0000311 return readSyntaxChar(reader, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000312 }
313 }
314
315 // Numbers must be turned into strings if they are the key part of a pair
316 virtual bool escapeNum() {
317 return colon_;
318 }
319
320 private:
321
322 bool first_;
323 bool colon_;
324};
325
326// Context class for lists
327class JSONListContext : public TJSONContext {
328
329public:
330
331 JSONListContext() :
332 first_(true) {
333 }
334
335 uint32_t write(TTransport &trans) {
336 if (first_) {
337 first_ = false;
338 return 0;
339 }
340 else {
341 trans.write(&kJSONElemSeparator, 1);
342 return 1;
343 }
344 }
345
David Reiss1e62ab42008-02-21 22:37:45 +0000346 uint32_t read(TJSONProtocol::LookaheadReader &reader) {
David Reissdb0ea152008-02-18 01:49:37 +0000347 if (first_) {
348 first_ = false;
349 return 0;
350 }
351 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000352 return readSyntaxChar(reader, kJSONElemSeparator);
David Reissdb0ea152008-02-18 01:49:37 +0000353 }
354 }
355
356 private:
357 bool first_;
358};
359
360
361TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) :
David Reiss6806fb82010-10-06 17:09:52 +0000362 TVirtualProtocol<TJSONProtocol>(ptrans),
David Reisse71115b2010-10-06 17:09:56 +0000363 trans_(ptrans.get()),
David Reiss1e62ab42008-02-21 22:37:45 +0000364 context_(new TJSONContext()),
365 reader_(*ptrans) {
David Reissdb0ea152008-02-18 01:49:37 +0000366}
367
368TJSONProtocol::~TJSONProtocol() {}
369
370void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) {
371 contexts_.push(context_);
372 context_ = c;
373}
374
375void TJSONProtocol::popContext() {
376 context_ = contexts_.top();
377 contexts_.pop();
378}
379
380// Write the character ch as a JSON escape sequence ("\u00xx")
381uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) {
382 trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(),
383 kJSONEscapePrefix.length());
384 uint8_t outCh = hexChar(ch >> 4);
385 trans_->write(&outCh, 1);
386 outCh = hexChar(ch);
387 trans_->write(&outCh, 1);
388 return 6;
389}
390
391// Write the character ch as part of a JSON string, escaping as appropriate.
392uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) {
393 if (ch >= 0x30) {
394 if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\'
395 trans_->write(&kJSONBackslash, 1);
396 trans_->write(&kJSONBackslash, 1);
397 return 2;
398 }
399 else {
400 trans_->write(&ch, 1);
401 return 1;
402 }
403 }
404 else {
405 uint8_t outCh = kJSONCharTable[ch];
406 // Check if regular character, backslash escaped, or JSON escaped
407 if (outCh == 1) {
408 trans_->write(&ch, 1);
409 return 1;
410 }
411 else if (outCh > 1) {
412 trans_->write(&kJSONBackslash, 1);
413 trans_->write(&outCh, 1);
414 return 2;
415 }
416 else {
417 return writeJSONEscapeChar(ch);
418 }
419 }
420}
421
422// Write out the contents of the string str as a JSON string, escaping
423// characters as appropriate.
424uint32_t TJSONProtocol::writeJSONString(const std::string &str) {
425 uint32_t result = context_->write(*trans_);
426 result += 2; // For quotes
427 trans_->write(&kJSONStringDelimiter, 1);
428 std::string::const_iterator iter(str.begin());
429 std::string::const_iterator end(str.end());
430 while (iter != end) {
431 result += writeJSONChar(*iter++);
432 }
433 trans_->write(&kJSONStringDelimiter, 1);
434 return result;
435}
436
437// Write out the contents of the string as JSON string, base64-encoding
438// the string's contents, and escaping as appropriate
439uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) {
440 uint32_t result = context_->write(*trans_);
441 result += 2; // For quotes
442 trans_->write(&kJSONStringDelimiter, 1);
443 uint8_t b[4];
444 const uint8_t *bytes = (const uint8_t *)str.c_str();
445 uint32_t len = str.length();
446 while (len >= 3) {
447 // Encode 3 bytes at a time
448 base64_encode(bytes, 3, b);
449 trans_->write(b, 4);
450 result += 4;
451 bytes += 3;
452 len -=3;
453 }
454 if (len) { // Handle remainder
455 base64_encode(bytes, len, b);
456 trans_->write(b, len + 1);
457 result += len + 1;
458 }
459 trans_->write(&kJSONStringDelimiter, 1);
460 return result;
461}
462
463// Convert the given integer type to a JSON number, or a string
464// if the context requires it (eg: key in a map pair).
465template <typename NumberType>
466uint32_t TJSONProtocol::writeJSONInteger(NumberType num) {
467 uint32_t result = context_->write(*trans_);
468 std::string val(boost::lexical_cast<std::string>(num));
469 bool escapeNum = context_->escapeNum();
470 if (escapeNum) {
471 trans_->write(&kJSONStringDelimiter, 1);
472 result += 1;
473 }
474 trans_->write((const uint8_t *)val.c_str(), val.length());
475 result += val.length();
476 if (escapeNum) {
477 trans_->write(&kJSONStringDelimiter, 1);
478 result += 1;
479 }
480 return result;
481}
482
483// Convert the given double to a JSON string, which is either the number,
484// "NaN" or "Infinity" or "-Infinity".
485uint32_t TJSONProtocol::writeJSONDouble(double num) {
486 uint32_t result = context_->write(*trans_);
487 std::string val(boost::lexical_cast<std::string>(num));
488
489 // Normalize output of boost::lexical_cast for NaNs and Infinities
490 bool special = false;
491 switch (val[0]) {
492 case 'N':
493 case 'n':
494 val = kThriftNan;
495 special = true;
496 break;
497 case 'I':
498 case 'i':
499 val = kThriftInfinity;
500 special = true;
501 break;
502 case '-':
503 if ((val[1] == 'I') || (val[1] == 'i')) {
504 val = kThriftNegativeInfinity;
505 special = true;
506 }
507 break;
508 }
509
510 bool escapeNum = special || context_->escapeNum();
511 if (escapeNum) {
512 trans_->write(&kJSONStringDelimiter, 1);
513 result += 1;
514 }
515 trans_->write((const uint8_t *)val.c_str(), val.length());
516 result += val.length();
517 if (escapeNum) {
518 trans_->write(&kJSONStringDelimiter, 1);
519 result += 1;
520 }
521 return result;
522}
523
524uint32_t TJSONProtocol::writeJSONObjectStart() {
525 uint32_t result = context_->write(*trans_);
526 trans_->write(&kJSONObjectStart, 1);
527 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
528 return result + 1;
529}
530
531uint32_t TJSONProtocol::writeJSONObjectEnd() {
532 popContext();
533 trans_->write(&kJSONObjectEnd, 1);
534 return 1;
535}
536
537uint32_t TJSONProtocol::writeJSONArrayStart() {
538 uint32_t result = context_->write(*trans_);
539 trans_->write(&kJSONArrayStart, 1);
540 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
541 return result + 1;
542}
543
544uint32_t TJSONProtocol::writeJSONArrayEnd() {
545 popContext();
546 trans_->write(&kJSONArrayEnd, 1);
547 return 1;
548}
549
550uint32_t TJSONProtocol::writeMessageBegin(const std::string& name,
551 const TMessageType messageType,
552 const int32_t seqid) {
553 uint32_t result = writeJSONArrayStart();
554 result += writeJSONInteger(kThriftVersion1);
555 result += writeJSONString(name);
556 result += writeJSONInteger(messageType);
557 result += writeJSONInteger(seqid);
558 return result;
559}
560
561uint32_t TJSONProtocol::writeMessageEnd() {
562 return writeJSONArrayEnd();
563}
564
David Reiss64120002008-04-29 23:12:24 +0000565uint32_t TJSONProtocol::writeStructBegin(const char* name) {
Roger Meier3b771a12010-11-17 22:11:26 +0000566 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000567 return writeJSONObjectStart();
568}
569
570uint32_t TJSONProtocol::writeStructEnd() {
571 return writeJSONObjectEnd();
572}
573
David Reiss64120002008-04-29 23:12:24 +0000574uint32_t TJSONProtocol::writeFieldBegin(const char* name,
David Reissdb0ea152008-02-18 01:49:37 +0000575 const TType fieldType,
576 const int16_t fieldId) {
Roger Meier3b771a12010-11-17 22:11:26 +0000577 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000578 uint32_t result = writeJSONInteger(fieldId);
579 result += writeJSONObjectStart();
580 result += writeJSONString(getTypeNameForTypeID(fieldType));
581 return result;
582}
583
584uint32_t TJSONProtocol::writeFieldEnd() {
585 return writeJSONObjectEnd();
586}
587
588uint32_t TJSONProtocol::writeFieldStop() {
589 return 0;
590}
591
592uint32_t TJSONProtocol::writeMapBegin(const TType keyType,
593 const TType valType,
594 const uint32_t size) {
595 uint32_t result = writeJSONArrayStart();
596 result += writeJSONString(getTypeNameForTypeID(keyType));
597 result += writeJSONString(getTypeNameForTypeID(valType));
598 result += writeJSONInteger((int64_t)size);
599 result += writeJSONObjectStart();
600 return result;
601}
602
603uint32_t TJSONProtocol::writeMapEnd() {
604 return writeJSONObjectEnd() + writeJSONArrayEnd();
605}
606
607uint32_t TJSONProtocol::writeListBegin(const TType elemType,
608 const uint32_t size) {
609 uint32_t result = writeJSONArrayStart();
610 result += writeJSONString(getTypeNameForTypeID(elemType));
611 result += writeJSONInteger((int64_t)size);
612 return result;
613}
614
615uint32_t TJSONProtocol::writeListEnd() {
616 return writeJSONArrayEnd();
617}
618
619uint32_t TJSONProtocol::writeSetBegin(const TType elemType,
620 const uint32_t size) {
621 uint32_t result = writeJSONArrayStart();
622 result += writeJSONString(getTypeNameForTypeID(elemType));
623 result += writeJSONInteger((int64_t)size);
624 return result;
625}
626
627uint32_t TJSONProtocol::writeSetEnd() {
628 return writeJSONArrayEnd();
629}
630
631uint32_t TJSONProtocol::writeBool(const bool value) {
632 return writeJSONInteger(value);
633}
634
635uint32_t TJSONProtocol::writeByte(const int8_t byte) {
David Reiss1e62ab42008-02-21 22:37:45 +0000636 // writeByte() must be handled specially becuase boost::lexical cast sees
David Reissdb0ea152008-02-18 01:49:37 +0000637 // int8_t as a text type instead of an integer type
638 return writeJSONInteger((int16_t)byte);
639}
640
641uint32_t TJSONProtocol::writeI16(const int16_t i16) {
642 return writeJSONInteger(i16);
643}
644
645uint32_t TJSONProtocol::writeI32(const int32_t i32) {
646 return writeJSONInteger(i32);
647}
648
649uint32_t TJSONProtocol::writeI64(const int64_t i64) {
650 return writeJSONInteger(i64);
651}
652
653uint32_t TJSONProtocol::writeDouble(const double dub) {
654 return writeJSONDouble(dub);
655}
656
657uint32_t TJSONProtocol::writeString(const std::string& str) {
658 return writeJSONString(str);
659}
660
661uint32_t TJSONProtocol::writeBinary(const std::string& str) {
662 return writeJSONBase64(str);
663}
664
665 /**
666 * Reading functions
667 */
668
David Reiss1e62ab42008-02-21 22:37:45 +0000669// Reads 1 byte and verifies that it matches ch.
David Reissdb0ea152008-02-18 01:49:37 +0000670uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) {
David Reiss1e62ab42008-02-21 22:37:45 +0000671 return readSyntaxChar(reader_, ch);
David Reissdb0ea152008-02-18 01:49:37 +0000672}
673
674// Decodes the four hex parts of a JSON escaped string character and returns
675// the character via out. The first two characters must be "00".
676uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) {
677 uint8_t b[2];
678 readJSONSyntaxChar(kJSONZeroChar);
679 readJSONSyntaxChar(kJSONZeroChar);
David Reiss1e62ab42008-02-21 22:37:45 +0000680 b[0] = reader_.read();
681 b[1] = reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000682 *out = (hexVal(b[0]) << 4) + hexVal(b[1]);
683 return 4;
684}
685
686// Decodes a JSON string, including unescaping, and returns the string via str
687uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) {
David Reiss1e62ab42008-02-21 22:37:45 +0000688 uint32_t result = (skipContext ? 0 : context_->read(reader_));
David Reissdb0ea152008-02-18 01:49:37 +0000689 result += readJSONSyntaxChar(kJSONStringDelimiter);
David Reiss1e62ab42008-02-21 22:37:45 +0000690 uint8_t ch;
David Reiss91630732008-02-28 21:11:39 +0000691 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000692 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000693 ch = reader_.read();
694 ++result;
695 if (ch == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000696 break;
697 }
David Reiss1e62ab42008-02-21 22:37:45 +0000698 if (ch == kJSONBackslash) {
699 ch = reader_.read();
700 ++result;
701 if (ch == kJSONEscapeChar) {
702 result += readJSONEscapeChar(&ch);
David Reissdb0ea152008-02-18 01:49:37 +0000703 }
704 else {
David Reiss1e62ab42008-02-21 22:37:45 +0000705 size_t pos = kEscapeChars.find(ch);
David Reissdb0ea152008-02-18 01:49:37 +0000706 if (pos == std::string::npos) {
707 throw TProtocolException(TProtocolException::INVALID_DATA,
708 "Expected control char, got '" +
David Reiss1e62ab42008-02-21 22:37:45 +0000709 std::string((const char *)&ch, 1) + "'.");
David Reissdb0ea152008-02-18 01:49:37 +0000710 }
David Reiss1e62ab42008-02-21 22:37:45 +0000711 ch = kEscapeCharVals[pos];
David Reissdb0ea152008-02-18 01:49:37 +0000712 }
713 }
David Reiss1e62ab42008-02-21 22:37:45 +0000714 str += ch;
David Reissdb0ea152008-02-18 01:49:37 +0000715 }
716 return result;
717}
718
719// Reads a block of base64 characters, decoding it, and returns via str
720uint32_t TJSONProtocol::readJSONBase64(std::string &str) {
721 std::string tmp;
722 uint32_t result = readJSONString(tmp);
723 uint8_t *b = (uint8_t *)tmp.c_str();
724 uint32_t len = tmp.length();
David Reiss91630732008-02-28 21:11:39 +0000725 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000726 while (len >= 4) {
727 base64_decode(b, 4);
728 str.append((const char *)b, 3);
729 b += 4;
730 len -= 4;
731 }
732 // Don't decode if we hit the end or got a single leftover byte (invalid
733 // base64 but legal for skip of regular string type)
734 if (len > 1) {
735 base64_decode(b, len);
736 str.append((const char *)b, len - 1);
737 }
738 return result;
739}
740
741// Reads a sequence of characters, stopping at the first one that is not
742// a valid JSON numeric character.
743uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) {
744 uint32_t result = 0;
David Reiss91630732008-02-28 21:11:39 +0000745 str.clear();
David Reissdb0ea152008-02-18 01:49:37 +0000746 while (true) {
David Reiss1e62ab42008-02-21 22:37:45 +0000747 uint8_t ch = reader_.peek();
David Reissdb0ea152008-02-18 01:49:37 +0000748 if (!isJSONNumeric(ch)) {
749 break;
750 }
David Reiss1e62ab42008-02-21 22:37:45 +0000751 reader_.read();
David Reissdb0ea152008-02-18 01:49:37 +0000752 str += ch;
753 ++result;
754 }
755 return result;
756}
757
758// Reads a sequence of characters and assembles them into a number,
759// returning them via num
760template <typename NumberType>
761uint32_t TJSONProtocol::readJSONInteger(NumberType &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000762 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000763 if (context_->escapeNum()) {
764 result += readJSONSyntaxChar(kJSONStringDelimiter);
765 }
766 std::string str;
767 result += readJSONNumericChars(str);
768 try {
769 num = boost::lexical_cast<NumberType>(str);
770 }
771 catch (boost::bad_lexical_cast e) {
772 throw new TProtocolException(TProtocolException::INVALID_DATA,
773 "Expected numeric value; got \"" + str +
774 "\"");
775 }
776 if (context_->escapeNum()) {
777 result += readJSONSyntaxChar(kJSONStringDelimiter);
778 }
779 return result;
780}
781
782// Reads a JSON number or string and interprets it as a double.
783uint32_t TJSONProtocol::readJSONDouble(double &num) {
David Reiss1e62ab42008-02-21 22:37:45 +0000784 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000785 std::string str;
David Reiss1e62ab42008-02-21 22:37:45 +0000786 if (reader_.peek() == kJSONStringDelimiter) {
David Reissdb0ea152008-02-18 01:49:37 +0000787 result += readJSONString(str, true);
788 // Check for NaN, Infinity and -Infinity
789 if (str == kThriftNan) {
790 num = HUGE_VAL/HUGE_VAL; // generates NaN
791 }
792 else if (str == kThriftInfinity) {
793 num = HUGE_VAL;
794 }
795 else if (str == kThriftNegativeInfinity) {
796 num = -HUGE_VAL;
797 }
798 else {
799 if (!context_->escapeNum()) {
800 // Throw exception -- we should not be in a string in this case
801 throw new TProtocolException(TProtocolException::INVALID_DATA,
802 "Numeric data unexpectedly quoted");
803 }
804 try {
805 num = boost::lexical_cast<double>(str);
806 }
807 catch (boost::bad_lexical_cast e) {
808 throw new TProtocolException(TProtocolException::INVALID_DATA,
809 "Expected numeric value; got \"" + str +
810 "\"");
811 }
812 }
813 }
814 else {
815 if (context_->escapeNum()) {
816 // This will throw - we should have had a quote if escapeNum == true
817 readJSONSyntaxChar(kJSONStringDelimiter);
818 }
819 result += readJSONNumericChars(str);
820 try {
821 num = boost::lexical_cast<double>(str);
822 }
823 catch (boost::bad_lexical_cast e) {
824 throw new TProtocolException(TProtocolException::INVALID_DATA,
825 "Expected numeric value; got \"" + str +
826 "\"");
827 }
828 }
829 return result;
830}
831
832uint32_t TJSONProtocol::readJSONObjectStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000833 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000834 result += readJSONSyntaxChar(kJSONObjectStart);
835 pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext()));
836 return result;
837}
838
839uint32_t TJSONProtocol::readJSONObjectEnd() {
840 uint32_t result = readJSONSyntaxChar(kJSONObjectEnd);
841 popContext();
842 return result;
843}
844
845uint32_t TJSONProtocol::readJSONArrayStart() {
David Reiss1e62ab42008-02-21 22:37:45 +0000846 uint32_t result = context_->read(reader_);
David Reissdb0ea152008-02-18 01:49:37 +0000847 result += readJSONSyntaxChar(kJSONArrayStart);
848 pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext()));
849 return result;
850}
851
852uint32_t TJSONProtocol::readJSONArrayEnd() {
853 uint32_t result = readJSONSyntaxChar(kJSONArrayEnd);
854 popContext();
855 return result;
856}
857
858uint32_t TJSONProtocol::readMessageBegin(std::string& name,
859 TMessageType& messageType,
860 int32_t& seqid) {
861 uint32_t result = readJSONArrayStart();
David Reissdb0ea152008-02-18 01:49:37 +0000862 uint64_t tmpVal = 0;
863 result += readJSONInteger(tmpVal);
864 if (tmpVal != kThriftVersion1) {
865 throw TProtocolException(TProtocolException::BAD_VERSION,
866 "Message contained bad version.");
867 }
868 result += readJSONString(name);
869 result += readJSONInteger(tmpVal);
870 messageType = (TMessageType)tmpVal;
871 result += readJSONInteger(tmpVal);
872 seqid = tmpVal;
873 return result;
874}
875
876uint32_t TJSONProtocol::readMessageEnd() {
877 return readJSONArrayEnd();
878}
879
880uint32_t TJSONProtocol::readStructBegin(std::string& name) {
Roger Meier3b771a12010-11-17 22:11:26 +0000881 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000882 return readJSONObjectStart();
883}
884
885uint32_t TJSONProtocol::readStructEnd() {
886 return readJSONObjectEnd();
887}
888
889uint32_t TJSONProtocol::readFieldBegin(std::string& name,
890 TType& fieldType,
891 int16_t& fieldId) {
Roger Meier3b771a12010-11-17 22:11:26 +0000892 (void) name;
David Reissdb0ea152008-02-18 01:49:37 +0000893 uint32_t result = 0;
David Reiss1e62ab42008-02-21 22:37:45 +0000894 // Check if we hit the end of the list
895 uint8_t ch = reader_.peek();
896 if (ch == kJSONObjectEnd) {
T Jake Lucianib5e62212009-01-31 22:36:20 +0000897 fieldType = apache::thrift::protocol::T_STOP;
David Reissdb0ea152008-02-18 01:49:37 +0000898 }
899 else {
900 uint64_t tmpVal = 0;
901 std::string tmpStr;
902 result += readJSONInteger(tmpVal);
903 fieldId = tmpVal;
904 result += readJSONObjectStart();
905 result += readJSONString(tmpStr);
906 fieldType = getTypeIDForTypeName(tmpStr);
907 }
908 return result;
909}
910
911uint32_t TJSONProtocol::readFieldEnd() {
912 return readJSONObjectEnd();
913}
914
915uint32_t TJSONProtocol::readMapBegin(TType& keyType,
916 TType& valType,
917 uint32_t& size) {
918 uint64_t tmpVal = 0;
919 std::string tmpStr;
920 uint32_t result = readJSONArrayStart();
921 result += readJSONString(tmpStr);
922 keyType = getTypeIDForTypeName(tmpStr);
923 result += readJSONString(tmpStr);
924 valType = getTypeIDForTypeName(tmpStr);
925 result += readJSONInteger(tmpVal);
926 size = tmpVal;
927 result += readJSONObjectStart();
928 return result;
929}
930
931uint32_t TJSONProtocol::readMapEnd() {
932 return readJSONObjectEnd() + readJSONArrayEnd();
933}
934
935uint32_t TJSONProtocol::readListBegin(TType& elemType,
936 uint32_t& size) {
937 uint64_t tmpVal = 0;
938 std::string tmpStr;
939 uint32_t result = readJSONArrayStart();
940 result += readJSONString(tmpStr);
941 elemType = getTypeIDForTypeName(tmpStr);
942 result += readJSONInteger(tmpVal);
943 size = tmpVal;
944 return result;
945}
946
947uint32_t TJSONProtocol::readListEnd() {
948 return readJSONArrayEnd();
949}
950
951uint32_t TJSONProtocol::readSetBegin(TType& elemType,
952 uint32_t& size) {
953 uint64_t tmpVal = 0;
954 std::string tmpStr;
955 uint32_t result = readJSONArrayStart();
956 result += readJSONString(tmpStr);
957 elemType = getTypeIDForTypeName(tmpStr);
958 result += readJSONInteger(tmpVal);
959 size = tmpVal;
960 return result;
961}
962
963uint32_t TJSONProtocol::readSetEnd() {
964 return readJSONArrayEnd();
965}
966
967uint32_t TJSONProtocol::readBool(bool& value) {
968 return readJSONInteger(value);
969}
970
971// readByte() must be handled properly becuase boost::lexical cast sees int8_t
972// as a text type instead of an integer type
973uint32_t TJSONProtocol::readByte(int8_t& byte) {
974 int16_t tmp = (int16_t) byte;
975 uint32_t result = readJSONInteger(tmp);
976 assert(tmp < 256);
977 byte = (int8_t)tmp;
978 return result;
979}
980
981uint32_t TJSONProtocol::readI16(int16_t& i16) {
982 return readJSONInteger(i16);
983}
984
985uint32_t TJSONProtocol::readI32(int32_t& i32) {
986 return readJSONInteger(i32);
987}
988
989uint32_t TJSONProtocol::readI64(int64_t& i64) {
990 return readJSONInteger(i64);
991}
992
993uint32_t TJSONProtocol::readDouble(double& dub) {
994 return readJSONDouble(dub);
995}
996
997uint32_t TJSONProtocol::readString(std::string &str) {
998 return readJSONString(str);
999}
1000
1001uint32_t TJSONProtocol::readBinary(std::string &str) {
1002 return readJSONBase64(str);
1003}
1004
T Jake Lucianib5e62212009-01-31 22:36:20 +00001005}}} // apache::thrift::protocol