@creditkarma/thrift-server-core
Version:
Thrift core library in TypeScript
557 lines • 18.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompactProtocol = exports.CompactType = void 0;
const errors_1 = require("../errors");
const binary_1 = require("../binary");
const logger_1 = require("../logger");
const types_1 = require("../types");
const TProtocol_1 = require("./TProtocol");
const PROTOCOL_ID = -126;
const VERSION_N = 1;
const VERSION_MASK = 0x1f;
const TYPE_MASK = -32;
const TYPE_BITS = 7;
const TYPE_SHIFT_AMOUNT = 5;
var CompactType;
(function (CompactType) {
CompactType[CompactType["STOP"] = 0] = "STOP";
CompactType[CompactType["BOOLEAN_TRUE"] = 1] = "BOOLEAN_TRUE";
CompactType[CompactType["BOOLEAN_FALSE"] = 2] = "BOOLEAN_FALSE";
CompactType[CompactType["BYTE"] = 3] = "BYTE";
CompactType[CompactType["I16"] = 4] = "I16";
CompactType[CompactType["I32"] = 5] = "I32";
CompactType[CompactType["I64"] = 6] = "I64";
CompactType[CompactType["DOUBLE"] = 7] = "DOUBLE";
CompactType[CompactType["BINARY"] = 8] = "BINARY";
CompactType[CompactType["LIST"] = 9] = "LIST";
CompactType[CompactType["SET"] = 10] = "SET";
CompactType[CompactType["MAP"] = 11] = "MAP";
CompactType[CompactType["STRUCT"] = 12] = "STRUCT";
})(CompactType = exports.CompactType || (exports.CompactType = {}));
const TTypeToCType = [
CompactType.STOP,
0,
CompactType.BOOLEAN_TRUE,
CompactType.BYTE,
CompactType.DOUBLE,
0,
CompactType.I16,
0,
CompactType.I32,
0,
CompactType.I64,
CompactType.BINARY,
CompactType.STRUCT,
CompactType.MAP,
CompactType.SET,
CompactType.LIST,
];
class CompactProtocol extends TProtocol_1.TProtocol {
constructor(trans, logger = logger_1.defaultLogger) {
super(trans, logger);
this.getTType = function (type) {
switch (type) {
case CompactType.STOP:
return types_1.TType.STOP;
case CompactType.BOOLEAN_FALSE:
case CompactType.BOOLEAN_TRUE:
return types_1.TType.BOOL;
case CompactType.BYTE:
return types_1.TType.BYTE;
case CompactType.I16:
return types_1.TType.I16;
case CompactType.I32:
return types_1.TType.I32;
case CompactType.I64:
return types_1.TType.I64;
case CompactType.DOUBLE:
return types_1.TType.DOUBLE;
case CompactType.BINARY:
return types_1.TType.STRING;
case CompactType.LIST:
return types_1.TType.LIST;
case CompactType.SET:
return types_1.TType.SET;
case CompactType.MAP:
return types_1.TType.MAP;
case CompactType.STRUCT:
return types_1.TType.STRUCT;
default:
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.INVALID_DATA, `Unknown type: ${type}`);
}
};
this._lastField = [];
this._lastFieldId = 0;
this._booleanField = {
name: null,
hasBoolValue: false,
};
this._boolValue = {
hasBoolValue: false,
boolValue: false,
};
}
getCompactType(ttype) {
return TTypeToCType[ttype];
}
writeMessageBegin(name, type, requestId) {
this.writeByte(PROTOCOL_ID);
this.writeByte((VERSION_N & VERSION_MASK) |
((type << TYPE_SHIFT_AMOUNT) & TYPE_MASK));
this.writeVarint32(requestId);
this.writeString(name);
if (this.requestId) {
this.logger(['warn', 'CompactProtocol'], `requestId already set: ${name}`);
}
else {
this.requestId = requestId;
}
}
writeMessageEnd() {
if (this.requestId !== null) {
this.requestId = null;
}
else {
this.logger(['warn', 'CompactProtocol'], 'No requestId to unset');
}
}
writeStructBegin(name) {
this._lastField.push(this._lastFieldId);
this._lastFieldId = 0;
}
writeStructEnd() {
this._lastFieldId = this._lastField.pop();
}
writeFieldBegin(name, type, id) {
if (type !== types_1.TType.BOOL) {
return this.writeFieldBeginInternal(name, type, id, -1);
}
else {
this._booleanField.name = name;
this._booleanField.fieldType = type;
this._booleanField.fieldId = id;
}
}
writeFieldEnd() { }
writeFieldStop() {
this.writeByte(CompactType.STOP);
}
writeMapBegin(keyType, valueType, size) {
if (size === 0) {
this.writeByte(0);
}
else {
this.writeVarint32(size);
this.writeByte((this.getCompactType(keyType) << 4) |
this.getCompactType(valueType));
}
}
writeMapEnd() { }
writeListBegin(elementType, size) {
this.writeCollectionBegin(elementType, size);
}
writeListEnd() { }
writeSetBegin(elementType, size) {
this.writeCollectionBegin(elementType, size);
}
writeSetEnd() { }
writeBool(bool) {
if (this._booleanField.name !== null) {
this.writeFieldBeginInternal(this._booleanField.name, this._booleanField.fieldType, this._booleanField.fieldId, bool ? CompactType.BOOLEAN_TRUE : CompactType.BOOLEAN_FALSE);
this._booleanField.name = null;
}
else {
this.writeByte(bool ? CompactType.BOOLEAN_TRUE : CompactType.BOOLEAN_FALSE);
}
}
writeByte(byte) {
this.transport.write(Buffer.from([byte]));
}
writeI16(i16) {
this.writeVarint32(this.i32ToZigzag(i16));
}
writeI32(i32) {
this.writeVarint32(this.i32ToZigzag(i32));
}
writeI64(i64) {
this.writeVarint64(this.i64ToZigzag(i64));
}
writeDouble(dub) {
const buff = Buffer.alloc(8);
this.transport.write((0, binary_1.writeDoubleLE)(buff, dub));
}
writeStringOrBinary(name, encoding, data) {
if (typeof data === 'string') {
this.writeVarint32(Buffer.byteLength(data, encoding));
this.transport.write(Buffer.from(data, encoding));
}
else if (data instanceof Buffer ||
Object.prototype.toString.call(data) === '[object Buffer]') {
this.writeVarint32(data.length);
this.transport.write(data);
}
else {
throw new Error(`${name} called without a string/Buffer argument: ${data}`);
}
}
writeString(data) {
this.writeStringOrBinary('writeString', 'utf8', data);
}
writeBinary(data) {
this.writeStringOrBinary('writeBinary', 'binary', data);
}
readMessageBegin() {
const protocolId = this.transport.readByte();
if (protocolId !== PROTOCOL_ID) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.BAD_VERSION, `Bad protocol identifier ${protocolId}`);
}
else {
const versionAndType = this.transport.readByte();
const version = versionAndType & VERSION_MASK;
if (version !== VERSION_N) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.BAD_VERSION, `Bad protocol version ${version}`);
}
const messageType = (versionAndType >> TYPE_SHIFT_AMOUNT) & TYPE_BITS;
const requestId = this.readVarint32();
const fieldName = this.readString();
return {
fieldName,
messageType,
requestId,
};
}
}
readMessageEnd() { }
readStructBegin() {
this._lastField.push(this._lastFieldId);
this._lastFieldId = 0;
return { fieldName: '' };
}
readStructEnd() {
this._lastFieldId = this._lastField.pop();
}
readFieldBegin() {
let fieldId = 0;
const byte = this.transport.readByte();
const type = byte & 0x0f;
if (type === CompactType.STOP) {
return {
fieldName: '',
fieldType: types_1.TType.STOP,
fieldId,
};
}
else {
const modifier = (byte & 0x000000f0) >>> 4;
if (modifier === 0) {
fieldId = this.readI16();
}
else {
fieldId = this._lastFieldId + modifier;
}
const fieldType = this.getTType(type);
if (type === CompactType.BOOLEAN_TRUE ||
type === CompactType.BOOLEAN_FALSE) {
this._boolValue.hasBoolValue = true;
this._boolValue.boolValue =
type === CompactType.BOOLEAN_TRUE ? true : false;
}
this._lastFieldId = fieldId;
return {
fieldName: '',
fieldType,
fieldId,
};
}
}
readFieldEnd() { }
readMapBegin() {
const size = this.readVarint32();
if (size < 0) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.NEGATIVE_SIZE, `Negative map size`);
}
else {
let kvType = 0;
if (size !== 0) {
kvType = this.transport.readByte();
}
const keyType = this.getTType((kvType & 0xf0) >>> 4);
const valueType = this.getTType(kvType & 0xf);
return { keyType, valueType, size };
}
}
readMapEnd() { }
readListBegin() {
const sizeType = this.transport.readByte();
let size = (sizeType >>> 4) & 0x0000000f;
if (size === 15) {
size = this.readVarint32();
}
if (size < 0) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.NEGATIVE_SIZE, `Negative list size`);
}
else {
const elementType = this.getTType(sizeType & 0x0000000f);
return { elementType, size };
}
}
readListEnd() { }
readSetBegin() {
return this.readListBegin();
}
readSetEnd() { }
readBool() {
let value = false;
if (this._boolValue.hasBoolValue === true) {
value = this._boolValue.boolValue;
this._boolValue.hasBoolValue = false;
}
else {
const res = this.transport.readByte();
value = res === CompactType.BOOLEAN_TRUE;
}
return value;
}
readByte() {
return this.transport.readByte();
}
readI16() {
return this.readI32();
}
readI32() {
return this.zigzagToI32(this.readVarint32());
}
readI64() {
return this.zigzagToI64(this.readVarint64());
}
readDouble() {
const buff = this.transport.read(8);
return (0, binary_1.readDoubleLE)(buff);
}
readBinary() {
const size = this.readVarint32();
if (size === 0) {
return Buffer.alloc(0);
}
if (size < 0) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.NEGATIVE_SIZE, `Negative binary size`);
}
else {
return this.transport.read(size);
}
}
readString() {
const size = this.readVarint32();
if (size === 0) {
return '';
}
else {
if (size < 0) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.NEGATIVE_SIZE, `Negative string size`);
}
return this.transport.readString(size);
}
}
skip(type) {
switch (type) {
case types_1.TType.STOP:
return;
case types_1.TType.BOOL:
this.readBool();
break;
case types_1.TType.BYTE:
this.readByte();
break;
case types_1.TType.I16:
this.readI16();
break;
case types_1.TType.I32:
this.readI32();
break;
case types_1.TType.I64:
this.readI64();
break;
case types_1.TType.DOUBLE:
this.readDouble();
break;
case types_1.TType.STRING:
this.readString();
break;
case types_1.TType.STRUCT:
this.readStructBegin();
while (true) {
const fieldBegin = this.readFieldBegin();
if (fieldBegin.fieldType === types_1.TType.STOP) {
break;
}
this.skip(fieldBegin.fieldType);
this.readFieldEnd();
}
this.readStructEnd();
break;
case types_1.TType.MAP:
const mapBegin = this.readMapBegin();
for (let i = 0; i < mapBegin.size; ++i) {
this.skip(mapBegin.keyType);
this.skip(mapBegin.valueType);
}
this.readMapEnd();
break;
case types_1.TType.SET:
const setBegin = this.readSetBegin();
for (let i2 = 0; i2 < setBegin.size; ++i2) {
this.skip(setBegin.elementType);
}
this.readSetEnd();
break;
case types_1.TType.LIST:
const listBegin = this.readListBegin();
for (let i3 = 0; i3 < listBegin.size; ++i3) {
this.skip(listBegin.elementType);
}
this.readListEnd();
break;
default:
throw new Error('Invalid type: ' + type);
}
}
flush() {
return this.transport.flush();
}
zigzagToI32(i32) {
return (i32 >>> 1) ^ (-1 * (i32 & 1));
}
zigzagToI64(i64) {
let hi = i64.buffer.readUInt32BE(0);
let lo = i64.buffer.readUInt32BE(4);
const lowBit = i64.buffer.readUInt8(7) & 0x01;
const mask = lowBit ? 0xffffffff : 0;
const hiLo = hi << 31;
hi = (hi >>> 1) ^ mask;
lo = ((lo >>> 1) | hiLo) ^ mask;
return new types_1.Int64(hi, lo);
}
readVarint32() {
return this.readVarint64().toNumber();
}
readVarint64() {
let rsize = 0;
let lo = 0;
let hi = 0;
let shift = 0;
while (true) {
const b = this.transport.readByte();
rsize++;
if (shift <= 25) {
lo = lo | ((b & 0x7f) << shift);
}
else if (25 < shift && shift < 32) {
lo = lo | ((b & 0x7f) << shift);
hi = hi | ((b & 0x7f) >>> (32 - shift));
}
else {
hi = hi | ((b & 0x7f) << (shift - 32));
}
shift += 7;
if (!(b & 0x80)) {
break;
}
if (rsize >= 10) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.INVALID_DATA, `Variable-length int over 10 bytes.`);
}
}
return new types_1.Int64(hi, lo);
}
writeFieldBeginInternal(name, fieldType, fieldId, typeOverride) {
const typeToWrite = typeOverride === -1 ? this.getCompactType(fieldType) : typeOverride;
if (fieldId > this._lastFieldId && fieldId - this._lastFieldId <= 15) {
this.writeByte(((fieldId - this._lastFieldId) << 4) | typeToWrite);
}
else {
this.writeByte(typeToWrite);
this.writeI16(fieldId);
}
this._lastFieldId = fieldId;
}
writeCollectionBegin(elementType, size) {
if (size <= 14) {
this.writeByte((size << 4) | this.getCompactType(elementType));
}
else {
this.writeByte(0xf0 | this.getCompactType(elementType));
this.writeVarint32(size);
}
}
writeVarint32(i32) {
const buf = Buffer.alloc(5);
let wsize = 0;
while (true) {
if ((i32 & ~0x7f) === 0) {
buf[wsize++] = i32;
break;
}
else {
buf[wsize++] = (i32 & 0x7f) | 0x80;
i32 = i32 >>> 7;
}
}
const wbuf = Buffer.alloc(wsize);
buf.copy(wbuf, 0, 0, wsize);
this.transport.write(wbuf);
}
writeVarint64(i64) {
if (typeof i64 === 'number') {
i64 = new types_1.Int64(i64);
}
if (!(i64 instanceof types_1.Int64)) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.INVALID_DATA, `Expected Int64 or Number, found: ${i64}`);
}
else {
const buf = Buffer.alloc(10);
let wsize = 0;
let hi = i64.buffer.readUInt32BE(0);
let lo = i64.buffer.readUInt32BE(4);
let mask = 0;
while (true) {
if ((lo & ~0x7f) === 0 && hi === 0) {
buf[wsize++] = lo;
break;
}
else {
buf[wsize++] = (lo & 0x7f) | 0x80;
mask = hi << 25;
lo = lo >>> 7;
hi = hi >>> 7;
lo = lo | mask;
}
}
const wbuf = Buffer.alloc(wsize);
buf.copy(wbuf, 0, 0, wsize);
this.transport.write(wbuf);
}
}
i64ToZigzag(i64) {
if (typeof i64 === 'string') {
i64 = types_1.Int64.fromDecimalString(i64);
}
else if (typeof i64 === 'number') {
i64 = new types_1.Int64(i64);
}
if (!(0, types_1.isInt64)(i64)) {
throw new errors_1.TProtocolException(errors_1.TProtocolExceptionType.INVALID_DATA, `Expected Int64, number, or decimal string but found type ${typeof i64}`);
}
else {
let hi = i64.buffer.readUInt32BE(0);
let lo = i64.buffer.readUInt32BE(4);
const sign = hi >>> 31;
hi = ((hi << 1) | (lo >>> 31)) ^ (!!sign ? 0xffffffff : 0);
lo = (lo << 1) ^ (!!sign ? 0xffffffff : 0);
return new types_1.Int64(hi, lo);
}
}
i32ToZigzag(i32) {
return (i32 << 1) ^ (i32 & 0x80000000 ? 0xffffffff : 0);
}
}
exports.CompactProtocol = CompactProtocol;
//# sourceMappingURL=CompactProtocol.js.map