in lib/d/src/thrift/protocol/compact.d [33:605]
final class TCompactProtocol(Transport = TTransport) if (
isTTransport!Transport
) : TProtocol {
/**
* Constructs a new instance.
*
* Params:
* trans = The transport to use.
* containerSizeLimit = If positive, the container size is limited to the
* given number of items.
* stringSizeLimit = If positive, the string length is limited to the
* given number of bytes.
*/
this(Transport trans, int containerSizeLimit = 0, int stringSizeLimit = 0) {
trans_ = trans;
this.containerSizeLimit = containerSizeLimit;
this.stringSizeLimit = stringSizeLimit;
}
Transport transport() @property {
return trans_;
}
void reset() {
lastFieldId_ = 0;
fieldIdStack_ = null;
booleanField_ = TField.init;
hasBoolValue_ = false;
}
/**
* If positive, limits the number of items of deserialized containers to the
* given amount.
*
* This is useful to avoid allocating excessive amounts of memory when broken
* data is received. If the limit is exceeded, a SIZE_LIMIT-type
* TProtocolException is thrown.
*
* Defaults to zero (no limit).
*/
int containerSizeLimit;
/**
* If positive, limits the length of deserialized strings/binary data to the
* given number of bytes.
*
* This is useful to avoid allocating excessive amounts of memory when broken
* data is received. If the limit is exceeded, a SIZE_LIMIT-type
* TProtocolException is thrown.
*
* Defaults to zero (no limit).
*/
int stringSizeLimit;
/*
* Writing methods.
*/
void writeBool(bool b) {
if (booleanField_.name !is null) {
// we haven't written the field header yet
writeFieldBeginInternal(booleanField_,
b ? CType.BOOLEAN_TRUE : CType.BOOLEAN_FALSE);
booleanField_.name = null;
} else {
// we're not part of a field, so just write the value
writeByte(b ? CType.BOOLEAN_TRUE : CType.BOOLEAN_FALSE);
}
}
void writeByte(byte b) {
trans_.write((cast(ubyte*)&b)[0..1]);
}
void writeI16(short i16) {
writeVarint32(i32ToZigzag(i16));
}
void writeI32(int i32) {
writeVarint32(i32ToZigzag(i32));
}
void writeI64(long i64) {
writeVarint64(i64ToZigzag(i64));
}
void writeDouble(double dub) {
ulong bits = hostToLe(*cast(ulong*)(&dub));
trans_.write((cast(ubyte*)&bits)[0 .. 8]);
}
void writeString(string str) {
writeBinary(cast(ubyte[])str);
}
void writeBinary(ubyte[] buf) {
assert(buf.length <= int.max);
writeVarint32(cast(int)buf.length);
trans_.write(buf);
}
void writeMessageBegin(TMessage msg) {
writeByte(cast(byte)PROTOCOL_ID);
writeByte(cast(byte)((VERSION_N & VERSION_MASK) |
((cast(int)msg.type << TYPE_SHIFT_AMOUNT) & TYPE_MASK)));
writeVarint32(msg.seqid);
writeString(msg.name);
}
void writeMessageEnd() {}
void writeStructBegin(TStruct tstruct) {
fieldIdStack_ ~= lastFieldId_;
lastFieldId_ = 0;
}
void writeStructEnd() {
lastFieldId_ = fieldIdStack_[$ - 1];
fieldIdStack_ = fieldIdStack_[0 .. $ - 1];
fieldIdStack_.assumeSafeAppend();
}
void writeFieldBegin(TField field) {
if (field.type == TType.BOOL) {
booleanField_.name = field.name;
booleanField_.type = field.type;
booleanField_.id = field.id;
} else {
return writeFieldBeginInternal(field);
}
}
void writeFieldEnd() {}
void writeFieldStop() {
writeByte(TType.STOP);
}
void writeListBegin(TList list) {
writeCollectionBegin(list.elemType, list.size);
}
void writeListEnd() {}
void writeMapBegin(TMap map) {
if (map.size == 0) {
writeByte(0);
} else {
assert(map.size <= int.max);
writeVarint32(cast(int)map.size);
writeByte(cast(byte)(toCType(map.keyType) << 4 | toCType(map.valueType)));
}
}
void writeMapEnd() {}
void writeSetBegin(TSet set) {
writeCollectionBegin(set.elemType, set.size);
}
void writeSetEnd() {}
/*
* Reading methods.
*/
bool readBool() {
if (hasBoolValue_ == true) {
hasBoolValue_ = false;
return boolValue_;
}
return readByte() == CType.BOOLEAN_TRUE;
}
byte readByte() {
ubyte[1] b = void;
trans_.readAll(b);
return cast(byte)b[0];
}
short readI16() {
return cast(short)zigzagToI32(readVarint32());
}
int readI32() {
return zigzagToI32(readVarint32());
}
long readI64() {
return zigzagToI64(readVarint64());
}
double readDouble() {
IntBuf!long b = void;
trans_.readAll(b.bytes);
b.value = leToHost(b.value);
return *cast(double*)(&b.value);
}
string readString() {
return cast(string)readBinary();
}
ubyte[] readBinary() {
auto size = readVarint32();
checkSize(size, stringSizeLimit);
if (size == 0) {
return null;
}
auto buf = uninitializedArray!(ubyte[])(size);
trans_.readAll(buf);
return buf;
}
TMessage readMessageBegin() {
TMessage msg = void;
auto protocolId = readByte();
if (protocolId != cast(byte)PROTOCOL_ID) {
throw new TProtocolException("Bad protocol identifier",
TProtocolException.Type.BAD_VERSION);
}
auto versionAndType = readByte();
auto ver = versionAndType & VERSION_MASK;
if (ver != VERSION_N) {
throw new TProtocolException("Bad protocol version",
TProtocolException.Type.BAD_VERSION);
}
msg.type = cast(TMessageType)((versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS);
msg.seqid = readVarint32();
msg.name = readString();
return msg;
}
void readMessageEnd() {}
TStruct readStructBegin() {
fieldIdStack_ ~= lastFieldId_;
lastFieldId_ = 0;
return TStruct();
}
void readStructEnd() {
lastFieldId_ = fieldIdStack_[$ - 1];
fieldIdStack_ = fieldIdStack_[0 .. $ - 1];
}
TField readFieldBegin() {
TField f = void;
f.name = null;
auto bite = readByte();
auto type = cast(CType)(bite & 0x0f);
if (type == CType.STOP) {
// Struct stop byte, nothing more to do.
f.id = 0;
f.type = TType.STOP;
return f;
}
// Mask off the 4 MSB of the type header, which could contain a field id
// delta.
auto modifier = cast(short)((bite & 0xf0) >> 4);
if (modifier > 0) {
f.id = cast(short)(lastFieldId_ + modifier);
} else {
// Delta encoding not used, just read the id as usual.
f.id = readI16();
}
f.type = getTType(type);
if (type == CType.BOOLEAN_TRUE || type == CType.BOOLEAN_FALSE) {
// For boolean fields, the value is encoded in the type – keep it around
// for the readBool() call.
hasBoolValue_ = true;
boolValue_ = (type == CType.BOOLEAN_TRUE ? true : false);
}
lastFieldId_ = f.id;
return f;
}
void readFieldEnd() {}
TList readListBegin() {
auto sizeAndType = readByte();
auto lsize = (sizeAndType >> 4) & 0xf;
if (lsize == 0xf) {
lsize = readVarint32();
}
checkSize(lsize, containerSizeLimit);
TList l = void;
l.elemType = getTType(cast(CType)(sizeAndType & 0x0f));
l.size = cast(size_t)lsize;
return l;
}
void readListEnd() {}
TMap readMapBegin() {
TMap m = void;
auto size = readVarint32();
ubyte kvType;
if (size != 0) {
kvType = readByte();
}
checkSize(size, containerSizeLimit);
m.size = size;
m.keyType = getTType(cast(CType)(kvType >> 4));
m.valueType = getTType(cast(CType)(kvType & 0xf));
return m;
}
void readMapEnd() {}
TSet readSetBegin() {
auto sizeAndType = readByte();
auto lsize = (sizeAndType >> 4) & 0xf;
if (lsize == 0xf) {
lsize = readVarint32();
}
checkSize(lsize, containerSizeLimit);
TSet s = void;
s.elemType = getTType(cast(CType)(sizeAndType & 0xf));
s.size = cast(size_t)lsize;
return s;
}
void readSetEnd() {}
private:
void writeFieldBeginInternal(TField field, byte typeOverride = -1) {
// If there's a type override, use that.
auto typeToWrite = (typeOverride == -1 ? toCType(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
writeByte(cast(byte)((field.id - lastFieldId_) << 4 | typeToWrite));
} else {
// write them separate
writeByte(cast(byte)typeToWrite);
writeI16(field.id);
}
lastFieldId_ = field.id;
}
void writeCollectionBegin(TType elemType, size_t size) {
if (size <= 14) {
writeByte(cast(byte)(size << 4 | toCType(elemType)));
} else {
assert(size <= int.max);
writeByte(cast(byte)(0xf0 | toCType(elemType)));
writeVarint32(cast(int)size);
}
}
void writeVarint32(uint n) {
ubyte[5] buf = void;
ubyte wsize;
while (true) {
if ((n & ~0x7F) == 0) {
buf[wsize++] = cast(ubyte)n;
break;
} else {
buf[wsize++] = cast(ubyte)((n & 0x7F) | 0x80);
n >>= 7;
}
}
trans_.write(buf[0 .. wsize]);
}
/*
* Write an i64 as a varint. Results in 1-10 bytes on the wire.
*/
void writeVarint64(ulong n) {
ubyte[10] buf = void;
ubyte wsize;
while (true) {
if ((n & ~0x7FL) == 0) {
buf[wsize++] = cast(ubyte)n;
break;
} else {
buf[wsize++] = cast(ubyte)((n & 0x7F) | 0x80);
n >>= 7;
}
}
trans_.write(buf[0 .. wsize]);
}
/*
* Convert l into a zigzag long. This allows negative numbers to be
* represented compactly as a varint.
*/
ulong i64ToZigzag(long l) {
return (l << 1) ^ (l >> 63);
}
/*
* Convert n into a zigzag int. This allows negative numbers to be
* represented compactly as a varint.
*/
uint i32ToZigzag(int n) {
return (n << 1) ^ (n >> 31);
}
CType toCType(TType type) {
final switch (type) {
case TType.STOP:
return CType.STOP;
case TType.BOOL:
return CType.BOOLEAN_TRUE;
case TType.BYTE:
return CType.BYTE;
case TType.DOUBLE:
return CType.DOUBLE;
case TType.I16:
return CType.I16;
case TType.I32:
return CType.I32;
case TType.I64:
return CType.I64;
case TType.STRING:
return CType.BINARY;
case TType.STRUCT:
return CType.STRUCT;
case TType.MAP:
return CType.MAP;
case TType.SET:
return CType.SET;
case TType.LIST:
return CType.LIST;
case TType.VOID:
assert(false, "Invalid type passed.");
}
}
int readVarint32() {
return cast(int)readVarint64();
}
long readVarint64() {
ulong val;
ubyte shift;
ubyte[10] buf = void; // 64 bits / (7 bits/byte) = 10 bytes.
auto bufSize = buf.sizeof;
auto borrowed = trans_.borrow(buf.ptr, bufSize);
ubyte rsize;
if (borrowed) {
// Fast path.
while (true) {
auto bite = borrowed[rsize];
rsize++;
val |= cast(ulong)(bite & 0x7f) << shift;
shift += 7;
if (!(bite & 0x80)) {
trans_.consume(rsize);
return val;
}
// Have to check for invalid data so we don't crash.
if (rsize == buf.sizeof) {
throw new TProtocolException(TProtocolException.Type.INVALID_DATA,
"Variable-length int over 10 bytes.");
}
}
} else {
// Slow path.
while (true) {
ubyte[1] bite;
trans_.readAll(bite);
++rsize;
val |= cast(ulong)(bite[0] & 0x7f) << shift;
shift += 7;
if (!(bite[0] & 0x80)) {
return val;
}
// Might as well check for invalid data on the slow path too.
if (rsize >= buf.sizeof) {
throw new TProtocolException(TProtocolException.Type.INVALID_DATA,
"Variable-length int over 10 bytes.");
}
}
}
}
/*
* Convert from zigzag int to int.
*/
int zigzagToI32(uint n) {
return (n >> 1) ^ -(n & 1);
}
/*
* Convert from zigzag long to long.
*/
long zigzagToI64(ulong n) {
return (n >> 1) ^ -(n & 1);
}
TType getTType(CType type) {
final switch (type) {
case CType.STOP:
return TType.STOP;
case CType.BOOLEAN_FALSE:
return TType.BOOL;
case CType.BOOLEAN_TRUE:
return TType.BOOL;
case CType.BYTE:
return TType.BYTE;
case CType.I16:
return TType.I16;
case CType.I32:
return TType.I32;
case CType.I64:
return TType.I64;
case CType.DOUBLE:
return TType.DOUBLE;
case CType.BINARY:
return TType.STRING;
case CType.LIST:
return TType.LIST;
case CType.SET:
return TType.SET;
case CType.MAP:
return TType.MAP;
case CType.STRUCT:
return TType.STRUCT;
}
}
void checkSize(int size, int limit) {
if (size < 0) {
throw new TProtocolException(TProtocolException.Type.NEGATIVE_SIZE);
} else if (limit > 0 && size > limit) {
throw new TProtocolException(TProtocolException.Type.SIZE_LIMIT);
}
}
enum PROTOCOL_ID = 0x82;
enum VERSION_N = 1;
enum VERSION_MASK = 0b0001_1111;
enum TYPE_MASK = 0b1110_0000;
enum TYPE_BITS = 0b0000_0111;
enum TYPE_SHIFT_AMOUNT = 5;
// Probably need to implement a better stack at some point.
short[] fieldIdStack_;
short lastFieldId_;
TField booleanField_;
bool hasBoolValue_;
bool boolValue_;
Transport trans_;
}