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 != NULL) { |
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 = NULL; |
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 | BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t)); |
257 | BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559); |
258 | |
259 | uint64_t 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 | uint32_t 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 | int16_t 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 | BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t)); |
657 | BOOST_STATIC_ASSERT(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_ == NULL) { |
699 | void* new_string_buf = std::realloc(string_buf_, (uint32_t)size); |
700 | if (new_string_buf == NULL) { |
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 != NULL) { |
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_ |
827 | |