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