import { UnsupportedInputError } from './UnsupportedInputError' ; export class Converter { public static arrayBufferViewToStringUTF8 ( arrayBufferView ) { try { const string = this . arrayBufferViewToString ( arrayBufferView ) ; return string ; } catch ( error ) { if ( typeof window === 'object' && 'TextDecoder' in window ) { return new TextDecoder ( 'utf-8' ) . decode ( arrayBufferView ) ; } return Converter . arrayBufferViewToBaselineString ( arrayBufferView ) ; } } public static arrayBufferViewToBaselineString ( arrayBufferView ) { const chunkSize = 32000 ; const array = Array . from ( arrayBufferView ) ; const chunkCount = Math . ceil ( array . length / chunkSize ) ; return Array . from ( { length : chunkCount } , ( value , index ) => String . fromCharCode . apply ( null , array . slice ( index * chunkSize , ( index + 1 ) * chunkSize ) ) ) . join ( '' ) ; } public static jsonToArrayBufferView ( objectSource ) { const length = Object . keys ( objectSource ) . length ; const arrayBufferView = new Uint8Array ( length ) ; for ( const key in objectSource ) { if ( objectSource . hasOwnProperty ( key ) ) { arrayBufferView [ parseInt ( key , 10 ) ] = objectSource [ key ] ; } } return arrayBufferView ; } public static numberArrayToArrayBufferView ( array ) { const arrayBuffer = new ArrayBuffer ( array . length ) ; const arrayBufferView = new Uint8Array ( arrayBuffer ) ; for ( let i = 0 ; i < arrayBufferView . length ; i ++ ) { arrayBufferView [ i ] = array [ i ] ; } return arrayBufferView ; } public static stringToArrayBufferViewUTF16 ( data ) { const arrayBuffer = new ArrayBuffer ( data . length * 2 ) ; const arrayBufferView = new Uint16Array ( arrayBuffer ) ; for ( let i = 0 , strLen = data . length ; i < strLen ; i ++ ) { arrayBufferView [ i ] = data . charCodeAt ( i ) ; } return arrayBufferView ; } public static toArrayBufferView ( data ) { switch ( data . constructor . name ) { case 'ArrayBuffer' : return new Uint8Array ( data as ArrayBuffer ) ; case 'Array' : case 'Buffer' : return this . numberArrayToArrayBufferView ( data as number [ ] | Buffer ) ; case 'Number' : return this . stringToArrayBufferViewUTF8 ( data . toString ( ) ) ; case 'String' : return this . stringToArrayBufferViewUTF8 ( data as string ) ; case 'Uint8Array' : return data as Uint8Array ; default : throw new UnsupportedInputError ( `${ data . constructor . name } is unsupported.` + ` Please provide a type of 'ArrayBuffer', 'Array', 'Buffer', 'Number', 'String' or 'Uint8Array'.` ) ; } } public static toString ( data ) { switch ( data . constructor . name ) { case 'Array' : const arrayBufferView = this . numberArrayToArrayBufferView ( data as number [ ] ) ; return this . arrayBufferViewToStringUTF8 ( arrayBufferView ) ; case 'Number' : return data . toString ( ) ; case 'String' : return data as string ; case 'Uint8Array' : return this . arrayBufferViewToStringUTF8 ( data as Uint8Array ) ; default : throw new UnsupportedInputError ( `${ data . constructor . name } is unsupported.` + ` Please provide a 'String', 'Uint8Array' or 'Array'.` ) ; } } public static stringToArrayBufferViewUTF8 ( data ) { const escapedString = encodeURIComponent ( data ) ; const binaryString = escapedString . replace ( /%([0-9A-F]{2})/g , ( match , position ) => { const code = parseInt ( `0x${ position }` , 16 ) ; return String . fromCharCode ( code ) ; } ) ; const arrayBufferView = new Uint8Array ( binaryString . length ) ; binaryString . split ( '' ) . forEach ( ( character , index ) => { arrayBufferView [ index ] = character . charCodeAt ( 0 ) ; } ) ; return arrayBufferView ; } private static arrayBufferViewToString ( arrayBufferView ) { const binaryString = Converter . arrayBufferViewToBaselineString ( arrayBufferView ) ; const escapedString = binaryString . replace ( /(.)/g , ( match ) => { const code = match . charCodeAt ( 0 ) . toString ( 16 ) . toUpperCase ( ) ; if ( code . length < 2 ) { return `0${ code }` ; } return `%${ code }` ; } ) ; return decodeURIComponent ( escapedString ) ; } }