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