| // Copyright (c) 2006- Facebook | 
 | // Distributed under the Thrift Software License | 
 | // | 
 | // See accompanying file LICENSE or visit the Thrift site at: | 
 | // http://developers.facebook.com/thrift/ | 
 |  | 
 | /* | 
 |  | 
 | IMPLEMENTATION DETAILS | 
 |  | 
 | TDenseProtocol was designed to have a smaller serialized form than | 
 | TBinaryProtocol.  This is accomplished using two techniques.  The first is | 
 | variable-length integer encoding.  We use the same technique that the Standard | 
 | MIDI File format uses for "variable-length quantities" | 
 | (http://en.wikipedia.org/wiki/Variable-length_quantity). | 
 | All integers (including i16, but not byte) are first cast to uint64_t, | 
 | then written out as variable-length quantities.  This has the unfortunate side | 
 | effect that all negative numbers require 10 bytes, but negative numbers tend | 
 | to be far less common than positive ones. | 
 |  | 
 | The second technique eliminating the field ids used by TBinaryProtocol.  This | 
 | decision required support from the Thrift compiler and also sacrifices some of | 
 | the backward and forward compatibility of TBinaryProtocol. | 
 |  | 
 | We considered implementing this technique by generating separate readers and | 
 | writers for the dense protocol (this is how Pillar, Thrift's predecessor, | 
 | worked), but this idea had a few problems: | 
 | - Our abstractions go out the window. | 
 | - We would have to maintain a second code generator. | 
 | - Preserving compatibility with old versions of the structures would be a | 
 |   nightmare. | 
 |  | 
 | Therefore, we chose an alternate implementation that stored the description of | 
 | the data neither in the data itself (like TBinaryProtocol) nor in the | 
 | serialization code (like Pillar), but instead in a separate data structure, | 
 | called a TypeSpec.  TypeSpecs are generated by the Thrift compiler | 
 | (specifically in the t_cpp_generator), and their structure should be | 
 | documented there (TODO(dreiss): s/should be/is/). | 
 |  | 
 | We maintain a stack of TypeSpecs within the protocol so it knows where the | 
 | generated code is in the reading/writing process.  For example, if we are | 
 | writing an i32 contained in a struct bar, contained in a struct foo, then the | 
 | stack would look like: TOP , i32 , struct bar , struct foo , BOTTOM. | 
 | The following invariant: whenever we are about to read/write an object | 
 | (structBegin, containerBegin, or a scalar), the TypeSpec on the top of the | 
 | stack must match the type being read/written.  The main reasons that this | 
 | invariant must be maintained is that if we ever start reading a structure, we | 
 | must have its exact TypeSpec in order to pass the right tags to the | 
 | deserializer. | 
 |  | 
 | We use the following strategies for maintaining this invariant: | 
 |  | 
 | - For structures, we have a separate stack of indexes, one for each structure | 
 |   on the TypeSpec stack.  These are indexes into the list of fields in the | 
 |   structure's TypeSpec.  When we {read,write}FieldBegin, we push on the | 
 |   TypeSpec for the field. | 
 | - When we begin writing a list or set, we push on the TypeSpec for the | 
 |   element type. | 
 | - For maps, we have a separate stack of booleans, one for each map on the | 
 |   TypeSpec stack.  The boolean is true if we are writing the key for that | 
 |   map, and false if we are writing the value.  Maps are the trickiest case | 
 |   because the generated code does not call any protocol method between | 
 |   the key and the value.  As a result, we potentially have to switch | 
 |   between map key state and map value state after reading/writing any object. | 
 | - This job is handled by the stateTransition method.  It is called after | 
 |   reading/writing every object.  It pops the current TypeSpec off the stack, | 
 |   then optionally pushes a new one on, depending on what the next TypeSpec is. | 
 |   If it is a struct, the job is left to the next writeFieldBegin.  If it is a | 
 |   set or list, the just-popped typespec is pushed back on.  If it is a map, | 
 |   the top of the key/value stack is toggled, and the appropriate TypeSpec | 
 |   is pushed. | 
 |  | 
 | Optional fields are a little tricky also.  We write a zero byte if they are | 
 | absent and prefix them with an 0x01 byte if they are present | 
 | */ | 
 |  | 
 | #define __STDC_LIMIT_MACROS | 
 | #include <stdint.h> | 
 | #include "TDenseProtocol.h" | 
 | #include "TReflectionLocal.h" | 
 |  | 
 | // Leaving this on for now.  Disabling it will turn off asserts, which should | 
 | // give a performance boost.  When we have *really* thorough test cases, | 
 | // we should drop this. | 
 | #define DEBUG_TDENSEPROTOCOL | 
 |  | 
 | // NOTE: Assertions should *only* be used to detect bugs in code, | 
 | //       either in TDenseProtocol itself, or in code using it. | 
 | //       (For example, using the wrong TypeSpec.) | 
 | //       Invalid data should NEVER cause an assertion failure, | 
 | //       no matter how grossly corrupted, nor how ingeniously crafted. | 
 | #ifdef DEBUG_TDENSEPROTOCOL | 
 | #undef NDEBUG | 
 | #else | 
 | #define NDEBUG | 
 | #endif | 
 | #include <cassert> | 
 |  | 
 | using std::string; | 
 |  | 
 | #ifdef __GNUC__ | 
 | #define UNLIKELY(val) (__builtin_expect((val), 0)) | 
 | #else | 
 | #define UNLIKELY(val) (val) | 
 | #endif | 
 |  | 
 | namespace facebook { namespace thrift { namespace protocol { | 
 |  | 
 | const int TDenseProtocol::FP_PREFIX_LEN = | 
 |   facebook::thrift::reflection::local::FP_PREFIX_LEN; | 
 |  | 
 | // Top TypeSpec.  TypeSpec of the structure being encoded. | 
 | #define TTS  (ts_stack_.back())  // type = TypeSpec* | 
 | // InDeX.  Index into TTS of the current/next field to encode. | 
 | #define IDX (idx_stack_.back())  // type = int | 
 | // Field TypeSpec.  TypeSpec of the current/next field to encode. | 
 | #define FTS (TTS->tstruct.specs[IDX])  // type = TypeSpec* | 
 | // Field MeTa.  Metadata of the current/next field to encode. | 
 | #define FMT (TTS->tstruct.metas[IDX])  // type = FieldMeta | 
 | // SubType 1/2.  TypeSpec of the first/second subtype of this container. | 
 | #define ST1 (TTS->tcontainer.subtype1) | 
 | #define ST2 (TTS->tcontainer.subtype2) | 
 |  | 
 |  | 
 | /** | 
 |  * Checks that @c ttype is indeed the ttype that we should be writing, | 
 |  * according to our typespec.  Aborts if the test fails and debugging in on. | 
 |  */ | 
 | inline void TDenseProtocol::checkTType(const TType ttype) { | 
 |   assert(!ts_stack_.empty()); | 
 |   assert(TTS->ttype == ttype); | 
 | } | 
 |  | 
 | /** | 
 |  * Makes sure that the TypeSpec stack is correct for the next object. | 
 |  * See top-of-file comments. | 
 |  */ | 
 | inline void TDenseProtocol::stateTransition() { | 
 |   TypeSpec* old_tts = ts_stack_.back(); | 
 |   ts_stack_.pop_back(); | 
 |  | 
 |   // If this is the end of the top-level write, we should have just popped | 
 |   // the TypeSpec passed to the constructor. | 
 |   if (ts_stack_.empty()) { | 
 |     assert(old_tts = type_spec_); | 
 |     return; | 
 |   } | 
 |  | 
 |   switch (TTS->ttype) { | 
 |  | 
 |     case T_STRUCT: | 
 |       assert(old_tts == FTS); | 
 |       break; | 
 |  | 
 |     case T_LIST: | 
 |     case T_SET: | 
 |       assert(old_tts == ST1); | 
 |       ts_stack_.push_back(old_tts); | 
 |       break; | 
 |  | 
 |     case T_MAP: | 
 |       assert(old_tts == (mkv_stack_.back() ? ST1 : ST2)); | 
 |       mkv_stack_.back() = !mkv_stack_.back(); | 
 |       ts_stack_.push_back(mkv_stack_.back() ? ST1 : ST2); | 
 |       break; | 
 |  | 
 |     default: | 
 |       assert(!"Invalid TType in stateTransition."); | 
 |       break; | 
 |  | 
 |   } | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Variable-length quantity functions. | 
 |  */ | 
 |  | 
 | inline uint32_t TDenseProtocol::vlqRead(uint64_t& vlq) { | 
 |   uint32_t used = 0; | 
 |   uint64_t val = 0; | 
 |   uint8_t buf[10];  // 64 bits / (7 bits/byte) = 10 bytes. | 
 |   uint32_t buf_size = sizeof(buf); | 
 |   const uint8_t* borrowed = trans_->borrow(buf, &buf_size); | 
 |  | 
 |   // Fast path.  TODO(dreiss): Make it faster. | 
 |   if (borrowed != NULL) { | 
 |     while (true) { | 
 |       uint8_t byte = borrowed[used]; | 
 |       used++; | 
 |       val = (val << 7) | (byte & 0x7f); | 
 |       if (!(byte & 0x80)) { | 
 |         vlq = val; | 
 |         trans_->consume(used); | 
 |         return used; | 
 |       } | 
 |       // Have to check for invalid data so we don't crash. | 
 |       if (UNLIKELY(used == sizeof(buf))) { | 
 |         resetState(); | 
 |         throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes."); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Slow path. | 
 |   else { | 
 |     while (true) { | 
 |       uint8_t byte; | 
 |       used += trans_->readAll(&byte, 1); | 
 |       val = (val << 7) | (byte & 0x7f); | 
 |       if (!(byte & 0x80)) { | 
 |         vlq = val; | 
 |         return used; | 
 |       } | 
 |       // Might as well check for invalid data on the slow path too. | 
 |       if (UNLIKELY(used >= sizeof(buf))) { | 
 |         resetState(); | 
 |         throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes."); | 
 |       } | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | inline uint32_t TDenseProtocol::vlqWrite(uint64_t vlq) { | 
 |   uint8_t buf[10];  // 64 bits / (7 bits/byte) = 10 bytes. | 
 |   int32_t pos = sizeof(buf) - 1; | 
 |  | 
 |   // Write the thing from back to front. | 
 |   buf[pos] = vlq & 0x7f; | 
 |   vlq >>= 7; | 
 |   pos--; | 
 |  | 
 |   while (vlq > 0) { | 
 |     assert(pos >= 0); | 
 |     buf[pos] = (vlq | 0x80); | 
 |     vlq >>= 7; | 
 |     pos--; | 
 |   } | 
 |  | 
 |   // Back up one step before writing. | 
 |   pos++; | 
 |  | 
 |   trans_->write(buf+pos, sizeof(buf) - pos); | 
 |   return sizeof(buf) - pos; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* | 
 |  * Writing functions. | 
 |  */ | 
 |  | 
 | uint32_t TDenseProtocol::writeMessageBegin(const std::string& name, | 
 |                                            const TMessageType messageType, | 
 |                                            const int32_t seqid) { | 
 |   throw TApplicationException("TDenseProtocol doesn't work with messages (yet)."); | 
 |  | 
 |   int32_t version = (VERSION_2) | ((int32_t)messageType); | 
 |   uint32_t wsize = 0; | 
 |   wsize += subWriteI32(version); | 
 |   wsize += subWriteString(name); | 
 |   wsize += subWriteI32(seqid); | 
 |   return wsize; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeMessageEnd() { | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeStructBegin(const char* name) { | 
 |   uint32_t xfer = 0; | 
 |  | 
 |   // The TypeSpec stack should be empty if this is the top-level read/write. | 
 |   // If it is, we push the TypeSpec passed to the constructor. | 
 |   if (ts_stack_.empty()) { | 
 |     assert(standalone_); | 
 |  | 
 |     if (type_spec_ == NULL) { | 
 |       resetState(); | 
 |       throw TApplicationException("TDenseProtocol: No type specified."); | 
 |     } else { | 
 |       assert(type_spec_->ttype == T_STRUCT); | 
 |       ts_stack_.push_back(type_spec_); | 
 |       // Write out a prefix of the structure fingerprint. | 
 |       trans_->write(type_spec_->fp_prefix, FP_PREFIX_LEN); | 
 |       xfer += FP_PREFIX_LEN; | 
 |     } | 
 |   } | 
 |  | 
 |   // We need a new field index for this structure. | 
 |   idx_stack_.push_back(0); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeStructEnd() { | 
 |   idx_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeFieldBegin(const char* name, | 
 |                                          const TType fieldType, | 
 |                                          const int16_t fieldId) { | 
 |   uint32_t xfer = 0; | 
 |  | 
 |   // Skip over optional fields. | 
 |   while (FMT.tag != fieldId) { | 
 |     // TODO(dreiss): Old meta here. | 
 |     assert(FTS->ttype != T_STOP); | 
 |     assert(FMT.is_optional); | 
 |     // Write a zero byte so the reader can skip it. | 
 |     xfer += subWriteBool(false); | 
 |     // And advance to the next field. | 
 |     IDX++; | 
 |   } | 
 |  | 
 |   // TODO(dreiss): give a better exception. | 
 |   assert(FTS->ttype == fieldType); | 
 |  | 
 |   if (FMT.is_optional) { | 
 |     subWriteBool(true); | 
 |     xfer += 1; | 
 |   } | 
 |  | 
 |   // writeFieldStop shares all lot of logic up to this point. | 
 |   // Instead of replicating it all, we just call this method from that one | 
 |   // and use a gross special case here. | 
 |   if (UNLIKELY(FTS->ttype != T_STOP)) { | 
 |     // For normal fields, push the TypeSpec that we're about to use. | 
 |     ts_stack_.push_back(FTS); | 
 |   } | 
 |   return xfer; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeFieldEnd() { | 
 |   // Just move on to the next field. | 
 |   IDX++; | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeFieldStop() { | 
 |   return TDenseProtocol::writeFieldBegin("", T_STOP, 0); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeMapBegin(const TType keyType, | 
 |                                        const TType valType, | 
 |                                        const uint32_t size) { | 
 |   checkTType(T_MAP); | 
 |  | 
 |   assert(keyType == ST1->ttype); | 
 |   assert(valType == ST2->ttype); | 
 |  | 
 |   ts_stack_.push_back(ST1); | 
 |   mkv_stack_.push_back(true); | 
 |  | 
 |   return subWriteI32((int32_t)size); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeMapEnd() { | 
 |   // Pop off the value type, as well as our entry in the map key/value stack. | 
 |   // stateTransition takes care of popping off our TypeSpec. | 
 |   ts_stack_.pop_back(); | 
 |   mkv_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeListBegin(const TType elemType, | 
 |                                         const uint32_t size) { | 
 |   checkTType(T_LIST); | 
 |  | 
 |   assert(elemType == ST1->ttype); | 
 |   ts_stack_.push_back(ST1); | 
 |   return subWriteI32((int32_t)size); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeListEnd() { | 
 |   // Pop off the element type.  stateTransition takes care of popping off ours. | 
 |   ts_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeSetBegin(const TType elemType, | 
 |                                        const uint32_t size) { | 
 |   checkTType(T_SET); | 
 |  | 
 |   assert(elemType == ST1->ttype); | 
 |   ts_stack_.push_back(ST1); | 
 |   return subWriteI32((int32_t)size); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeSetEnd() { | 
 |   // Pop off the element type.  stateTransition takes care of popping off ours. | 
 |   ts_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeBool(const bool value) { | 
 |   checkTType(T_BOOL); | 
 |   stateTransition(); | 
 |   return TBinaryProtocol::writeBool(value); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeByte(const int8_t byte) { | 
 |   checkTType(T_BYTE); | 
 |   stateTransition(); | 
 |   return TBinaryProtocol::writeByte(byte); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeI16(const int16_t i16) { | 
 |   checkTType(T_I16); | 
 |   stateTransition(); | 
 |   return vlqWrite(i16); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeI32(const int32_t i32) { | 
 |   checkTType(T_I32); | 
 |   stateTransition(); | 
 |   return vlqWrite(i32); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeI64(const int64_t i64) { | 
 |   checkTType(T_I64); | 
 |   stateTransition(); | 
 |   return vlqWrite(i64); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeDouble(const double dub) { | 
 |   checkTType(T_DOUBLE); | 
 |   stateTransition(); | 
 |   return TBinaryProtocol::writeDouble(dub); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeString(const std::string& str) { | 
 |   checkTType(T_STRING); | 
 |   stateTransition(); | 
 |   return subWriteString(str); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::writeBinary(const std::string& str) { | 
 |   return TDenseProtocol::writeString(str); | 
 | } | 
 |  | 
 | inline uint32_t TDenseProtocol::subWriteI32(const int32_t i32) { | 
 |   return vlqWrite(i32); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::subWriteString(const std::string& str) { | 
 |   uint32_t size = str.size(); | 
 |   uint32_t xfer = subWriteI32((int32_t)size); | 
 |   if (size > 0) { | 
 |     trans_->write((uint8_t*)str.data(), size); | 
 |   } | 
 |   return xfer + size; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* | 
 |  * Reading functions | 
 |  * | 
 |  * These have a lot of the same logic as the writing functions, so if | 
 |  * something is confusing, look for comments in the corresponding writer. | 
 |  */ | 
 |  | 
 | uint32_t TDenseProtocol::readMessageBegin(std::string& name, | 
 |                                           TMessageType& messageType, | 
 |                                           int32_t& seqid) { | 
 |   throw TApplicationException("TDenseProtocol doesn't work with messages (yet)."); | 
 |  | 
 |   uint32_t xfer = 0; | 
 |   int32_t sz; | 
 |   xfer += subReadI32(sz); | 
 |  | 
 |   if (sz < 0) { | 
 |     // Check for correct version number | 
 |     int32_t version = sz & VERSION_MASK; | 
 |     if (version != VERSION_2) { | 
 |       throw TProtocolException(TProtocolException::BAD_VERSION, "Bad version identifier"); | 
 |     } | 
 |     messageType = (TMessageType)(sz & 0x000000ff); | 
 |     xfer += subReadString(name); | 
 |     xfer += subReadI32(seqid); | 
 |   } else { | 
 |     throw TProtocolException(TProtocolException::BAD_VERSION, "No version identifier... old protocol client in strict mode?"); | 
 |   } | 
 |   return xfer; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readMessageEnd() { | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readStructBegin(string& name) { | 
 |   uint32_t xfer = 0; | 
 |  | 
 |   if (ts_stack_.empty()) { | 
 |     assert(standalone_); | 
 |  | 
 |     if (type_spec_ == NULL) { | 
 |       resetState(); | 
 |       throw TApplicationException("TDenseProtocol: No type specified."); | 
 |     } else { | 
 |       assert(type_spec_->ttype == T_STRUCT); | 
 |       ts_stack_.push_back(type_spec_); | 
 |  | 
 |       // Check the fingerprint prefix. | 
 |       uint8_t buf[FP_PREFIX_LEN]; | 
 |       xfer += trans_->read(buf, FP_PREFIX_LEN); | 
 |       if (std::memcmp(buf, type_spec_->fp_prefix, FP_PREFIX_LEN) != 0) { | 
 |         resetState(); | 
 |         throw TProtocolException(TProtocolException::INVALID_DATA, | 
 |             "Fingerprint in data does not match type_spec."); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // We need a new field index for this structure. | 
 |   idx_stack_.push_back(0); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readStructEnd() { | 
 |   idx_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readFieldBegin(string& name, | 
 |                                         TType& fieldType, | 
 |                                         int16_t& fieldId) { | 
 |   uint32_t xfer = 0; | 
 |  | 
 |   // For optional fields, check to see if they are there. | 
 |   while (FMT.is_optional) { | 
 |     bool is_present; | 
 |     xfer += subReadBool(is_present); | 
 |     if (is_present) { | 
 |       break; | 
 |     } | 
 |     IDX++; | 
 |   } | 
 |  | 
 |   // Once we hit a mandatory field, or an optional field that is present, | 
 |   // we know that FMT and FTS point to the appropriate field. | 
 |  | 
 |   fieldId   = FMT.tag; | 
 |   fieldType = FTS->ttype; | 
 |  | 
 |   // Normally, we push the TypeSpec that we are about to read, | 
 |   // but no reading is done for T_STOP. | 
 |   if (FTS->ttype != T_STOP) { | 
 |     ts_stack_.push_back(FTS); | 
 |   } | 
 |   return xfer; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readFieldEnd() { | 
 |   IDX++; | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readMapBegin(TType& keyType, | 
 |                                       TType& valType, | 
 |                                       uint32_t& size) { | 
 |   checkTType(T_MAP); | 
 |  | 
 |   uint32_t xfer = 0; | 
 |   int32_t sizei; | 
 |   xfer += subReadI32(sizei); | 
 |   if (sizei < 0) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::NEGATIVE_SIZE); | 
 |   } else if (container_limit_ && sizei > container_limit_) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::SIZE_LIMIT); | 
 |   } | 
 |   size = (uint32_t)sizei; | 
 |  | 
 |   keyType = ST1->ttype; | 
 |   valType = ST2->ttype; | 
 |  | 
 |   ts_stack_.push_back(ST1); | 
 |   mkv_stack_.push_back(true); | 
 |  | 
 |   return xfer; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readMapEnd() { | 
 |   ts_stack_.pop_back(); | 
 |   mkv_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readListBegin(TType& elemType, | 
 |                                        uint32_t& size) { | 
 |   checkTType(T_LIST); | 
 |  | 
 |   uint32_t xfer = 0; | 
 |   int32_t sizei; | 
 |   xfer += subReadI32(sizei); | 
 |   if (sizei < 0) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::NEGATIVE_SIZE); | 
 |   } else if (container_limit_ && sizei > container_limit_) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::SIZE_LIMIT); | 
 |   } | 
 |   size = (uint32_t)sizei; | 
 |  | 
 |   elemType = ST1->ttype; | 
 |  | 
 |   ts_stack_.push_back(ST1); | 
 |  | 
 |   return xfer; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readListEnd() { | 
 |   ts_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readSetBegin(TType& elemType, | 
 |                                       uint32_t& size) { | 
 |   checkTType(T_SET); | 
 |  | 
 |   uint32_t xfer = 0; | 
 |   int32_t sizei; | 
 |   xfer += subReadI32(sizei); | 
 |   if (sizei < 0) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::NEGATIVE_SIZE); | 
 |   } else if (container_limit_ && sizei > container_limit_) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::SIZE_LIMIT); | 
 |   } | 
 |   size = (uint32_t)sizei; | 
 |  | 
 |   elemType = ST1->ttype; | 
 |  | 
 |   ts_stack_.push_back(ST1); | 
 |  | 
 |   return xfer; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readSetEnd() { | 
 |   ts_stack_.pop_back(); | 
 |   stateTransition(); | 
 |   return 0; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readBool(bool& value) { | 
 |   checkTType(T_BOOL); | 
 |   stateTransition(); | 
 |   return TBinaryProtocol::readBool(value); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readByte(int8_t& byte) { | 
 |   checkTType(T_BYTE); | 
 |   stateTransition(); | 
 |   return TBinaryProtocol::readByte(byte); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readI16(int16_t& i16) { | 
 |   checkTType(T_I16); | 
 |   stateTransition(); | 
 |   uint64_t u64; | 
 |   uint32_t rv = vlqRead(u64); | 
 |   int64_t val = (int64_t)u64; | 
 |   if (UNLIKELY(val > INT16_MAX || val < INT16_MIN)) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::INVALID_DATA, | 
 |                              "i16 out of range."); | 
 |   } | 
 |   i16 = (int16_t)val; | 
 |   return rv; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readI32(int32_t& i32) { | 
 |   checkTType(T_I32); | 
 |   stateTransition(); | 
 |   uint64_t u64; | 
 |   uint32_t rv = vlqRead(u64); | 
 |   int64_t val = (int64_t)u64; | 
 |   if (UNLIKELY(val > INT32_MAX || val < INT32_MIN)) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::INVALID_DATA, | 
 |                              "i32 out of range."); | 
 |   } | 
 |   i32 = (int32_t)val; | 
 |   return rv; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readI64(int64_t& i64) { | 
 |   checkTType(T_I64); | 
 |   stateTransition(); | 
 |   uint64_t u64; | 
 |   uint32_t rv = vlqRead(u64); | 
 |   int64_t val = (int64_t)u64; | 
 |   if (UNLIKELY(val > INT64_MAX || val < INT64_MIN)) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::INVALID_DATA, | 
 |                              "i64 out of range."); | 
 |   } | 
 |   i64 = (int64_t)val; | 
 |   return rv; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readDouble(double& dub) { | 
 |   checkTType(T_DOUBLE); | 
 |   stateTransition(); | 
 |   return TBinaryProtocol::readDouble(dub); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readString(std::string& str) { | 
 |   checkTType(T_STRING); | 
 |   stateTransition(); | 
 |   return subReadString(str); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::readBinary(std::string& str) { | 
 |   return TDenseProtocol::readString(str); | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::subReadI32(int32_t& i32) { | 
 |   uint64_t u64; | 
 |   uint32_t rv = vlqRead(u64); | 
 |   int64_t val = (int64_t)u64; | 
 |   if (UNLIKELY(val > INT32_MAX || val < INT32_MIN)) { | 
 |     resetState(); | 
 |     throw TProtocolException(TProtocolException::INVALID_DATA, | 
 |                              "i32 out of range."); | 
 |   } | 
 |   i32 = (int32_t)val; | 
 |   return rv; | 
 | } | 
 |  | 
 | uint32_t TDenseProtocol::subReadString(std::string& str) { | 
 |   uint32_t xfer; | 
 |   int32_t size; | 
 |   xfer = subReadI32(size); | 
 |   return xfer + readStringBody(str, size); | 
 | } | 
 |  | 
 | }}} // facebook::thrift::protocol |