blob: 62d6485d77fb146be01b042b772db9a34fc3f0b6 [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,
Roger Meier0069cc42010-10-13 18:10:18 +000061 CT_STRUCT = 0x0C
David Reisse71115b2010-10-06 17:09:56 +000062};
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) {
Roger Meier3b771a12010-11-17 22:11:26 +0000134 (void) name;
David Reisse4d4ea02009-04-02 21:37:17 +0000135 lastField_.push(lastFieldId_);
136 lastFieldId_ = 0;
137 return 0;
138}
139
140/**
141 * Write a struct end. This doesn't actually put anything on the wire. We use
142 * this as an opportunity to pop the last field from the current struct off
143 * of the field stack.
144 */
David Reisse71115b2010-10-06 17:09:56 +0000145template <class Transport_>
146uint32_t TCompactProtocolT<Transport_>::writeStructEnd() {
David Reisse4d4ea02009-04-02 21:37:17 +0000147 lastFieldId_ = lastField_.top();
148 lastField_.pop();
149 return 0;
150}
151
152/**
153 * Write a List header.
154 */
David Reisse71115b2010-10-06 17:09:56 +0000155template <class Transport_>
156uint32_t TCompactProtocolT<Transport_>::writeListBegin(const TType elemType,
157 const uint32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000158 return writeCollectionBegin(elemType, size);
159}
160
161/**
162 * Write a set header.
163 */
David Reisse71115b2010-10-06 17:09:56 +0000164template <class Transport_>
165uint32_t TCompactProtocolT<Transport_>::writeSetBegin(const TType elemType,
166 const uint32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000167 return writeCollectionBegin(elemType, size);
168}
169
170/**
171 * Write a map header. If the map is empty, omit the key and value type
172 * headers, as we don't need any additional information to skip it.
173 */
David Reisse71115b2010-10-06 17:09:56 +0000174template <class Transport_>
175uint32_t TCompactProtocolT<Transport_>::writeMapBegin(const TType keyType,
176 const TType valType,
177 const uint32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000178 uint32_t wsize = 0;
179
180 if (size == 0) {
181 wsize += writeByte(0);
182 } else {
183 wsize += writeVarint32(size);
184 wsize += writeByte(getCompactType(keyType) << 4 | getCompactType(valType));
185 }
186 return wsize;
187}
188
189/**
190 * Write a boolean value. Potentially, this could be a boolean field, in
191 * which case the field header info isn't written yet. If so, decide what the
192 * right type header is for the value and then write the field header.
193 * Otherwise, write a single byte.
194 */
David Reisse71115b2010-10-06 17:09:56 +0000195template <class Transport_>
196uint32_t TCompactProtocolT<Transport_>::writeBool(const bool value) {
David Reisse4d4ea02009-04-02 21:37:17 +0000197 uint32_t wsize = 0;
198
199 if (booleanField_.name != NULL) {
200 // we haven't written the field header yet
Roger Meier64a799d2013-06-04 20:59:01 +0200201 wsize
202 += writeFieldBeginInternal(booleanField_.name,
203 booleanField_.fieldType,
204 booleanField_.fieldId,
205 static_cast<int8_t>(value
206 ? detail::compact::CT_BOOLEAN_TRUE
207 : detail::compact::CT_BOOLEAN_FALSE));
David Reisse4d4ea02009-04-02 21:37:17 +0000208 booleanField_.name = NULL;
209 } else {
210 // we're not part of a field, so just write the value
Roger Meier64a799d2013-06-04 20:59:01 +0200211 wsize
212 += writeByte(static_cast<int8_t>(value
213 ? detail::compact::CT_BOOLEAN_TRUE
214 : detail::compact::CT_BOOLEAN_FALSE));
David Reisse4d4ea02009-04-02 21:37:17 +0000215 }
216 return wsize;
217}
218
David Reisse71115b2010-10-06 17:09:56 +0000219template <class Transport_>
220uint32_t TCompactProtocolT<Transport_>::writeByte(const int8_t byte) {
David Reisse4d4ea02009-04-02 21:37:17 +0000221 trans_->write((uint8_t*)&byte, 1);
222 return 1;
223}
224
225/**
226 * Write an i16 as a zigzag varint.
227 */
David Reisse71115b2010-10-06 17:09:56 +0000228template <class Transport_>
229uint32_t TCompactProtocolT<Transport_>::writeI16(const int16_t i16) {
David Reisse4d4ea02009-04-02 21:37:17 +0000230 return writeVarint32(i32ToZigzag(i16));
231}
232
233/**
234 * Write an i32 as a zigzag varint.
235 */
David Reisse71115b2010-10-06 17:09:56 +0000236template <class Transport_>
237uint32_t TCompactProtocolT<Transport_>::writeI32(const int32_t i32) {
David Reisse4d4ea02009-04-02 21:37:17 +0000238 return writeVarint32(i32ToZigzag(i32));
239}
240
241/**
242 * Write an i64 as a zigzag varint.
243 */
David Reisse71115b2010-10-06 17:09:56 +0000244template <class Transport_>
245uint32_t TCompactProtocolT<Transport_>::writeI64(const int64_t i64) {
David Reisse4d4ea02009-04-02 21:37:17 +0000246 return writeVarint64(i64ToZigzag(i64));
247}
248
249/**
250 * Write a double to the wire as 8 bytes.
251 */
David Reisse71115b2010-10-06 17:09:56 +0000252template <class Transport_>
253uint32_t TCompactProtocolT<Transport_>::writeDouble(const double dub) {
David Reisse4d4ea02009-04-02 21:37:17 +0000254 BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
255 BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
256
257 uint64_t bits = bitwise_cast<uint64_t>(dub);
258 bits = htolell(bits);
259 trans_->write((uint8_t*)&bits, 8);
260 return 8;
261}
262
263/**
264 * Write a string to the wire with a varint size preceeding.
265 */
David Reisse71115b2010-10-06 17:09:56 +0000266template <class Transport_>
267uint32_t TCompactProtocolT<Transport_>::writeString(const std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000268 return writeBinary(str);
269}
270
David Reisse71115b2010-10-06 17:09:56 +0000271template <class Transport_>
272uint32_t TCompactProtocolT<Transport_>::writeBinary(const std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000273 uint32_t ssize = str.size();
274 uint32_t wsize = writeVarint32(ssize) + ssize;
275 trans_->write((uint8_t*)str.data(), ssize);
276 return wsize;
277}
278
279//
280// Internal Writing methods
281//
282
283/**
284 * The workhorse of writeFieldBegin. It has the option of doing a
285 * 'type override' of the type header. This is used specifically in the
286 * boolean field case.
287 */
David Reisse71115b2010-10-06 17:09:56 +0000288template <class Transport_>
289int32_t TCompactProtocolT<Transport_>::writeFieldBeginInternal(
290 const char* name,
291 const TType fieldType,
292 const int16_t fieldId,
293 int8_t typeOverride) {
Roger Meier3b771a12010-11-17 22:11:26 +0000294 (void) name;
David Reisse4d4ea02009-04-02 21:37:17 +0000295 uint32_t wsize = 0;
296
297 // if there's a type override, use that.
298 int8_t typeToWrite = (typeOverride == -1 ? getCompactType(fieldType) : typeOverride);
299
300 // check if we can use delta encoding for the field id
301 if (fieldId > lastFieldId_ && fieldId - lastFieldId_ <= 15) {
302 // write them together
Roger Meier64a799d2013-06-04 20:59:01 +0200303 wsize += writeByte(static_cast<int8_t>((fieldId - lastFieldId_)
304 << 4 | typeToWrite));
David Reisse4d4ea02009-04-02 21:37:17 +0000305 } else {
306 // write them separate
307 wsize += writeByte(typeToWrite);
308 wsize += writeI16(fieldId);
309 }
310
311 lastFieldId_ = fieldId;
312 return wsize;
313}
314
315/**
316 * Abstract method for writing the start of lists and sets. List and sets on
317 * the wire differ only by the type indicator.
318 */
David Reisse71115b2010-10-06 17:09:56 +0000319template <class Transport_>
Roger Meier64a799d2013-06-04 20:59:01 +0200320uint32_t TCompactProtocolT<Transport_>::writeCollectionBegin(const TType elemType,
David Reisse71115b2010-10-06 17:09:56 +0000321 int32_t size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000322 uint32_t wsize = 0;
323 if (size <= 14) {
Roger Meier64a799d2013-06-04 20:59:01 +0200324 wsize += writeByte(static_cast<int8_t>(size
325 << 4 | getCompactType(elemType)));
David Reisse4d4ea02009-04-02 21:37:17 +0000326 } else {
327 wsize += writeByte(0xf0 | getCompactType(elemType));
328 wsize += writeVarint32(size);
329 }
330 return wsize;
331}
332
333/**
334 * Write an i32 as a varint. Results in 1-5 bytes on the wire.
335 */
David Reisse71115b2010-10-06 17:09:56 +0000336template <class Transport_>
337uint32_t TCompactProtocolT<Transport_>::writeVarint32(uint32_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000338 uint8_t buf[5];
339 uint32_t wsize = 0;
340
341 while (true) {
342 if ((n & ~0x7F) == 0) {
343 buf[wsize++] = (int8_t)n;
344 break;
345 } else {
346 buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
347 n >>= 7;
348 }
349 }
350 trans_->write(buf, wsize);
351 return wsize;
352}
353
354/**
355 * Write an i64 as a varint. Results in 1-10 bytes on the wire.
356 */
David Reisse71115b2010-10-06 17:09:56 +0000357template <class Transport_>
358uint32_t TCompactProtocolT<Transport_>::writeVarint64(uint64_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000359 uint8_t buf[10];
360 uint32_t wsize = 0;
361
362 while (true) {
363 if ((n & ~0x7FL) == 0) {
364 buf[wsize++] = (int8_t)n;
365 break;
366 } else {
367 buf[wsize++] = (int8_t)((n & 0x7F) | 0x80);
368 n >>= 7;
369 }
370 }
371 trans_->write(buf, wsize);
372 return wsize;
373}
374
375/**
376 * Convert l into a zigzag long. This allows negative numbers to be
377 * represented compactly as a varint.
378 */
David Reisse71115b2010-10-06 17:09:56 +0000379template <class Transport_>
380uint64_t TCompactProtocolT<Transport_>::i64ToZigzag(const int64_t l) {
David Reisse4d4ea02009-04-02 21:37:17 +0000381 return (l << 1) ^ (l >> 63);
382}
383
384/**
385 * Convert n into a zigzag int. This allows negative numbers to be
386 * represented compactly as a varint.
387 */
David Reisse71115b2010-10-06 17:09:56 +0000388template <class Transport_>
389uint32_t TCompactProtocolT<Transport_>::i32ToZigzag(const int32_t n) {
David Reisse4d4ea02009-04-02 21:37:17 +0000390 return (n << 1) ^ (n >> 31);
391}
392
393/**
David Reisse71115b2010-10-06 17:09:56 +0000394 * Given a TType value, find the appropriate detail::compact::Types value
David Reisse4d4ea02009-04-02 21:37:17 +0000395 */
David Reisse71115b2010-10-06 17:09:56 +0000396template <class Transport_>
Roger Meier64a799d2013-06-04 20:59:01 +0200397int8_t TCompactProtocolT<Transport_>::getCompactType(const TType ttype) {
David Reisse71115b2010-10-06 17:09:56 +0000398 return detail::compact::TTypeToCType[ttype];
David Reisse4d4ea02009-04-02 21:37:17 +0000399}
400
401//
402// Reading Methods
403//
404
405/**
406 * Read a message header.
407 */
David Reisse71115b2010-10-06 17:09:56 +0000408template <class Transport_>
409uint32_t TCompactProtocolT<Transport_>::readMessageBegin(
410 std::string& name,
411 TMessageType& messageType,
412 int32_t& seqid) {
David Reisse4d4ea02009-04-02 21:37:17 +0000413 uint32_t rsize = 0;
414 int8_t protocolId;
415 int8_t versionAndType;
416 int8_t version;
417
418 rsize += readByte(protocolId);
419 if (protocolId != PROTOCOL_ID) {
420 throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol identifier");
421 }
422
423 rsize += readByte(versionAndType);
424 version = (int8_t)(versionAndType & VERSION_MASK);
425 if (version != VERSION_N) {
426 throw TProtocolException(TProtocolException::BAD_VERSION, "Bad protocol version");
427 }
428
429 messageType = (TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & 0x03);
430 rsize += readVarint32(seqid);
431 rsize += readString(name);
432
433 return rsize;
434}
435
436/**
437 * Read a struct begin. There's nothing on the wire for this, but it is our
438 * opportunity to push a new struct begin marker on the field stack.
439 */
David Reisse71115b2010-10-06 17:09:56 +0000440template <class Transport_>
441uint32_t TCompactProtocolT<Transport_>::readStructBegin(std::string& name) {
David Reisse4d4ea02009-04-02 21:37:17 +0000442 name = "";
443 lastField_.push(lastFieldId_);
444 lastFieldId_ = 0;
445 return 0;
446}
447
448/**
449 * Doesn't actually consume any wire data, just removes the last field for
450 * this struct from the field stack.
451 */
David Reisse71115b2010-10-06 17:09:56 +0000452template <class Transport_>
453uint32_t TCompactProtocolT<Transport_>::readStructEnd() {
David Reisse4d4ea02009-04-02 21:37:17 +0000454 lastFieldId_ = lastField_.top();
455 lastField_.pop();
456 return 0;
457}
458
459/**
460 * Read a field header off the wire.
461 */
David Reisse71115b2010-10-06 17:09:56 +0000462template <class Transport_>
463uint32_t TCompactProtocolT<Transport_>::readFieldBegin(std::string& name,
464 TType& fieldType,
465 int16_t& fieldId) {
Roger Meier3b771a12010-11-17 22:11:26 +0000466 (void) name;
David Reisse4d4ea02009-04-02 21:37:17 +0000467 uint32_t rsize = 0;
468 int8_t byte;
469 int8_t type;
470
471 rsize += readByte(byte);
472 type = (byte & 0x0f);
473
474 // if it's a stop, then we can return immediately, as the struct is over.
475 if (type == T_STOP) {
476 fieldType = T_STOP;
477 fieldId = 0;
478 return rsize;
479 }
480
481 // mask off the 4 MSB of the type header. it could contain a field id delta.
482 int16_t modifier = (int16_t)(((uint8_t)byte & 0xf0) >> 4);
483 if (modifier == 0) {
484 // not a delta, look ahead for the zigzag varint field id.
485 rsize += readI16(fieldId);
486 } else {
487 fieldId = (int16_t)(lastFieldId_ + modifier);
488 }
489 fieldType = getTType(type);
490
491 // if this happens to be a boolean field, the value is encoded in the type
David Reisse71115b2010-10-06 17:09:56 +0000492 if (type == detail::compact::CT_BOOLEAN_TRUE ||
493 type == detail::compact::CT_BOOLEAN_FALSE) {
David Reisse4d4ea02009-04-02 21:37:17 +0000494 // save the boolean value in a special instance variable.
495 boolValue_.hasBoolValue = true;
David Reisse71115b2010-10-06 17:09:56 +0000496 boolValue_.boolValue =
497 (type == detail::compact::CT_BOOLEAN_TRUE ? true : false);
David Reisse4d4ea02009-04-02 21:37:17 +0000498 }
499
500 // push the new field onto the field stack so we can keep the deltas going.
501 lastFieldId_ = fieldId;
502 return rsize;
503}
504
505/**
506 * Read a map header off the wire. If the size is zero, skip reading the key
507 * and value type. This means that 0-length maps will yield TMaps without the
508 * "correct" types.
509 */
David Reisse71115b2010-10-06 17:09:56 +0000510template <class Transport_>
511uint32_t TCompactProtocolT<Transport_>::readMapBegin(TType& keyType,
512 TType& valType,
513 uint32_t& size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000514 uint32_t rsize = 0;
515 int8_t kvType = 0;
516 int32_t msize = 0;
517
518 rsize += readVarint32(msize);
519 if (msize != 0)
520 rsize += readByte(kvType);
521
522 if (msize < 0) {
523 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
524 } else if (container_limit_ && msize > container_limit_) {
525 throw TProtocolException(TProtocolException::SIZE_LIMIT);
526 }
527
528 keyType = getTType((int8_t)((uint8_t)kvType >> 4));
529 valType = getTType((int8_t)((uint8_t)kvType & 0xf));
530 size = (uint32_t)msize;
531
532 return rsize;
533}
534
535/**
536 * Read a list header off the wire. If the list size is 0-14, the size will
537 * be packed into the element type header. If it's a longer list, the 4 MSB
538 * of the element type header will be 0xF, and a varint will follow with the
539 * true size.
540 */
David Reisse71115b2010-10-06 17:09:56 +0000541template <class Transport_>
542uint32_t TCompactProtocolT<Transport_>::readListBegin(TType& elemType,
543 uint32_t& size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000544 int8_t size_and_type;
545 uint32_t rsize = 0;
546 int32_t lsize;
547
548 rsize += readByte(size_and_type);
549
550 lsize = ((uint8_t)size_and_type >> 4) & 0x0f;
551 if (lsize == 15) {
552 rsize += readVarint32(lsize);
553 }
554
555 if (lsize < 0) {
556 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
557 } else if (container_limit_ && lsize > container_limit_) {
558 throw TProtocolException(TProtocolException::SIZE_LIMIT);
559 }
560
561 elemType = getTType((int8_t)(size_and_type & 0x0f));
562 size = (uint32_t)lsize;
563
564 return rsize;
565}
566
567/**
568 * Read a set header off the wire. If the set size is 0-14, the size will
569 * be packed into the element type header. If it's a longer set, the 4 MSB
570 * of the element type header will be 0xF, and a varint will follow with the
571 * true size.
572 */
David Reisse71115b2010-10-06 17:09:56 +0000573template <class Transport_>
574uint32_t TCompactProtocolT<Transport_>::readSetBegin(TType& elemType,
575 uint32_t& size) {
David Reisse4d4ea02009-04-02 21:37:17 +0000576 return readListBegin(elemType, size);
577}
578
579/**
580 * Read a boolean off the wire. If this is a boolean field, the value should
581 * already have been read during readFieldBegin, so we'll just consume the
582 * pre-stored value. Otherwise, read a byte.
583 */
David Reisse71115b2010-10-06 17:09:56 +0000584template <class Transport_>
585uint32_t TCompactProtocolT<Transport_>::readBool(bool& value) {
David Reisse4d4ea02009-04-02 21:37:17 +0000586 if (boolValue_.hasBoolValue == true) {
587 value = boolValue_.boolValue;
588 boolValue_.hasBoolValue = false;
589 return 0;
590 } else {
591 int8_t val;
592 readByte(val);
David Reisse71115b2010-10-06 17:09:56 +0000593 value = (val == detail::compact::CT_BOOLEAN_TRUE);
David Reisse4d4ea02009-04-02 21:37:17 +0000594 return 1;
595 }
596}
597
598/**
599 * Read a single byte off the wire. Nothing interesting here.
600 */
David Reisse71115b2010-10-06 17:09:56 +0000601template <class Transport_>
602uint32_t TCompactProtocolT<Transport_>::readByte(int8_t& byte) {
David Reisse4d4ea02009-04-02 21:37:17 +0000603 uint8_t b[1];
604 trans_->readAll(b, 1);
605 byte = *(int8_t*)b;
606 return 1;
607}
608
609/**
610 * Read an i16 from the wire as a zigzag varint.
611 */
David Reisse71115b2010-10-06 17:09:56 +0000612template <class Transport_>
613uint32_t TCompactProtocolT<Transport_>::readI16(int16_t& i16) {
David Reisse4d4ea02009-04-02 21:37:17 +0000614 int32_t value;
615 uint32_t rsize = readVarint32(value);
616 i16 = (int16_t)zigzagToI32(value);
617 return rsize;
618}
619
620/**
621 * Read an i32 from the wire as a zigzag varint.
622 */
David Reisse71115b2010-10-06 17:09:56 +0000623template <class Transport_>
624uint32_t TCompactProtocolT<Transport_>::readI32(int32_t& i32) {
David Reisse4d4ea02009-04-02 21:37:17 +0000625 int32_t value;
626 uint32_t rsize = readVarint32(value);
627 i32 = zigzagToI32(value);
628 return rsize;
629}
630
631/**
632 * Read an i64 from the wire as a zigzag varint.
633 */
David Reisse71115b2010-10-06 17:09:56 +0000634template <class Transport_>
635uint32_t TCompactProtocolT<Transport_>::readI64(int64_t& i64) {
David Reisse4d4ea02009-04-02 21:37:17 +0000636 int64_t value;
637 uint32_t rsize = readVarint64(value);
638 i64 = zigzagToI64(value);
639 return rsize;
640}
641
642/**
643 * No magic here - just read a double off the wire.
644 */
David Reisse71115b2010-10-06 17:09:56 +0000645template <class Transport_>
646uint32_t TCompactProtocolT<Transport_>::readDouble(double& dub) {
David Reisse4d4ea02009-04-02 21:37:17 +0000647 BOOST_STATIC_ASSERT(sizeof(double) == sizeof(uint64_t));
648 BOOST_STATIC_ASSERT(std::numeric_limits<double>::is_iec559);
649
Carl Yeksigian3e937112013-06-03 13:46:51 -0400650 union {
651 uint64_t bits;
652 uint8_t b[8];
653 } u;
654 trans_->readAll(u.b, 8);
655 u.bits = letohll(u.bits);
656 dub = bitwise_cast<double>(u.bits);
David Reisse4d4ea02009-04-02 21:37:17 +0000657 return 8;
658}
659
David Reisse71115b2010-10-06 17:09:56 +0000660template <class Transport_>
661uint32_t TCompactProtocolT<Transport_>::readString(std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000662 return readBinary(str);
663}
664
665/**
666 * Read a byte[] from the wire.
667 */
David Reisse71115b2010-10-06 17:09:56 +0000668template <class Transport_>
669uint32_t TCompactProtocolT<Transport_>::readBinary(std::string& str) {
David Reisse4d4ea02009-04-02 21:37:17 +0000670 int32_t rsize = 0;
671 int32_t size;
672
673 rsize += readVarint32(size);
674 // Catch empty string case
675 if (size == 0) {
676 str = "";
677 return rsize;
678 }
679
680 // Catch error cases
681 if (size < 0) {
682 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
683 }
684 if (string_limit_ > 0 && size > string_limit_) {
685 throw TProtocolException(TProtocolException::SIZE_LIMIT);
686 }
687
688 // Use the heap here to prevent stack overflow for v. large strings
689 if (size > string_buf_size_ || string_buf_ == NULL) {
690 void* new_string_buf = std::realloc(string_buf_, (uint32_t)size);
691 if (new_string_buf == NULL) {
David Reissf6735092010-10-06 17:10:49 +0000692 throw std::bad_alloc();
David Reisse4d4ea02009-04-02 21:37:17 +0000693 }
694 string_buf_ = (uint8_t*)new_string_buf;
695 string_buf_size_ = size;
696 }
697 trans_->readAll(string_buf_, size);
698 str.assign((char*)string_buf_, size);
699
700 return rsize + (uint32_t)size;
701}
702
703/**
704 * Read an i32 from the wire as a varint. The MSB of each byte is set
705 * if there is another byte to follow. This can read up to 5 bytes.
706 */
David Reisse71115b2010-10-06 17:09:56 +0000707template <class Transport_>
708uint32_t TCompactProtocolT<Transport_>::readVarint32(int32_t& i32) {
David Reisse4d4ea02009-04-02 21:37:17 +0000709 int64_t val;
710 uint32_t rsize = readVarint64(val);
711 i32 = (int32_t)val;
712 return rsize;
713}
714
715/**
716 * Read an i64 from the wire as a proper varint. The MSB of each byte is set
717 * if there is another byte to follow. This can read up to 10 bytes.
718 */
David Reisse71115b2010-10-06 17:09:56 +0000719template <class Transport_>
720uint32_t TCompactProtocolT<Transport_>::readVarint64(int64_t& i64) {
David Reisse4d4ea02009-04-02 21:37:17 +0000721 uint32_t rsize = 0;
722 uint64_t val = 0;
723 int shift = 0;
724 uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes.
725 uint32_t buf_size = sizeof(buf);
726 const uint8_t* borrowed = trans_->borrow(buf, &buf_size);
727
728 // Fast path.
729 if (borrowed != NULL) {
730 while (true) {
731 uint8_t byte = borrowed[rsize];
732 rsize++;
733 val |= (uint64_t)(byte & 0x7f) << shift;
734 shift += 7;
735 if (!(byte & 0x80)) {
736 i64 = val;
737 trans_->consume(rsize);
738 return rsize;
739 }
740 // Have to check for invalid data so we don't crash.
741 if (UNLIKELY(rsize == sizeof(buf))) {
742 throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
743 }
744 }
745 }
746
747 // Slow path.
748 else {
749 while (true) {
750 uint8_t byte;
751 rsize += trans_->readAll(&byte, 1);
752 val |= (uint64_t)(byte & 0x7f) << shift;
753 shift += 7;
754 if (!(byte & 0x80)) {
755 i64 = val;
756 return rsize;
757 }
758 // Might as well check for invalid data on the slow path too.
759 if (UNLIKELY(rsize >= sizeof(buf))) {
760 throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
761 }
762 }
763 }
764}
765
766/**
767 * Convert from zigzag int to int.
768 */
David Reisse71115b2010-10-06 17:09:56 +0000769template <class Transport_>
770int32_t TCompactProtocolT<Transport_>::zigzagToI32(uint32_t n) {
Roger Meier64a799d2013-06-04 20:59:01 +0200771 return (n >> 1) ^ -static_cast<int32_t>(n & 1);
David Reisse4d4ea02009-04-02 21:37:17 +0000772}
773
774/**
775 * Convert from zigzag long to long.
776 */
David Reisse71115b2010-10-06 17:09:56 +0000777template <class Transport_>
778int64_t TCompactProtocolT<Transport_>::zigzagToI64(uint64_t n) {
Roger Meier64a799d2013-06-04 20:59:01 +0200779 return (n >> 1) ^ -static_cast<int32_t>(n & 1);
David Reisse4d4ea02009-04-02 21:37:17 +0000780}
781
David Reisse71115b2010-10-06 17:09:56 +0000782template <class Transport_>
783TType TCompactProtocolT<Transport_>::getTType(int8_t type) {
David Reisse4d4ea02009-04-02 21:37:17 +0000784 switch (type) {
785 case T_STOP:
786 return T_STOP;
David Reisse71115b2010-10-06 17:09:56 +0000787 case detail::compact::CT_BOOLEAN_FALSE:
788 case detail::compact::CT_BOOLEAN_TRUE:
David Reisse4d4ea02009-04-02 21:37:17 +0000789 return T_BOOL;
David Reisse71115b2010-10-06 17:09:56 +0000790 case detail::compact::CT_BYTE:
David Reisse4d4ea02009-04-02 21:37:17 +0000791 return T_BYTE;
David Reisse71115b2010-10-06 17:09:56 +0000792 case detail::compact::CT_I16:
David Reisse4d4ea02009-04-02 21:37:17 +0000793 return T_I16;
David Reisse71115b2010-10-06 17:09:56 +0000794 case detail::compact::CT_I32:
David Reisse4d4ea02009-04-02 21:37:17 +0000795 return T_I32;
David Reisse71115b2010-10-06 17:09:56 +0000796 case detail::compact::CT_I64:
David Reisse4d4ea02009-04-02 21:37:17 +0000797 return T_I64;
David Reisse71115b2010-10-06 17:09:56 +0000798 case detail::compact::CT_DOUBLE:
David Reisse4d4ea02009-04-02 21:37:17 +0000799 return T_DOUBLE;
David Reisse71115b2010-10-06 17:09:56 +0000800 case detail::compact::CT_BINARY:
David Reisse4d4ea02009-04-02 21:37:17 +0000801 return T_STRING;
David Reisse71115b2010-10-06 17:09:56 +0000802 case detail::compact::CT_LIST:
David Reisse4d4ea02009-04-02 21:37:17 +0000803 return T_LIST;
David Reisse71115b2010-10-06 17:09:56 +0000804 case detail::compact::CT_SET:
David Reisse4d4ea02009-04-02 21:37:17 +0000805 return T_SET;
David Reisse71115b2010-10-06 17:09:56 +0000806 case detail::compact::CT_MAP:
David Reisse4d4ea02009-04-02 21:37:17 +0000807 return T_MAP;
David Reisse71115b2010-10-06 17:09:56 +0000808 case detail::compact::CT_STRUCT:
David Reisse4d4ea02009-04-02 21:37:17 +0000809 return T_STRUCT;
810 default:
Roger Meiera7ab94d2013-03-22 22:34:16 +0100811 throw TException(std::string("don't know what type: ") + (char)type);
David Reisse4d4ea02009-04-02 21:37:17 +0000812 }
813 return T_STOP;
814}
815
816}}} // apache::thrift::protocol
David Reisse71115b2010-10-06 17:09:56 +0000817
818#endif // _THRIFT_PROTOCOL_TCOMPACTPROTOCOL_TCC_