UNPKG

@creditkarma/thrift-server-core

Version:
557 lines 18.8 kB
"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