]>
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 | #ifndef _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ | |
20 | #define _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ 1 | |
21 | ||
22 | #include <limits> | |
23 | ||
24 | #include "thrift/config.h" | |
25 | ||
26 | /* | |
27 | * TCompactProtocol::i*ToZigzag depend on the fact that the right shift | |
28 | * operator on a signed integer is an arithmetic (sign-extending) shift. | |
29 | * If this is not the case, the current implementation will not work. | |
30 | * If anyone encounters this error, we can try to figure out the best | |
31 | * way to implement an arithmetic right shift on their platform. | |
32 | */ | |
33 | #if !defined(SIGNED_RIGHT_SHIFT_IS) || !defined(ARITHMETIC_RIGHT_SHIFT) | |
34 | # error "Unable to determine the behavior of a signed right shift" | |
35 | #endif | |
36 | #if SIGNED_RIGHT_SHIFT_IS != ARITHMETIC_RIGHT_SHIFT | |
37 | # error "TCompactProtocol currently only works if a signed right shift is arithmetic" | |
38 | #endif | |
39 | ||
40 | #ifdef __GNUC__ | |
41 | #define UNLIKELY(val) (__builtin_expect((val), 0)) | |
42 | #else | |
43 | #define UNLIKELY(val) (val) | |
44 | #endif | |
45 | ||
46 | namespace apache { namespace thrift { namespace protocol { | |
47 | ||
48 | namespace detail { namespace compact { | |
49 | ||
50 | enum Types { | |
51 | CT_STOP = 0x00, | |
52 | CT_BOOLEAN_TRUE = 0x01, | |
53 | CT_BOOLEAN_FALSE = 0x02, | |
54 | CT_BYTE = 0x03, | |
55 | CT_I16 = 0x04, | |
56 | CT_I32 = 0x05, | |
57 | CT_I64 = 0x06, | |
58 | CT_DOUBLE = 0x07, | |
59 | CT_BINARY = 0x08, | |
60 | CT_LIST = 0x09, | |
61 | CT_SET = 0x0A, | |
62 | CT_MAP = 0x0B, | |
63 | CT_STRUCT = 0x0C | |
64 | }; | |
65 | ||
66 | const int8_t TTypeToCType[16] = { | |
67 | CT_STOP, // T_STOP | |
68 | 0, // unused | |
69 | CT_BOOLEAN_TRUE, // T_BOOL | |
70 | CT_BYTE, // T_BYTE | |
71 | CT_DOUBLE, // T_DOUBLE | |
72 | 0, // unused | |
73 | CT_I16, // T_I16 | |
74 | 0, // unused | |
75 | CT_I32, // T_I32 | |
76 | 0, // unused | |
77 | CT_I64, // T_I64 | |
78 | CT_BINARY, // T_STRING | |
79 | CT_STRUCT, // T_STRUCT | |
80 | CT_MAP, // T_MAP | |
81 | CT_SET, // T_SET | |
82 | CT_LIST, // T_LIST | |
83 | }; | |
84 | ||
85 | }} // end detail::compact namespace | |
86 | ||
87 | ||
88 | template <class Transport_> | |
89 | uint32_t TCompactProtocolT<Transport_>::writeMessageBegin( | |
90 | const std::string& name, | |
91 | const TMessageType messageType, | |
92 | const int32_t seqid) { | |
93 | uint32_t wsize = 0; | |
94 | wsize += writeByte(PROTOCOL_ID); | |
95 | wsize += writeByte((VERSION_N & VERSION_MASK) | (((int32_t)messageType << TYPE_SHIFT_AMOUNT) & TYPE_MASK)); | |
96 | wsize += writeVarint32(seqid); | |
97 | wsize += writeString(name); | |
98 | return wsize; | |
99 | } | |
100 | ||
101 | /** | |
102 | * Write a field header containing the field id and field type. If the | |
103 | * difference between the current field id and the last one is small (< 15), | |
104 | * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the | |
105 | * field id will follow the type header as a zigzag varint. | |
106 | */ | |
107 | template <class Transport_> | |
108 | uint32_t TCompactProtocolT<Transport_>::writeFieldBegin(const char* name, | |
109 | const TType fieldType, | |
110 | const int16_t fieldId) { | |
111 | if (fieldType == T_BOOL) { | |
112 | booleanField_.name = name; | |
113 | booleanField_.fieldType = fieldType; | |
114 | booleanField_.fieldId = fieldId; | |
115 | } else { | |
116 | return writeFieldBeginInternal(name, fieldType, fieldId, -1); | |
117 | } | |
118 | return 0; | |
119 | } | |
120 | ||
121 | /** | |
122 | * Write the STOP symbol so we know there are no more fields in this struct. | |
123 | */ | |
124 | template <class Transport_> | |
125 | uint32_t TCompactProtocolT<Transport_>::writeFieldStop() { | |
126 | return writeByte(T_STOP); | |
127 | } | |
128 | ||
129 | /** | |
130 | * Write a struct begin. This doesn't actually put anything on the wire. We | |
131 | * use it as an opportunity to put special placeholder markers on the field | |
132 | * stack so we can get the field id deltas correct. | |
133 | */ | |
134 | template <class Transport_> | |
135 | uint32_t TCompactProtocolT<Transport_>::writeStructBegin(const char* name) { | |
136 | (void) name; | |
137 | lastField_.push(lastFieldId_); | |
138 | lastFieldId_ = 0; | |
139 | return 0; | |
140 | } | |
141 | ||
142 | /** | |
143 | * Write a struct end. This doesn't actually put anything on the wire. We use | |
144 | * this as an opportunity to pop the last field from the current struct off | |
145 | * of the field stack. | |
146 | */ | |
147 | template <class Transport_> | |
148 | uint32_t TCompactProtocolT<Transport_>::writeStructEnd() { | |
149 | lastFieldId_ = lastField_.top(); | |
150 | lastField_.pop(); | |
151 | return 0; | |
152 | } | |
153 | ||
154 | /** | |
155 | * Write a List header. | |
156 | */ | |
157 | template <class Transport_> | |
158 | uint32_t TCompactProtocolT<Transport_>::writeListBegin(const TType elemType, | |
159 | const uint32_t size) { | |
160 | return writeCollectionBegin(elemType, size); | |
161 | } | |
162 | ||
163 | /** | |
164 | * Write a set header. | |
165 | */ | |
166 | template <class Transport_> | |
167 | uint32_t TCompactProtocolT<Transport_>::writeSetBegin(const TType elemType, | |
168 | const uint32_t size) { | |
169 | return writeCollectionBegin(elemType, size); | |
170 | } | |
171 | ||
172 | /** | |
173 | * Write a map header. If the map is empty, omit the key and value type | |
174 | * headers, as we don't need any additional information to skip it. | |
175 | */ | |
176 | template <class Transport_> | |
177 | uint32_t TCompactProtocolT<Transport_>::writeMapBegin(const TType keyType, | |
178 | const TType valType, | |
179 | const uint32_t size) { | |
180 | uint32_t wsize = 0; | |
181 | ||
182 | if (size == 0) { | |
183 | wsize += writeByte(0); | |
184 | } else { | |
185 | wsize += writeVarint32(size); | |
186 | wsize += writeByte(getCompactType(keyType) << 4 | getCompactType(valType)); | |
187 | } | |
188 | return wsize; | |
189 | } | |
190 | ||
191 | /** | |
192 | * Write a boolean value. Potentially, this could be a boolean field, in | |
193 | * which case the field header info isn't written yet. If so, decide what the | |
194 | * right type header is for the value and then write the field header. | |
195 | * Otherwise, write a single byte. | |
196 | */ | |
197 | template <class Transport_> | |
198 | uint32_t TCompactProtocolT<Transport_>::writeBool(const bool value) { | |
199 | uint32_t wsize = 0; | |
200 | ||
201 | if (booleanField_.name != nullptr) { | |
202 | // we haven't written the field header yet | |
203 | wsize | |
204 | += writeFieldBeginInternal(booleanField_.name, | |
205 | booleanField_.fieldType, | |
206 | booleanField_.fieldId, | |
207 | static_cast<int8_t>(value | |
208 | ? detail::compact::CT_BOOLEAN_TRUE | |
209 | : detail::compact::CT_BOOLEAN_FALSE)); | |
210 | booleanField_.name = nullptr; | |
211 | } else { | |
212 | // we're not part of a field, so just write the value | |
213 | wsize | |
214 | += writeByte(static_cast<int8_t>(value | |
215 | ? detail::compact::CT_BOOLEAN_TRUE | |
216 | : detail::compact::CT_BOOLEAN_FALSE)); | |
217 | } | |
218 | return wsize; | |
219 | } | |
220 | ||
221 | template <class Transport_> | |
222 | uint32_t TCompactProtocolT<Transport_>::writeByte(const int8_t byte) { | |
223 | trans_->write((uint8_t*)&byte, 1); | |
224 | return 1; | |
225 | } | |
226 | ||
227 | /** | |
228 | * Write an i16 as a zigzag varint. | |
229 | */ | |
230 | template <class Transport_> | |
231 | uint32_t TCompactProtocolT<Transport_>::writeI16(const int16_t i16) { | |
232 | return writeVarint32(i32ToZigzag(i16)); | |
233 | } | |
234 | ||
235 | /** | |
236 | * Write an i32 as a zigzag varint. | |
237 | */ | |
238 | template <class Transport_> | |
239 | uint32_t TCompactProtocolT<Transport_>::writeI32(const int32_t i32) { | |
240 | return writeVarint32(i32ToZigzag(i32)); | |
241 | } | |
242 | ||
243 | /** | |
244 | * Write an i64 as a zigzag varint. | |
245 | */ | |
246 | template <class Transport_> | |
247 | uint32_t TCompactProtocolT<Transport_>::writeI64(const int64_t i64) { | |
248 | return writeVarint64(i64ToZigzag(i64)); | |
249 | } | |
250 | ||
251 | /** | |
252 | * Write a double to the wire as 8 bytes. | |
253 | */ | |
254 | template <class Transport_> | |
255 | uint32_t TCompactProtocolT<Transport_>::writeDouble(const double dub) { | |
256 | static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)"); | |
257 | static_assert(std::numeric_limits<double>::is_iec559, "std::numeric_limits<double>::is_iec559"); | |
258 | ||
259 | auto bits = bitwise_cast<uint64_t>(dub); | |
260 | bits = THRIFT_htolell(bits); | |
261 | trans_->write((uint8_t*)&bits, 8); | |
262 | return 8; | |
263 | } | |
264 | ||
265 | /** | |
266 | * Write a string to the wire with a varint size preceding. | |
267 | */ | |
268 | template <class Transport_> | |
269 | uint32_t TCompactProtocolT<Transport_>::writeString(const std::string& str) { | |
270 | return writeBinary(str); | |
271 | } | |
272 | ||
273 | template <class Transport_> | |
274 | uint32_t TCompactProtocolT<Transport_>::writeBinary(const std::string& str) { | |
275 | if(str.size() > (std::numeric_limits<uint32_t>::max)()) | |
276 | throw TProtocolException(TProtocolException::SIZE_LIMIT); | |
277 | auto ssize = static_cast<uint32_t>(str.size()); | |
278 | uint32_t wsize = writeVarint32(ssize) ; | |
279 | // checking ssize + wsize > uint_max, but we don't want to overflow while checking for overflows. | |
280 | // transforming the check to ssize > uint_max - wsize | |
281 | if(ssize > (std::numeric_limits<uint32_t>::max)() - wsize) | |
282 | throw TProtocolException(TProtocolException::SIZE_LIMIT); | |
283 | wsize += ssize; | |
284 | trans_->write((uint8_t*)str.data(), ssize); | |
285 | return wsize; | |
286 | } | |
287 | ||
288 | // | |
289 | // Internal Writing methods | |
290 | // | |
291 | ||
292 | /** | |
293 | * The workhorse of writeFieldBegin. It has the option of doing a | |
294 | * 'type override' of the type header. This is used specifically in the | |
295 | * boolean field case. | |
296 | */ | |
297 | template <class Transport_> | |
298 | int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal( | |
299 | const char* name, | |
300 | const TType fieldType, | |
301 | const int16_t fieldId, | |
302 | int8_t typeOverride) { | |
303 | (void) name; | |
304 | uint32_t wsize = 0; | |
305 | ||
306 | // if there's a type override, use that. | |
307 | int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride); | |
308 | ||
309 | // check if we can use delta encoding for the field id | |
310 | if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) { | |
311 | // write them together | |
312 | wsize += writeByte(static_cast<int8_t>((fieldId - lastFieldId_) | |
313 | << 4 | typeToWrite)); | |
314 | } else { | |
315 | // write them separate | |
316 | wsize += writeByte(typeToWrite); | |
317 | wsize += writeI16(fieldId); | |
318 | } | |
319 | ||
320 | lastFieldId_ = fieldId; | |
321 | return wsize; | |
322 | } | |
323 | ||
324 | /** | |
325 | * Abstract method for writing the start of lists and sets. List and sets on | |
326 | * the wire differ only by the type indicator. | |
327 | */ | |
328 | template <class Transport_> | |
329 | uint32_t TCompactProtocolT<Transport_>::writeCollectionBegin(const TType elemType, | |
330 | int32_t size) { | |
331 | uint32_t wsize = 0; | |
332 | if (size <= 14) { | |
333 | wsize += writeByte(static_cast<int8_t>(size | |
334 | << 4 | getCompactType(elemType))); | |
335 | } else { | |
336 | wsize += writeByte(0xf0 | getCompactType(elemType)); | |
337 | wsize += writeVarint32(size); | |
338 | } | |
339 | return wsize; | |
340 | } | |
341 | ||
342 | /** | |
343 | * Write an i32 as a varint. Results in 1-5 bytes on the wire. | |
344 | */ | |
345 | template <class Transport_> | |
346 | uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) { | |
347 | uint8_t buf[5]; | |
348 | uint32_t wsize = 0; | |
349 | ||
350 | while (true) { | |
351 | if ((n & ~0x7F) == 0) { | |
352 | buf[wsize++] = (int8_t)n; | |
353 | break; | |
354 | } else { | |
355 | buf[wsize++] = (int8_t)((n & 0x7F) | 0x80); | |
356 | n >>= 7; | |
357 | } | |
358 | } | |
359 | trans_->write(buf, wsize); | |
360 | return wsize; | |
361 | } | |
362 | ||
363 | /** | |
364 | * Write an i64 as a varint. Results in 1-10 bytes on the wire. | |
365 | */ | |
366 | template <class Transport_> | |
367 | uint32_t TCompactProtocolT<Transport_>::writeVarint64(uint64_t n) { | |
368 | uint8_t buf[10]; | |
369 | uint32_t wsize = 0; | |
370 | ||
371 | while (true) { | |
372 | if ((n & ~0x7FL) == 0) { | |
373 | buf[wsize++] = (int8_t)n; | |
374 | break; | |
375 | } else { | |
376 | buf[wsize++] = (int8_t)((n & 0x7F) | 0x80); | |
377 | n >>= 7; | |
378 | } | |
379 | } | |
380 | trans_->write(buf, wsize); | |
381 | return wsize; | |
382 | } | |
383 | ||
384 | /** | |
385 | * Convert l into a zigzag long. This allows negative numbers to be | |
386 | * represented compactly as a varint. | |
387 | */ | |
388 | template <class Transport_> | |
389 | uint64_t TCompactProtocolT<Transport_>::i64ToZigzag(const int64_t l) { | |
390 | return (static_cast<uint64_t>(l) << 1) ^ (l >> 63); | |
391 | } | |
392 | ||
393 | /** | |
394 | * Convert n into a zigzag int. This allows negative numbers to be | |
395 | * represented compactly as a varint. | |
396 | */ | |
397 | template <class Transport_> | |
398 | uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) { | |
399 | return (static_cast<uint32_t>(n) << 1) ^ (n >> 31); | |
400 | } | |
401 | ||
402 | /** | |
403 | * Given a TType value, find the appropriate detail::compact::Types value | |
404 | */ | |
405 | template <class Transport_> | |
406 | int8_t TCompactProtocolT<Transport_>::getCompactType(const TType ttype) { | |
407 | return detail::compact::TTypeToCType[ttype]; | |
408 | } | |
409 | ||
410 | // | |
411 | // Reading Methods | |
412 | // | |
413 | ||
414 | /** | |
415 | * Read a message header. | |
416 | */ | |
417 | template <class Transport_> | |
418 | uint32_t TCompactProtocolT<Transport_>::readMessageBegin( | |
419 | std::string& name, | |
420 | TMessageType& messageType, | |
421 | int32_t& seqid) { | |
422 | uint32_t rsize = 0; | |
423 | int8_t protocolId; | |
424 | int8_t versionAndType; | |
425 | int8_t version; | |
426 | ||
427 | rsize += readByte(protocolId); | |
428 | if (protocolId != PROTOCOL_ID) { | |
429 | throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier"); | |
430 | } | |
431 | ||
432 | rsize += readByte(versionAndType); | |
433 | version = (int8_t)(versionAndType & VERSION_MASK); | |
434 | if (version != VERSION_N) { | |
435 | throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version"); | |
436 | } | |
437 | ||
438 | messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS); | |
439 | rsize += readVarint32(seqid); | |
440 | rsize += readString(name); | |
441 | ||
442 | return rsize; | |
443 | } | |
444 | ||
445 | /** | |
446 | * Read a struct begin. There's nothing on the wire for this, but it is our | |
447 | * opportunity to push a new struct begin marker on the field stack. | |
448 | */ | |
449 | template <class Transport_> | |
450 | uint32_t TCompactProtocolT<Transport_>::readStructBegin(std::string& name) { | |
451 | name = ""; | |
452 | lastField_.push(lastFieldId_); | |
453 | lastFieldId_ = 0; | |
454 | return 0; | |
455 | } | |
456 | ||
457 | /** | |
458 | * Doesn't actually consume any wire data, just removes the last field for | |
459 | * this struct from the field stack. | |
460 | */ | |
461 | template <class Transport_> | |
462 | uint32_t TCompactProtocolT<Transport_>::readStructEnd() { | |
463 | lastFieldId_ = lastField_.top(); | |
464 | lastField_.pop(); | |
465 | return 0; | |
466 | } | |
467 | ||
468 | /** | |
469 | * Read a field header off the wire. | |
470 | */ | |
471 | template <class Transport_> | |
472 | uint32_t TCompactProtocolT<Transport_>::readFieldBegin(std::string& name, | |
473 | TType& fieldType, | |
474 | int16_t& fieldId) { | |
475 | (void) name; | |
476 | uint32_t rsize = 0; | |
477 | int8_t byte; | |
478 | int8_t type; | |
479 | ||
480 | rsize += readByte(byte); | |
481 | type = (byte & 0x0f); | |
482 | ||
483 | // if it's a stop, then we can return immediately, as the struct is over. | |
484 | if (type == T_STOP) { | |
485 | fieldType = T_STOP; | |
486 | fieldId = 0; | |
487 | return rsize; | |
488 | } | |
489 | ||
490 | // mask off the 4 MSB of the type header. it could contain a field id delta. | |
491 | auto modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4); | |
492 | if (modifier == 0) { | |
493 | // not a delta, look ahead for the zigzag varint field id. | |
494 | rsize += readI16(fieldId); | |
495 | } else { | |
496 | fieldId = (int16_t)(lastFieldId_ + modifier); | |
497 | } | |
498 | fieldType = getTType(type); | |
499 | ||
500 | // if this happens to be a boolean field, the value is encoded in the type | |
501 | if (type == detail::compact::CT_BOOLEAN_TRUE || | |
502 | type == detail::compact::CT_BOOLEAN_FALSE) { | |
503 | // save the boolean value in a special instance variable. | |
504 | boolValue_.hasBoolValue = true; | |
505 | boolValue_.boolValue = | |
506 | (type == detail::compact::CT_BOOLEAN_TRUE ? true : false); | |
507 | } | |
508 | ||
509 | // push the new field onto the field stack so we can keep the deltas going. | |
510 | lastFieldId_ = fieldId; | |
511 | return rsize; | |
512 | } | |
513 | ||
514 | /** | |
515 | * Read a map header off the wire. If the size is zero, skip reading the key | |
516 | * and value type. This means that 0-length maps will yield TMaps without the | |
517 | * "correct" types. | |
518 | */ | |
519 | template <class Transport_> | |
520 | uint32_t TCompactProtocolT<Transport_>::readMapBegin(TType& keyType, | |
521 | TType& valType, | |
522 | uint32_t& size) { | |
523 | uint32_t rsize = 0; | |
524 | int8_t kvType = 0; | |
525 | int32_t msize = 0; | |
526 | ||
527 | rsize += readVarint32(msize); | |
528 | if (msize != 0) | |
529 | rsize += readByte(kvType); | |
530 | ||
531 | if (msize < 0) { | |
532 | throw TProtocolException(TProtocolException::NEGATIVE_SIZE); | |
533 | } else if (container_limit_ && msize > container_limit_) { | |
534 | throw TProtocolException(TProtocolException::SIZE_LIMIT); | |
535 | } | |
536 | ||
537 | keyType = getTType((int8_t)((uint8_t)kvType >> 4)); | |
538 | valType = getTType((int8_t)((uint8_t)kvType & 0xf)); | |
539 | size = (uint32_t)msize; | |
540 | ||
541 | return rsize; | |
542 | } | |
543 | ||
544 | /** | |
545 | * Read a list header off the wire. If the list size is 0-14, the size will | |
546 | * be packed into the element type header. If it's a longer list, the 4 MSB | |
547 | * of the element type header will be 0xF, and a varint will follow with the | |
548 | * true size. | |
549 | */ | |
550 | template <class Transport_> | |
551 | uint32_t TCompactProtocolT<Transport_>::readListBegin(TType& elemType, | |
552 | uint32_t& size) { | |
553 | int8_t size_and_type; | |
554 | uint32_t rsize = 0; | |
555 | int32_t lsize; | |
556 | ||
557 | rsize += readByte(size_and_type); | |
558 | ||
559 | lsize = ((uint8_t)size_and_type >> 4) & 0x0f; | |
560 | if (lsize == 15) { | |
561 | rsize += readVarint32(lsize); | |
562 | } | |
563 | ||
564 | if (lsize < 0) { | |
565 | throw TProtocolException(TProtocolException::NEGATIVE_SIZE); | |
566 | } else if (container_limit_ && lsize > container_limit_) { | |
567 | throw TProtocolException(TProtocolException::SIZE_LIMIT); | |
568 | } | |
569 | ||
570 | elemType = getTType((int8_t)(size_and_type & 0x0f)); | |
571 | size = (uint32_t)lsize; | |
572 | ||
573 | return rsize; | |
574 | } | |
575 | ||
576 | /** | |
577 | * Read a set header off the wire. If the set size is 0-14, the size will | |
578 | * be packed into the element type header. If it's a longer set, the 4 MSB | |
579 | * of the element type header will be 0xF, and a varint will follow with the | |
580 | * true size. | |
581 | */ | |
582 | template <class Transport_> | |
583 | uint32_t TCompactProtocolT<Transport_>::readSetBegin(TType& elemType, | |
584 | uint32_t& size) { | |
585 | return readListBegin(elemType, size); | |
586 | } | |
587 | ||
588 | /** | |
589 | * Read a boolean off the wire. If this is a boolean field, the value should | |
590 | * already have been read during readFieldBegin, so we'll just consume the | |
591 | * pre-stored value. Otherwise, read a byte. | |
592 | */ | |
593 | template <class Transport_> | |
594 | uint32_t TCompactProtocolT<Transport_>::readBool(bool& value) { | |
595 | if (boolValue_.hasBoolValue == true) { | |
596 | value = boolValue_.boolValue; | |
597 | boolValue_.hasBoolValue = false; | |
598 | return 0; | |
599 | } else { | |
600 | int8_t val; | |
601 | readByte(val); | |
602 | value = (val == detail::compact::CT_BOOLEAN_TRUE); | |
603 | return 1; | |
604 | } | |
605 | } | |
606 | ||
607 | /** | |
608 | * Read a single byte off the wire. Nothing interesting here. | |
609 | */ | |
610 | template <class Transport_> | |
611 | uint32_t TCompactProtocolT<Transport_>::readByte(int8_t& byte) { | |
612 | uint8_t b[1]; | |
613 | trans_->readAll(b, 1); | |
614 | byte = *(int8_t*)b; | |
615 | return 1; | |
616 | } | |
617 | ||
618 | /** | |
619 | * Read an i16 from the wire as a zigzag varint. | |
620 | */ | |
621 | template <class Transport_> | |
622 | uint32_t TCompactProtocolT<Transport_>::readI16(int16_t& i16) { | |
623 | int32_t value; | |
624 | uint32_t rsize = readVarint32(value); | |
625 | i16 = (int16_t)zigzagToI32(value); | |
626 | return rsize; | |
627 | } | |
628 | ||
629 | /** | |
630 | * Read an i32 from the wire as a zigzag varint. | |
631 | */ | |
632 | template <class Transport_> | |
633 | uint32_t TCompactProtocolT<Transport_>::readI32(int32_t& i32) { | |
634 | int32_t value; | |
635 | uint32_t rsize = readVarint32(value); | |
636 | i32 = zigzagToI32(value); | |
637 | return rsize; | |
638 | } | |
639 | ||
640 | /** | |
641 | * Read an i64 from the wire as a zigzag varint. | |
642 | */ | |
643 | template <class Transport_> | |
644 | uint32_t TCompactProtocolT<Transport_>::readI64(int64_t& i64) { | |
645 | int64_t value; | |
646 | uint32_t rsize = readVarint64(value); | |
647 | i64 = zigzagToI64(value); | |
648 | return rsize; | |
649 | } | |
650 | ||
651 | /** | |
652 | * No magic here - just read a double off the wire. | |
653 | */ | |
654 | template <class Transport_> | |
655 | uint32_t TCompactProtocolT<Transport_>::readDouble(double& dub) { | |
656 | static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) == sizeof(uint64_t)"); | |
657 | static_assert(std::numeric_limits<double>::is_iec559, "std::numeric_limits<double>::is_iec559"); | |
658 | ||
659 | union { | |
660 | uint64_t bits; | |
661 | uint8_t b[8]; | |
662 | } u; | |
663 | trans_->readAll(u.b, 8); | |
664 | u.bits = THRIFT_letohll(u.bits); | |
665 | dub = bitwise_cast<double>(u.bits); | |
666 | return 8; | |
667 | } | |
668 | ||
669 | template <class Transport_> | |
670 | uint32_t TCompactProtocolT<Transport_>::readString(std::string& str) { | |
671 | return readBinary(str); | |
672 | } | |
673 | ||
674 | /** | |
675 | * Read a byte[] from the wire. | |
676 | */ | |
677 | template <class Transport_> | |
678 | uint32_t TCompactProtocolT<Transport_>::readBinary(std::string& str) { | |
679 | int32_t rsize = 0; | |
680 | int32_t size; | |
681 | ||
682 | rsize += readVarint32(size); | |
683 | // Catch empty string case | |
684 | if (size == 0) { | |
685 | str = ""; | |
686 | return rsize; | |
687 | } | |
688 | ||
689 | // Catch error cases | |
690 | if (size < 0) { | |
691 | throw TProtocolException(TProtocolException::NEGATIVE_SIZE); | |
692 | } | |
693 | if (string_limit_ > 0 && size > string_limit_) { | |
694 | throw TProtocolException(TProtocolException::SIZE_LIMIT); | |
695 | } | |
696 | ||
697 | // Use the heap here to prevent stack overflow for v. large strings | |
698 | if (size > string_buf_size_ || string_buf_ == nullptr) { | |
699 | void* new_string_buf = std::realloc(string_buf_, (uint32_t)size); | |
700 | if (new_string_buf == nullptr) { | |
701 | throw std::bad_alloc(); | |
702 | } | |
703 | string_buf_ = (uint8_t*)new_string_buf; | |
704 | string_buf_size_ = size; | |
705 | } | |
706 | trans_->readAll(string_buf_, size); | |
707 | str.assign((char*)string_buf_, size); | |
708 | ||
709 | return rsize + (uint32_t)size; | |
710 | } | |
711 | ||
712 | /** | |
713 | * Read an i32 from the wire as a varint. The MSB of each byte is set | |
714 | * if there is another byte to follow. This can read up to 5 bytes. | |
715 | */ | |
716 | template <class Transport_> | |
717 | uint32_t TCompactProtocolT<Transport_>::readVarint32(int32_t& i32) { | |
718 | int64_t val; | |
719 | uint32_t rsize = readVarint64(val); | |
720 | i32 = (int32_t)val; | |
721 | return rsize; | |
722 | } | |
723 | ||
724 | /** | |
725 | * Read an i64 from the wire as a proper varint. The MSB of each byte is set | |
726 | * if there is another byte to follow. This can read up to 10 bytes. | |
727 | */ | |
728 | template <class Transport_> | |
729 | uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) { | |
730 | uint32_t rsize = 0; | |
731 | uint64_t val = 0; | |
732 | int shift = 0; | |
733 | uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes. | |
734 | uint32_t buf_size = sizeof(buf); | |
735 | const uint8_t* borrowed = trans_->borrow(buf, &buf_size); | |
736 | ||
737 | // Fast path. | |
738 | if (borrowed != nullptr) { | |
739 | while (true) { | |
740 | uint8_t byte = borrowed[rsize]; | |
741 | rsize++; | |
742 | val |= (uint64_t)(byte & 0x7f) << shift; | |
743 | shift += 7; | |
744 | if (!(byte & 0x80)) { | |
745 | i64 = val; | |
746 | trans_->consume(rsize); | |
747 | return rsize; | |
748 | } | |
749 | // Have to check for invalid data so we don't crash. | |
750 | if (UNLIKELY(rsize == sizeof(buf))) { | |
751 | throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes."); | |
752 | } | |
753 | } | |
754 | } | |
755 | ||
756 | // Slow path. | |
757 | else { | |
758 | while (true) { | |
759 | uint8_t byte; | |
760 | rsize += trans_->readAll(&byte, 1); | |
761 | val |= (uint64_t)(byte & 0x7f) << shift; | |
762 | shift += 7; | |
763 | if (!(byte & 0x80)) { | |
764 | i64 = val; | |
765 | return rsize; | |
766 | } | |
767 | // Might as well check for invalid data on the slow path too. | |
768 | if (UNLIKELY(rsize >= sizeof(buf))) { | |
769 | throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes."); | |
770 | } | |
771 | } | |
772 | } | |
773 | } | |
774 | ||
775 | /** | |
776 | * Convert from zigzag int to int. | |
777 | */ | |
778 | template <class Transport_> | |
779 | int32_t TCompactProtocolT<Transport_>::zigzagToI32(uint32_t n) { | |
780 | return (n >> 1) ^ static_cast<uint32_t>(-static_cast<int32_t>(n & 1)); | |
781 | } | |
782 | ||
783 | /** | |
784 | * Convert from zigzag long to long. | |
785 | */ | |
786 | template <class Transport_> | |
787 | int64_t TCompactProtocolT<Transport_>::zigzagToI64(uint64_t n) { | |
788 | return (n >> 1) ^ static_cast<uint64_t>(-static_cast<int64_t>(n & 1)); | |
789 | } | |
790 | ||
791 | template <class Transport_> | |
792 | TType TCompactProtocolT<Transport_>::getTType(int8_t type) { | |
793 | switch (type) { | |
794 | case T_STOP: | |
795 | return T_STOP; | |
796 | case detail::compact::CT_BOOLEAN_FALSE: | |
797 | case detail::compact::CT_BOOLEAN_TRUE: | |
798 | return T_BOOL; | |
799 | case detail::compact::CT_BYTE: | |
800 | return T_BYTE; | |
801 | case detail::compact::CT_I16: | |
802 | return T_I16; | |
803 | case detail::compact::CT_I32: | |
804 | return T_I32; | |
805 | case detail::compact::CT_I64: | |
806 | return T_I64; | |
807 | case detail::compact::CT_DOUBLE: | |
808 | return T_DOUBLE; | |
809 | case detail::compact::CT_BINARY: | |
810 | return T_STRING; | |
811 | case detail::compact::CT_LIST: | |
812 | return T_LIST; | |
813 | case detail::compact::CT_SET: | |
814 | return T_SET; | |
815 | case detail::compact::CT_MAP: | |
816 | return T_MAP; | |
817 | case detail::compact::CT_STRUCT: | |
818 | return T_STRUCT; | |
819 | default: | |
820 | throw TException(std::string("don't know what type: ") + (char)type); | |
821 | } | |
822 | } | |
823 | ||
824 | }}} // apache::thrift::protocol | |
825 | ||
826 | #endif // _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ |