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