blob: 2b4f6f1417e9910f2ad67f50111fb35e6471c99f [file] [log] [blame]
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +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 *
19 * Contains some contributions under the Thrift Software License.
20 * Please see doc/old-thrift-license.txt in the Thrift distribution for
21 * details.
Jens Geyerf509df92013-04-25 20:38:55 +020022 */
23
24using System;
25using System.Text;
26using Thrift.Transport;
27using System.Collections;
28using System.IO;
29using System.Collections.Generic;
30
31namespace Thrift.Protocol
32{
33 public class TCompactProtocol : TProtocol
34 {
35 private static TStruct ANONYMOUS_STRUCT = new TStruct("");
36 private static TField TSTOP = new TField("", TType.Stop, (short)0);
37
38 private static byte[] ttypeToCompactType = new byte[16];
39
40 private const byte PROTOCOL_ID = 0x82;
41 private const byte VERSION = 1;
42 private const byte VERSION_MASK = 0x1f; // 0001 1111
43 private const byte TYPE_MASK = 0xE0; // 1110 0000
Jens Geyera86886e2014-09-17 22:25:48 +020044 private const byte TYPE_BITS = 0x07; // 0000 0111
Jens Geyerf509df92013-04-25 20:38:55 +020045 private const int TYPE_SHIFT_AMOUNT = 5;
46
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000047 /**
48 * All of the on-wire type codes.
Jens Geyerf509df92013-04-25 20:38:55 +020049 */
50 private static class Types
51 {
52 public const byte STOP = 0x00;
53 public const byte BOOLEAN_TRUE = 0x01;
54 public const byte BOOLEAN_FALSE = 0x02;
55 public const byte BYTE = 0x03;
56 public const byte I16 = 0x04;
57 public const byte I32 = 0x05;
58 public const byte I64 = 0x06;
59 public const byte DOUBLE = 0x07;
60 public const byte BINARY = 0x08;
61 public const byte LIST = 0x09;
62 public const byte SET = 0x0A;
63 public const byte MAP = 0x0B;
64 public const byte STRUCT = 0x0C;
65 }
66
Jens Geyerd5436f52014-10-03 19:50:38 +020067 /**
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000068 * Used to keep track of the last field for the current and previous structs,
69 * so we can do the delta stuff.
Jens Geyerf509df92013-04-25 20:38:55 +020070 */
71 private Stack<short> lastField_ = new Stack<short>(15);
72
73 private short lastFieldId_ = 0;
74
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000075 /**
Jens Geyerd5436f52014-10-03 19:50:38 +020076 * If we encounter a boolean field begin, save the TField here so it can
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000077 * have the value incorporated.
Jens Geyerf509df92013-04-25 20:38:55 +020078 */
79 private Nullable<TField> booleanField_;
80
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000081 /**
Jens Geyerd5436f52014-10-03 19:50:38 +020082 * If we Read a field header, and it's a boolean field, save the boolean
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000083 * value here so that ReadBool can use it.
Jens Geyerf509df92013-04-25 20:38:55 +020084 */
85 private Nullable<Boolean> boolValue_;
86
87
88 #region CompactProtocol Factory
89
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +000090 /**
Jens Geyerd5436f52014-10-03 19:50:38 +020091 * Factory
92 */
Jens Geyerf509df92013-04-25 20:38:55 +020093 public class Factory : TProtocolFactory
94 {
95 public Factory() { }
96
97 public TProtocol GetProtocol(TTransport trans)
98 {
99 return new TCompactProtocol(trans);
100 }
101 }
102
103 #endregion
104
105 public TCompactProtocol(TTransport trans)
106 : base(trans)
107 {
108 ttypeToCompactType[(int)TType.Stop] = Types.STOP;
109 ttypeToCompactType[(int)TType.Bool] = Types.BOOLEAN_TRUE;
110 ttypeToCompactType[(int)TType.Byte] = Types.BYTE;
111 ttypeToCompactType[(int)TType.I16] = Types.I16;
112 ttypeToCompactType[(int)TType.I32] = Types.I32;
113 ttypeToCompactType[(int)TType.I64] = Types.I64;
114 ttypeToCompactType[(int)TType.Double] = Types.DOUBLE;
115 ttypeToCompactType[(int)TType.String] = Types.BINARY;
116 ttypeToCompactType[(int)TType.List] = Types.LIST;
117 ttypeToCompactType[(int)TType.Set] = Types.SET;
118 ttypeToCompactType[(int)TType.Map] = Types.MAP;
119 ttypeToCompactType[(int)TType.Struct] = Types.STRUCT;
120 }
121
122 public void reset()
123 {
124 lastField_.Clear();
125 lastFieldId_ = 0;
126 }
127
128 #region Write Methods
129
130
Jens Geyerd5436f52014-10-03 19:50:38 +0200131 /**
132 * Writes a byte without any possibility of all that field header nonsense.
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000133 * Used internally by other writing methods that know they need to Write a byte.
Jens Geyerf509df92013-04-25 20:38:55 +0200134 */
135 private byte[] byteDirectBuffer = new byte[1];
136 private void WriteByteDirect(byte b)
137 {
138 byteDirectBuffer[0] = b;
139 trans.Write(byteDirectBuffer);
140 }
141
Jens Geyerd5436f52014-10-03 19:50:38 +0200142 /**
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000143 * Writes a byte without any possibility of all that field header nonsense.
Jens Geyerf509df92013-04-25 20:38:55 +0200144 */
145 private void WriteByteDirect(int n)
146 {
147 WriteByteDirect((byte)n);
148 }
149
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000150 /**
151 * Write an i32 as a varint. Results in 1-5 bytes on the wire.
152 * TODO: make a permanent buffer like WriteVarint64?
Jens Geyerf509df92013-04-25 20:38:55 +0200153 */
154 byte[] i32buf = new byte[5];
155 private void WriteVarint32(uint n)
156 {
157 int idx = 0;
158 while (true)
159 {
160 if ((n & ~0x7F) == 0)
161 {
162 i32buf[idx++] = (byte)n;
163 // WriteByteDirect((byte)n);
164 break;
165 // return;
166 }
167 else
168 {
169 i32buf[idx++] = (byte)((n & 0x7F) | 0x80);
170 // WriteByteDirect((byte)((n & 0x7F) | 0x80));
171 n >>= 7;
172 }
173 }
174 trans.Write(i32buf, 0, idx);
175 }
176
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000177 /**
178 * Write a message header to the wire. Compact Protocol messages contain the
179 * protocol version so we can migrate forwards in the future if need be.
Jens Geyerf509df92013-04-25 20:38:55 +0200180 */
181 public override void WriteMessageBegin(TMessage message)
182 {
183 WriteByteDirect(PROTOCOL_ID);
184 WriteByteDirect((byte)((VERSION & VERSION_MASK) | ((((uint)message.Type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK)));
185 WriteVarint32((uint)message.SeqID);
186 WriteString(message.Name);
187 }
188
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000189 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200190 * Write a struct begin. This doesn't actually put anything on the wire. We
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000191 * use it as an opportunity to put special placeholder markers on the field
192 * stack so we can get the field id deltas correct.
Jens Geyerf509df92013-04-25 20:38:55 +0200193 */
194 public override void WriteStructBegin(TStruct strct)
195 {
196 lastField_.Push(lastFieldId_);
197 lastFieldId_ = 0;
198 }
199
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000200 /**
201 * Write a struct end. This doesn't actually put anything on the wire. We use
202 * this as an opportunity to pop the last field from the current struct off
203 * of the field stack.
Jens Geyerf509df92013-04-25 20:38:55 +0200204 */
205 public override void WriteStructEnd()
206 {
207 lastFieldId_ = lastField_.Pop();
208 }
209
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000210 /**
211 * Write a field header containing the field id and field type. If the
212 * difference between the current field id and the last one is small (< 15),
213 * then the field id will be encoded in the 4 MSB as a delta. Otherwise, the
214 * field id will follow the type header as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200215 */
216 public override void WriteFieldBegin(TField field)
217 {
218 if (field.Type == TType.Bool)
219 {
220 // we want to possibly include the value, so we'll wait.
221 booleanField_ = field;
222 }
223 else
224 {
225 WriteFieldBeginInternal(field, 0xFF);
226 }
227 }
228
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000229 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200230 * The workhorse of WriteFieldBegin. It has the option of doing a
231 * 'type override' of the type header. This is used specifically in the
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000232 * boolean field case.
Jens Geyerf509df92013-04-25 20:38:55 +0200233 */
234 private void WriteFieldBeginInternal(TField field, byte typeOverride)
235 {
236 // short lastField = lastField_.Pop();
237
238 // if there's a type override, use that.
239 byte typeToWrite = typeOverride == 0xFF ? getCompactType(field.Type) : typeOverride;
240
241 // check if we can use delta encoding for the field id
242 if (field.ID > lastFieldId_ && field.ID - lastFieldId_ <= 15)
243 {
244 // Write them together
245 WriteByteDirect((field.ID - lastFieldId_) << 4 | typeToWrite);
246 }
247 else
248 {
249 // Write them separate
250 WriteByteDirect(typeToWrite);
251 WriteI16(field.ID);
252 }
253
254 lastFieldId_ = field.ID;
255 // lastField_.push(field.id);
256 }
257
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000258 /**
259 * Write the STOP symbol so we know there are no more fields in this struct.
Jens Geyerf509df92013-04-25 20:38:55 +0200260 */
261 public override void WriteFieldStop()
262 {
263 WriteByteDirect(Types.STOP);
264 }
265
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000266 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200267 * Write a map header. If the map is empty, omit the key and value type
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000268 * headers, as we don't need any additional information to skip it.
Jens Geyerf509df92013-04-25 20:38:55 +0200269 */
270 public override void WriteMapBegin(TMap map)
271 {
272 if (map.Count == 0)
273 {
274 WriteByteDirect(0);
275 }
276 else
277 {
278 WriteVarint32((uint)map.Count);
279 WriteByteDirect(getCompactType(map.KeyType) << 4 | getCompactType(map.ValueType));
280 }
281 }
282
Jens Geyerd5436f52014-10-03 19:50:38 +0200283 /**
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000284 * Write a list header.
Jens Geyerf509df92013-04-25 20:38:55 +0200285 */
286 public override void WriteListBegin(TList list)
287 {
288 WriteCollectionBegin(list.ElementType, list.Count);
289 }
290
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000291 /**
292 * Write a set header.
Jens Geyerf509df92013-04-25 20:38:55 +0200293 */
294 public override void WriteSetBegin(TSet set)
295 {
296 WriteCollectionBegin(set.ElementType, set.Count);
297 }
298
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000299 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200300 * Write a boolean value. Potentially, this could be a boolean field, in
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000301 * which case the field header info isn't written yet. If so, decide what the
Jens Geyerd5436f52014-10-03 19:50:38 +0200302 * right type header is for the value and then Write the field header.
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000303 * Otherwise, Write a single byte.
Jens Geyerf509df92013-04-25 20:38:55 +0200304 */
305 public override void WriteBool(Boolean b)
306 {
307 if (booleanField_ != null)
308 {
309 // we haven't written the field header yet
310 WriteFieldBeginInternal(booleanField_.Value, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
311 booleanField_ = null;
312 }
313 else
314 {
315 // we're not part of a field, so just Write the value.
316 WriteByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE);
317 }
318 }
319
Jens Geyerd5436f52014-10-03 19:50:38 +0200320 /**
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000321 * Write a byte. Nothing to see here!
Jens Geyerf509df92013-04-25 20:38:55 +0200322 */
323 public override void WriteByte(sbyte b)
324 {
325 WriteByteDirect((byte)b);
326 }
327
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000328 /**
329 * Write an I16 as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200330 */
331 public override void WriteI16(short i16)
332 {
333 WriteVarint32(intToZigZag(i16));
334 }
335
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000336 /**
337 * Write an i32 as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200338 */
339 public override void WriteI32(int i32)
340 {
341 WriteVarint32(intToZigZag(i32));
342 }
343
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000344 /**
345 * Write an i64 as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200346 */
347 public override void WriteI64(long i64)
348 {
349 WriteVarint64(longToZigzag(i64));
350 }
351
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000352 /**
353 * Write a double to the wire as 8 bytes.
Jens Geyerf509df92013-04-25 20:38:55 +0200354 */
355 public override void WriteDouble(double dub)
356 {
357 byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
358 fixedLongToBytes(BitConverter.DoubleToInt64Bits(dub), data, 0);
359 trans.Write(data);
360 }
361
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000362 /**
363 * Write a string to the wire with a varint size preceding.
Jens Geyerf509df92013-04-25 20:38:55 +0200364 */
365 public override void WriteString(String str)
366 {
367 byte[] bytes = UTF8Encoding.UTF8.GetBytes(str);
368 WriteBinary(bytes, 0, bytes.Length);
369 }
370
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000371 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200372 * Write a byte array, using a varint for the size.
Jens Geyerf509df92013-04-25 20:38:55 +0200373 */
374 public override void WriteBinary(byte[] bin)
375 {
376 WriteBinary(bin, 0, bin.Length);
377 }
378
379 private void WriteBinary(byte[] buf, int offset, int length)
380 {
381 WriteVarint32((uint)length);
382 trans.Write(buf, offset, length);
383 }
384
385 //
Jens Geyerd5436f52014-10-03 19:50:38 +0200386 // These methods are called by structs, but don't actually have any wire
Jens Geyerf509df92013-04-25 20:38:55 +0200387 // output or purpose.
Jens Geyerd5436f52014-10-03 19:50:38 +0200388 //
Jens Geyerf509df92013-04-25 20:38:55 +0200389
390 public override void WriteMessageEnd() { }
391 public override void WriteMapEnd() { }
392 public override void WriteListEnd() { }
393 public override void WriteSetEnd() { }
394 public override void WriteFieldEnd() { }
395
396 //
397 // Internal writing methods
398 //
399
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000400 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200401 * Abstract method for writing the start of lists and sets. List and sets on
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000402 * the wire differ only by the type indicator.
Jens Geyerf509df92013-04-25 20:38:55 +0200403 */
404 protected void WriteCollectionBegin(TType elemType, int size)
405 {
406 if (size <= 14)
407 {
408 WriteByteDirect(size << 4 | getCompactType(elemType));
409 }
410 else
411 {
412 WriteByteDirect(0xf0 | getCompactType(elemType));
413 WriteVarint32((uint)size);
414 }
415 }
416
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000417 /**
418 * Write an i64 as a varint. Results in 1-10 bytes on the wire.
Jens Geyerf509df92013-04-25 20:38:55 +0200419 */
420 byte[] varint64out = new byte[10];
421 private void WriteVarint64(ulong n)
422 {
423 int idx = 0;
424 while (true)
425 {
426 if ((n & ~(ulong)0x7FL) == 0)
427 {
428 varint64out[idx++] = (byte)n;
429 break;
430 }
431 else
432 {
433 varint64out[idx++] = ((byte)((n & 0x7F) | 0x80));
434 n >>= 7;
435 }
436 }
437 trans.Write(varint64out, 0, idx);
438 }
439
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000440 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200441 * Convert l into a zigzag long. This allows negative numbers to be
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000442 * represented compactly as a varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200443 */
444 private ulong longToZigzag(long n)
445 {
Henrique Mendonçada7982e2013-05-31 18:20:42 +0200446 return (ulong)(n << 1) ^ (ulong)(n >> 63);
Jens Geyerf509df92013-04-25 20:38:55 +0200447 }
448
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000449 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200450 * Convert n into a zigzag int. This allows negative numbers to be
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000451 * represented compactly as a varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200452 */
453 private uint intToZigZag(int n)
454 {
Henrique Mendonçada7982e2013-05-31 18:20:42 +0200455 return (uint)(n << 1) ^ (uint)(n >> 31);
Jens Geyerf509df92013-04-25 20:38:55 +0200456 }
457
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000458 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200459 * Convert a long into little-endian bytes in buf starting at off and going
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000460 * until off+7.
Jens Geyerf509df92013-04-25 20:38:55 +0200461 */
462 private void fixedLongToBytes(long n, byte[] buf, int off)
463 {
464 buf[off + 0] = (byte)(n & 0xff);
465 buf[off + 1] = (byte)((n >> 8) & 0xff);
466 buf[off + 2] = (byte)((n >> 16) & 0xff);
467 buf[off + 3] = (byte)((n >> 24) & 0xff);
468 buf[off + 4] = (byte)((n >> 32) & 0xff);
469 buf[off + 5] = (byte)((n >> 40) & 0xff);
470 buf[off + 6] = (byte)((n >> 48) & 0xff);
471 buf[off + 7] = (byte)((n >> 56) & 0xff);
472 }
473
474 #endregion
475
476 #region ReadMethods
477
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000478 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200479 * Read a message header.
Jens Geyerf509df92013-04-25 20:38:55 +0200480 */
481 public override TMessage ReadMessageBegin()
Jens Geyer1c99e702014-03-17 22:50:39 +0200482 {
Jens Geyerf509df92013-04-25 20:38:55 +0200483 byte protocolId = (byte)ReadByte();
484 if (protocolId != PROTOCOL_ID)
485 {
486 throw new TProtocolException("Expected protocol id " + PROTOCOL_ID.ToString("X") + " but got " + protocolId.ToString("X"));
Jens Geyer1c99e702014-03-17 22:50:39 +0200487 }
Jens Geyerf509df92013-04-25 20:38:55 +0200488 byte versionAndType = (byte)ReadByte();
489 byte version = (byte)(versionAndType & VERSION_MASK);
490 if (version != VERSION)
491 {
492 throw new TProtocolException("Expected version " + VERSION + " but got " + version);
493 }
Jens Geyera86886e2014-09-17 22:25:48 +0200494 byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
Jens Geyerf509df92013-04-25 20:38:55 +0200495 int seqid = (int)ReadVarint32();
496 String messageName = ReadString();
497 return new TMessage(messageName, (TMessageType)type, seqid);
498 }
499
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000500 /**
501 * Read a struct begin. There's nothing on the wire for this, but it is our
502 * opportunity to push a new struct begin marker onto the field stack.
Jens Geyerf509df92013-04-25 20:38:55 +0200503 */
504 public override TStruct ReadStructBegin()
505 {
506 lastField_.Push(lastFieldId_);
507 lastFieldId_ = 0;
508 return ANONYMOUS_STRUCT;
509 }
510
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000511 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200512 * Doesn't actually consume any wire data, just removes the last field for
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000513 * this struct from the field stack.
Jens Geyerf509df92013-04-25 20:38:55 +0200514 */
515 public override void ReadStructEnd()
516 {
517 // consume the last field we Read off the wire.
518 lastFieldId_ = lastField_.Pop();
519 }
520
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000521 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200522 * Read a field header off the wire.
Jens Geyerf509df92013-04-25 20:38:55 +0200523 */
524 public override TField ReadFieldBegin()
Jens Geyer1c99e702014-03-17 22:50:39 +0200525 {
Jens Geyerf509df92013-04-25 20:38:55 +0200526 byte type = (byte)ReadByte();
527
528 // if it's a stop, then we can return immediately, as the struct is over.
529 if (type == Types.STOP)
530 {
531 return TSTOP;
532 }
533
534 short fieldId;
535
536 // mask off the 4 MSB of the type header. it could contain a field id delta.
537 short modifier = (short)((type & 0xf0) >> 4);
538 if (modifier == 0)
539 {
540 // not a delta. look ahead for the zigzag varint field id.
541 fieldId = ReadI16();
542 }
543 else
544 {
545 // has a delta. add the delta to the last Read field id.
546 fieldId = (short)(lastFieldId_ + modifier);
547 }
548
549 TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId);
550
551 // if this happens to be a boolean field, the value is encoded in the type
552 if (isBoolType(type))
553 {
554 // save the boolean value in a special instance variable.
555 boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? true : false;
556 }
557
558 // push the new field onto the field stack so we can keep the deltas going.
559 lastFieldId_ = field.ID;
560 return field;
561 }
562
Jens Geyerd5436f52014-10-03 19:50:38 +0200563 /**
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000564 * Read a map header off the wire. If the size is zero, skip Reading the key
565 * and value type. This means that 0-length maps will yield TMaps without the
566 * "correct" types.
Jens Geyerf509df92013-04-25 20:38:55 +0200567 */
568 public override TMap ReadMapBegin()
569 {
Jens Geyer1c99e702014-03-17 22:50:39 +0200570 int size = (int)ReadVarint32();
Jens Geyerf509df92013-04-25 20:38:55 +0200571 byte keyAndValueType = size == 0 ? (byte)0 : (byte)ReadByte();
572 return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size);
573 }
574
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000575 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200576 * Read a list header off the wire. If the list size is 0-14, the size will
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000577 * be packed into the element type header. If it's a longer list, the 4 MSB
578 * of the element type header will be 0xF, and a varint will follow with the
579 * true size.
Jens Geyerf509df92013-04-25 20:38:55 +0200580 */
581 public override TList ReadListBegin()
Jens Geyer1c99e702014-03-17 22:50:39 +0200582 {
Jens Geyerf509df92013-04-25 20:38:55 +0200583 byte size_and_type = (byte)ReadByte();
584 int size = (size_and_type >> 4) & 0x0f;
585 if (size == 15)
586 {
587 size = (int)ReadVarint32();
588 }
589 TType type = getTType(size_and_type);
590 return new TList(type, size);
591 }
592
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000593 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200594 * Read a set header off the wire. If the set size is 0-14, the size will
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000595 * be packed into the element type header. If it's a longer set, the 4 MSB
596 * of the element type header will be 0xF, and a varint will follow with the
597 * true size.
Jens Geyerf509df92013-04-25 20:38:55 +0200598 */
599 public override TSet ReadSetBegin()
600 {
601 return new TSet(ReadListBegin());
602 }
603
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000604 /**
605 * Read a boolean off the wire. If this is a boolean field, the value should
606 * already have been Read during ReadFieldBegin, so we'll just consume the
607 * pre-stored value. Otherwise, Read a byte.
Jens Geyerf509df92013-04-25 20:38:55 +0200608 */
609 public override Boolean ReadBool()
610 {
611 if (boolValue_ != null)
612 {
613 bool result = boolValue_.Value;
614 boolValue_ = null;
615 return result;
616 }
617 return ReadByte() == Types.BOOLEAN_TRUE;
618 }
619
620 byte[] byteRawBuf = new byte[1];
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000621 /**
622 * Read a single byte off the wire. Nothing interesting here.
Jens Geyerf509df92013-04-25 20:38:55 +0200623 */
624 public override sbyte ReadByte()
625 {
626 trans.ReadAll(byteRawBuf, 0, 1);
627 return (sbyte)byteRawBuf[0];
628 }
629
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000630 /**
631 * Read an i16 from the wire as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200632 */
633 public override short ReadI16()
634 {
635 return (short)zigzagToInt(ReadVarint32());
636 }
637
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000638 /**
639 * Read an i32 from the wire as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200640 */
641 public override int ReadI32()
642 {
643 return zigzagToInt(ReadVarint32());
644 }
645
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000646 /**
647 * Read an i64 from the wire as a zigzag varint.
Jens Geyerf509df92013-04-25 20:38:55 +0200648 */
649 public override long ReadI64()
650 {
651 return zigzagToLong(ReadVarint64());
652 }
653
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000654 /**
655 * No magic here - just Read a double off the wire.
Jens Geyerf509df92013-04-25 20:38:55 +0200656 */
657 public override double ReadDouble()
658 {
659 byte[] longBits = new byte[8];
660 trans.ReadAll(longBits, 0, 8);
661 return BitConverter.Int64BitsToDouble(bytesToLong(longBits));
662 }
663
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000664 /**
665 * Reads a byte[] (via ReadBinary), and then UTF-8 decodes it.
Jens Geyerf509df92013-04-25 20:38:55 +0200666 */
667 public override String ReadString()
668 {
669 int length = (int)ReadVarint32();
670
671 if (length == 0)
672 {
673 return "";
674 }
675
676 return Encoding.UTF8.GetString(ReadBinary(length));
677 }
678
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000679 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200680 * Read a byte[] from the wire.
Jens Geyerf509df92013-04-25 20:38:55 +0200681 */
682 public override byte[] ReadBinary()
683 {
684 int length = (int)ReadVarint32();
685 if (length == 0) return new byte[0];
686
687 byte[] buf = new byte[length];
688 trans.ReadAll(buf, 0, length);
689 return buf;
690 }
691
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000692 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200693 * Read a byte[] of a known length from the wire.
Jens Geyerf509df92013-04-25 20:38:55 +0200694 */
695 private byte[] ReadBinary(int length)
696 {
697 if (length == 0) return new byte[0];
698
699 byte[] buf = new byte[length];
700 trans.ReadAll(buf, 0, length);
701 return buf;
702 }
703
704 //
Jens Geyerd5436f52014-10-03 19:50:38 +0200705 // These methods are here for the struct to call, but don't have any wire
Jens Geyerf509df92013-04-25 20:38:55 +0200706 // encoding.
707 //
708 public override void ReadMessageEnd() { }
709 public override void ReadFieldEnd() { }
710 public override void ReadMapEnd() { }
711 public override void ReadListEnd() { }
712 public override void ReadSetEnd() { }
713
714 //
715 // Internal Reading methods
716 //
717
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000718 /**
719 * Read an i32 from the wire as a varint. The MSB of each byte is set
720 * if there is another byte to follow. This can Read up to 5 bytes.
Jens Geyerf509df92013-04-25 20:38:55 +0200721 */
722 private uint ReadVarint32()
723 {
724 uint result = 0;
725 int shift = 0;
726 while (true)
Jens Geyer1c99e702014-03-17 22:50:39 +0200727 {
Jens Geyerf509df92013-04-25 20:38:55 +0200728 byte b = (byte)ReadByte();
729 result |= (uint)(b & 0x7f) << shift;
730 if ((b & 0x80) != 0x80) break;
731 shift += 7;
732 }
733 return result;
734 }
735
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000736 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200737 * Read an i64 from the wire as a proper varint. The MSB of each byte is set
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000738 * if there is another byte to follow. This can Read up to 10 bytes.
Jens Geyerf509df92013-04-25 20:38:55 +0200739 */
740 private ulong ReadVarint64()
741 {
742 int shift = 0;
743 ulong result = 0;
744 while (true)
Jens Geyer1c99e702014-03-17 22:50:39 +0200745 {
Jens Geyerf509df92013-04-25 20:38:55 +0200746 byte b = (byte)ReadByte();
747 result |= (ulong)(b & 0x7f) << shift;
748 if ((b & 0x80) != 0x80) break;
749 shift += 7;
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000750 }
Jens Geyerd5436f52014-10-03 19:50:38 +0200751
Jens Geyerf509df92013-04-25 20:38:55 +0200752 return result;
753 }
754
755 #endregion
756
757 //
758 // encoding helpers
759 //
760
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000761 /**
762 * Convert from zigzag int to int.
Jens Geyerf509df92013-04-25 20:38:55 +0200763 */
764 private int zigzagToInt(uint n)
765 {
766 return (int)(n >> 1) ^ (-(int)(n & 1));
767 }
768
Jens Geyerd5436f52014-10-03 19:50:38 +0200769 /**
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000770 * Convert from zigzag long to long.
Jens Geyerf509df92013-04-25 20:38:55 +0200771 */
772 private long zigzagToLong(ulong n)
773 {
774 return (long)(n >> 1) ^ (-(long)(n & 1));
775 }
776
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000777 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200778 * Note that it's important that the mask bytes are long literals,
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000779 * otherwise they'll default to ints, and when you shift an int left 56 bits,
780 * you just get a messed up int.
Jens Geyerf509df92013-04-25 20:38:55 +0200781 */
782 private long bytesToLong(byte[] bytes)
783 {
784 return
785 ((bytes[7] & 0xffL) << 56) |
786 ((bytes[6] & 0xffL) << 48) |
787 ((bytes[5] & 0xffL) << 40) |
788 ((bytes[4] & 0xffL) << 32) |
789 ((bytes[3] & 0xffL) << 24) |
790 ((bytes[2] & 0xffL) << 16) |
791 ((bytes[1] & 0xffL) << 8) |
792 ((bytes[0] & 0xffL));
793 }
794
795 //
796 // type testing and converting
797 //
798
799 private Boolean isBoolType(byte b)
800 {
801 int lowerNibble = b & 0x0f;
802 return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE;
803 }
804
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000805 /**
Jens Geyerd5436f52014-10-03 19:50:38 +0200806 * Given a TCompactProtocol.Types constant, convert it to its corresponding
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000807 * TType value.
Jens Geyerf509df92013-04-25 20:38:55 +0200808 */
809 private TType getTType(byte type)
810 {
811 switch ((byte)(type & 0x0f))
812 {
813 case Types.STOP:
814 return TType.Stop;
815 case Types.BOOLEAN_FALSE:
816 case Types.BOOLEAN_TRUE:
817 return TType.Bool;
818 case Types.BYTE:
819 return TType.Byte;
820 case Types.I16:
821 return TType.I16;
822 case Types.I32:
823 return TType.I32;
824 case Types.I64:
825 return TType.I64;
826 case Types.DOUBLE:
827 return TType.Double;
828 case Types.BINARY:
829 return TType.String;
830 case Types.LIST:
831 return TType.List;
832 case Types.SET:
833 return TType.Set;
834 case Types.MAP:
835 return TType.Map;
836 case Types.STRUCT:
837 return TType.Struct;
838 default:
839 throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f));
840 }
841 }
842
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000843 /**
844 * Given a TType value, find the appropriate TCompactProtocol.Types constant.
Jens Geyerf509df92013-04-25 20:38:55 +0200845 */
846 private byte getCompactType(TType ttype)
847 {
848 return ttypeToCompactType[(int)ttype];
849 }
850 }
Henrique Mendoncabd5db3a2012-10-03 09:26:32 +0000851}