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