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