| // Copyright (c) 2006- Facebook |
| // Distributed under the Thrift Software License |
| // |
| // See accompanying file LICENSE or visit the Thrift site at: |
| // http://developers.facebook.com/thrift/ |
| |
| #include "TJSONProtocol.h" |
| |
| #include <math.h> |
| #include "TBase64Utils.h" |
| #include <transport/TTransportException.h> |
| |
| using namespace facebook::thrift::transport; |
| |
| namespace facebook { namespace thrift { namespace protocol { |
| |
| |
| // Static data |
| |
| static const uint8_t kJSONObjectStart = '{'; |
| static const uint8_t kJSONObjectEnd = '}'; |
| static const uint8_t kJSONArrayStart = '['; |
| static const uint8_t kJSONArrayEnd = ']'; |
| static const uint8_t kJSONNewline = '\n'; |
| static const uint8_t kJSONPairSeparator = ':'; |
| static const uint8_t kJSONElemSeparator = ','; |
| static const uint8_t kJSONBackslash = '\\'; |
| static const uint8_t kJSONStringDelimiter = '"'; |
| static const uint8_t kJSONZeroChar = '0'; |
| static const uint8_t kJSONEscapeChar = 'u'; |
| |
| static const std::string kJSONEscapePrefix("\\u00"); |
| |
| static const uint8_t kThriftVersion1 = 1; |
| |
| static const std::string kThriftNan("NaN"); |
| static const std::string kThriftInfinity("Infinity"); |
| static const std::string kThriftNegativeInfinity("-Infinity"); |
| |
| static const std::string kTypeNameBool("tf"); |
| static const std::string kTypeNameByte("i8"); |
| static const std::string kTypeNameI16("i16"); |
| static const std::string kTypeNameI32("i32"); |
| static const std::string kTypeNameI64("i64"); |
| static const std::string kTypeNameDouble("dbl"); |
| static const std::string kTypeNameStruct("rec"); |
| static const std::string kTypeNameString("str"); |
| static const std::string kTypeNameMap("map"); |
| static const std::string kTypeNameList("lst"); |
| static const std::string kTypeNameSet("set"); |
| |
| static const std::string &getTypeNameForTypeID(TType typeID) { |
| switch (typeID) { |
| case T_BOOL: |
| return kTypeNameBool; |
| case T_BYTE: |
| return kTypeNameByte; |
| case T_I16: |
| return kTypeNameI16; |
| case T_I32: |
| return kTypeNameI32; |
| case T_I64: |
| return kTypeNameI64; |
| case T_DOUBLE: |
| return kTypeNameDouble; |
| case T_STRING: |
| return kTypeNameString; |
| case T_STRUCT: |
| return kTypeNameStruct; |
| case T_MAP: |
| return kTypeNameMap; |
| case T_SET: |
| return kTypeNameSet; |
| case T_LIST: |
| return kTypeNameList; |
| default: |
| throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, |
| "Unrecognized type"); |
| } |
| } |
| |
| static TType getTypeIDForTypeName(const std::string &name) { |
| TType result = T_STOP; // Sentinel value |
| if (name.length() > 0) { |
| switch (name[0]) { |
| case 'd': |
| result = T_DOUBLE; |
| break; |
| case 'i': |
| switch (name[1]) { |
| case '8': |
| result = T_BYTE; |
| break; |
| case '1': |
| result = T_I16; |
| break; |
| case '3': |
| result = T_I32; |
| break; |
| case '6': |
| result = T_I64; |
| break; |
| } |
| break; |
| case 'l': |
| result = T_LIST; |
| break; |
| case 'm': |
| result = T_MAP; |
| break; |
| case 'r': |
| result = T_STRUCT; |
| break; |
| case 's': |
| if (name[1] == 't') { |
| result = T_STRING; |
| } |
| else if (name[1] == 'e') { |
| result = T_SET; |
| } |
| break; |
| case 't': |
| result = T_BOOL; |
| break; |
| } |
| } |
| if (result == T_STOP) { |
| throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, |
| "Unrecognized type"); |
| } |
| return result; |
| } |
| |
| |
| // This table describes the handling for the first 0x30 characters |
| // 0 : escape using "\u00xx" notation |
| // 1 : just output index |
| // <other> : escape using "\<other>" notation |
| static const uint8_t kJSONCharTable[0x30] = { |
| // 0 1 2 3 4 5 6 7 8 9 A B C D E F |
| 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, // 0 |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 |
| 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 |
| }; |
| |
| |
| // This string's characters must match up with the elements in kEscapeCharVals. |
| // I don't have '/' on this list even though it appears on www.json.org -- |
| // it is not in the RFC |
| const static std::string kEscapeChars("\"\\bfnrt"); |
| |
| // The elements of this array must match up with the sequence of characters in |
| // kEscapeChars |
| const static uint8_t kEscapeCharVals[7] = { |
| '"', '\\', '\b', '\f', '\n', '\r', '\t', |
| }; |
| |
| |
| // Static helper functions |
| |
| // Read 1 character from the transport trans and verify that it is the |
| // expected character ch. |
| // Throw a protocol exception if it is not. |
| static uint32_t readSyntaxChar(TJSONProtocol::LookaheadReader &reader, |
| uint8_t ch) { |
| uint8_t ch2 = reader.read(); |
| if (ch2 != ch) { |
| throw TProtocolException(TProtocolException::INVALID_DATA, |
| "Expected \'" + std::string((char *)&ch, 1) + |
| "\'; got \'" + std::string((char *)&ch2, 1) + |
| "\'."); |
| } |
| return 1; |
| } |
| |
| // Return the integer value of a hex character ch. |
| // Throw a protocol exception if the character is not [0-9a-f]. |
| static uint8_t hexVal(uint8_t ch) { |
| if ((ch >= '0') && (ch <= '9')) { |
| return ch - '0'; |
| } |
| else if ((ch >= 'a') && (ch <= 'f')) { |
| return ch - 'a'; |
| } |
| else { |
| throw TProtocolException(TProtocolException::INVALID_DATA, |
| "Expected hex val ([0-9a-f]); got \'" |
| + std::string((char *)&ch, 1) + "\'."); |
| } |
| } |
| |
| // Return the hex character representing the integer val. The value is masked |
| // to make sure it is in the correct range. |
| static uint8_t hexChar(uint8_t val) { |
| val &= 0x0F; |
| if (val < 10) { |
| return val + '0'; |
| } |
| else { |
| return val + 'a'; |
| } |
| } |
| |
| // Return true if the character ch is in [-+0-9.Ee]; false otherwise |
| static bool isJSONNumeric(uint8_t ch) { |
| switch (ch) { |
| case '+': |
| case '-': |
| case '.': |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| case 'E': |
| case 'e': |
| return true; |
| } |
| return false; |
| } |
| |
| |
| /** |
| * Class to serve as base JSON context and as base class for other context |
| * implementations |
| */ |
| class TJSONContext { |
| |
| public: |
| |
| TJSONContext() {}; |
| |
| virtual ~TJSONContext() {}; |
| |
| /** |
| * Write context data to the transport. Default is to do nothing. |
| */ |
| virtual uint32_t write(TTransport &trans) { |
| return 0; |
| }; |
| |
| /** |
| * Read context data from the transport. Default is to do nothing. |
| */ |
| virtual uint32_t read(TJSONProtocol::LookaheadReader &reader) { |
| return 0; |
| }; |
| |
| /** |
| * Return true if numbers need to be escaped as strings in this context. |
| * Default behavior is to return false. |
| */ |
| virtual bool escapeNum() { |
| return false; |
| } |
| }; |
| |
| // Context class for object member key-value pairs |
| class JSONPairContext : public TJSONContext { |
| |
| public: |
| |
| JSONPairContext() : |
| first_(true), |
| colon_(true) { |
| } |
| |
| uint32_t write(TTransport &trans) { |
| if (first_) { |
| first_ = false; |
| colon_ = true; |
| return 0; |
| } |
| else { |
| trans.write(colon_ ? &kJSONPairSeparator : &kJSONElemSeparator, 1); |
| colon_ = !colon_; |
| return 1; |
| } |
| } |
| |
| uint32_t read(TJSONProtocol::LookaheadReader &reader) { |
| if (first_) { |
| first_ = false; |
| colon_ = true; |
| return 0; |
| } |
| else { |
| uint8_t ch = (colon_ ? kJSONPairSeparator : kJSONElemSeparator); |
| colon_ = !colon_; |
| return readSyntaxChar(reader, ch); |
| } |
| } |
| |
| // Numbers must be turned into strings if they are the key part of a pair |
| virtual bool escapeNum() { |
| return colon_; |
| } |
| |
| private: |
| |
| bool first_; |
| bool colon_; |
| }; |
| |
| // Context class for lists |
| class JSONListContext : public TJSONContext { |
| |
| public: |
| |
| JSONListContext() : |
| first_(true) { |
| } |
| |
| uint32_t write(TTransport &trans) { |
| if (first_) { |
| first_ = false; |
| return 0; |
| } |
| else { |
| trans.write(&kJSONElemSeparator, 1); |
| return 1; |
| } |
| } |
| |
| uint32_t read(TJSONProtocol::LookaheadReader &reader) { |
| if (first_) { |
| first_ = false; |
| return 0; |
| } |
| else { |
| return readSyntaxChar(reader, kJSONElemSeparator); |
| } |
| } |
| |
| private: |
| bool first_; |
| }; |
| |
| |
| TJSONProtocol::TJSONProtocol(boost::shared_ptr<TTransport> ptrans) : |
| TProtocol(ptrans), |
| context_(new TJSONContext()), |
| reader_(*ptrans) { |
| } |
| |
| TJSONProtocol::~TJSONProtocol() {} |
| |
| void TJSONProtocol::pushContext(boost::shared_ptr<TJSONContext> c) { |
| contexts_.push(context_); |
| context_ = c; |
| } |
| |
| void TJSONProtocol::popContext() { |
| context_ = contexts_.top(); |
| contexts_.pop(); |
| } |
| |
| // Write the character ch as a JSON escape sequence ("\u00xx") |
| uint32_t TJSONProtocol::writeJSONEscapeChar(uint8_t ch) { |
| trans_->write((const uint8_t *)kJSONEscapePrefix.c_str(), |
| kJSONEscapePrefix.length()); |
| uint8_t outCh = hexChar(ch >> 4); |
| trans_->write(&outCh, 1); |
| outCh = hexChar(ch); |
| trans_->write(&outCh, 1); |
| return 6; |
| } |
| |
| // Write the character ch as part of a JSON string, escaping as appropriate. |
| uint32_t TJSONProtocol::writeJSONChar(uint8_t ch) { |
| if (ch >= 0x30) { |
| if (ch == kJSONBackslash) { // Only special character >= 0x30 is '\' |
| trans_->write(&kJSONBackslash, 1); |
| trans_->write(&kJSONBackslash, 1); |
| return 2; |
| } |
| else { |
| trans_->write(&ch, 1); |
| return 1; |
| } |
| } |
| else { |
| uint8_t outCh = kJSONCharTable[ch]; |
| // Check if regular character, backslash escaped, or JSON escaped |
| if (outCh == 1) { |
| trans_->write(&ch, 1); |
| return 1; |
| } |
| else if (outCh > 1) { |
| trans_->write(&kJSONBackslash, 1); |
| trans_->write(&outCh, 1); |
| return 2; |
| } |
| else { |
| return writeJSONEscapeChar(ch); |
| } |
| } |
| } |
| |
| // Write out the contents of the string str as a JSON string, escaping |
| // characters as appropriate. |
| uint32_t TJSONProtocol::writeJSONString(const std::string &str) { |
| uint32_t result = context_->write(*trans_); |
| result += 2; // For quotes |
| trans_->write(&kJSONStringDelimiter, 1); |
| std::string::const_iterator iter(str.begin()); |
| std::string::const_iterator end(str.end()); |
| while (iter != end) { |
| result += writeJSONChar(*iter++); |
| } |
| trans_->write(&kJSONStringDelimiter, 1); |
| return result; |
| } |
| |
| // Write out the contents of the string as JSON string, base64-encoding |
| // the string's contents, and escaping as appropriate |
| uint32_t TJSONProtocol::writeJSONBase64(const std::string &str) { |
| uint32_t result = context_->write(*trans_); |
| result += 2; // For quotes |
| trans_->write(&kJSONStringDelimiter, 1); |
| uint8_t b[4]; |
| const uint8_t *bytes = (const uint8_t *)str.c_str(); |
| uint32_t len = str.length(); |
| while (len >= 3) { |
| // Encode 3 bytes at a time |
| base64_encode(bytes, 3, b); |
| trans_->write(b, 4); |
| result += 4; |
| bytes += 3; |
| len -=3; |
| } |
| if (len) { // Handle remainder |
| base64_encode(bytes, len, b); |
| trans_->write(b, len + 1); |
| result += len + 1; |
| } |
| trans_->write(&kJSONStringDelimiter, 1); |
| return result; |
| } |
| |
| // Convert the given integer type to a JSON number, or a string |
| // if the context requires it (eg: key in a map pair). |
| template <typename NumberType> |
| uint32_t TJSONProtocol::writeJSONInteger(NumberType num) { |
| uint32_t result = context_->write(*trans_); |
| std::string val(boost::lexical_cast<std::string>(num)); |
| bool escapeNum = context_->escapeNum(); |
| if (escapeNum) { |
| trans_->write(&kJSONStringDelimiter, 1); |
| result += 1; |
| } |
| trans_->write((const uint8_t *)val.c_str(), val.length()); |
| result += val.length(); |
| if (escapeNum) { |
| trans_->write(&kJSONStringDelimiter, 1); |
| result += 1; |
| } |
| return result; |
| } |
| |
| // Convert the given double to a JSON string, which is either the number, |
| // "NaN" or "Infinity" or "-Infinity". |
| uint32_t TJSONProtocol::writeJSONDouble(double num) { |
| uint32_t result = context_->write(*trans_); |
| std::string val(boost::lexical_cast<std::string>(num)); |
| |
| // Normalize output of boost::lexical_cast for NaNs and Infinities |
| bool special = false; |
| switch (val[0]) { |
| case 'N': |
| case 'n': |
| val = kThriftNan; |
| special = true; |
| break; |
| case 'I': |
| case 'i': |
| val = kThriftInfinity; |
| special = true; |
| break; |
| case '-': |
| if ((val[1] == 'I') || (val[1] == 'i')) { |
| val = kThriftNegativeInfinity; |
| special = true; |
| } |
| break; |
| } |
| |
| bool escapeNum = special || context_->escapeNum(); |
| if (escapeNum) { |
| trans_->write(&kJSONStringDelimiter, 1); |
| result += 1; |
| } |
| trans_->write((const uint8_t *)val.c_str(), val.length()); |
| result += val.length(); |
| if (escapeNum) { |
| trans_->write(&kJSONStringDelimiter, 1); |
| result += 1; |
| } |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::writeJSONObjectStart() { |
| uint32_t result = context_->write(*trans_); |
| trans_->write(&kJSONObjectStart, 1); |
| pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext())); |
| return result + 1; |
| } |
| |
| uint32_t TJSONProtocol::writeJSONObjectEnd() { |
| popContext(); |
| trans_->write(&kJSONObjectEnd, 1); |
| return 1; |
| } |
| |
| uint32_t TJSONProtocol::writeJSONArrayStart() { |
| uint32_t result = context_->write(*trans_); |
| trans_->write(&kJSONArrayStart, 1); |
| pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext())); |
| return result + 1; |
| } |
| |
| uint32_t TJSONProtocol::writeJSONArrayEnd() { |
| popContext(); |
| trans_->write(&kJSONArrayEnd, 1); |
| return 1; |
| } |
| |
| uint32_t TJSONProtocol::writeMessageBegin(const std::string& name, |
| const TMessageType messageType, |
| const int32_t seqid) { |
| uint32_t result = writeJSONArrayStart(); |
| result += writeJSONInteger(kThriftVersion1); |
| result += writeJSONString(name); |
| result += writeJSONInteger(messageType); |
| result += writeJSONInteger(seqid); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::writeMessageEnd() { |
| return writeJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::writeStructBegin(const std::string& name) { |
| return writeJSONObjectStart(); |
| } |
| |
| uint32_t TJSONProtocol::writeStructEnd() { |
| return writeJSONObjectEnd(); |
| } |
| |
| uint32_t TJSONProtocol::writeFieldBegin(const std::string& name, |
| const TType fieldType, |
| const int16_t fieldId) { |
| uint32_t result = writeJSONInteger(fieldId); |
| result += writeJSONObjectStart(); |
| result += writeJSONString(getTypeNameForTypeID(fieldType)); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::writeFieldEnd() { |
| return writeJSONObjectEnd(); |
| } |
| |
| uint32_t TJSONProtocol::writeFieldStop() { |
| return 0; |
| } |
| |
| uint32_t TJSONProtocol::writeMapBegin(const TType keyType, |
| const TType valType, |
| const uint32_t size) { |
| uint32_t result = writeJSONArrayStart(); |
| result += writeJSONString(getTypeNameForTypeID(keyType)); |
| result += writeJSONString(getTypeNameForTypeID(valType)); |
| result += writeJSONInteger((int64_t)size); |
| result += writeJSONObjectStart(); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::writeMapEnd() { |
| return writeJSONObjectEnd() + writeJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::writeListBegin(const TType elemType, |
| const uint32_t size) { |
| uint32_t result = writeJSONArrayStart(); |
| result += writeJSONString(getTypeNameForTypeID(elemType)); |
| result += writeJSONInteger((int64_t)size); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::writeListEnd() { |
| return writeJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::writeSetBegin(const TType elemType, |
| const uint32_t size) { |
| uint32_t result = writeJSONArrayStart(); |
| result += writeJSONString(getTypeNameForTypeID(elemType)); |
| result += writeJSONInteger((int64_t)size); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::writeSetEnd() { |
| return writeJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::writeBool(const bool value) { |
| return writeJSONInteger(value); |
| } |
| |
| uint32_t TJSONProtocol::writeByte(const int8_t byte) { |
| // writeByte() must be handled specially becuase boost::lexical cast sees |
| // int8_t as a text type instead of an integer type |
| return writeJSONInteger((int16_t)byte); |
| } |
| |
| uint32_t TJSONProtocol::writeI16(const int16_t i16) { |
| return writeJSONInteger(i16); |
| } |
| |
| uint32_t TJSONProtocol::writeI32(const int32_t i32) { |
| return writeJSONInteger(i32); |
| } |
| |
| uint32_t TJSONProtocol::writeI64(const int64_t i64) { |
| return writeJSONInteger(i64); |
| } |
| |
| uint32_t TJSONProtocol::writeDouble(const double dub) { |
| return writeJSONDouble(dub); |
| } |
| |
| uint32_t TJSONProtocol::writeString(const std::string& str) { |
| return writeJSONString(str); |
| } |
| |
| uint32_t TJSONProtocol::writeBinary(const std::string& str) { |
| return writeJSONBase64(str); |
| } |
| |
| /** |
| * Reading functions |
| */ |
| |
| // Reads 1 byte and verifies that it matches ch. |
| uint32_t TJSONProtocol::readJSONSyntaxChar(uint8_t ch) { |
| return readSyntaxChar(reader_, ch); |
| } |
| |
| // Decodes the four hex parts of a JSON escaped string character and returns |
| // the character via out. The first two characters must be "00". |
| uint32_t TJSONProtocol::readJSONEscapeChar(uint8_t *out) { |
| uint8_t b[2]; |
| readJSONSyntaxChar(kJSONZeroChar); |
| readJSONSyntaxChar(kJSONZeroChar); |
| b[0] = reader_.read(); |
| b[1] = reader_.read(); |
| *out = (hexVal(b[0]) << 4) + hexVal(b[1]); |
| return 4; |
| } |
| |
| // Decodes a JSON string, including unescaping, and returns the string via str |
| uint32_t TJSONProtocol::readJSONString(std::string &str, bool skipContext) { |
| uint32_t result = (skipContext ? 0 : context_->read(reader_)); |
| result += readJSONSyntaxChar(kJSONStringDelimiter); |
| uint8_t ch; |
| str.clear(); |
| while (true) { |
| ch = reader_.read(); |
| ++result; |
| if (ch == kJSONStringDelimiter) { |
| break; |
| } |
| if (ch == kJSONBackslash) { |
| ch = reader_.read(); |
| ++result; |
| if (ch == kJSONEscapeChar) { |
| result += readJSONEscapeChar(&ch); |
| } |
| else { |
| size_t pos = kEscapeChars.find(ch); |
| if (pos == std::string::npos) { |
| throw TProtocolException(TProtocolException::INVALID_DATA, |
| "Expected control char, got '" + |
| std::string((const char *)&ch, 1) + "'."); |
| } |
| ch = kEscapeCharVals[pos]; |
| } |
| } |
| str += ch; |
| } |
| return result; |
| } |
| |
| // Reads a block of base64 characters, decoding it, and returns via str |
| uint32_t TJSONProtocol::readJSONBase64(std::string &str) { |
| std::string tmp; |
| uint32_t result = readJSONString(tmp); |
| uint8_t *b = (uint8_t *)tmp.c_str(); |
| uint32_t len = tmp.length(); |
| str.clear(); |
| while (len >= 4) { |
| base64_decode(b, 4); |
| str.append((const char *)b, 3); |
| b += 4; |
| len -= 4; |
| } |
| // Don't decode if we hit the end or got a single leftover byte (invalid |
| // base64 but legal for skip of regular string type) |
| if (len > 1) { |
| base64_decode(b, len); |
| str.append((const char *)b, len - 1); |
| } |
| return result; |
| } |
| |
| // Reads a sequence of characters, stopping at the first one that is not |
| // a valid JSON numeric character. |
| uint32_t TJSONProtocol::readJSONNumericChars(std::string &str) { |
| uint32_t result = 0; |
| str.clear(); |
| while (true) { |
| uint8_t ch = reader_.peek(); |
| if (!isJSONNumeric(ch)) { |
| break; |
| } |
| reader_.read(); |
| str += ch; |
| ++result; |
| } |
| return result; |
| } |
| |
| // Reads a sequence of characters and assembles them into a number, |
| // returning them via num |
| template <typename NumberType> |
| uint32_t TJSONProtocol::readJSONInteger(NumberType &num) { |
| uint32_t result = context_->read(reader_); |
| if (context_->escapeNum()) { |
| result += readJSONSyntaxChar(kJSONStringDelimiter); |
| } |
| std::string str; |
| result += readJSONNumericChars(str); |
| try { |
| num = boost::lexical_cast<NumberType>(str); |
| } |
| catch (boost::bad_lexical_cast e) { |
| throw new TProtocolException(TProtocolException::INVALID_DATA, |
| "Expected numeric value; got \"" + str + |
| "\""); |
| } |
| if (context_->escapeNum()) { |
| result += readJSONSyntaxChar(kJSONStringDelimiter); |
| } |
| return result; |
| } |
| |
| // Reads a JSON number or string and interprets it as a double. |
| uint32_t TJSONProtocol::readJSONDouble(double &num) { |
| uint32_t result = context_->read(reader_); |
| std::string str; |
| if (reader_.peek() == kJSONStringDelimiter) { |
| result += readJSONString(str, true); |
| // Check for NaN, Infinity and -Infinity |
| if (str == kThriftNan) { |
| num = HUGE_VAL/HUGE_VAL; // generates NaN |
| } |
| else if (str == kThriftInfinity) { |
| num = HUGE_VAL; |
| } |
| else if (str == kThriftNegativeInfinity) { |
| num = -HUGE_VAL; |
| } |
| else { |
| if (!context_->escapeNum()) { |
| // Throw exception -- we should not be in a string in this case |
| throw new TProtocolException(TProtocolException::INVALID_DATA, |
| "Numeric data unexpectedly quoted"); |
| } |
| try { |
| num = boost::lexical_cast<double>(str); |
| } |
| catch (boost::bad_lexical_cast e) { |
| throw new TProtocolException(TProtocolException::INVALID_DATA, |
| "Expected numeric value; got \"" + str + |
| "\""); |
| } |
| } |
| } |
| else { |
| if (context_->escapeNum()) { |
| // This will throw - we should have had a quote if escapeNum == true |
| readJSONSyntaxChar(kJSONStringDelimiter); |
| } |
| result += readJSONNumericChars(str); |
| try { |
| num = boost::lexical_cast<double>(str); |
| } |
| catch (boost::bad_lexical_cast e) { |
| throw new TProtocolException(TProtocolException::INVALID_DATA, |
| "Expected numeric value; got \"" + str + |
| "\""); |
| } |
| } |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readJSONObjectStart() { |
| uint32_t result = context_->read(reader_); |
| result += readJSONSyntaxChar(kJSONObjectStart); |
| pushContext(boost::shared_ptr<TJSONContext>(new JSONPairContext())); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readJSONObjectEnd() { |
| uint32_t result = readJSONSyntaxChar(kJSONObjectEnd); |
| popContext(); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readJSONArrayStart() { |
| uint32_t result = context_->read(reader_); |
| result += readJSONSyntaxChar(kJSONArrayStart); |
| pushContext(boost::shared_ptr<TJSONContext>(new JSONListContext())); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readJSONArrayEnd() { |
| uint32_t result = readJSONSyntaxChar(kJSONArrayEnd); |
| popContext(); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readMessageBegin(std::string& name, |
| TMessageType& messageType, |
| int32_t& seqid) { |
| uint32_t result = readJSONArrayStart(); |
| uint64_t tmpVal = 0; |
| result += readJSONInteger(tmpVal); |
| if (tmpVal != kThriftVersion1) { |
| throw TProtocolException(TProtocolException::BAD_VERSION, |
| "Message contained bad version."); |
| } |
| result += readJSONString(name); |
| result += readJSONInteger(tmpVal); |
| messageType = (TMessageType)tmpVal; |
| result += readJSONInteger(tmpVal); |
| seqid = tmpVal; |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readMessageEnd() { |
| return readJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::readStructBegin(std::string& name) { |
| return readJSONObjectStart(); |
| } |
| |
| uint32_t TJSONProtocol::readStructEnd() { |
| return readJSONObjectEnd(); |
| } |
| |
| uint32_t TJSONProtocol::readFieldBegin(std::string& name, |
| TType& fieldType, |
| int16_t& fieldId) { |
| uint32_t result = 0; |
| // Check if we hit the end of the list |
| uint8_t ch = reader_.peek(); |
| if (ch == kJSONObjectEnd) { |
| fieldType = facebook::thrift::protocol::T_STOP; |
| } |
| else { |
| uint64_t tmpVal = 0; |
| std::string tmpStr; |
| result += readJSONInteger(tmpVal); |
| fieldId = tmpVal; |
| result += readJSONObjectStart(); |
| result += readJSONString(tmpStr); |
| fieldType = getTypeIDForTypeName(tmpStr); |
| } |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readFieldEnd() { |
| return readJSONObjectEnd(); |
| } |
| |
| uint32_t TJSONProtocol::readMapBegin(TType& keyType, |
| TType& valType, |
| uint32_t& size) { |
| uint64_t tmpVal = 0; |
| std::string tmpStr; |
| uint32_t result = readJSONArrayStart(); |
| result += readJSONString(tmpStr); |
| keyType = getTypeIDForTypeName(tmpStr); |
| result += readJSONString(tmpStr); |
| valType = getTypeIDForTypeName(tmpStr); |
| result += readJSONInteger(tmpVal); |
| size = tmpVal; |
| result += readJSONObjectStart(); |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readMapEnd() { |
| return readJSONObjectEnd() + readJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::readListBegin(TType& elemType, |
| uint32_t& size) { |
| uint64_t tmpVal = 0; |
| std::string tmpStr; |
| uint32_t result = readJSONArrayStart(); |
| result += readJSONString(tmpStr); |
| elemType = getTypeIDForTypeName(tmpStr); |
| result += readJSONInteger(tmpVal); |
| size = tmpVal; |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readListEnd() { |
| return readJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::readSetBegin(TType& elemType, |
| uint32_t& size) { |
| uint64_t tmpVal = 0; |
| std::string tmpStr; |
| uint32_t result = readJSONArrayStart(); |
| result += readJSONString(tmpStr); |
| elemType = getTypeIDForTypeName(tmpStr); |
| result += readJSONInteger(tmpVal); |
| size = tmpVal; |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readSetEnd() { |
| return readJSONArrayEnd(); |
| } |
| |
| uint32_t TJSONProtocol::readBool(bool& value) { |
| return readJSONInteger(value); |
| } |
| |
| // readByte() must be handled properly becuase boost::lexical cast sees int8_t |
| // as a text type instead of an integer type |
| uint32_t TJSONProtocol::readByte(int8_t& byte) { |
| int16_t tmp = (int16_t) byte; |
| uint32_t result = readJSONInteger(tmp); |
| assert(tmp < 256); |
| byte = (int8_t)tmp; |
| return result; |
| } |
| |
| uint32_t TJSONProtocol::readI16(int16_t& i16) { |
| return readJSONInteger(i16); |
| } |
| |
| uint32_t TJSONProtocol::readI32(int32_t& i32) { |
| return readJSONInteger(i32); |
| } |
| |
| uint32_t TJSONProtocol::readI64(int64_t& i64) { |
| return readJSONInteger(i64); |
| } |
| |
| uint32_t TJSONProtocol::readDouble(double& dub) { |
| return readJSONDouble(dub); |
| } |
| |
| uint32_t TJSONProtocol::readString(std::string &str) { |
| return readJSONString(str); |
| } |
| |
| uint32_t TJSONProtocol::readBinary(std::string &str) { |
| return readJSONBase64(str); |
| } |
| |
| }}} // facebook::thrift::protocol |