David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 1 | // Copyright (c) 2006- Facebook |
| 2 | // Distributed under the Thrift Software License |
| 3 | // |
| 4 | // See accompanying file LICENSE or visit the Thrift site at: |
| 5 | // http://developers.facebook.com/thrift/ |
| 6 | |
| 7 | #include "TDebugProtocol.h" |
| 8 | |
| 9 | #include <cassert> |
| 10 | #include <cctype> |
| 11 | #include <cstdio> |
| 12 | #include <stdexcept> |
| 13 | #include <boost/static_assert.hpp> |
| 14 | #include <boost/lexical_cast.hpp> |
| 15 | |
| 16 | using std::string; |
| 17 | |
| 18 | |
| 19 | static string byte_to_hex(const uint8_t byte) { |
| 20 | char buf[3]; |
| 21 | int ret = std::sprintf(buf, "%02x", (int)byte); |
| 22 | assert(ret == 2); |
| 23 | assert(buf[2] == '\0'); |
| 24 | return buf; |
| 25 | } |
| 26 | |
| 27 | |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 28 | namespace facebook { namespace thrift { namespace protocol { |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 29 | |
| 30 | string TDebugProtocol::fieldTypeName(TType type) { |
| 31 | switch (type) { |
| 32 | case T_STOP : return "stop" ; |
| 33 | case T_VOID : return "void" ; |
| 34 | case T_BOOL : return "bool" ; |
| 35 | case T_BYTE : return "byte" ; |
| 36 | case T_I16 : return "i16" ; |
| 37 | case T_I32 : return "i32" ; |
| 38 | case T_U64 : return "u64" ; |
| 39 | case T_I64 : return "i64" ; |
| 40 | case T_DOUBLE : return "double" ; |
| 41 | case T_STRING : return "string" ; |
| 42 | case T_STRUCT : return "struct" ; |
| 43 | case T_MAP : return "map" ; |
| 44 | case T_SET : return "set" ; |
| 45 | case T_LIST : return "list" ; |
| 46 | case T_UTF8 : return "utf8" ; |
| 47 | case T_UTF16 : return "utf16" ; |
| 48 | default: return "unknown"; |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | void TDebugProtocol::indentUp() { |
| 53 | indent_str_ += string(indent_inc, ' '); |
| 54 | } |
| 55 | |
| 56 | void TDebugProtocol::indentDown() { |
| 57 | if (indent_str_.length() < (string::size_type)indent_inc) { |
| 58 | throw TProtocolException(TProtocolException::INVALID_DATA); |
| 59 | } |
| 60 | indent_str_.erase(indent_str_.length() - indent_inc); |
| 61 | } |
| 62 | |
| 63 | uint32_t TDebugProtocol::writePlain(const string& str) { |
| 64 | trans_->write((uint8_t*)str.data(), str.length()); |
| 65 | return str.length(); |
| 66 | } |
| 67 | |
| 68 | uint32_t TDebugProtocol::writeIndented(const string& str) { |
| 69 | trans_->write((uint8_t*)indent_str_.data(), indent_str_.length()); |
| 70 | trans_->write((uint8_t*)str.data(), str.length()); |
| 71 | return indent_str_.length() + str.length(); |
| 72 | } |
| 73 | |
| 74 | uint32_t TDebugProtocol::startItem() { |
| 75 | uint32_t size; |
| 76 | |
| 77 | switch (write_state_.back()) { |
| 78 | case UNINIT: |
| 79 | // XXX figure out what to do here. |
| 80 | //throw TProtocolException(TProtocolException::INVALID_DATA); |
| 81 | //return writeIndented(str); |
| 82 | return 0; |
| 83 | case STRUCT: |
| 84 | return 0; |
| 85 | case SET: |
| 86 | return writeIndented(""); |
| 87 | case MAP_KEY: |
| 88 | return writeIndented(""); |
| 89 | case MAP_VALUE: |
| 90 | return writePlain(" -> "); |
| 91 | case LIST: |
| 92 | size = writeIndented( |
| 93 | "[" + boost::lexical_cast<string>(list_idx_.back()) + "] = "); |
| 94 | list_idx_.back()++; |
| 95 | return size; |
| 96 | default: |
| 97 | throw std::logic_error("Invalid enum value."); |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | uint32_t TDebugProtocol::endItem() { |
| 102 | //uint32_t size; |
| 103 | |
| 104 | switch (write_state_.back()) { |
| 105 | case UNINIT: |
| 106 | // XXX figure out what to do here. |
| 107 | //throw TProtocolException(TProtocolException::INVALID_DATA); |
| 108 | //return writeIndented(str); |
| 109 | return 0; |
| 110 | case STRUCT: |
| 111 | return writePlain(",\n"); |
| 112 | case SET: |
| 113 | return writePlain(",\n"); |
| 114 | case MAP_KEY: |
| 115 | write_state_.back() = MAP_VALUE; |
| 116 | return 0; |
| 117 | case MAP_VALUE: |
| 118 | write_state_.back() = MAP_KEY; |
| 119 | return writePlain(",\n"); |
| 120 | case LIST: |
| 121 | return writePlain(",\n"); |
| 122 | default: |
| 123 | throw std::logic_error("Invalid enum value."); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | uint32_t TDebugProtocol::writeItem(const std::string& str) { |
| 128 | uint32_t size = 0; |
| 129 | size += startItem(); |
| 130 | size += writePlain(str); |
| 131 | size += endItem(); |
| 132 | return size; |
| 133 | } |
| 134 | |
| 135 | uint32_t TDebugProtocol::writeMessageBegin(const std::string& name, |
| 136 | const TMessageType messageType, |
| 137 | const int32_t seqid) { |
David Reiss | 642f2d3 | 2008-04-08 05:06:32 +0000 | [diff] [blame] | 138 | string mtype; |
| 139 | switch (messageType) { |
| 140 | case T_CALL : mtype = "call" ; break; |
| 141 | case T_REPLY : mtype = "reply" ; break; |
| 142 | case T_EXCEPTION : mtype = "exn" ; break; |
| 143 | } |
| 144 | |
| 145 | uint32_t size = writeIndented("(" + mtype + ") " + name + "("); |
| 146 | indentUp(); |
| 147 | return size; |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 148 | } |
| 149 | |
| 150 | uint32_t TDebugProtocol::writeMessageEnd() { |
David Reiss | 642f2d3 | 2008-04-08 05:06:32 +0000 | [diff] [blame] | 151 | indentDown(); |
| 152 | return writeIndented(")\n"); |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 153 | } |
| 154 | |
David Reiss | 6412000 | 2008-04-29 23:12:24 +0000 | [diff] [blame^] | 155 | uint32_t TDebugProtocol::writeStructBegin(const char* name) { |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 156 | uint32_t size = 0; |
| 157 | size += startItem(); |
David Reiss | 6412000 | 2008-04-29 23:12:24 +0000 | [diff] [blame^] | 158 | size += writePlain(string(name) + " {\n"); |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 159 | indentUp(); |
| 160 | write_state_.push_back(STRUCT); |
| 161 | return size; |
| 162 | } |
| 163 | |
| 164 | uint32_t TDebugProtocol::writeStructEnd() { |
| 165 | indentDown(); |
| 166 | write_state_.pop_back(); |
| 167 | uint32_t size = 0; |
| 168 | size += writeIndented("}"); |
| 169 | size += endItem(); |
| 170 | return size; |
| 171 | } |
| 172 | |
David Reiss | 6412000 | 2008-04-29 23:12:24 +0000 | [diff] [blame^] | 173 | uint32_t TDebugProtocol::writeFieldBegin(const char* name, |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 174 | const TType fieldType, |
| 175 | const int16_t fieldId) { |
| 176 | // sprintf(id_str, "%02d", fieldId); |
| 177 | string id_str = boost::lexical_cast<string>(fieldId); |
| 178 | if (id_str.length() == 1) id_str = '0' + id_str; |
| 179 | |
| 180 | return writeIndented( |
| 181 | id_str + ": " + |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 182 | name + " (" + |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 183 | fieldTypeName(fieldType) + ") = "); |
| 184 | } |
| 185 | |
| 186 | uint32_t TDebugProtocol::writeFieldEnd() { |
| 187 | assert(write_state_.back() == STRUCT); |
| 188 | return 0; |
| 189 | } |
| 190 | |
| 191 | uint32_t TDebugProtocol::writeFieldStop() { |
| 192 | return 0; |
| 193 | //writeIndented("***STOP***\n"); |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 194 | } |
| 195 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 196 | uint32_t TDebugProtocol::writeMapBegin(const TType keyType, |
| 197 | const TType valType, |
| 198 | const uint32_t size) { |
| 199 | // TODO(dreiss): Optimize short maps? |
| 200 | uint32_t bsize = 0; |
| 201 | bsize += startItem(); |
| 202 | bsize += writePlain( |
| 203 | "map<" + fieldTypeName(keyType) + "," + fieldTypeName(valType) + ">" |
| 204 | "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| 205 | indentUp(); |
| 206 | write_state_.push_back(MAP_KEY); |
| 207 | return bsize; |
| 208 | } |
| 209 | |
| 210 | uint32_t TDebugProtocol::writeMapEnd() { |
| 211 | indentDown(); |
| 212 | write_state_.pop_back(); |
| 213 | uint32_t size = 0; |
| 214 | size += writeIndented("}"); |
| 215 | size += endItem(); |
| 216 | return size; |
| 217 | } |
| 218 | |
| 219 | uint32_t TDebugProtocol::writeListBegin(const TType elemType, |
| 220 | const uint32_t size) { |
| 221 | // TODO(dreiss): Optimize short arrays. |
| 222 | uint32_t bsize = 0; |
| 223 | bsize += startItem(); |
| 224 | bsize += writePlain( |
| 225 | "list<" + fieldTypeName(elemType) + ">" |
| 226 | "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| 227 | indentUp(); |
| 228 | write_state_.push_back(LIST); |
| 229 | list_idx_.push_back(0); |
| 230 | return bsize; |
| 231 | } |
| 232 | |
| 233 | uint32_t TDebugProtocol::writeListEnd() { |
| 234 | indentDown(); |
| 235 | write_state_.pop_back(); |
| 236 | list_idx_.pop_back(); |
| 237 | uint32_t size = 0; |
| 238 | size += writeIndented("}"); |
| 239 | size += endItem(); |
| 240 | return size; |
| 241 | } |
| 242 | |
| 243 | uint32_t TDebugProtocol::writeSetBegin(const TType elemType, |
| 244 | const uint32_t size) { |
| 245 | // TODO(dreiss): Optimize short sets. |
| 246 | uint32_t bsize = 0; |
| 247 | bsize += startItem(); |
| 248 | bsize += writePlain( |
| 249 | "set<" + fieldTypeName(elemType) + ">" |
| 250 | "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| 251 | indentUp(); |
| 252 | write_state_.push_back(SET); |
| 253 | return bsize; |
| 254 | } |
| 255 | |
| 256 | uint32_t TDebugProtocol::writeSetEnd() { |
| 257 | indentDown(); |
| 258 | write_state_.pop_back(); |
| 259 | uint32_t size = 0; |
| 260 | size += writeIndented("}"); |
| 261 | size += endItem(); |
| 262 | return size; |
| 263 | } |
| 264 | |
| 265 | uint32_t TDebugProtocol::writeBool(const bool value) { |
| 266 | return writeItem(value ? "true" : "false"); |
| 267 | } |
| 268 | |
| 269 | uint32_t TDebugProtocol::writeByte(const int8_t byte) { |
| 270 | return writeItem("0x" + byte_to_hex(byte)); |
| 271 | } |
| 272 | |
| 273 | uint32_t TDebugProtocol::writeI16(const int16_t i16) { |
| 274 | return writeItem(boost::lexical_cast<string>(i16)); |
| 275 | } |
| 276 | |
| 277 | uint32_t TDebugProtocol::writeI32(const int32_t i32) { |
| 278 | return writeItem(boost::lexical_cast<string>(i32)); |
| 279 | } |
| 280 | |
| 281 | uint32_t TDebugProtocol::writeI64(const int64_t i64) { |
| 282 | return writeItem(boost::lexical_cast<string>(i64)); |
| 283 | } |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 284 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 285 | uint32_t TDebugProtocol::writeDouble(const double dub) { |
| 286 | return writeItem(boost::lexical_cast<string>(dub)); |
| 287 | } |
| 288 | |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 289 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 290 | uint32_t TDebugProtocol::writeString(const string& str) { |
| 291 | // XXX Raw/UTF-8? |
| 292 | |
David Reiss | a80f0fb | 2008-04-08 05:06:15 +0000 | [diff] [blame] | 293 | string to_show = str; |
| 294 | if (to_show.length() > (string::size_type)string_limit_) { |
| 295 | to_show = str.substr(0, string_prefix_size_); |
| 296 | to_show += "[...](" + boost::lexical_cast<string>(str.length()) + ")"; |
| 297 | } |
| 298 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 299 | string output = "\""; |
| 300 | |
David Reiss | a80f0fb | 2008-04-08 05:06:15 +0000 | [diff] [blame] | 301 | for (string::const_iterator it = to_show.begin(); it != to_show.end(); ++it) { |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 302 | if (*it == '\\') { |
David Reiss | b54deb1 | 2008-04-08 19:38:49 +0000 | [diff] [blame] | 303 | output += "\\\\"; |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 304 | } else if (*it == '"') { |
| 305 | output += "\\\""; |
| 306 | } else if (std::isprint(*it)) { |
| 307 | output += *it; |
| 308 | } else { |
| 309 | switch (*it) { |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 310 | case '\a': output += "\\a"; break; |
| 311 | case '\b': output += "\\b"; break; |
| 312 | case '\f': output += "\\f"; break; |
| 313 | case '\n': output += "\\n"; break; |
| 314 | case '\r': output += "\\r"; break; |
| 315 | case '\t': output += "\\t"; break; |
| 316 | case '\v': output += "\\v"; break; |
| 317 | default: |
| 318 | output += "\\x"; |
| 319 | output += byte_to_hex(*it); |
| 320 | } |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | output += '\"'; |
| 325 | return writeItem(output); |
| 326 | } |
| 327 | |
David Reiss | c005b1b | 2008-02-15 01:38:18 +0000 | [diff] [blame] | 328 | uint32_t TDebugProtocol::writeBinary(const string& str) { |
David Reiss | a80f0fb | 2008-04-08 05:06:15 +0000 | [diff] [blame] | 329 | // XXX Hex? |
David Reiss | c005b1b | 2008-02-15 01:38:18 +0000 | [diff] [blame] | 330 | return TDebugProtocol::writeString(str); |
| 331 | } |
| 332 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 333 | }}} // facebook::thrift::protocol |