blob: 55f39028e7c137df35ce1c469bb29d9b647583fc [file] [log] [blame]
David Reiss4e7530d2007-09-04 21:49:53 +00001// Copyright (c) 2006- Facebook
2// Distributed under the Thrift Software License
3//
4// See accompanying file LICENSE or visit the Thrift site at:
5// http://developers.facebook.com/thrift/
6
David Reissce161a92007-09-11 22:09:42 +00007/*
8
9IMPLEMENTATION DETAILS
10
11TDenseProtocol was designed to have a smaller serialized form than
12TBinaryProtocol. This is accomplished using two techniques. The first is
13variable-length integer encoding. We use the same technique that the Standard
14MIDI File format uses for "variable-length quantities"
15(http://en.wikipedia.org/wiki/Variable-length_quantity).
16All integers (including i16, but not byte) are first cast to uint64_t,
17then written out as variable-length quantities. This has the unfortunate side
18effect that all negative numbers require 10 bytes, but negative numbers tend
19to be far less common than positive ones.
20
21The second technique eliminating the field ids used by TBinaryProtocol. This
22decision required support from the Thrift compiler and also sacrifices some of
23the backward and forward compatibility of TBinaryProtocol.
24
25We considered implementing this technique by generating separate readers and
26writers for the dense protocol (this is how Pillar, Thrift's predecessor,
27worked), but this idea had a few problems:
28- Our abstractions go out the window.
29- We would have to maintain a second code generator.
30- Preserving compatibility with old versions of the structures would be a
31 nightmare.
32
33Therefore, we chose an alternate implementation that stored the description of
34the data neither in the data itself (like TBinaryProtocol) nor in the
35serialization code (like Pillar), but instead in a separate data structure,
36called a TypeSpec. TypeSpecs are generated by the Thrift compiler
37(specifically in the t_cpp_generator), and their structure should be
38documented there (TODO(dreiss): s/should be/is/).
39
40We maintain a stack of TypeSpecs within the protocol so it knows where the
41generated code is in the reading/writing process. For example, if we are
42writing an i32 contained in a struct bar, contained in a struct foo, then the
43stack would look like: TOP , i32 , struct bar , struct foo , BOTTOM.
44The following invariant: whenever we are about to read/write an object
45(structBegin, containerBegin, or a scalar), the TypeSpec on the top of the
46stack must match the type being read/written. The main reasons that this
47invariant must be maintained is that if we ever start reading a structure, we
48must have its exact TypeSpec in order to pass the right tags to the
49deserializer.
50
51We use the following strategies for maintaining this invariant:
52
53- For structures, we have a separate stack of indexes, one for each structure
54 on the TypeSpec stack. These are indexes into the list of fields in the
55 structure's TypeSpec. When we {read,write}FieldBegin, we push on the
56 TypeSpec for the field.
57- When we begin writing a list or set, we push on the TypeSpec for the
58 element type.
59- For maps, we have a separate stack of booleans, one for each map on the
60 TypeSpec stack. The boolean is true if we are writing the key for that
61 map, and false if we are writing the value. Maps are the trickiest case
62 because the generated code does not call any protocol method between
63 the key and the value. As a result, we potentially have to switch
64 between map key state and map value state after reading/writing any object.
65- This job is handled by the stateTransition method. It is called after
66 reading/writing every object. It pops the current TypeSpec off the stack,
67 then optionally pushes a new one on, depending on what the next TypeSpec is.
68 If it is a struct, the job is left to the next writeFieldBegin. If it is a
69 set or list, the just-popped typespec is pushed back on. If it is a map,
70 the top of the key/value stack is toggled, and the appropriate TypeSpec
71 is pushed.
72
73Optional fields are a little tricky also. We write a zero byte if they are
74absent and prefix them with an 0x01 byte if they are present
75*/
76
David Reisse67c0e62007-09-07 01:34:12 +000077#define __STDC_LIMIT_MACROS
78#include <stdint.h>
David Reiss4e7530d2007-09-04 21:49:53 +000079#include "TDenseProtocol.h"
80#include "TReflectionLocal.h"
81
David Reissce161a92007-09-11 22:09:42 +000082// Leaving this on for now. Disabling it will turn off asserts, which should
83// give a performance boost. When we have *really* thorough test cases,
84// we should drop this.
David Reiss4e7530d2007-09-04 21:49:53 +000085#define DEBUG_TDENSEPROTOCOL
86
David Reissce161a92007-09-11 22:09:42 +000087// NOTE: Assertions should *only* be used to detect bugs in code,
88// either in TDenseProtocol itself, or in code using it.
89// (For example, using the wrong TypeSpec.)
90// Invalid data should NEVER cause an assertion failure,
91// no matter how grossly corrupted, nor how ingeniously crafted.
David Reiss4e7530d2007-09-04 21:49:53 +000092#ifdef DEBUG_TDENSEPROTOCOL
93#undef NDEBUG
David Reissce161a92007-09-11 22:09:42 +000094#else
95#define NDEBUG
David Reiss4e7530d2007-09-04 21:49:53 +000096#endif
97#include <cassert>
98
99using std::string;
100
David Reisse67c0e62007-09-07 01:34:12 +0000101#ifdef __GNUC__
102#define UNLIKELY(val) (__builtin_expect((val), 0))
103#else
104#define UNLIKELY(val) (val)
105#endif
106
David Reiss4e7530d2007-09-04 21:49:53 +0000107namespace facebook { namespace thrift { namespace protocol {
108
David Reissce161a92007-09-11 22:09:42 +0000109const int TDenseProtocol::FP_PREFIX_LEN =
110 facebook::thrift::reflection::local::FP_PREFIX_LEN;
111
David Reiss4e7530d2007-09-04 21:49:53 +0000112// Top TypeSpec. TypeSpec of the structure being encoded.
113#define TTS (ts_stack_.back()) // type = TypeSpec*
114// InDeX. Index into TTS of the current/next field to encode.
115#define IDX (idx_stack_.back()) // type = int
116// Field TypeSpec. TypeSpec of the current/next field to encode.
117#define FTS (TTS->tstruct.specs[IDX]) // type = TypeSpec*
118// Field MeTa. Metadata of the current/next field to encode.
119#define FMT (TTS->tstruct.metas[IDX]) // type = FieldMeta
120// SubType 1/2. TypeSpec of the first/second subtype of this container.
121#define ST1 (TTS->tcontainer.subtype1)
122#define ST2 (TTS->tcontainer.subtype2)
123
124
David Reissce161a92007-09-11 22:09:42 +0000125/**
126 * Checks that @c ttype is indeed the ttype that we should be writing,
127 * according to our typespec. Aborts if the test fails and debugging in on.
128 */
David Reiss4e7530d2007-09-04 21:49:53 +0000129inline void TDenseProtocol::checkTType(const TType ttype) {
130 assert(!ts_stack_.empty());
131 assert(TTS->ttype == ttype);
132}
133
David Reissce161a92007-09-11 22:09:42 +0000134/**
135 * Makes sure that the TypeSpec stack is correct for the next object.
136 * See top-of-file comments.
137 */
David Reiss4e7530d2007-09-04 21:49:53 +0000138inline void TDenseProtocol::stateTransition() {
139 TypeSpec* old_tts = ts_stack_.back();
140 ts_stack_.pop_back();
141
David Reissce161a92007-09-11 22:09:42 +0000142 // If this is the end of the top-level write, we should have just popped
143 // the TypeSpec passed to the constructor.
David Reiss4e7530d2007-09-04 21:49:53 +0000144 if (ts_stack_.empty()) {
145 assert(old_tts = type_spec_);
146 return;
147 }
148
149 switch (TTS->ttype) {
150
151 case T_STRUCT:
152 assert(old_tts == FTS);
153 break;
154
155 case T_LIST:
156 case T_SET:
157 assert(old_tts == ST1);
158 ts_stack_.push_back(old_tts);
159 break;
160
161 case T_MAP:
162 assert(old_tts == (mkv_stack_.back() ? ST1 : ST2));
163 mkv_stack_.back() = !mkv_stack_.back();
164 ts_stack_.push_back(mkv_stack_.back() ? ST1 : ST2);
165 break;
166
167 default:
168 assert(!"Invalid TType in stateTransition.");
169 break;
170
171 }
172}
173
David Reissce161a92007-09-11 22:09:42 +0000174
175/*
176 * Variable-length quantity functions.
177 */
178
179inline uint32_t TDenseProtocol::vlqRead(uint64_t& vlq) {
180 uint32_t used = 0;
181 uint64_t val = 0;
182 uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes.
David Reissd46eb092008-02-02 00:54:48 +0000183 uint32_t buf_size = sizeof(buf);
184 const uint8_t* borrowed = trans_->borrow(buf, &buf_size);
David Reissce161a92007-09-11 22:09:42 +0000185
186 // Fast path. TODO(dreiss): Make it faster.
David Reissd46eb092008-02-02 00:54:48 +0000187 if (borrowed != NULL) {
David Reissce161a92007-09-11 22:09:42 +0000188 while (true) {
David Reissd46eb092008-02-02 00:54:48 +0000189 uint8_t byte = borrowed[used];
David Reissce161a92007-09-11 22:09:42 +0000190 used++;
191 val = (val << 7) | (byte & 0x7f);
192 if (!(byte & 0x80)) {
193 vlq = val;
194 trans_->consume(used);
195 return used;
196 }
197 // Have to check for invalid data so we don't crash.
198 if (UNLIKELY(used == sizeof(buf))) {
199 resetState();
200 throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
201 }
202 }
203 }
204
205 // Slow path.
206 else {
207 while (true) {
208 uint8_t byte;
209 used += trans_->readAll(&byte, 1);
210 val = (val << 7) | (byte & 0x7f);
211 if (!(byte & 0x80)) {
212 vlq = val;
213 return used;
214 }
215 // Might as well check for invalid data on the slow path too.
216 if (UNLIKELY(used >= sizeof(buf))) {
217 resetState();
218 throw TProtocolException(TProtocolException::INVALID_DATA, "Variable-length int over 10 bytes.");
219 }
220 }
221 }
222}
223
224inline uint32_t TDenseProtocol::vlqWrite(uint64_t vlq) {
225 uint8_t buf[10]; // 64 bits / (7 bits/byte) = 10 bytes.
226 int32_t pos = sizeof(buf) - 1;
227
228 // Write the thing from back to front.
229 buf[pos] = vlq & 0x7f;
230 vlq >>= 7;
231 pos--;
232
233 while (vlq > 0) {
234 assert(pos >= 0);
235 buf[pos] = (vlq | 0x80);
236 vlq >>= 7;
237 pos--;
238 }
239
240 // Back up one step before writing.
241 pos++;
242
243 trans_->write(buf+pos, sizeof(buf) - pos);
244 return sizeof(buf) - pos;
245}
246
247
248
249/*
250 * Writing functions.
251 */
252
David Reiss4e7530d2007-09-04 21:49:53 +0000253uint32_t TDenseProtocol::writeMessageBegin(const std::string& name,
254 const TMessageType messageType,
255 const int32_t seqid) {
David Reisse67c0e62007-09-07 01:34:12 +0000256 throw TApplicationException("TDenseProtocol doesn't work with messages (yet).");
257
David Reiss4e7530d2007-09-04 21:49:53 +0000258 int32_t version = (VERSION_2) | ((int32_t)messageType);
259 uint32_t wsize = 0;
260 wsize += subWriteI32(version);
261 wsize += subWriteString(name);
262 wsize += subWriteI32(seqid);
263 return wsize;
264}
265
266uint32_t TDenseProtocol::writeMessageEnd() {
267 return 0;
268}
269
David Reiss4e7530d2007-09-04 21:49:53 +0000270uint32_t TDenseProtocol::writeStructBegin(const string& name) {
David Reissce161a92007-09-11 22:09:42 +0000271 uint32_t xfer = 0;
272
273 // The TypeSpec stack should be empty if this is the top-level read/write.
274 // If it is, we push the TypeSpec passed to the constructor.
David Reiss4e7530d2007-09-04 21:49:53 +0000275 if (ts_stack_.empty()) {
David Reissce161a92007-09-11 22:09:42 +0000276 assert(standalone_);
277
David Reiss4e7530d2007-09-04 21:49:53 +0000278 if (type_spec_ == NULL) {
David Reissce161a92007-09-11 22:09:42 +0000279 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000280 throw TApplicationException("TDenseProtocol: No type specified.");
281 } else {
David Reissce161a92007-09-11 22:09:42 +0000282 assert(type_spec_->ttype == T_STRUCT);
David Reiss4e7530d2007-09-04 21:49:53 +0000283 ts_stack_.push_back(type_spec_);
David Reissce161a92007-09-11 22:09:42 +0000284 // Write out a prefix of the structure fingerprint.
285 trans_->write(type_spec_->fp_prefix, FP_PREFIX_LEN);
286 xfer += FP_PREFIX_LEN;
David Reiss4e7530d2007-09-04 21:49:53 +0000287 }
288 }
289
David Reissce161a92007-09-11 22:09:42 +0000290 // We need a new field index for this structure.
David Reiss4e7530d2007-09-04 21:49:53 +0000291 idx_stack_.push_back(0);
292 return 0;
293}
294
295uint32_t TDenseProtocol::writeStructEnd() {
296 idx_stack_.pop_back();
297 stateTransition();
298 return 0;
299}
300
301uint32_t TDenseProtocol::writeFieldBegin(const string& name,
302 const TType fieldType,
303 const int16_t fieldId) {
304 uint32_t xfer = 0;
305
David Reissce161a92007-09-11 22:09:42 +0000306 // Skip over optional fields.
David Reiss4e7530d2007-09-04 21:49:53 +0000307 while (FMT.tag != fieldId) {
308 // TODO(dreiss): Old meta here.
309 assert(FTS->ttype != T_STOP);
310 assert(FMT.is_optional);
David Reissce161a92007-09-11 22:09:42 +0000311 // Write a zero byte so the reader can skip it.
David Reiss4e7530d2007-09-04 21:49:53 +0000312 xfer += subWriteBool(false);
David Reissce161a92007-09-11 22:09:42 +0000313 // And advance to the next field.
David Reiss4e7530d2007-09-04 21:49:53 +0000314 IDX++;
315 }
316
317 // TODO(dreiss): give a better exception.
318 assert(FTS->ttype == fieldType);
319
320 if (FMT.is_optional) {
321 subWriteBool(true);
322 xfer += 1;
323 }
324
David Reissce161a92007-09-11 22:09:42 +0000325 // writeFieldStop shares all lot of logic up to this point.
326 // Instead of replicating it all, we just call this method from that one
327 // and use a gross special case here.
328 if (UNLIKELY(FTS->ttype != T_STOP)) {
329 // For normal fields, push the TypeSpec that we're about to use.
David Reiss4e7530d2007-09-04 21:49:53 +0000330 ts_stack_.push_back(FTS);
331 }
332 return xfer;
333}
334
335uint32_t TDenseProtocol::writeFieldEnd() {
David Reissce161a92007-09-11 22:09:42 +0000336 // Just move on to the next field.
David Reiss4e7530d2007-09-04 21:49:53 +0000337 IDX++;
338 return 0;
339}
340
341uint32_t TDenseProtocol::writeFieldStop() {
David Reissce161a92007-09-11 22:09:42 +0000342 return TDenseProtocol::writeFieldBegin("", T_STOP, 0);
David Reiss4e7530d2007-09-04 21:49:53 +0000343}
344
345uint32_t TDenseProtocol::writeMapBegin(const TType keyType,
346 const TType valType,
347 const uint32_t size) {
348 checkTType(T_MAP);
349
350 assert(keyType == ST1->ttype);
351 assert(valType == ST2->ttype);
352
353 ts_stack_.push_back(ST1);
354 mkv_stack_.push_back(true);
355
356 return subWriteI32((int32_t)size);
357}
358
359uint32_t TDenseProtocol::writeMapEnd() {
David Reissce161a92007-09-11 22:09:42 +0000360 // Pop off the value type, as well as our entry in the map key/value stack.
361 // stateTransition takes care of popping off our TypeSpec.
David Reiss4e7530d2007-09-04 21:49:53 +0000362 ts_stack_.pop_back();
363 mkv_stack_.pop_back();
364 stateTransition();
365 return 0;
366}
367
368uint32_t TDenseProtocol::writeListBegin(const TType elemType,
369 const uint32_t size) {
370 checkTType(T_LIST);
371
372 assert(elemType == ST1->ttype);
373 ts_stack_.push_back(ST1);
374 return subWriteI32((int32_t)size);
375}
376
377uint32_t TDenseProtocol::writeListEnd() {
David Reissce161a92007-09-11 22:09:42 +0000378 // Pop off the element type. stateTransition takes care of popping off ours.
David Reiss4e7530d2007-09-04 21:49:53 +0000379 ts_stack_.pop_back();
380 stateTransition();
381 return 0;
382}
383
384uint32_t TDenseProtocol::writeSetBegin(const TType elemType,
385 const uint32_t size) {
386 checkTType(T_SET);
387
388 assert(elemType == ST1->ttype);
389 ts_stack_.push_back(ST1);
390 return subWriteI32((int32_t)size);
391}
392
393uint32_t TDenseProtocol::writeSetEnd() {
David Reissce161a92007-09-11 22:09:42 +0000394 // Pop off the element type. stateTransition takes care of popping off ours.
David Reiss4e7530d2007-09-04 21:49:53 +0000395 ts_stack_.pop_back();
396 stateTransition();
397 return 0;
398}
399
400uint32_t TDenseProtocol::writeBool(const bool value) {
401 checkTType(T_BOOL);
402 stateTransition();
403 return TBinaryProtocol::writeBool(value);
404}
405
406uint32_t TDenseProtocol::writeByte(const int8_t byte) {
407 checkTType(T_BYTE);
408 stateTransition();
409 return TBinaryProtocol::writeByte(byte);
410}
411
David Reiss4e7530d2007-09-04 21:49:53 +0000412uint32_t TDenseProtocol::writeI16(const int16_t i16) {
David Reiss4e7530d2007-09-04 21:49:53 +0000413 checkTType(T_I16);
414 stateTransition();
David Reissce161a92007-09-11 22:09:42 +0000415 return vlqWrite(i16);
David Reiss4e7530d2007-09-04 21:49:53 +0000416}
417
418uint32_t TDenseProtocol::writeI32(const int32_t i32) {
David Reiss4e7530d2007-09-04 21:49:53 +0000419 checkTType(T_I32);
420 stateTransition();
David Reissce161a92007-09-11 22:09:42 +0000421 return vlqWrite(i32);
David Reiss4e7530d2007-09-04 21:49:53 +0000422}
423
424uint32_t TDenseProtocol::writeI64(const int64_t i64) {
David Reiss4e7530d2007-09-04 21:49:53 +0000425 checkTType(T_I64);
426 stateTransition();
David Reissce161a92007-09-11 22:09:42 +0000427 return vlqWrite(i64);
David Reiss4e7530d2007-09-04 21:49:53 +0000428}
429
430uint32_t TDenseProtocol::writeDouble(const double dub) {
431 checkTType(T_DOUBLE);
432 stateTransition();
433 return TBinaryProtocol::writeDouble(dub);
434}
435
436uint32_t TDenseProtocol::writeString(const std::string& str) {
437 checkTType(T_STRING);
438 stateTransition();
439 return subWriteString(str);
440}
441
David Reiss4e7530d2007-09-04 21:49:53 +0000442inline uint32_t TDenseProtocol::subWriteI32(const int32_t i32) {
David Reissce161a92007-09-11 22:09:42 +0000443 return vlqWrite(i32);
David Reiss4e7530d2007-09-04 21:49:53 +0000444}
445
David Reiss4e7530d2007-09-04 21:49:53 +0000446uint32_t TDenseProtocol::subWriteString(const std::string& str) {
447 uint32_t size = str.size();
448 uint32_t xfer = subWriteI32((int32_t)size);
449 if (size > 0) {
450 trans_->write((uint8_t*)str.data(), size);
451 }
452 return xfer + size;
453}
454
David Reisse67c0e62007-09-07 01:34:12 +0000455
David Reisse67c0e62007-09-07 01:34:12 +0000456
David Reissce161a92007-09-11 22:09:42 +0000457/*
David Reiss4e7530d2007-09-04 21:49:53 +0000458 * Reading functions
David Reissce161a92007-09-11 22:09:42 +0000459 *
460 * These have a lot of the same logic as the writing functions, so if
461 * something is confusing, look for comments in the corresponding writer.
David Reiss4e7530d2007-09-04 21:49:53 +0000462 */
463
464uint32_t TDenseProtocol::readMessageBegin(std::string& name,
465 TMessageType& messageType,
466 int32_t& seqid) {
David Reisse67c0e62007-09-07 01:34:12 +0000467 throw TApplicationException("TDenseProtocol doesn't work with messages (yet).");
468
David Reiss4e7530d2007-09-04 21:49:53 +0000469 uint32_t xfer = 0;
470 int32_t sz;
471 xfer += subReadI32(sz);
472
473 if (sz < 0) {
474 // Check for correct version number
475 int32_t version = sz & VERSION_MASK;
476 if (version != VERSION_2) {
477 throw TProtocolException(TProtocolException::BAD_VERSION, "Bad version identifier");
478 }
479 messageType = (TMessageType)(sz & 0x000000ff);
480 xfer += subReadString(name);
481 xfer += subReadI32(seqid);
482 } else {
483 throw TProtocolException(TProtocolException::BAD_VERSION, "No version identifier... old protocol client in strict mode?");
484 }
485 return xfer;
486}
487
488uint32_t TDenseProtocol::readMessageEnd() {
489 return 0;
490}
491
492uint32_t TDenseProtocol::readStructBegin(string& name) {
David Reissce161a92007-09-11 22:09:42 +0000493 uint32_t xfer = 0;
494
495 if (ts_stack_.empty()) {
496 assert(standalone_);
497
498 if (type_spec_ == NULL) {
499 resetState();
500 throw TApplicationException("TDenseProtocol: No type specified.");
501 } else {
502 assert(type_spec_->ttype == T_STRUCT);
503 ts_stack_.push_back(type_spec_);
504
505 // Check the fingerprint prefix.
506 uint8_t buf[FP_PREFIX_LEN];
507 xfer += trans_->read(buf, FP_PREFIX_LEN);
508 if (std::memcmp(buf, type_spec_->fp_prefix, FP_PREFIX_LEN) != 0) {
509 resetState();
510 throw TProtocolException(TProtocolException::INVALID_DATA,
511 "Fingerprint in data does not match type_spec.");
512 }
513 }
514 }
515
516 // We need a new field index for this structure.
517 idx_stack_.push_back(0);
518 return 0;
David Reiss4e7530d2007-09-04 21:49:53 +0000519}
520
521uint32_t TDenseProtocol::readStructEnd() {
522 idx_stack_.pop_back();
523 stateTransition();
524 return 0;
525}
526
527uint32_t TDenseProtocol::readFieldBegin(string& name,
528 TType& fieldType,
529 int16_t& fieldId) {
530 uint32_t xfer = 0;
531
David Reissce161a92007-09-11 22:09:42 +0000532 // For optional fields, check to see if they are there.
David Reiss4e7530d2007-09-04 21:49:53 +0000533 while (FMT.is_optional) {
534 bool is_present;
535 xfer += subReadBool(is_present);
536 if (is_present) {
537 break;
538 }
539 IDX++;
540 }
541
David Reissce161a92007-09-11 22:09:42 +0000542 // Once we hit a mandatory field, or an optional field that is present,
543 // we know that FMT and FTS point to the appropriate field.
544
David Reiss4e7530d2007-09-04 21:49:53 +0000545 fieldId = FMT.tag;
546 fieldType = FTS->ttype;
547
David Reissce161a92007-09-11 22:09:42 +0000548 // Normally, we push the TypeSpec that we are about to read,
549 // but no reading is done for T_STOP.
David Reiss4e7530d2007-09-04 21:49:53 +0000550 if (FTS->ttype != T_STOP) {
551 ts_stack_.push_back(FTS);
552 }
553 return xfer;
554}
555
556uint32_t TDenseProtocol::readFieldEnd() {
557 IDX++;
558 return 0;
559}
560
561uint32_t TDenseProtocol::readMapBegin(TType& keyType,
562 TType& valType,
563 uint32_t& size) {
564 checkTType(T_MAP);
565
566 uint32_t xfer = 0;
567 int32_t sizei;
568 xfer += subReadI32(sizei);
569 if (sizei < 0) {
David Reissce161a92007-09-11 22:09:42 +0000570 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000571 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
572 } else if (container_limit_ && sizei > container_limit_) {
David Reissce161a92007-09-11 22:09:42 +0000573 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000574 throw TProtocolException(TProtocolException::SIZE_LIMIT);
575 }
576 size = (uint32_t)sizei;
577
578 keyType = ST1->ttype;
579 valType = ST2->ttype;
580
581 ts_stack_.push_back(ST1);
582 mkv_stack_.push_back(true);
583
584 return xfer;
585}
586
587uint32_t TDenseProtocol::readMapEnd() {
588 ts_stack_.pop_back();
589 mkv_stack_.pop_back();
590 stateTransition();
591 return 0;
592}
593
594uint32_t TDenseProtocol::readListBegin(TType& elemType,
595 uint32_t& size) {
596 checkTType(T_LIST);
597
598 uint32_t xfer = 0;
599 int32_t sizei;
600 xfer += subReadI32(sizei);
601 if (sizei < 0) {
David Reissce161a92007-09-11 22:09:42 +0000602 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000603 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
604 } else if (container_limit_ && sizei > container_limit_) {
David Reissce161a92007-09-11 22:09:42 +0000605 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000606 throw TProtocolException(TProtocolException::SIZE_LIMIT);
607 }
608 size = (uint32_t)sizei;
609
610 elemType = ST1->ttype;
611
612 ts_stack_.push_back(ST1);
613
614 return xfer;
615}
616
617uint32_t TDenseProtocol::readListEnd() {
618 ts_stack_.pop_back();
619 stateTransition();
620 return 0;
621}
622
623uint32_t TDenseProtocol::readSetBegin(TType& elemType,
624 uint32_t& size) {
625 checkTType(T_SET);
626
627 uint32_t xfer = 0;
628 int32_t sizei;
629 xfer += subReadI32(sizei);
630 if (sizei < 0) {
David Reissce161a92007-09-11 22:09:42 +0000631 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000632 throw TProtocolException(TProtocolException::NEGATIVE_SIZE);
633 } else if (container_limit_ && sizei > container_limit_) {
David Reissce161a92007-09-11 22:09:42 +0000634 resetState();
David Reiss4e7530d2007-09-04 21:49:53 +0000635 throw TProtocolException(TProtocolException::SIZE_LIMIT);
636 }
637 size = (uint32_t)sizei;
638
639 elemType = ST1->ttype;
640
641 ts_stack_.push_back(ST1);
642
643 return xfer;
644}
645
646uint32_t TDenseProtocol::readSetEnd() {
647 ts_stack_.pop_back();
648 stateTransition();
649 return 0;
650}
651
652uint32_t TDenseProtocol::readBool(bool& value) {
653 checkTType(T_BOOL);
654 stateTransition();
655 return TBinaryProtocol::readBool(value);
656}
657
658uint32_t TDenseProtocol::readByte(int8_t& byte) {
659 checkTType(T_BYTE);
660 stateTransition();
661 return TBinaryProtocol::readByte(byte);
662}
663
664uint32_t TDenseProtocol::readI16(int16_t& i16) {
665 checkTType(T_I16);
666 stateTransition();
David Reisse67c0e62007-09-07 01:34:12 +0000667 uint64_t u64;
David Reissce161a92007-09-11 22:09:42 +0000668 uint32_t rv = vlqRead(u64);
David Reisse67c0e62007-09-07 01:34:12 +0000669 int64_t val = (int64_t)u64;
670 if (UNLIKELY(val > INT16_MAX || val < INT16_MIN)) {
David Reissce161a92007-09-11 22:09:42 +0000671 resetState();
David Reisse67c0e62007-09-07 01:34:12 +0000672 throw TProtocolException(TProtocolException::INVALID_DATA,
673 "i16 out of range.");
674 }
675 i16 = (int16_t)val;
676 return rv;
David Reiss4e7530d2007-09-04 21:49:53 +0000677}
678
679uint32_t TDenseProtocol::readI32(int32_t& i32) {
680 checkTType(T_I32);
681 stateTransition();
David Reisse67c0e62007-09-07 01:34:12 +0000682 uint64_t u64;
David Reissce161a92007-09-11 22:09:42 +0000683 uint32_t rv = vlqRead(u64);
David Reisse67c0e62007-09-07 01:34:12 +0000684 int64_t val = (int64_t)u64;
685 if (UNLIKELY(val > INT32_MAX || val < INT32_MIN)) {
David Reissce161a92007-09-11 22:09:42 +0000686 resetState();
David Reisse67c0e62007-09-07 01:34:12 +0000687 throw TProtocolException(TProtocolException::INVALID_DATA,
688 "i32 out of range.");
689 }
690 i32 = (int32_t)val;
691 return rv;
David Reiss4e7530d2007-09-04 21:49:53 +0000692}
693
694uint32_t TDenseProtocol::readI64(int64_t& i64) {
695 checkTType(T_I64);
696 stateTransition();
David Reisse67c0e62007-09-07 01:34:12 +0000697 uint64_t u64;
David Reissce161a92007-09-11 22:09:42 +0000698 uint32_t rv = vlqRead(u64);
David Reisse67c0e62007-09-07 01:34:12 +0000699 int64_t val = (int64_t)u64;
700 if (UNLIKELY(val > INT64_MAX || val < INT64_MIN)) {
David Reissce161a92007-09-11 22:09:42 +0000701 resetState();
David Reisse67c0e62007-09-07 01:34:12 +0000702 throw TProtocolException(TProtocolException::INVALID_DATA,
703 "i64 out of range.");
704 }
705 i64 = (int64_t)val;
706 return rv;
David Reiss4e7530d2007-09-04 21:49:53 +0000707}
708
709uint32_t TDenseProtocol::readDouble(double& dub) {
710 checkTType(T_DOUBLE);
711 stateTransition();
712 return TBinaryProtocol::readDouble(dub);
713}
714
715uint32_t TDenseProtocol::readString(std::string& str) {
716 checkTType(T_STRING);
717 stateTransition();
718 return subReadString(str);
719}
720
David Reisse67c0e62007-09-07 01:34:12 +0000721uint32_t TDenseProtocol::subReadI32(int32_t& i32) {
722 uint64_t u64;
David Reissce161a92007-09-11 22:09:42 +0000723 uint32_t rv = vlqRead(u64);
David Reisse67c0e62007-09-07 01:34:12 +0000724 int64_t val = (int64_t)u64;
725 if (UNLIKELY(val > INT32_MAX || val < INT32_MIN)) {
David Reissce161a92007-09-11 22:09:42 +0000726 resetState();
David Reisse67c0e62007-09-07 01:34:12 +0000727 throw TProtocolException(TProtocolException::INVALID_DATA,
728 "i32 out of range.");
729 }
730 i32 = (int32_t)val;
731 return rv;
732}
733
734uint32_t TDenseProtocol::subReadString(std::string& str) {
735 uint32_t xfer;
736 int32_t size;
737 xfer = subReadI32(size);
738 return xfer + readStringBody(str, size);
739}
740
David Reiss4e7530d2007-09-04 21:49:53 +0000741}}} // facebook::thrift::protocol