| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include <thrift/protocol/TDebugProtocol.h> |
| |
| #include <cassert> |
| #include <cctype> |
| #include <cstdio> |
| #include <stdexcept> |
| #include <boost/static_assert.hpp> |
| #include <boost/lexical_cast.hpp> |
| |
| using std::string; |
| |
| static string byte_to_hex(const uint8_t byte) { |
| char buf[3]; |
| int ret = std::sprintf(buf, "%02x", (int)byte); |
| THRIFT_UNUSED_VARIABLE(ret); |
| assert(ret == 2); |
| assert(buf[2] == '\0'); |
| return buf; |
| } |
| |
| namespace apache { |
| namespace thrift { |
| namespace protocol { |
| |
| string TDebugProtocol::fieldTypeName(TType type) { |
| switch (type) { |
| case T_STOP: |
| return "stop"; |
| case T_VOID: |
| return "void"; |
| case T_BOOL: |
| return "bool"; |
| case T_BYTE: |
| return "byte"; |
| case T_I16: |
| return "i16"; |
| case T_I32: |
| return "i32"; |
| case T_U64: |
| return "u64"; |
| case T_I64: |
| return "i64"; |
| case T_DOUBLE: |
| return "double"; |
| case T_STRING: |
| return "string"; |
| case T_STRUCT: |
| return "struct"; |
| case T_MAP: |
| return "map"; |
| case T_SET: |
| return "set"; |
| case T_LIST: |
| return "list"; |
| case T_UTF8: |
| return "utf8"; |
| case T_UTF16: |
| return "utf16"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| void TDebugProtocol::indentUp() { |
| indent_str_ += string(indent_inc, ' '); |
| } |
| |
| void TDebugProtocol::indentDown() { |
| if (indent_str_.length() < (string::size_type)indent_inc) { |
| throw TProtocolException(TProtocolException::INVALID_DATA); |
| } |
| indent_str_.erase(indent_str_.length() - indent_inc); |
| } |
| |
| uint32_t TDebugProtocol::writePlain(const string& str) { |
| if (str.length() > (std::numeric_limits<uint32_t>::max)()) |
| throw TProtocolException(TProtocolException::SIZE_LIMIT); |
| trans_->write((uint8_t*)str.data(), static_cast<uint32_t>(str.length())); |
| return static_cast<uint32_t>(str.length()); |
| } |
| |
| uint32_t TDebugProtocol::writeIndented(const string& str) { |
| if (str.length() > (std::numeric_limits<uint32_t>::max)()) |
| throw TProtocolException(TProtocolException::SIZE_LIMIT); |
| if (indent_str_.length() > (std::numeric_limits<uint32_t>::max)()) |
| throw TProtocolException(TProtocolException::SIZE_LIMIT); |
| uint64_t total_len = indent_str_.length() + str.length(); |
| if (total_len > (std::numeric_limits<uint32_t>::max)()) |
| throw TProtocolException(TProtocolException::SIZE_LIMIT); |
| trans_->write((uint8_t*)indent_str_.data(), static_cast<uint32_t>(indent_str_.length())); |
| trans_->write((uint8_t*)str.data(), static_cast<uint32_t>(str.length())); |
| return static_cast<uint32_t>(indent_str_.length() + str.length()); |
| } |
| |
| uint32_t TDebugProtocol::startItem() { |
| uint32_t size; |
| |
| switch (write_state_.back()) { |
| case UNINIT: |
| // XXX figure out what to do here. |
| // throw TProtocolException(TProtocolException::INVALID_DATA); |
| // return writeIndented(str); |
| return 0; |
| case STRUCT: |
| return 0; |
| case SET: |
| return writeIndented(""); |
| case MAP_KEY: |
| return writeIndented(""); |
| case MAP_VALUE: |
| return writePlain(" -> "); |
| case LIST: |
| size = writeIndented("[" + boost::lexical_cast<string>(list_idx_.back()) + "] = "); |
| list_idx_.back()++; |
| return size; |
| default: |
| throw std::logic_error("Invalid enum value."); |
| } |
| } |
| |
| uint32_t TDebugProtocol::endItem() { |
| // uint32_t size; |
| |
| switch (write_state_.back()) { |
| case UNINIT: |
| // XXX figure out what to do here. |
| // throw TProtocolException(TProtocolException::INVALID_DATA); |
| // return writeIndented(str); |
| return 0; |
| case STRUCT: |
| return writePlain(",\n"); |
| case SET: |
| return writePlain(",\n"); |
| case MAP_KEY: |
| write_state_.back() = MAP_VALUE; |
| return 0; |
| case MAP_VALUE: |
| write_state_.back() = MAP_KEY; |
| return writePlain(",\n"); |
| case LIST: |
| return writePlain(",\n"); |
| default: |
| throw std::logic_error("Invalid enum value."); |
| } |
| } |
| |
| uint32_t TDebugProtocol::writeItem(const std::string& str) { |
| uint32_t size = 0; |
| size += startItem(); |
| size += writePlain(str); |
| size += endItem(); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeMessageBegin(const std::string& name, |
| const TMessageType messageType, |
| const int32_t seqid) { |
| (void)seqid; |
| string mtype; |
| switch (messageType) { |
| case T_CALL: |
| mtype = "call"; |
| break; |
| case T_REPLY: |
| mtype = "reply"; |
| break; |
| case T_EXCEPTION: |
| mtype = "exn"; |
| break; |
| case T_ONEWAY: |
| mtype = "oneway"; |
| break; |
| } |
| |
| uint32_t size = writeIndented("(" + mtype + ") " + name + "("); |
| indentUp(); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeMessageEnd() { |
| indentDown(); |
| return writeIndented(")\n"); |
| } |
| |
| uint32_t TDebugProtocol::writeStructBegin(const char* name) { |
| uint32_t size = 0; |
| size += startItem(); |
| size += writePlain(string(name) + " {\n"); |
| indentUp(); |
| write_state_.push_back(STRUCT); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeStructEnd() { |
| indentDown(); |
| write_state_.pop_back(); |
| uint32_t size = 0; |
| size += writeIndented("}"); |
| size += endItem(); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeFieldBegin(const char* name, |
| const TType fieldType, |
| const int16_t fieldId) { |
| // sprintf(id_str, "%02d", fieldId); |
| string id_str = boost::lexical_cast<string>(fieldId); |
| if (id_str.length() == 1) |
| id_str = '0' + id_str; |
| |
| return writeIndented(id_str + ": " + name + " (" + fieldTypeName(fieldType) + ") = "); |
| } |
| |
| uint32_t TDebugProtocol::writeFieldEnd() { |
| assert(write_state_.back() == STRUCT); |
| return 0; |
| } |
| |
| uint32_t TDebugProtocol::writeFieldStop() { |
| return 0; |
| // writeIndented("***STOP***\n"); |
| } |
| |
| uint32_t TDebugProtocol::writeMapBegin(const TType keyType, |
| const TType valType, |
| const uint32_t size) { |
| // TODO(dreiss): Optimize short maps? |
| uint32_t bsize = 0; |
| bsize += startItem(); |
| bsize += writePlain( |
| "map<" + fieldTypeName(keyType) + "," + fieldTypeName(valType) + ">" |
| "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| indentUp(); |
| write_state_.push_back(MAP_KEY); |
| return bsize; |
| } |
| |
| uint32_t TDebugProtocol::writeMapEnd() { |
| indentDown(); |
| write_state_.pop_back(); |
| uint32_t size = 0; |
| size += writeIndented("}"); |
| size += endItem(); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeListBegin(const TType elemType, const uint32_t size) { |
| // TODO(dreiss): Optimize short arrays. |
| uint32_t bsize = 0; |
| bsize += startItem(); |
| bsize += writePlain( |
| "list<" + fieldTypeName(elemType) + ">" |
| "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| indentUp(); |
| write_state_.push_back(LIST); |
| list_idx_.push_back(0); |
| return bsize; |
| } |
| |
| uint32_t TDebugProtocol::writeListEnd() { |
| indentDown(); |
| write_state_.pop_back(); |
| list_idx_.pop_back(); |
| uint32_t size = 0; |
| size += writeIndented("}"); |
| size += endItem(); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeSetBegin(const TType elemType, const uint32_t size) { |
| // TODO(dreiss): Optimize short sets. |
| uint32_t bsize = 0; |
| bsize += startItem(); |
| bsize += writePlain( |
| "set<" + fieldTypeName(elemType) + ">" |
| "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| indentUp(); |
| write_state_.push_back(SET); |
| return bsize; |
| } |
| |
| uint32_t TDebugProtocol::writeSetEnd() { |
| indentDown(); |
| write_state_.pop_back(); |
| uint32_t size = 0; |
| size += writeIndented("}"); |
| size += endItem(); |
| return size; |
| } |
| |
| uint32_t TDebugProtocol::writeBool(const bool value) { |
| return writeItem(value ? "true" : "false"); |
| } |
| |
| uint32_t TDebugProtocol::writeByte(const int8_t byte) { |
| return writeItem("0x" + byte_to_hex(byte)); |
| } |
| |
| uint32_t TDebugProtocol::writeI16(const int16_t i16) { |
| return writeItem(boost::lexical_cast<string>(i16)); |
| } |
| |
| uint32_t TDebugProtocol::writeI32(const int32_t i32) { |
| return writeItem(boost::lexical_cast<string>(i32)); |
| } |
| |
| uint32_t TDebugProtocol::writeI64(const int64_t i64) { |
| return writeItem(boost::lexical_cast<string>(i64)); |
| } |
| |
| uint32_t TDebugProtocol::writeDouble(const double dub) { |
| return writeItem(boost::lexical_cast<string>(dub)); |
| } |
| |
| uint32_t TDebugProtocol::writeString(const string& str) { |
| // XXX Raw/UTF-8? |
| |
| string to_show = str; |
| if (to_show.length() > (string::size_type)string_limit_) { |
| to_show = str.substr(0, string_prefix_size_); |
| to_show += "[...](" + boost::lexical_cast<string>(str.length()) + ")"; |
| } |
| |
| string output = "\""; |
| |
| for (string::const_iterator it = to_show.begin(); it != to_show.end(); ++it) { |
| if (*it == '\\') { |
| output += "\\\\"; |
| } else if (*it == '"') { |
| output += "\\\""; |
| } else if (std::isprint(*it)) { |
| output += *it; |
| } else { |
| switch (*it) { |
| case '\a': |
| output += "\\a"; |
| break; |
| case '\b': |
| output += "\\b"; |
| break; |
| case '\f': |
| output += "\\f"; |
| break; |
| case '\n': |
| output += "\\n"; |
| break; |
| case '\r': |
| output += "\\r"; |
| break; |
| case '\t': |
| output += "\\t"; |
| break; |
| case '\v': |
| output += "\\v"; |
| break; |
| default: |
| output += "\\x"; |
| output += byte_to_hex(*it); |
| } |
| } |
| } |
| |
| output += '\"'; |
| return writeItem(output); |
| } |
| |
| uint32_t TDebugProtocol::writeBinary(const string& str) { |
| // XXX Hex? |
| return TDebugProtocol::writeString(str); |
| } |
| } |
| } |
| } // apache::thrift::protocol |