blob: 0a2eaeda035281d951d3c0d367feb1fe95996f1d [file] [log] [blame]
/*
* 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 <thrift/protocol/TUuidUtils.hpp>
#include <thrift/TToString.h>
#include <cassert>
#include <cctype>
#include <cstdio>
#include <stdexcept>
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_UUID:
return "uuid";
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("[" + to_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 = to_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) + ">"
"[" + to_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) + ">"
"[" + to_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) + ">"
"[" + to_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(to_string(i16));
}
uint32_t TDebugProtocol::writeI32(const int32_t i32) {
return writeItem(to_string(i32));
}
uint32_t TDebugProtocol::writeI64(const int64_t i64) {
return writeItem(to_string(i64));
}
uint32_t TDebugProtocol::writeDouble(const double dub) {
return writeItem(to_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 += "[...](" + to_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 += "\\\"";
// passing characters <0 to std::isprint causes asserts. isprint takes an
// int, so we need to be careful of sign extension
} else if (std::isprint((unsigned char)*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);
}
uint32_t TDebugProtocol::writeUUID(const string& str) {
std::string out_raw;
uuid_encode(str, out_raw);
std::string out_encoded;
uuid_decode(out_raw, out_encoded);
size_t size = writePlain("{\n");
indentUp();
size += writeIndented("[in ] = \"" + str + "\",\n");
size += writeIndented("[raw] = ");
size += writeString(out_raw);
size += writeIndented("[enc] = \"" + out_encoded + "\"\n");
indentDown();
size += writeIndented("}\n");
return size;
}
}
}
} // apache::thrift::protocol