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) { |
| 138 | throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, |
| 139 | "TDebugProtocol does not support messages (yet)."); |
| 140 | } |
| 141 | |
| 142 | uint32_t TDebugProtocol::writeMessageEnd() { |
| 143 | throw TProtocolException(TProtocolException::NOT_IMPLEMENTED, |
| 144 | "TDebugProtocol does not support messages (yet)."); |
| 145 | } |
| 146 | |
| 147 | uint32_t TDebugProtocol::writeStructBegin(const string& name) { |
| 148 | uint32_t size = 0; |
| 149 | size += startItem(); |
| 150 | size += writePlain(name + " {\n"); |
| 151 | indentUp(); |
| 152 | write_state_.push_back(STRUCT); |
| 153 | return size; |
| 154 | } |
| 155 | |
| 156 | uint32_t TDebugProtocol::writeStructEnd() { |
| 157 | indentDown(); |
| 158 | write_state_.pop_back(); |
| 159 | uint32_t size = 0; |
| 160 | size += writeIndented("}"); |
| 161 | size += endItem(); |
| 162 | return size; |
| 163 | } |
| 164 | |
| 165 | uint32_t TDebugProtocol::writeFieldBegin(const string& name, |
| 166 | const TType fieldType, |
| 167 | const int16_t fieldId) { |
| 168 | // sprintf(id_str, "%02d", fieldId); |
| 169 | string id_str = boost::lexical_cast<string>(fieldId); |
| 170 | if (id_str.length() == 1) id_str = '0' + id_str; |
| 171 | |
| 172 | return writeIndented( |
| 173 | id_str + ": " + |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame^] | 174 | name + " (" + |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 175 | fieldTypeName(fieldType) + ") = "); |
| 176 | } |
| 177 | |
| 178 | uint32_t TDebugProtocol::writeFieldEnd() { |
| 179 | assert(write_state_.back() == STRUCT); |
| 180 | return 0; |
| 181 | } |
| 182 | |
| 183 | uint32_t TDebugProtocol::writeFieldStop() { |
| 184 | return 0; |
| 185 | //writeIndented("***STOP***\n"); |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame^] | 186 | } |
| 187 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 188 | uint32_t TDebugProtocol::writeMapBegin(const TType keyType, |
| 189 | const TType valType, |
| 190 | const uint32_t size) { |
| 191 | // TODO(dreiss): Optimize short maps? |
| 192 | uint32_t bsize = 0; |
| 193 | bsize += startItem(); |
| 194 | bsize += writePlain( |
| 195 | "map<" + fieldTypeName(keyType) + "," + fieldTypeName(valType) + ">" |
| 196 | "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| 197 | indentUp(); |
| 198 | write_state_.push_back(MAP_KEY); |
| 199 | return bsize; |
| 200 | } |
| 201 | |
| 202 | uint32_t TDebugProtocol::writeMapEnd() { |
| 203 | indentDown(); |
| 204 | write_state_.pop_back(); |
| 205 | uint32_t size = 0; |
| 206 | size += writeIndented("}"); |
| 207 | size += endItem(); |
| 208 | return size; |
| 209 | } |
| 210 | |
| 211 | uint32_t TDebugProtocol::writeListBegin(const TType elemType, |
| 212 | const uint32_t size) { |
| 213 | // TODO(dreiss): Optimize short arrays. |
| 214 | uint32_t bsize = 0; |
| 215 | bsize += startItem(); |
| 216 | bsize += writePlain( |
| 217 | "list<" + fieldTypeName(elemType) + ">" |
| 218 | "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| 219 | indentUp(); |
| 220 | write_state_.push_back(LIST); |
| 221 | list_idx_.push_back(0); |
| 222 | return bsize; |
| 223 | } |
| 224 | |
| 225 | uint32_t TDebugProtocol::writeListEnd() { |
| 226 | indentDown(); |
| 227 | write_state_.pop_back(); |
| 228 | list_idx_.pop_back(); |
| 229 | uint32_t size = 0; |
| 230 | size += writeIndented("}"); |
| 231 | size += endItem(); |
| 232 | return size; |
| 233 | } |
| 234 | |
| 235 | uint32_t TDebugProtocol::writeSetBegin(const TType elemType, |
| 236 | const uint32_t size) { |
| 237 | // TODO(dreiss): Optimize short sets. |
| 238 | uint32_t bsize = 0; |
| 239 | bsize += startItem(); |
| 240 | bsize += writePlain( |
| 241 | "set<" + fieldTypeName(elemType) + ">" |
| 242 | "[" + boost::lexical_cast<string>(size) + "] {\n"); |
| 243 | indentUp(); |
| 244 | write_state_.push_back(SET); |
| 245 | return bsize; |
| 246 | } |
| 247 | |
| 248 | uint32_t TDebugProtocol::writeSetEnd() { |
| 249 | indentDown(); |
| 250 | write_state_.pop_back(); |
| 251 | uint32_t size = 0; |
| 252 | size += writeIndented("}"); |
| 253 | size += endItem(); |
| 254 | return size; |
| 255 | } |
| 256 | |
| 257 | uint32_t TDebugProtocol::writeBool(const bool value) { |
| 258 | return writeItem(value ? "true" : "false"); |
| 259 | } |
| 260 | |
| 261 | uint32_t TDebugProtocol::writeByte(const int8_t byte) { |
| 262 | return writeItem("0x" + byte_to_hex(byte)); |
| 263 | } |
| 264 | |
| 265 | uint32_t TDebugProtocol::writeI16(const int16_t i16) { |
| 266 | return writeItem(boost::lexical_cast<string>(i16)); |
| 267 | } |
| 268 | |
| 269 | uint32_t TDebugProtocol::writeI32(const int32_t i32) { |
| 270 | return writeItem(boost::lexical_cast<string>(i32)); |
| 271 | } |
| 272 | |
| 273 | uint32_t TDebugProtocol::writeI64(const int64_t i64) { |
| 274 | return writeItem(boost::lexical_cast<string>(i64)); |
| 275 | } |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame^] | 276 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 277 | uint32_t TDebugProtocol::writeDouble(const double dub) { |
| 278 | return writeItem(boost::lexical_cast<string>(dub)); |
| 279 | } |
| 280 | |
David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame^] | 281 | |
David Reiss | 00dcccf | 2007-07-21 01:18:10 +0000 | [diff] [blame] | 282 | uint32_t TDebugProtocol::writeString(const string& str) { |
| 283 | // XXX Raw/UTF-8? |
| 284 | |
| 285 | string output = "\""; |
| 286 | |
| 287 | for (string::const_iterator it = str.begin(); it != str.end(); ++it) { |
| 288 | if (*it == '\\') { |
| 289 | output += "\\"; |
| 290 | } else if (*it == '"') { |
| 291 | output += "\\\""; |
| 292 | } else if (std::isprint(*it)) { |
| 293 | output += *it; |
| 294 | } else { |
| 295 | switch (*it) { |
| 296 | case '\"': output += "\\\""; break; |
| 297 | case '\a': output += "\\a"; break; |
| 298 | case '\b': output += "\\b"; break; |
| 299 | case '\f': output += "\\f"; break; |
| 300 | case '\n': output += "\\n"; break; |
| 301 | case '\r': output += "\\r"; break; |
| 302 | case '\t': output += "\\t"; break; |
| 303 | case '\v': output += "\\v"; break; |
| 304 | default: |
| 305 | output += "\\x"; |
| 306 | output += byte_to_hex(*it); |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | output += '\"'; |
| 312 | return writeItem(output); |
| 313 | } |
| 314 | |
| 315 | }}} // facebook::thrift::protocol |