]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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 | ||
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 | ||
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 | ||
82 | void TDebugProtocol::indentUp() { | |
83 | indent_str_ += string(indent_inc, ' '); | |
84 | } | |
85 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
198 | uint32_t TDebugProtocol::writeMessageEnd() { | |
199 | indentDown(); | |
200 | return writeIndented(")\n"); | |
201 | } | |
202 | ||
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 | ||
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 | ||
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 | ||
232 | uint32_t TDebugProtocol::writeFieldEnd() { | |
233 | assert(write_state_.back() == STRUCT); | |
234 | return 0; | |
235 | } | |
236 | ||
237 | uint32_t TDebugProtocol::writeFieldStop() { | |
238 | return 0; | |
239 | // writeIndented("***STOP***\n"); | |
240 | } | |
241 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
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 | ||
309 | uint32_t TDebugProtocol::writeBool(const bool value) { | |
310 | return writeItem(value ? "true" : "false"); | |
311 | } | |
312 | ||
313 | uint32_t TDebugProtocol::writeByte(const int8_t byte) { | |
314 | return writeItem("0x" + byte_to_hex(byte)); | |
315 | } | |
316 | ||
317 | uint32_t TDebugProtocol::writeI16(const int16_t i16) { | |
318 | return writeItem(to_string(i16)); | |
319 | } | |
320 | ||
321 | uint32_t TDebugProtocol::writeI32(const int32_t i32) { | |
322 | return writeItem(to_string(i32)); | |
323 | } | |
324 | ||
325 | uint32_t TDebugProtocol::writeI64(const int64_t i64) { | |
326 | return writeItem(to_string(i64)); | |
327 | } | |
328 | ||
329 | uint32_t TDebugProtocol::writeDouble(const double dub) { | |
330 | return writeItem(to_string(dub)); | |
331 | } | |
332 | ||
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 | ||
387 | uint32_t TDebugProtocol::writeBinary(const string& str) { | |
388 | // XXX Hex? | |
389 | return TDebugProtocol::writeString(str); | |
390 | } | |
391 | } | |
392 | } | |
393 | } // apache::thrift::protocol |