import is from "is_js" ; import { string as varString , bool as varBool , bytes as varBytes , varint , } from "protocol-buffers-encodings" ; import typeToTyp3 from "../../utils/encoderHelper" ; const decoder = ( bytes , varType ) => { const val = varType . decode ( bytes , 0 ) ; const offset = varType . encodingLength ( val ) ; return { val , offset } ; } ; category param { Buffer } bytes param { Object } type returns { Object } export const unMarshalBinaryLengthPrefixed = ( bytes , type ) : any => { if ( bytes . length === 0 ) throw new TypeError ( "Cannot decode empty bytes" ) ; const { offset : len } = decoder ( bytes , varint ) ; if ( len < 0 ) throw new Error ( `Error reading msg byte-length prefix: got code ${ len }` ) ; bytes = bytes . slice ( len ) ; return unMarshalBinaryBare ( bytes , type ) ; } ; category param { Buffer } bytes param { Object } type returns { Object } export const unMarshalBinaryBare = ( bytes , type ) : any => { if ( ! is . object ( type ) ) throw new TypeError ( "type should be object" ) ; if ( ! Buffer . isBuffer ( bytes ) ) throw new TypeError ( "bytes must be buffer" ) ; if ( is . array ( type ) ) { if ( ! is . object ( type [ 0 ] ) ) throw new TypeError ( "type should be object" ) ; return decodeArrayBinary ( bytes , type [ 0 ] ) ; } return decodeBinary ( bytes , type ) ; } ; const decodeBinary = ( bytes , type , isLengthPrefixed ? ) : any => { if ( Buffer . isBuffer ( type ) ) { return decoder ( bytes , varBytes ) ; } if ( is . array ( type ) ) { return decodeArrayBinary ( bytes , type ) ; } if ( is . number ( type ) ) { return decoder ( bytes , varint ) ; } if ( is . boolean ( type ) ) { return decoder ( bytes , varBool ) ; } if ( is . string ( type ) ) { return decoder ( bytes , varString ) ; } if ( is . object ( type ) ) { return decodeObjectBinary ( bytes , type , isLengthPrefixed ) ; } return ; } ; const setDefaultValue = ( type , key ) => { if ( is . object ( type [ key ] ) ) type [ key ] = null ; if ( is . number ( type [ key ] ) ) type [ key ] = 0 ; if ( is . boolean ( type [ key ] ) ) type [ key ] = false ; if ( is . string ( type [ key ] ) ) type [ key ] = "" ; } ; const decodeObjectBinary = ( bytes , type , isLengthPrefixed ? ) => { let objectOffset = 0 ; if ( isLengthPrefixed ) { const { offset : len } = decoder ( bytes , varint ) ; bytes = bytes . slice ( len ) ; objectOffset += len ; } if ( type . aminoPrefix ) { bytes = bytes . slice ( 4 ) ; objectOffset += 4 ; } let lastFieldNum = 0 ; const keys = Object . keys ( type ) . filter ( ( key ) => key !== "aminoPrefix" ) ; keys . forEach ( ( key , index ) => { if ( is . array ( type [ key ] ) ) { const { offset , val } = decodeArrayBinary ( bytes , type [ key ] [ 0 ] ) ; objectOffset += offset ; type [ key ] = val ; bytes = bytes . slice ( offset ) ; } else { const { fieldNum , typ } = decodeFieldNumberAndTyp3 ( bytes ) ; if ( index + 1 !== fieldNum || fieldNum < 0 ) { setDefaultValue ( type , key ) ; return ; } if ( fieldNum <= lastFieldNum ) { throw new Error ( `encountered fieldNum: ${ fieldNum }, but we have already seen fnum: ${ lastFieldNum }` ) ; } lastFieldNum = fieldNum ; if ( index + 1 !== fieldNum ) { throw new Error ( "field number is not expected" ) ; } const typeWanted = typeToTyp3 ( type [ key ] ) ; if ( typ !== typeWanted ) { throw new Error ( "field type is not expected" ) ; } bytes = bytes . slice ( 1 ) ; const { val , offset } = decodeBinary ( bytes , type [ key ] , true ) ; type [ key ] = val ; bytes = bytes . slice ( offset ) ; objectOffset += offset + 1 ; } } ) ; return { val : type , offset : objectOffset } ; } ; const decodeArrayBinary = ( bytes , type ) => { const arr = [ ] ; let arrayOffset = 0 ; let { fieldNum : fieldNumber } = decodeFieldNumberAndTyp3 ( bytes ) ; while ( true ) { const { fieldNum } = decodeFieldNumberAndTyp3 ( bytes ) ; if ( fieldNum !== fieldNumber || fieldNum < 0 ) break ; bytes = bytes . slice ( 1 ) ; if ( bytes . length > 0 && bytes . readUInt8 ( 0 ) === 0x00 ) continue ; const { offset , val } = decodeBinary ( bytes , type , true ) ; arr . push ( { ... val } ) ; bytes = bytes . slice ( offset + 1 ) ; arrayOffset += offset + 1 ; fieldNumber = fieldNum ; } return { val : arr , offset : arrayOffset } ; } ; export const decodeFieldNumberAndTyp3 = ( bytes ) : any => { if ( bytes . length < 2 ) { return { fieldNum : - 1 } ; } const { val } = decoder ( bytes , varint ) ; const typ = val & 7 ; const fieldNum = val >> 3 ; if ( fieldNum > 1 << ( 29 - 1 ) ) { throw new Error ( `invalid field num ${ fieldNum }` ) ; } return { fieldNum , typ } ; } ;