'use strict' ; import { base64decode , base64encode } from '../platform/util.js' ; export class Buf extends Uint8Array { public static concat = ( arrays ) : Buf => { const result = new Uint8Array ( arrays . reduce ( ( totalLen , arr ) => totalLen + arr . length , 0 ) ) ; let offset = 0 ; for ( const array of arrays ) { result . set ( array , offset ) ; offset += array . length ; } return Buf . fromUint8 ( result ) ; } ; public static with = ( input ) : Buf => { if ( input instanceof Buf ) { return input ; } else if ( input instanceof Uint8Array ) { return Buf . fromUint8 ( input ) ; } else { return Buf . fromUtfStr ( input ) ; } } ; public static fromUint8 = ( u8a ) : Buf => { return new Buf ( u8a ) ; } ; public static fromRawBytesStr = ( rawStr ) : Buf => { const length = rawStr . length ; const buf = new Buf ( length ) ; for ( let i = 0 ; i < length ; i ++ ) { buf [ i ] = rawStr . charCodeAt ( i ) ; } return buf ; } ; public static fromUtfStr = ( utfStr ) : Buf => { let codePoint ; const length = utfStr . length ; let leadSurrogate ; const bytes = [ ] ; for ( let i = 0 ; i < length ; ++ i ) { codePoint = utfStr . charCodeAt ( i ) ; if ( codePoint > 0xD7FF && codePoint < 0xE000 ) { if ( ! leadSurrogate ) { if ( codePoint > 0xDBFF ) { bytes . push ( 0xEF , 0xBF , 0xBD ) ; continue ; } else if ( i + 1 === length ) { bytes . push ( 0xEF , 0xBF , 0xBD ) ; continue ; } leadSurrogate = codePoint ; continue ; } if ( codePoint < 0xDC00 ) { bytes . push ( 0xEF , 0xBF , 0xBD ) ; leadSurrogate = codePoint ; continue ; } codePoint = ( leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 ) + 0x10000 ; } else if ( leadSurrogate ) { bytes . push ( 0xEF , 0xBF , 0xBD ) ; } leadSurrogate = undefined ; if ( codePoint < 0x80 ) { bytes . push ( codePoint ) ; } else if ( codePoint < 0x800 ) { bytes . push ( codePoint >> 0x6 | 0xC0 , codePoint & 0x3F | 0x80 ) ; } else if ( codePoint < 0x10000 ) { bytes . push ( codePoint >> 0xC | 0xE0 , codePoint >> 0x6 & 0x3F | 0x80 , codePoint & 0x3F | 0x80 ) ; } else if ( codePoint < 0x110000 ) { bytes . push ( codePoint >> 0x12 | 0xF0 , codePoint >> 0xC & 0x3F | 0x80 , codePoint >> 0x6 & 0x3F | 0x80 , codePoint & 0x3F | 0x80 ) ; } else { throw new Error ( 'Invalid code point' ) ; } } return new Buf ( bytes ) ; } ; public static fromBase64Str = ( b64str ) : Buf => { return Buf . fromRawBytesStr ( base64decode ( b64str ) ) ; } ; public static fromBase64UrlStr = ( b64UrlStr ) : Buf => { return Buf . fromBase64Str ( b64UrlStr . replace ( /-/g , '+' ) . replace ( /_/g , '/' ) ) ; } ; public toString = ( mode = 'inform' ) : string => { return this . toUtfStr ( mode ) ; } ; public toUtfStr = ( mode = 'inform' ) : string => { const length = this . length ; let bytesLeftInChar = 0 ; let utf8string = '' ; let binaryChar = '' ; for ( let i = 0 ; i < length ; i ++ ) { if ( this [ i ] < 128 ) { if ( bytesLeftInChar ) { utf8string += String . fromCharCode ( this [ i - 1 ] ) ; } bytesLeftInChar = 0 ; binaryChar = '' ; utf8string += String . fromCharCode ( this [ i ] ) ; } else { if ( ! bytesLeftInChar ) { if ( this [ i ] >= 128 && this [ i ] < 192 ) { utf8string += String . fromCharCode ( this [ i ] ) ; } else if ( this [ i ] >= 192 && this [ i ] < 224 ) { bytesLeftInChar = 1 ; binaryChar = this [ i ] . toString ( 2 ) . substr ( 3 ) ; } else if ( this [ i ] >= 224 && this [ i ] < 240 ) { bytesLeftInChar = 2 ; binaryChar = this [ i ] . toString ( 2 ) . substr ( 4 ) ; } else if ( this [ i ] >= 240 && this [ i ] < 248 ) { bytesLeftInChar = 3 ; binaryChar = this [ i ] . toString ( 2 ) . substr ( 5 ) ; } else if ( this [ i ] >= 248 && this [ i ] < 252 ) { bytesLeftInChar = 4 ; binaryChar = this [ i ] . toString ( 2 ) . substr ( 6 ) ; } else if ( this [ i ] >= 252 && this [ i ] < 254 ) { bytesLeftInChar = 5 ; binaryChar = this [ i ] . toString ( 2 ) . substr ( 7 ) ; } else { if ( mode === 'strict' || mode === 'inform' ) { const e = new Error ( 'Buf.toUtfStr: invalid utf-8 character beginning byte: ' + this [ i ] ) ; if ( mode === 'strict' ) { throw e ; } console . info ( e ) ; } } } else { binaryChar += this [ i ] . toString ( 2 ) . substr ( 2 ) ; bytesLeftInChar -- ; } if ( binaryChar && ! bytesLeftInChar ) { try { const codePoint = parseInt ( binaryChar , 2 ) ; utf8string += codePoint >= 0x10000 ? String . fromCodePoint ( codePoint ) : String . fromCharCode ( codePoint ) ; } catch ( e ) { if ( mode === 'inform' ) { console . log ( e ) ; } else if ( mode === 'strict' ) { throw e ; } } binaryChar = '' ; } } } return utf8string ; } ; public toRawBytesStr = ( ) : string => { const chunkSize = 0x8000 ; const length = this . length ; const chars = [ ] ; for ( let i = 0 ; i < length ; i += chunkSize ) { chars . push ( String . fromCharCode . apply ( undefined , Array . from ( this . subarray ( i , i + chunkSize ) ) ) ) ; } return chars . join ( '' ) ; } ; public toHexStr = ( ) : string => { const chars = [ ] ; for ( const v of this . values ( ) ) { chars . push ( ( ( '00' + v . toString ( 16 ) ) . slice ( - 2 ) ) . toUpperCase ( ) ) ; } return chars . join ( '' ) ; } ; public toBase64Str = ( ) : string => { return base64encode ( this . toRawBytesStr ( ) ) ; } ; public toBase64UrlStr = ( ) : string => { return this . toBase64Str ( ) . replace ( /\+/g , '-' ) . replace ( /\//g , '_' ) . replace ( /=+$/ , '' ) ; } ; }