/** | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF licenses this file | |
* to you under the Apache License, Version 2.0 (the | |
* "License"); you may not use this file except in compliance | |
* with the License. You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, | |
* software distributed under the License is distributed on an | |
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
* KIND, either express or implied. See the License for the | |
* specific language governing permissions and limitations | |
* under the License. | |
* | |
* Contains some contributions under the Thrift Software License. | |
* Please see doc/old-thrift-license.txt in the Thrift distribution for | |
* details. | |
*/ | |
using System; | |
using System.Text; | |
using Thrift.Transport; | |
using System.Collections; | |
using System.IO; | |
using System.Collections.Generic; | |
namespace Thrift.Protocol | |
{ | |
public class TCompactProtocol : TProtocol | |
{ | |
private static TStruct ANONYMOUS_STRUCT = new TStruct(""); | |
private static TField TSTOP = new TField("", TType.Stop, (short)0); | |
private static byte[] ttypeToCompactType = new byte[16]; | |
private const byte PROTOCOL_ID = 0x82; | |
private const byte VERSION = 1; | |
private const byte VERSION_MASK = 0x1f; // 0001 1111 | |
private const byte TYPE_MASK = 0xE0; // 1110 0000 | |
private const int TYPE_SHIFT_AMOUNT = 5; | |
/** | |
* All of the on-wire type codes. | |
*/ | |
private static class Types | |
{ | |
public const byte STOP = 0x00; | |
public const byte BOOLEAN_TRUE = 0x01; | |
public const byte BOOLEAN_FALSE = 0x02; | |
public const byte BYTE = 0x03; | |
public const byte I16 = 0x04; | |
public const byte I32 = 0x05; | |
public const byte I64 = 0x06; | |
public const byte DOUBLE = 0x07; | |
public const byte BINARY = 0x08; | |
public const byte LIST = 0x09; | |
public const byte SET = 0x0A; | |
public const byte MAP = 0x0B; | |
public const byte STRUCT = 0x0C; | |
} | |
/** | |
* Used to keep track of the last field for the current and previous structs, | |
* so we can do the delta stuff. | |
*/ | |
private Stack<short> lastField_ = new Stack<short>(15); | |
private short lastFieldId_ = 0; | |
/** | |
* If we encounter a boolean field begin, save the TField here so it can | |
* have the value incorporated. | |
*/ | |
private Nullable<TField> booleanField_; | |
/** | |
* If we Read a field header, and it's a boolean field, save the boolean | |
* value here so that ReadBool can use it. | |
*/ | |
private Nullable<Boolean> boolValue_; | |
#region CompactProtocol Factory | |
/** | |
* Factory | |
*/ | |
public class Factory : TProtocolFactory | |
{ | |
public Factory() { } | |
public TProtocol GetProtocol(TTransport trans) | |
{ | |
return new TCompactProtocol(trans); | |
} | |
} | |
#endregion | |
public TCompactProtocol(TTransport trans) | |
: base(trans) | |
{ | |
ttypeToCompactType[(int)TType.Stop] = Types.STOP; | |
ttypeToCompactType[(int)TType.Bool] = Types.BOOLEAN_TRUE; | |
ttypeToCompactType[(int)TType.Byte] = Types.BYTE; | |
ttypeToCompactType[(int)TType.I16] = Types.I16; | |
ttypeToCompactType[(int)TType.I32] = Types.I32; | |
ttypeToCompactType[(int)TType.I64] = Types.I64; | |
ttypeToCompactType[(int)TType.Double] = Types.DOUBLE; | |
ttypeToCompactType[(int)TType.String] = Types.BINARY; | |
ttypeToCompactType[(int)TType.List] = Types.LIST; | |
ttypeToCompactType[(int)TType.Set] = Types.SET; | |
ttypeToCompactType[(int)TType.Map] = Types.MAP; | |
ttypeToCompactType[(int)TType.Struct] = Types.STRUCT; | |
} | |
public void reset() | |
{ | |
lastField_.Clear(); | |
lastFieldId_ = 0; | |
} | |
#region Write Methods | |
/** | |
* Writes a byte without any possibility of all that field header nonsense. | |
* Used internally by other writing methods that know they need to Write a byte. | |
*/ | |
private byte[] byteDirectBuffer = new byte[1]; | |
private void WriteByteDirect(byte b) | |
{ | |
byteDirectBuffer[0] = b; | |
trans.Write(byteDirectBuffer); | |
} | |
/** | |
* Writes a byte without any possibility of all that field header nonsense. | |
*/ | |
private void WriteByteDirect(int n) | |
{ | |
WriteByteDirect((byte)n); | |
} | |
/** | |
* Write an i32 as a varint. Results in 1-5 bytes on the wire. | |
* TODO: make a permanent buffer like WriteVarint64? | |
*/ | |
byte[] i32buf = new byte[5]; | |
private void WriteVarint32(uint n) | |
{ | |
int idx = 0; | |
while (true) | |
{ | |
if ((n & ~0x7F) == 0) | |
{ | |
i32buf[idx++] = (byte)n; | |
// WriteByteDirect((byte)n); | |
break; | |
// return; | |
} | |
else | |
{ | |
i32buf[idx++] = (byte)((n & 0x7F) | 0x80); | |
// WriteByteDirect((byte)((n & 0x7F) | 0x80)); | |
n >>= 7; | |
} | |
} | |
trans.Write(i32buf, 0, idx); | |
} | |
/** | |
* Write a message header to the wire. Compact Protocol messages contain the | |
* protocol version so we can migrate forwards in the future if need be. | |
*/ | |
public override void WriteMessageBegin(TMessage message) | |
{ | |
WriteByteDirect(PROTOCOL_ID); | |
WriteByteDirect((byte)((VERSION & VERSION_MASK) | ((((uint)message.Type) << TYPE_SHIFT_AMOUNT) & TYPE_MASK))); | |
WriteVarint32((uint)message.SeqID); | |
WriteString(message.Name); | |
} | |
/** | |
* Write a struct begin. This doesn't actually put anything on the wire. We | |
* use it as an opportunity to put special placeholder markers on the field | |
* stack so we can get the field id deltas correct. | |
*/ | |
public override void WriteStructBegin(TStruct strct) | |
{ | |
lastField_.Push(lastFieldId_); | |
lastFieldId_ = 0; | |
} | |
/** | |
* Write a struct end. This doesn't actually put anything on the wire. We use | |
* this as an opportunity to pop the last field from the current struct off | |
* of the field stack. | |
*/ | |
public override void WriteStructEnd() | |
{ | |
lastFieldId_ = lastField_.Pop(); | |
} | |
/** | |
* Write a field header containing the field id and field type. If the | |
* difference between the current field id and the last one is small (< 15), | |
* then the field id will be encoded in the 4 MSB as a delta. Otherwise, the | |
* field id will follow the type header as a zigzag varint. | |
*/ | |
public override void WriteFieldBegin(TField field) | |
{ | |
if (field.Type == TType.Bool) | |
{ | |
// we want to possibly include the value, so we'll wait. | |
booleanField_ = field; | |
} | |
else | |
{ | |
WriteFieldBeginInternal(field, 0xFF); | |
} | |
} | |
/** | |
* The workhorse of WriteFieldBegin. It has the option of doing a | |
* 'type override' of the type header. This is used specifically in the | |
* boolean field case. | |
*/ | |
private void WriteFieldBeginInternal(TField field, byte typeOverride) | |
{ | |
// short lastField = lastField_.Pop(); | |
// if there's a type override, use that. | |
byte typeToWrite = typeOverride == 0xFF ? getCompactType(field.Type) : typeOverride; | |
// check if we can use delta encoding for the field id | |
if (field.ID > lastFieldId_ && field.ID - lastFieldId_ <= 15) | |
{ | |
// Write them together | |
WriteByteDirect((field.ID - lastFieldId_) << 4 | typeToWrite); | |
} | |
else | |
{ | |
// Write them separate | |
WriteByteDirect(typeToWrite); | |
WriteI16(field.ID); | |
} | |
lastFieldId_ = field.ID; | |
// lastField_.push(field.id); | |
} | |
/** | |
* Write the STOP symbol so we know there are no more fields in this struct. | |
*/ | |
public override void WriteFieldStop() | |
{ | |
WriteByteDirect(Types.STOP); | |
} | |
/** | |
* Write a map header. If the map is empty, omit the key and value type | |
* headers, as we don't need any additional information to skip it. | |
*/ | |
public override void WriteMapBegin(TMap map) | |
{ | |
if (map.Count == 0) | |
{ | |
WriteByteDirect(0); | |
} | |
else | |
{ | |
WriteVarint32((uint)map.Count); | |
WriteByteDirect(getCompactType(map.KeyType) << 4 | getCompactType(map.ValueType)); | |
} | |
} | |
/** | |
* Write a list header. | |
*/ | |
public override void WriteListBegin(TList list) | |
{ | |
WriteCollectionBegin(list.ElementType, list.Count); | |
} | |
/** | |
* Write a set header. | |
*/ | |
public override void WriteSetBegin(TSet set) | |
{ | |
WriteCollectionBegin(set.ElementType, set.Count); | |
} | |
/** | |
* Write a boolean value. Potentially, this could be a boolean field, in | |
* which case the field header info isn't written yet. If so, decide what the | |
* right type header is for the value and then Write the field header. | |
* Otherwise, Write a single byte. | |
*/ | |
public override void WriteBool(Boolean b) | |
{ | |
if (booleanField_ != null) | |
{ | |
// we haven't written the field header yet | |
WriteFieldBeginInternal(booleanField_.Value, b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE); | |
booleanField_ = null; | |
} | |
else | |
{ | |
// we're not part of a field, so just Write the value. | |
WriteByteDirect(b ? Types.BOOLEAN_TRUE : Types.BOOLEAN_FALSE); | |
} | |
} | |
/** | |
* Write a byte. Nothing to see here! | |
*/ | |
public override void WriteByte(byte b) | |
{ | |
WriteByteDirect(b); | |
} | |
/** | |
* Write an I16 as a zigzag varint. | |
*/ | |
public override void WriteI16(short i16) | |
{ | |
WriteVarint32(intToZigZag(i16)); | |
} | |
/** | |
* Write an i32 as a zigzag varint. | |
*/ | |
public override void WriteI32(int i32) | |
{ | |
WriteVarint32(intToZigZag(i32)); | |
} | |
/** | |
* Write an i64 as a zigzag varint. | |
*/ | |
public override void WriteI64(long i64) | |
{ | |
WriteVarint64(longToZigzag(i64)); | |
} | |
/** | |
* Write a double to the wire as 8 bytes. | |
*/ | |
public override void WriteDouble(double dub) | |
{ | |
byte[] data = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; | |
fixedLongToBytes(BitConverter.DoubleToInt64Bits(dub), data, 0); | |
trans.Write(data); | |
} | |
/** | |
* Write a string to the wire with a varint size preceding. | |
*/ | |
public override void WriteString(String str) | |
{ | |
byte[] bytes = UTF8Encoding.UTF8.GetBytes(str); | |
WriteBinary(bytes, 0, bytes.Length); | |
} | |
/** | |
* Write a byte array, using a varint for the size. | |
*/ | |
public override void WriteBinary(byte[] bin) | |
{ | |
WriteBinary(bin, 0, bin.Length); | |
} | |
private void WriteBinary(byte[] buf, int offset, int length) | |
{ | |
WriteVarint32((uint)length); | |
trans.Write(buf, offset, length); | |
} | |
// | |
// These methods are called by structs, but don't actually have any wire | |
// output or purpose. | |
// | |
public override void WriteMessageEnd() { } | |
public override void WriteMapEnd() { } | |
public override void WriteListEnd() { } | |
public override void WriteSetEnd() { } | |
public override void WriteFieldEnd() { } | |
// | |
// Internal writing methods | |
// | |
/** | |
* Abstract method for writing the start of lists and sets. List and sets on | |
* the wire differ only by the type indicator. | |
*/ | |
protected void WriteCollectionBegin(TType elemType, int size) | |
{ | |
if (size <= 14) | |
{ | |
WriteByteDirect(size << 4 | getCompactType(elemType)); | |
} | |
else | |
{ | |
WriteByteDirect(0xf0 | getCompactType(elemType)); | |
WriteVarint32((uint)size); | |
} | |
} | |
/** | |
* Write an i64 as a varint. Results in 1-10 bytes on the wire. | |
*/ | |
byte[] varint64out = new byte[10]; | |
private void WriteVarint64(ulong n) | |
{ | |
int idx = 0; | |
while (true) | |
{ | |
if ((n & ~(ulong)0x7FL) == 0) | |
{ | |
varint64out[idx++] = (byte)n; | |
break; | |
} | |
else | |
{ | |
varint64out[idx++] = ((byte)((n & 0x7F) | 0x80)); | |
n >>= 7; | |
} | |
} | |
trans.Write(varint64out, 0, idx); | |
} | |
/** | |
* Convert l into a zigzag long. This allows negative numbers to be | |
* represented compactly as a varint. | |
*/ | |
private ulong longToZigzag(long n) | |
{ | |
return (ulong)(((ulong)n << 1) ^ ((ulong)n >> 63)); | |
} | |
/** | |
* Convert n into a zigzag int. This allows negative numbers to be | |
* represented compactly as a varint. | |
*/ | |
private uint intToZigZag(int n) | |
{ | |
return (uint)(((uint)n << 1) ^ ((uint)n >> 31)); | |
} | |
/** | |
* Convert a long into little-endian bytes in buf starting at off and going | |
* until off+7. | |
*/ | |
private void fixedLongToBytes(long n, byte[] buf, int off) | |
{ | |
buf[off + 0] = (byte)(n & 0xff); | |
buf[off + 1] = (byte)((n >> 8) & 0xff); | |
buf[off + 2] = (byte)((n >> 16) & 0xff); | |
buf[off + 3] = (byte)((n >> 24) & 0xff); | |
buf[off + 4] = (byte)((n >> 32) & 0xff); | |
buf[off + 5] = (byte)((n >> 40) & 0xff); | |
buf[off + 6] = (byte)((n >> 48) & 0xff); | |
buf[off + 7] = (byte)((n >> 56) & 0xff); | |
} | |
#endregion | |
#region ReadMethods | |
/** | |
* Read a message header. | |
*/ | |
public override TMessage ReadMessageBegin() | |
{ | |
byte protocolId = ReadByte(); | |
if (protocolId != PROTOCOL_ID) | |
{ | |
throw new TProtocolException("Expected protocol id " + PROTOCOL_ID.ToString("X") + " but got " + protocolId.ToString("X")); | |
} | |
byte versionAndType = ReadByte(); | |
byte version = (byte)(versionAndType & VERSION_MASK); | |
if (version != VERSION) | |
{ | |
throw new TProtocolException("Expected version " + VERSION + " but got " + version); | |
} | |
byte type = (byte)((versionAndType >> TYPE_SHIFT_AMOUNT) & 0x03); | |
int seqid = (int)ReadVarint32(); | |
String messageName = ReadString(); | |
return new TMessage(messageName, (TMessageType)type, seqid); | |
} | |
/** | |
* Read a struct begin. There's nothing on the wire for this, but it is our | |
* opportunity to push a new struct begin marker onto the field stack. | |
*/ | |
public override TStruct ReadStructBegin() | |
{ | |
lastField_.Push(lastFieldId_); | |
lastFieldId_ = 0; | |
return ANONYMOUS_STRUCT; | |
} | |
/** | |
* Doesn't actually consume any wire data, just removes the last field for | |
* this struct from the field stack. | |
*/ | |
public override void ReadStructEnd() | |
{ | |
// consume the last field we Read off the wire. | |
lastFieldId_ = lastField_.Pop(); | |
} | |
/** | |
* Read a field header off the wire. | |
*/ | |
public override TField ReadFieldBegin() | |
{ | |
byte type = ReadByte(); | |
// if it's a stop, then we can return immediately, as the struct is over. | |
if (type == Types.STOP) | |
{ | |
return TSTOP; | |
} | |
short fieldId; | |
// mask off the 4 MSB of the type header. it could contain a field id delta. | |
short modifier = (short)((type & 0xf0) >> 4); | |
if (modifier == 0) | |
{ | |
// not a delta. look ahead for the zigzag varint field id. | |
fieldId = ReadI16(); | |
} | |
else | |
{ | |
// has a delta. add the delta to the last Read field id. | |
fieldId = (short)(lastFieldId_ + modifier); | |
} | |
TField field = new TField("", getTType((byte)(type & 0x0f)), fieldId); | |
// if this happens to be a boolean field, the value is encoded in the type | |
if (isBoolType(type)) | |
{ | |
// save the boolean value in a special instance variable. | |
boolValue_ = (byte)(type & 0x0f) == Types.BOOLEAN_TRUE ? true : false; | |
} | |
// push the new field onto the field stack so we can keep the deltas going. | |
lastFieldId_ = field.ID; | |
return field; | |
} | |
/** | |
* Read a map header off the wire. If the size is zero, skip Reading the key | |
* and value type. This means that 0-length maps will yield TMaps without the | |
* "correct" types. | |
*/ | |
public override TMap ReadMapBegin() | |
{ | |
int size = (int)ReadVarint32(); | |
byte keyAndValueType = size == 0 ? (byte)0 : ReadByte(); | |
return new TMap(getTType((byte)(keyAndValueType >> 4)), getTType((byte)(keyAndValueType & 0xf)), size); | |
} | |
/** | |
* Read a list header off the wire. If the list size is 0-14, the size will | |
* be packed into the element type header. If it's a longer list, the 4 MSB | |
* of the element type header will be 0xF, and a varint will follow with the | |
* true size. | |
*/ | |
public override TList ReadListBegin() | |
{ | |
byte size_and_type = ReadByte(); | |
int size = (size_and_type >> 4) & 0x0f; | |
if (size == 15) | |
{ | |
size = (int)ReadVarint32(); | |
} | |
TType type = getTType(size_and_type); | |
return new TList(type, size); | |
} | |
/** | |
* Read a set header off the wire. If the set size is 0-14, the size will | |
* be packed into the element type header. If it's a longer set, the 4 MSB | |
* of the element type header will be 0xF, and a varint will follow with the | |
* true size. | |
*/ | |
public override TSet ReadSetBegin() | |
{ | |
return new TSet(ReadListBegin()); | |
} | |
/** | |
* Read a boolean off the wire. If this is a boolean field, the value should | |
* already have been Read during ReadFieldBegin, so we'll just consume the | |
* pre-stored value. Otherwise, Read a byte. | |
*/ | |
public override Boolean ReadBool() | |
{ | |
if (boolValue_ != null) | |
{ | |
bool result = boolValue_.Value; | |
boolValue_ = null; | |
return result; | |
} | |
return ReadByte() == Types.BOOLEAN_TRUE; | |
} | |
byte[] byteRawBuf = new byte[1]; | |
/** | |
* Read a single byte off the wire. Nothing interesting here. | |
*/ | |
public override byte ReadByte() | |
{ | |
trans.ReadAll(byteRawBuf, 0, 1); | |
return byteRawBuf[0]; | |
} | |
/** | |
* Read an i16 from the wire as a zigzag varint. | |
*/ | |
public override short ReadI16() | |
{ | |
return (short)zigzagToInt(ReadVarint32()); | |
} | |
/** | |
* Read an i32 from the wire as a zigzag varint. | |
*/ | |
public override int ReadI32() | |
{ | |
return zigzagToInt(ReadVarint32()); | |
} | |
/** | |
* Read an i64 from the wire as a zigzag varint. | |
*/ | |
public override long ReadI64() | |
{ | |
return zigzagToLong(ReadVarint64()); | |
} | |
/** | |
* No magic here - just Read a double off the wire. | |
*/ | |
public override double ReadDouble() | |
{ | |
byte[] longBits = new byte[8]; | |
trans.ReadAll(longBits, 0, 8); | |
return BitConverter.Int64BitsToDouble(bytesToLong(longBits)); | |
} | |
/** | |
* Reads a byte[] (via ReadBinary), and then UTF-8 decodes it. | |
*/ | |
public override String ReadString() | |
{ | |
int length = (int)ReadVarint32(); | |
if (length == 0) | |
{ | |
return ""; | |
} | |
return Encoding.UTF8.GetString(ReadBinary(length)); | |
} | |
/** | |
* Read a byte[] from the wire. | |
*/ | |
public override byte[] ReadBinary() | |
{ | |
int length = (int)ReadVarint32(); | |
if (length == 0) return new byte[0]; | |
byte[] buf = new byte[length]; | |
trans.ReadAll(buf, 0, length); | |
return buf; | |
} | |
/** | |
* Read a byte[] of a known length from the wire. | |
*/ | |
private byte[] ReadBinary(int length) | |
{ | |
if (length == 0) return new byte[0]; | |
byte[] buf = new byte[length]; | |
trans.ReadAll(buf, 0, length); | |
return buf; | |
} | |
// | |
// These methods are here for the struct to call, but don't have any wire | |
// encoding. | |
// | |
public override void ReadMessageEnd() { } | |
public override void ReadFieldEnd() { } | |
public override void ReadMapEnd() { } | |
public override void ReadListEnd() { } | |
public override void ReadSetEnd() { } | |
// | |
// Internal Reading methods | |
// | |
/** | |
* Read an i32 from the wire as a varint. The MSB of each byte is set | |
* if there is another byte to follow. This can Read up to 5 bytes. | |
*/ | |
private uint ReadVarint32() | |
{ | |
uint result = 0; | |
int shift = 0; | |
while (true) | |
{ | |
byte b = ReadByte(); | |
result |= (uint)(b & 0x7f) << shift; | |
if ((b & 0x80) != 0x80) break; | |
shift += 7; | |
} | |
return result; | |
} | |
/** | |
* Read an i64 from the wire as a proper varint. The MSB of each byte is set | |
* if there is another byte to follow. This can Read up to 10 bytes. | |
*/ | |
private ulong ReadVarint64() | |
{ | |
int shift = 0; | |
ulong result = 0; | |
while (true) | |
{ | |
byte b = ReadByte(); | |
result |= (ulong)(b & 0x7f) << shift; | |
if ((b & 0x80) != 0x80) break; | |
shift += 7; | |
} | |
return result; | |
} | |
#endregion | |
// | |
// encoding helpers | |
// | |
/** | |
* Convert from zigzag int to int. | |
*/ | |
private int zigzagToInt(uint n) | |
{ | |
return (int)(n >> 1) ^ (-(int)(n & 1)); | |
} | |
/** | |
* Convert from zigzag long to long. | |
*/ | |
private long zigzagToLong(ulong n) | |
{ | |
return (long)(n >> 1) ^ (-(long)(n & 1)); | |
} | |
/** | |
* Note that it's important that the mask bytes are long literals, | |
* otherwise they'll default to ints, and when you shift an int left 56 bits, | |
* you just get a messed up int. | |
*/ | |
private long bytesToLong(byte[] bytes) | |
{ | |
return | |
((bytes[7] & 0xffL) << 56) | | |
((bytes[6] & 0xffL) << 48) | | |
((bytes[5] & 0xffL) << 40) | | |
((bytes[4] & 0xffL) << 32) | | |
((bytes[3] & 0xffL) << 24) | | |
((bytes[2] & 0xffL) << 16) | | |
((bytes[1] & 0xffL) << 8) | | |
((bytes[0] & 0xffL)); | |
} | |
// | |
// type testing and converting | |
// | |
private Boolean isBoolType(byte b) | |
{ | |
int lowerNibble = b & 0x0f; | |
return lowerNibble == Types.BOOLEAN_TRUE || lowerNibble == Types.BOOLEAN_FALSE; | |
} | |
/** | |
* Given a TCompactProtocol.Types constant, convert it to its corresponding | |
* TType value. | |
*/ | |
private TType getTType(byte type) | |
{ | |
switch ((byte)(type & 0x0f)) | |
{ | |
case Types.STOP: | |
return TType.Stop; | |
case Types.BOOLEAN_FALSE: | |
case Types.BOOLEAN_TRUE: | |
return TType.Bool; | |
case Types.BYTE: | |
return TType.Byte; | |
case Types.I16: | |
return TType.I16; | |
case Types.I32: | |
return TType.I32; | |
case Types.I64: | |
return TType.I64; | |
case Types.DOUBLE: | |
return TType.Double; | |
case Types.BINARY: | |
return TType.String; | |
case Types.LIST: | |
return TType.List; | |
case Types.SET: | |
return TType.Set; | |
case Types.MAP: | |
return TType.Map; | |
case Types.STRUCT: | |
return TType.Struct; | |
default: | |
throw new TProtocolException("don't know what type: " + (byte)(type & 0x0f)); | |
} | |
} | |
/** | |
* Given a TType value, find the appropriate TCompactProtocol.Types constant. | |
*/ | |
private byte getCompactType(TType ttype) | |
{ | |
return ttypeToCompactType[(int)ttype]; | |
} | |
} | |
} |