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