blob: 8ad999c2a41880254dcc885fdd6f77c600c79dd9 [file] [log] [blame]
David Reisse4d4ea02009-04-02 21:37:17 +00001/*
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 */
David Reisse71115b2010-10-06 17:09:56 +000019#ifndef _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_
20#define _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_ 1
David Reisse4d4ea02009-04-02 21:37:17 +000021
Bryan Duxbury141eab42009-04-03 15:05:28 +000022#include <limits>
David Reisse4d4ea02009-04-02 21:37:17 +000023
24/*
25 * TCompactProtocol::i*ToZigzag depend on the fact that the right shift
26 * operator on a signed integer is an arithmetic (sign-extending) shift.
27 * If this is not the case, the current implementation will not work.
28 * If anyone encounters this error, we can try to figure out the best
29 * way to implement an arithmetic right shift on their platform.
30 */
31#if !defined(SIGNED_RIGHT_SHIFT_IS) || !defined(ARITHMETIC_RIGHT_SHIFT)
32# error "Unable to determine the behavior of a signed right shift"
33#endif
34#if SIGNED_RIGHT_SHIFT_IS != ARITHMETIC_RIGHT_SHIFT
David Reisse71115b2010-10-06 17:09:56 +000035# error "TCompactProtocol currently only works if a signed right shift is arithmetic"
David Reisse4d4ea02009-04-02 21:37:17 +000036#endif
37
38#ifdef __GNUC__
39#define UNLIKELY(val) (__builtin_expect((val), 0))
40#else
41#define UNLIKELY(val) (val)
42#endif
43
44namespace apache { namespace thrift { namespace protocol {
45
David Reisse71115b2010-10-06 17:09:56 +000046namespace detail { namespace compact {
47
48enum Types {
49 CT_STOP = 0x00,
50 CT_BOOLEAN_TRUE = 0x01,
51 CT_BOOLEAN_FALSE = 0x02,
52 CT_BYTE = 0x03,
53 CT_I16 = 0x04,
54 CT_I32 = 0x05,
55 CT_I64 = 0x06,
56 CT_DOUBLE = 0x07,
57 CT_BINARY = 0x08,
58 CT_LIST = 0x09,
59 CT_SET = 0x0A,
60 CT_MAP = 0x0B,
61 CT_STRUCT = 0x0C,
62};
63
64const int8_t TTypeToCType[16] = {
65 CT_STOP, // T_STOP
66 0, // unused
67 CT_BOOLEAN_TRUE, // T_BOOL
68 CT_BYTE, // T_BYTE
69 CT_DOUBLE, // T_DOUBLE
70 0, // unused
71 CT_I16, // T_I16
72 0, // unused
73 CT_I32, // T_I32
74 0, // unused
75 CT_I64, // T_I64
76 CT_BINARY, // T_STRING
77 CT_STRUCT, // T_STRUCT
78 CT_MAP, // T_MAP
79 CT_SET, // T_SET
80 CT_LIST, // T_LIST
81};
82
83}} // end detail::compact namespace
David Reisse4d4ea02009-04-02 21:37:17 +000084
85
David Reisse71115b2010-10-06 17:09:56 +000086template <class Transport_>
87uint32_t TCompactProtocolT<Transport_>::writeMessageBegin(
88 const std::string& name,
89 const TMessageType messageType,
90 const int32_t seqid) {
David Reisse4d4ea02009-04-02 21:37:17 +000091 uint32_t wsize = 0;
92 wsize += writeByte(PROTOCOL_ID);
93 wsize += writeByte((VERSION_N & VERSION_MASK) | (((int32_t)messageType << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
94 wsize += writeVarint32(seqid);
95 wsize += writeString(name);
96 return wsize;
97}
98
99/**
100 * Write a field header containing the field id and field type. If the
101 * difference between the current field id and the last one is small (< 15),
102 * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
103 * field id will follow the type header as a zigzag varint.
104 */
David Reisse71115b2010-10-06 17:09:56 +0000105template <class Transport_>
106uint32_t TCompactProtocolT<Transport_>::writeFieldBegin(const char* name,
107 const TType fieldType,
108 const int16_t fieldId) {
David Reisse4d4ea02009-04-02 21:37:17 +0000109 if (fieldType == T_BOOL) {
110 booleanField_.name = name;
111 booleanField_.fieldType = fieldType;
112 booleanField_.fieldId = fieldId;
113 } else {
114 return writeFieldBeginInternal(name, fieldType, fieldId, -1);
115 }
116 return 0;
117}
118
119/**
120 * Write the STOP symbol so we know there are no more fields in this struct.
121 */
David Reisse71115b2010-10-06 17:09:56 +0000122template <class Transport_>
123uint32_t TCompactProtocolT<Transport_>::writeFieldStop() {
David Reisse4d4ea02009-04-02 21:37:17 +0000124 return writeByte(T_STOP);
125}
126
127/**
128 * Write a struct begin. This doesn't actually put anything on the wire. We
129 * use it as an opportunity to put special placeholder markers on the field
130 * stack so we can get the field id deltas correct.
131 */
David Reisse71115b2010-10-06 17:09:56 +0000132template <class Transport_>
133uint32_t TCompactProtocolT<Transport_>::writeStructBegin(const char* name) {
David Reisse4d4ea02009-04-02 21:37:17 +0000134 lastField_.push(lastFieldId_);
135 lastFieldId_ = 0;
136 return 0;
137}
138
139/**
140 * Write a struct end. This doesn't actually put anything on the wire. We use
141 * this as an opportunity to pop the last field from the current struct off
142 * of the field stack.
143 */
David Reisse71115b2010-10-06 17:09:56 +0000144template <class Transport_>
145uint32_t TCompactProtocolT<Transport_>::writeStructEnd() {
David Reisse4d4ea02009-04-02 21:37:17 +0000146 lastFieldId_ = lastField_.top();
147 lastField_.pop();
148 return 0;
149}
150
151/**
152 * Write a List header.
153 */
David Reisse71115b2010-10-06 17:09:56 +0000154template <class Transport_>
155uint32_t TCompactProtocolT<Transport_>::writeListBegin(const TType elemType,
156 const uint32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000157 return writeCollectionBegin(elemType, size);
158}
159
160/**
161 * Write a set header.
162 */
David Reisse71115b2010-10-06 17:09:56 +0000163template <class Transport_>
164uint32_t TCompactProtocolT<Transport_>::writeSetBegin(const TType elemType,
165 const uint32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000166 return writeCollectionBegin(elemType, size);
167}
168
169/**
170 * Write a map header. If the map is empty, omit the key and value type
171 * headers, as we don't need any additional information to skip it.
172 */
David Reisse71115b2010-10-06 17:09:56 +0000173template <class Transport_>
174uint32_t TCompactProtocolT<Transport_>::writeMapBegin(const TType keyType,
175 const TType valType,
176 const uint32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000177 uint32_t wsize = 0;
178
179 if (size == 0) {
180 wsize += writeByte(0);
181 } else {
182 wsize += writeVarint32(size);
183 wsize += writeByte(getCompactType(keyType) << 4 | getCompactType(valType));
184 }
185 return wsize;
186}
187
188/**
189 * Write a boolean value. Potentially, this could be a boolean field, in
190 * which case the field header info isn't written yet. If so, decide what the
191 * right type header is for the value and then write the field header.
192 * Otherwise, write a single byte.
193 */
David Reisse71115b2010-10-06 17:09:56 +0000194template <class Transport_>
195uint32_t TCompactProtocolT<Transport_>::writeBool(const bool value) {
David Reisse4d4ea02009-04-02 21:37:17 +0000196 uint32_t wsize = 0;
197
198 if (booleanField_.name != NULL) {
199 // we haven't written the field header yet
200 wsize += writeFieldBeginInternal(booleanField_.name,
201 booleanField_.fieldType,
202 booleanField_.fieldId,
David Reisse71115b2010-10-06 17:09:56 +0000203 value ? detail::compact::CT_BOOLEAN_TRUE :
204 detail::compact::CT_BOOLEAN_FALSE);
David Reisse4d4ea02009-04-02 21:37:17 +0000205 booleanField_.name = NULL;
206 } else {
207 // we're not part of a field, so just write the value
David Reisse71115b2010-10-06 17:09:56 +0000208 wsize += writeByte(value ? detail::compact::CT_BOOLEAN_TRUE :
209 detail::compact::CT_BOOLEAN_FALSE);
David Reisse4d4ea02009-04-02 21:37:17 +0000210 }
211 return wsize;
212}
213
David Reisse71115b2010-10-06 17:09:56 +0000214template <class Transport_>
215uint32_t TCompactProtocolT<Transport_>::writeByte(const int8_t byte) {
David Reisse4d4ea02009-04-02 21:37:17 +0000216 trans_->write((uint8_t*)&byte, 1);
217 return 1;
218}
219
220/**
221 * Write an i16 as a zigzag varint.
222 */
David Reisse71115b2010-10-06 17:09:56 +0000223template <class Transport_>
224uint32_t TCompactProtocolT<Transport_>::writeI16(const int16_t i16) {
David Reisse4d4ea02009-04-02 21:37:17 +0000225 return writeVarint32(i32ToZigzag(i16));
226}
227
228/**
229 * Write an i32 as a zigzag varint.
230 */
David Reisse71115b2010-10-06 17:09:56 +0000231template <class Transport_>
232uint32_t TCompactProtocolT<Transport_>::writeI32(const int32_t i32) {
David Reisse4d4ea02009-04-02 21:37:17 +0000233 return writeVarint32(i32ToZigzag(i32));
234}
235
236/**
237 * Write an i64 as a zigzag varint.
238 */
David Reisse71115b2010-10-06 17:09:56 +0000239template <class Transport_>
240uint32_t TCompactProtocolT<Transport_>::writeI64(const int64_t i64) {
David Reisse4d4ea02009-04-02 21:37:17 +0000241 return writeVarint64(i64ToZigzag(i64));
242}
243
244/**
245 * Write a double to the wire as 8 bytes.
246 */
David Reisse71115b2010-10-06 17:09:56 +0000247template <class Transport_>
248uint32_t TCompactProtocolT<Transport_>::writeDouble(const double dub) {
David Reisse4d4ea02009-04-02 21:37:17 +0000249 BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
250 BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
251
252 uint64_t bits = bitwise_cast<uint64_t>(dub);
253 bits = htolell(bits);
254 trans_->write((uint8_t*)&bits, 8);
255 return 8;
256}
257
258/**
259 * Write a string to the wire with a varint size preceeding.
260 */
David Reisse71115b2010-10-06 17:09:56 +0000261template <class Transport_>
262uint32_t TCompactProtocolT<Transport_>::writeString(const std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000263 return writeBinary(str);
264}
265
David Reisse71115b2010-10-06 17:09:56 +0000266template <class Transport_>
267uint32_t TCompactProtocolT<Transport_>::writeBinary(const std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000268 uint32_t ssize = str.size();
269 uint32_t wsize = writeVarint32(ssize) + ssize;
270 trans_->write((uint8_t*)str.data(), ssize);
271 return wsize;
272}
273
274//
275// Internal Writing methods
276//
277
278/**
279 * The workhorse of writeFieldBegin. It has the option of doing a
280 * 'type override' of the type header. This is used specifically in the
281 * boolean field case.
282 */
David Reisse71115b2010-10-06 17:09:56 +0000283template <class Transport_>
284int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal(
285 const char* name,
286 const TType fieldType,
287 const int16_t fieldId,
288 int8_t typeOverride) {
David Reisse4d4ea02009-04-02 21:37:17 +0000289 uint32_t wsize = 0;
290
291 // if there's a type override, use that.
292 int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride);
293
294 // check if we can use delta encoding for the field id
295 if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) {
296 // write them together
297 wsize += writeByte((fieldId - lastFieldId_) << 4 | typeToWrite);
298 } else {
299 // write them separate
300 wsize += writeByte(typeToWrite);
301 wsize += writeI16(fieldId);
302 }
303
304 lastFieldId_ = fieldId;
305 return wsize;
306}
307
308/**
309 * Abstract method for writing the start of lists and sets. List and sets on
310 * the wire differ only by the type indicator.
311 */
David Reisse71115b2010-10-06 17:09:56 +0000312template <class Transport_>
313uint32_t TCompactProtocolT<Transport_>::writeCollectionBegin(int8_t elemType,
314 int32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000315 uint32_t wsize = 0;
316 if (size <= 14) {
317 wsize += writeByte(size << 4 | getCompactType(elemType));
318 } else {
319 wsize += writeByte(0xf0 | getCompactType(elemType));
320 wsize += writeVarint32(size);
321 }
322 return wsize;
323}
324
325/**
326 * Write an i32 as a varint. Results in 1-5 bytes on the wire.
327 */
David Reisse71115b2010-10-06 17:09:56 +0000328template <class Transport_>
329uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000330 uint8_t buf[5];
331 uint32_t wsize = 0;
332
333 while (true) {
334 if ((n & ~0x7F) == 0) {
335 buf[wsize++] = (int8_t)n;
336 break;
337 } else {
338 buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
339 n >>= 7;
340 }
341 }
342 trans_->write(buf, wsize);
343 return wsize;
344}
345
346/**
347 * Write an i64 as a varint. Results in 1-10 bytes on the wire.
348 */
David Reisse71115b2010-10-06 17:09:56 +0000349template <class Transport_>
350uint32_t TCompactProtocolT<Transport_>::writeVarint64(uint64_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000351 uint8_t buf[10];
352 uint32_t wsize = 0;
353
354 while (true) {
355 if ((n & ~0x7FL) == 0) {
356 buf[wsize++] = (int8_t)n;
357 break;
358 } else {
359 buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
360 n >>= 7;
361 }
362 }
363 trans_->write(buf, wsize);
364 return wsize;
365}
366
367/**
368 * Convert l into a zigzag long. This allows negative numbers to be
369 * represented compactly as a varint.
370 */
David Reisse71115b2010-10-06 17:09:56 +0000371template <class Transport_>
372uint64_t TCompactProtocolT<Transport_>::i64ToZigzag(const int64_t l) {
David Reisse4d4ea02009-04-02 21:37:17 +0000373 return (l << 1) ^ (l >> 63);
374}
375
376/**
377 * Convert n into a zigzag int. This allows negative numbers to be
378 * represented compactly as a varint.
379 */
David Reisse71115b2010-10-06 17:09:56 +0000380template <class Transport_>
381uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000382 return (n << 1) ^ (n >> 31);
383}
384
385/**
David Reisse71115b2010-10-06 17:09:56 +0000386 * Given a TType value, find the appropriate detail::compact::Types value
David Reisse4d4ea02009-04-02 21:37:17 +0000387 */
David Reisse71115b2010-10-06 17:09:56 +0000388template <class Transport_>
389int8_t TCompactProtocolT<Transport_>::getCompactType(int8_t ttype) {
390 return detail::compact::TTypeToCType[ttype];
David Reisse4d4ea02009-04-02 21:37:17 +0000391}
392
393//
394// Reading Methods
395//
396
397/**
398 * Read a message header.
399 */
David Reisse71115b2010-10-06 17:09:56 +0000400template <class Transport_>
401uint32_t TCompactProtocolT<Transport_>::readMessageBegin(
402 std::string& name,
403 TMessageType& messageType,
404 int32_t& seqid) {
David Reisse4d4ea02009-04-02 21:37:17 +0000405 uint32_t rsize = 0;
406 int8_t protocolId;
407 int8_t versionAndType;
408 int8_t version;
409
410 rsize += readByte(protocolId);
411 if (protocolId != PROTOCOL_ID) {
412 throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier");
413 }
414
415 rsize += readByte(versionAndType);
416 version = (int8_t)(versionAndType & VERSION_MASK);
417 if (version != VERSION_N) {
418 throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version");
419 }
420
421 messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & 0x03);
422 rsize += readVarint32(seqid);
423 rsize += readString(name);
424
425 return rsize;
426}
427
428/**
429 * Read a struct begin. There's nothing on the wire for this, but it is our
430 * opportunity to push a new struct begin marker on the field stack.
431 */
David Reisse71115b2010-10-06 17:09:56 +0000432template <class Transport_>
433uint32_t TCompactProtocolT<Transport_>::readStructBegin(std::string& name) {
David Reisse4d4ea02009-04-02 21:37:17 +0000434 name = "";
435 lastField_.push(lastFieldId_);
436 lastFieldId_ = 0;
437 return 0;
438}
439
440/**
441 * Doesn't actually consume any wire data, just removes the last field for
442 * this struct from the field stack.
443 */
David Reisse71115b2010-10-06 17:09:56 +0000444template <class Transport_>
445uint32_t TCompactProtocolT<Transport_>::readStructEnd() {
David Reisse4d4ea02009-04-02 21:37:17 +0000446 lastFieldId_ = lastField_.top();
447 lastField_.pop();
448 return 0;
449}
450
451/**
452 * Read a field header off the wire.
453 */
David Reisse71115b2010-10-06 17:09:56 +0000454template <class Transport_>
455uint32_t TCompactProtocolT<Transport_>::readFieldBegin(std::string& name,
456 TType& fieldType,
457 int16_t& fieldId) {
David Reisse4d4ea02009-04-02 21:37:17 +0000458 uint32_t rsize = 0;
459 int8_t byte;
460 int8_t type;
461
462 rsize += readByte(byte);
463 type = (byte & 0x0f);
464
465 // if it's a stop, then we can return immediately, as the struct is over.
466 if (type == T_STOP) {
467 fieldType = T_STOP;
468 fieldId = 0;
469 return rsize;
470 }
471
472 // mask off the 4 MSB of the type header. it could contain a field id delta.
473 int16_t modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4);
474 if (modifier == 0) {
475 // not a delta, look ahead for the zigzag varint field id.
476 rsize += readI16(fieldId);
477 } else {
478 fieldId = (int16_t)(lastFieldId_ + modifier);
479 }
480 fieldType = getTType(type);
481
482 // if this happens to be a boolean field, the value is encoded in the type
David Reisse71115b2010-10-06 17:09:56 +0000483 if (type == detail::compact::CT_BOOLEAN_TRUE ||
484 type == detail::compact::CT_BOOLEAN_FALSE) {
David Reisse4d4ea02009-04-02 21:37:17 +0000485 // save the boolean value in a special instance variable.
486 boolValue_.hasBoolValue = true;
David Reisse71115b2010-10-06 17:09:56 +0000487 boolValue_.boolValue =
488 (type == detail::compact::CT_BOOLEAN_TRUE ? true : false);
David Reisse4d4ea02009-04-02 21:37:17 +0000489 }
490
491 // push the new field onto the field stack so we can keep the deltas going.
492 lastFieldId_ = fieldId;
493 return rsize;
494}
495
496/**
497 * Read a map header off the wire. If the size is zero, skip reading the key
498 * and value type. This means that 0-length maps will yield TMaps without the
499 * "correct" types.
500 */
David Reisse71115b2010-10-06 17:09:56 +0000501template <class Transport_>
502uint32_t TCompactProtocolT<Transport_>::readMapBegin(TType& keyType,
503 TType& valType,
504 uint32_t& size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000505 uint32_t rsize = 0;
506 int8_t kvType = 0;
507 int32_t msize = 0;
508
509 rsize += readVarint32(msize);
510 if (msize != 0)
511 rsize += readByte(kvType);
512
513 if (msize < 0) {
514 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
515 } else if (container_limit_ && msize > container_limit_) {
516 throw TProtocolException(TProtocolException::SIZE_LIMIT);
517 }
518
519 keyType = getTType((int8_t)((uint8_t)kvType >> 4));
520 valType = getTType((int8_t)((uint8_t)kvType & 0xf));
521 size = (uint32_t)msize;
522
523 return rsize;
524}
525
526/**
527 * Read a list header off the wire. If the list size is 0-14, the size will
528 * be packed into the element type header. If it's a longer list, the 4 MSB
529 * of the element type header will be 0xF, and a varint will follow with the
530 * true size.
531 */
David Reisse71115b2010-10-06 17:09:56 +0000532template <class Transport_>
533uint32_t TCompactProtocolT<Transport_>::readListBegin(TType& elemType,
534 uint32_t& size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000535 int8_t size_and_type;
536 uint32_t rsize = 0;
537 int32_t lsize;
538
539 rsize += readByte(size_and_type);
540
541 lsize = ((uint8_t)size_and_type >> 4) & 0x0f;
542 if (lsize == 15) {
543 rsize += readVarint32(lsize);
544 }
545
546 if (lsize < 0) {
547 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
548 } else if (container_limit_ && lsize > container_limit_) {
549 throw TProtocolException(TProtocolException::SIZE_LIMIT);
550 }
551
552 elemType = getTType((int8_t)(size_and_type & 0x0f));
553 size = (uint32_t)lsize;
554
555 return rsize;
556}
557
558/**
559 * Read a set header off the wire. If the set size is 0-14, the size will
560 * be packed into the element type header. If it's a longer set, the 4 MSB
561 * of the element type header will be 0xF, and a varint will follow with the
562 * true size.
563 */
David Reisse71115b2010-10-06 17:09:56 +0000564template <class Transport_>
565uint32_t TCompactProtocolT<Transport_>::readSetBegin(TType& elemType,
566 uint32_t& size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000567 return readListBegin(elemType, size);
568}
569
570/**
571 * Read a boolean off the wire. If this is a boolean field, the value should
572 * already have been read during readFieldBegin, so we'll just consume the
573 * pre-stored value. Otherwise, read a byte.
574 */
David Reisse71115b2010-10-06 17:09:56 +0000575template <class Transport_>
576uint32_t TCompactProtocolT<Transport_>::readBool(bool& value) {
David Reisse4d4ea02009-04-02 21:37:17 +0000577 if (boolValue_.hasBoolValue == true) {
578 value = boolValue_.boolValue;
579 boolValue_.hasBoolValue = false;
580 return 0;
581 } else {
582 int8_t val;
583 readByte(val);
David Reisse71115b2010-10-06 17:09:56 +0000584 value = (val == detail::compact::CT_BOOLEAN_TRUE);
David Reisse4d4ea02009-04-02 21:37:17 +0000585 return 1;
586 }
587}
588
589/**
590 * Read a single byte off the wire. Nothing interesting here.
591 */
David Reisse71115b2010-10-06 17:09:56 +0000592template <class Transport_>
593uint32_t TCompactProtocolT<Transport_>::readByte(int8_t& byte) {
David Reisse4d4ea02009-04-02 21:37:17 +0000594 uint8_t b[1];
595 trans_->readAll(b, 1);
596 byte = *(int8_t*)b;
597 return 1;
598}
599
600/**
601 * Read an i16 from the wire as a zigzag varint.
602 */
David Reisse71115b2010-10-06 17:09:56 +0000603template <class Transport_>
604uint32_t TCompactProtocolT<Transport_>::readI16(int16_t& i16) {
David Reisse4d4ea02009-04-02 21:37:17 +0000605 int32_t value;
606 uint32_t rsize = readVarint32(value);
607 i16 = (int16_t)zigzagToI32(value);
608 return rsize;
609}
610
611/**
612 * Read an i32 from the wire as a zigzag varint.
613 */
David Reisse71115b2010-10-06 17:09:56 +0000614template <class Transport_>
615uint32_t TCompactProtocolT<Transport_>::readI32(int32_t& i32) {
David Reisse4d4ea02009-04-02 21:37:17 +0000616 int32_t value;
617 uint32_t rsize = readVarint32(value);
618 i32 = zigzagToI32(value);
619 return rsize;
620}
621
622/**
623 * Read an i64 from the wire as a zigzag varint.
624 */
David Reisse71115b2010-10-06 17:09:56 +0000625template <class Transport_>
626uint32_t TCompactProtocolT<Transport_>::readI64(int64_t& i64) {
David Reisse4d4ea02009-04-02 21:37:17 +0000627 int64_t value;
628 uint32_t rsize = readVarint64(value);
629 i64 = zigzagToI64(value);
630 return rsize;
631}
632
633/**
634 * No magic here - just read a double off the wire.
635 */
David Reisse71115b2010-10-06 17:09:56 +0000636template <class Transport_>
637uint32_t TCompactProtocolT<Transport_>::readDouble(double& dub) {
David Reisse4d4ea02009-04-02 21:37:17 +0000638 BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
639 BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
640
641 uint64_t bits;
642 uint8_t b[8];
643 trans_->readAll(b, 8);
644 bits = *(uint64_t*)b;
645 bits = letohll(bits);
646 dub = bitwise_cast<double>(bits);
647 return 8;
648}
649
David Reisse71115b2010-10-06 17:09:56 +0000650template <class Transport_>
651uint32_t TCompactProtocolT<Transport_>::readString(std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000652 return readBinary(str);
653}
654
655/**
656 * Read a byte[] from the wire.
657 */
David Reisse71115b2010-10-06 17:09:56 +0000658template <class Transport_>
659uint32_t TCompactProtocolT<Transport_>::readBinary(std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000660 int32_t rsize = 0;
661 int32_t size;
662
663 rsize += readVarint32(size);
664 // Catch empty string case
665 if (size == 0) {
666 str = "";
667 return rsize;
668 }
669
670 // Catch error cases
671 if (size < 0) {
672 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
673 }
674 if (string_limit_ > 0 && size > string_limit_) {
675 throw TProtocolException(TProtocolException::SIZE_LIMIT);
676 }
677
678 // Use the heap here to prevent stack overflow for v. large strings
679 if (size > string_buf_size_ || string_buf_ == NULL) {
680 void* new_string_buf = std::realloc(string_buf_, (uint32_t)size);
681 if (new_string_buf == NULL) {
David Reissf6735092010-10-06 17:10:49 +0000682 throw std::bad_alloc();
David Reisse4d4ea02009-04-02 21:37:17 +0000683 }
684 string_buf_ = (uint8_t*)new_string_buf;
685 string_buf_size_ = size;
686 }
687 trans_->readAll(string_buf_, size);
688 str.assign((char*)string_buf_, size);
689
690 return rsize + (uint32_t)size;
691}
692
693/**
694 * Read an i32 from the wire as a varint. The MSB of each byte is set
695 * if there is another byte to follow. This can read up to 5 bytes.
696 */
David Reisse71115b2010-10-06 17:09:56 +0000697template <class Transport_>
698uint32_t TCompactProtocolT<Transport_>::readVarint32(int32_t& i32) {
David Reisse4d4ea02009-04-02 21:37:17 +0000699 int64_t val;
700 uint32_t rsize = readVarint64(val);
701 i32 = (int32_t)val;
702 return rsize;
703}
704
705/**
706 * Read an i64 from the wire as a proper varint. The MSB of each byte is set
707 * if there is another byte to follow. This can read up to 10 bytes.
708 */
David Reisse71115b2010-10-06 17:09:56 +0000709template <class Transport_>
710uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) {
David Reisse4d4ea02009-04-02 21:37:17 +0000711 uint32_t rsize = 0;
712 uint64_t val = 0;
713 int shift = 0;
714 uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes.
715 uint32_t buf_size = sizeof(buf);
716 const uint8_t* borrowed = trans_->borrow(buf, &buf_size);
717
718 // Fast path.
719 if (borrowed != NULL) {
720 while (true) {
721 uint8_t byte = borrowed[rsize];
722 rsize++;
723 val |= (uint64_t)(byte & 0x7f) << shift;
724 shift += 7;
725 if (!(byte & 0x80)) {
726 i64 = val;
727 trans_->consume(rsize);
728 return rsize;
729 }
730 // Have to check for invalid data so we don't crash.
731 if (UNLIKELY(rsize == sizeof(buf))) {
732 throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
733 }
734 }
735 }
736
737 // Slow path.
738 else {
739 while (true) {
740 uint8_t byte;
741 rsize += trans_->readAll(&byte, 1);
742 val |= (uint64_t)(byte & 0x7f) << shift;
743 shift += 7;
744 if (!(byte & 0x80)) {
745 i64 = val;
746 return rsize;
747 }
748 // Might as well check for invalid data on the slow path too.
749 if (UNLIKELY(rsize >= sizeof(buf))) {
750 throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
751 }
752 }
753 }
754}
755
756/**
757 * Convert from zigzag int to int.
758 */
David Reisse71115b2010-10-06 17:09:56 +0000759template <class Transport_>
760int32_t TCompactProtocolT<Transport_>::zigzagToI32(uint32_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000761 return (n >> 1) ^ -(n & 1);
762}
763
764/**
765 * Convert from zigzag long to long.
766 */
David Reisse71115b2010-10-06 17:09:56 +0000767template <class Transport_>
768int64_t TCompactProtocolT<Transport_>::zigzagToI64(uint64_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000769 return (n >> 1) ^ -(n & 1);
770}
771
David Reisse71115b2010-10-06 17:09:56 +0000772template <class Transport_>
773TType TCompactProtocolT<Transport_>::getTType(int8_t type) {
David Reisse4d4ea02009-04-02 21:37:17 +0000774 switch (type) {
775 case T_STOP:
776 return T_STOP;
David Reisse71115b2010-10-06 17:09:56 +0000777 case detail::compact::CT_BOOLEAN_FALSE:
778 case detail::compact::CT_BOOLEAN_TRUE:
David Reisse4d4ea02009-04-02 21:37:17 +0000779 return T_BOOL;
David Reisse71115b2010-10-06 17:09:56 +0000780 case detail::compact::CT_BYTE:
David Reisse4d4ea02009-04-02 21:37:17 +0000781 return T_BYTE;
David Reisse71115b2010-10-06 17:09:56 +0000782 case detail::compact::CT_I16:
David Reisse4d4ea02009-04-02 21:37:17 +0000783 return T_I16;
David Reisse71115b2010-10-06 17:09:56 +0000784 case detail::compact::CT_I32:
David Reisse4d4ea02009-04-02 21:37:17 +0000785 return T_I32;
David Reisse71115b2010-10-06 17:09:56 +0000786 case detail::compact::CT_I64:
David Reisse4d4ea02009-04-02 21:37:17 +0000787 return T_I64;
David Reisse71115b2010-10-06 17:09:56 +0000788 case detail::compact::CT_DOUBLE:
David Reisse4d4ea02009-04-02 21:37:17 +0000789 return T_DOUBLE;
David Reisse71115b2010-10-06 17:09:56 +0000790 case detail::compact::CT_BINARY:
David Reisse4d4ea02009-04-02 21:37:17 +0000791 return T_STRING;
David Reisse71115b2010-10-06 17:09:56 +0000792 case detail::compact::CT_LIST:
David Reisse4d4ea02009-04-02 21:37:17 +0000793 return T_LIST;
David Reisse71115b2010-10-06 17:09:56 +0000794 case detail::compact::CT_SET:
David Reisse4d4ea02009-04-02 21:37:17 +0000795 return T_SET;
David Reisse71115b2010-10-06 17:09:56 +0000796 case detail::compact::CT_MAP:
David Reisse4d4ea02009-04-02 21:37:17 +0000797 return T_MAP;
David Reisse71115b2010-10-06 17:09:56 +0000798 case detail::compact::CT_STRUCT:
David Reisse4d4ea02009-04-02 21:37:17 +0000799 return T_STRUCT;
800 default:
801 throw TException("don't know what type: " + type);
802 }
803 return T_STOP;
804}
805
806}}} // apache::thrift::protocol
David Reisse71115b2010-10-06 17:09:56 +0000807
808#endif // _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_