/**
 * This implementation is largely taken from the Apache project and reimplemented in TypeScript.
 *
 * The orginal project can be found here:
 * https://github.com/apache/thrift/blob/master/lib/nodejs/lib/thrift/binary_protocol.js
 */ import * as binary from '../binary' ; import { TProtocolException , TProtocolExceptionType } from '../errors' ; import { TTransport } from '../transports' ; import { IInt64 , Int64 , isInt64 , IThriftField , IThriftList , IThriftMap , IThriftMessage , IThriftSet , IThriftStruct , LogFunction , MessageType , TType , } from '../types' ; import { defaultLogger } from '../logger' ; import { TProtocol } from './TProtocol' ; const VERSION_MASK = - 65536 ; const VERSION_1 = - 2147418112 ; const TYPE_MASK = 0x000000ff ; export class BinaryProtocol extends TProtocol { constructor ( trans , logger = defaultLogger ) { super ( trans , logger ) ; } public writeMessageBegin ( name , type , requestId ) { this . writeI32 ( VERSION_1 | type ) ; this . writeString ( name ) ; this . writeI32 ( requestId ) ; if ( this . requestId ) { this . logger ( [ 'warn' , 'BinaryProtocol' ] , `requestId already set: ${ name }` ) ; } else { this . requestId = requestId ; } } public writeMessageEnd ( ) { if ( this . requestId !== null ) { this . requestId = null ; } else { this . logger ( [ 'warn' , 'BinaryProtocol' ] , 'No requestId to unset' ) ; } } public writeStructBegin ( name ) { } public writeStructEnd ( ) { } public writeFieldBegin ( name , type , id ) { this . writeByte ( type ) ; this . writeI16 ( id ) ; } public writeFieldEnd ( ) { } public writeFieldStop ( ) { this . writeByte ( TType . STOP ) ; } public writeMapBegin ( keyType , valueType , size ) { this . writeByte ( keyType ) ; this . writeByte ( valueType ) ; this . writeI32 ( size ) ; } public writeMapEnd ( ) { } public writeListBegin ( elementType , size ) { this . writeByte ( elementType ) ; this . writeI32 ( size ) ; } public writeListEnd ( ) { } public writeSetBegin ( elementType , size ) { this . writeByte ( elementType ) ; this . writeI32 ( size ) ; } public writeSetEnd ( ) { } public writeBool ( bool ) { if ( bool ) { this . writeByte ( 1 ) ; } else { this . writeByte ( 0 ) ; } } public writeByte ( byte ) { const value = Number ( byte ) ; if ( ! isNaN ( value ) ) { this . transport . write ( Buffer . from ( [ value ] ) ) ; } else { throw new TypeError ( `Expected number but found type ${ typeof byte }` ) ; } } public writeI16 ( i16 ) { const value = Number ( i16 ) ; if ( ! isNaN ( value ) ) { this . transport . write ( binary . writeI16 ( Buffer . alloc ( 2 ) , value ) ) ; } else { throw new TypeError ( `Expected number but found type ${ typeof i16 }` ) ; } } public writeI32 ( i32 ) { const value = Number ( i32 ) ; if ( ! isNaN ( value ) ) { this . transport . write ( binary . writeI32 ( Buffer . alloc ( 4 ) , value ) ) ; } else { throw new TypeError ( `Expected number but found type ${ typeof i32 }` ) ; } } public writeI64 ( i64 ) { if ( typeof i64 === 'number' ) { i64 = new Int64 ( i64 ) ; } else if ( typeof i64 === 'string' ) { i64 = Int64 . fromDecimalString ( i64 ) ; } if ( isInt64 ( i64 ) ) { this . transport . write ( i64 . buffer ) ; } else { throw new TypeError ( `Expected Int64, number, or decimal string but found type ${ typeof i64 }` ) ; } } public writeDouble ( dub ) { const value = Number ( dub ) ; if ( ! isNaN ( value ) ) { this . transport . write ( binary . writeDouble ( Buffer . alloc ( 8 ) , value ) ) ; } else { throw new TypeError ( `Expected number but found type ${ typeof dub }` ) ; } } public writeStringOrBinary ( name , encoding , data ) { if ( typeof data === 'string' ) { this . writeI32 ( Buffer . byteLength ( data , encoding ) ) ; this . transport . write ( Buffer . from ( data , encoding ) ) ; } else if ( data instanceof Buffer ) { this . writeI32 ( data . length ) ; this . transport . write ( data ) ; } else { throw new TypeError ( `Argument of type ${ typeof data } should be buffer or string` ) ; } } public writeString ( data ) { this . writeStringOrBinary ( 'writeString' , 'utf8' , data ) ; } public writeBinary ( data ) { this . writeStringOrBinary ( 'writeBinary' , 'binary' , data ) ; } public readMessageBegin ( ) { const size = this . readI32 ( ) ; if ( size < 0 ) { const version = size & VERSION_MASK ; if ( version !== VERSION_1 ) { throw new TProtocolException ( TProtocolExceptionType . BAD_VERSION , `Bad version in readMessageBegin: ${ size }` ) ; } return { fieldName : this . readString ( ) , messageType : size & TYPE_MASK , requestId : this . readI32 ( ) , } ; } else { return { fieldName : this . transport . readString ( size ) , messageType : this . readByte ( ) , requestId : this . readI32 ( ) , } ; } } public readMessageEnd ( ) { } public readStructBegin ( ) { return { fieldName : '' } ; } public readStructEnd ( ) { } public readFieldBegin ( ) { const type = this . readByte ( ) ; if ( type === TType . STOP ) { return { fieldName : '' , fieldType : type , fieldId : 0 } ; } else { const id = this . readI16 ( ) ; return { fieldName : '' , fieldType : type , fieldId : id } ; } } public readFieldEnd ( ) { } public readMapBegin ( ) { const keyType = this . readByte ( ) ; const valueType = this . readByte ( ) ; const size = this . readI32 ( ) ; return { keyType , valueType , size } ; } public readMapEnd ( ) { } public readListBegin ( ) { const elementType = this . readByte ( ) ; const size = this . readI32 ( ) ; return { elementType , size } ; } public readListEnd ( ) { } public readSetBegin ( ) { const elementType = this . readByte ( ) ; const size = this . readI32 ( ) ; return { elementType , size } ; } public readSetEnd ( ) { } public readBool ( ) { const byte = this . readByte ( ) ; return byte !== 0 ; } public readByte ( ) { return this . transport . readByte ( ) ; } public readI16 ( ) { return this . transport . readI16 ( ) ; } public readI32 ( ) { return this . transport . readI32 ( ) ; } public readI64 ( ) { const buff = this . transport . read ( 8 ) ; return new Int64 ( buff ) ; } public readDouble ( ) { return this . transport . readDouble ( ) ; } public readBinary ( ) { const len = this . readI32 ( ) ; if ( len === 0 ) { return Buffer . alloc ( 0 ) ; } else if ( len < 0 ) { throw new TProtocolException ( TProtocolExceptionType . NEGATIVE_SIZE , 'Negative binary size' ) ; } else { return this . transport . read ( len ) ; } } public readString ( ) { const len = this . readI32 ( ) ; if ( len === 0 ) { return '' ; } if ( len < 0 ) { throw new TProtocolException ( TProtocolExceptionType . NEGATIVE_SIZE , 'Negative string size' ) ; } return this . transport . readString ( len ) ; } public getTransport ( ) { return this . transport ; } public skip ( type ) { switch ( type ) { case TType . STOP : return ; case TType . BOOL : this . readBool ( ) ; break ; case TType . BYTE : this . readByte ( ) ; break ; case TType . I16 : this . readI16 ( ) ; break ; case TType . I32 : this . readI32 ( ) ; break ; case TType . I64 : this . readI64 ( ) ; break ; case TType . DOUBLE : this . readDouble ( ) ; break ; case TType . STRING : this . readString ( ) ; break ; case TType . STRUCT : this . readStructBegin ( ) ; while ( true ) { const fieldBegin = this . readFieldBegin ( ) ; if ( fieldBegin . fieldType === TType . STOP ) { break ; } this . skip ( fieldBegin . fieldType ) ; this . readFieldEnd ( ) ; } this . readStructEnd ( ) ; break ; case 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 TType . SET : const setBegin = this . readSetBegin ( ) ; for ( let i2 = 0 ; i2 < setBegin . size ; ++ i2 ) { this . skip ( setBegin . elementType ) ; } this . readSetEnd ( ) ; break ; case 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 ) ; } } }