/********************************************************************************
 * Copyright (C) 2020 TypeFox and others.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the Eclipse
 * Public License v. 2.0 are satisfied: GNU General Public License, version 2
 * with the GNU Classpath Exception which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 ********************************************************************************/ import { Buffer as SaferBuffer } from 'safer-buffer' ; import * as iconv from 'iconv-lite' ; import * as streams from './stream' ; const hasBuffer = ( typeof Buffer !== 'undefined' ) ; const hasTextEncoder = ( typeof TextEncoder !== 'undefined' ) ; const hasTextDecoder = ( typeof TextDecoder !== 'undefined' ) ; let textEncoder ; let textDecoder ; export class BinaryBuffer { static alloc ( byteLength ) { if ( hasBuffer ) { return new BinaryBuffer ( Buffer . allocUnsafe ( byteLength ) ) ; } else { return new BinaryBuffer ( new Uint8Array ( byteLength ) ) ; } } static wrap ( actual ) { if ( hasBuffer && ! ( Buffer . isBuffer ( actual ) ) ) { actual = Buffer . from ( actual . buffer , actual . byteOffset , actual . byteLength ) ; } return new BinaryBuffer ( actual ) ; } static fromString ( source ) { if ( hasBuffer ) { return new BinaryBuffer ( Buffer . from ( source ) ) ; } else if ( hasTextEncoder ) { if ( ! textEncoder ) { textEncoder = new TextEncoder ( ) ; } return new BinaryBuffer ( textEncoder . encode ( source ) ) ; } else { return new BinaryBuffer ( iconv . encode ( source , 'utf8' ) ) ; } } static concat ( buffers , totalLength ? ) { if ( typeof totalLength === 'undefined' ) { totalLength = 0 ; for ( let i = 0 , len = buffers . length ; i < len ; i ++ ) { totalLength += buffers [ i ] . byteLength ; } } const ret = BinaryBuffer . alloc ( totalLength ) ; let offset = 0 ; for ( let i = 0 , len = buffers . length ; i < len ; i ++ ) { const element = buffers [ i ] ; ret . set ( element , offset ) ; offset += element . byteLength ; } return ret ; } readonly buffer : Uint8Array ; readonly byteLength : number ; private constructor ( buffer ) { this . buffer = buffer ; this . byteLength = this . buffer . byteLength ; } toString ( ) { if ( hasBuffer ) { return this . buffer . toString ( ) ; } else if ( hasTextDecoder ) { if ( ! textDecoder ) { textDecoder = new TextDecoder ( ) ; } return textDecoder . decode ( this . buffer ) ; } else { return iconv . decode ( SaferBuffer . from ( this . buffer ) , 'utf8' ) ; } } slice ( start ? , end ? ) { return new BinaryBuffer ( this . buffer . subarray ( start , end ) ) ; } set ( array , offset ? ) ; set ( array , offset ? ) ; set ( array , offset ? ) { if ( array instanceof BinaryBuffer ) { this . buffer . set ( array . buffer , offset ) ; } else { this . buffer . set ( array , offset ) ; } } readUInt32BE ( offset ) { return ( this . buffer [ offset ] * 2 ** 24 + this . buffer [ offset + 1 ] * 2 ** 16 + this . buffer [ offset + 2 ] * 2 ** 8 + this . buffer [ offset + 3 ] ) ; } writeUInt32BE ( value , offset ) { this . buffer [ offset + 3 ] = value ; value = value >>> 8 ; this . buffer [ offset + 2 ] = value ; value = value >>> 8 ; this . buffer [ offset + 1 ] = value ; value = value >>> 8 ; this . buffer [ offset ] = value ; } readUInt32LE ( offset ) { return ( ( ( this . buffer [ offset + 0 ] << 0 ) >>> 0 ) | ( ( this . buffer [ offset + 1 ] << 8 ) >>> 0 ) | ( ( this . buffer [ offset + 2 ] << 16 ) >>> 0 ) | ( ( this . buffer [ offset + 3 ] << 24 ) >>> 0 ) ) ; } writeUInt32LE ( value , offset ) { this . buffer [ offset + 0 ] = ( value & 0b11111111 ) ; value = value >>> 8 ; this . buffer [ offset + 1 ] = ( value & 0b11111111 ) ; value = value >>> 8 ; this . buffer [ offset + 2 ] = ( value & 0b11111111 ) ; value = value >>> 8 ; this . buffer [ offset + 3 ] = ( value & 0b11111111 ) ; } readUInt8 ( offset ) { return this . buffer [ offset ] ; } writeUInt8 ( value , offset ) { this . buffer [ offset ] = value ; } } export interface BinaryBufferReadable extends streams . Readable < BinaryBuffer > { } export namespace BinaryBufferReadable { export function toBuffer ( readable ) { return streams . consumeReadable < BinaryBuffer > ( readable , chunks => BinaryBuffer . concat ( chunks ) ) ; } export function fromBuffer ( buffer ) { return streams . toReadable < BinaryBuffer > ( buffer ) ; } export function fromReadable ( readable ) { return { read ( ) { const value = readable . read ( ) ; if ( typeof value === 'string' ) { return BinaryBuffer . fromString ( value ) ; } return null ; } } ; } } export interface BinaryBufferReadableStream extends streams . ReadableStream < BinaryBuffer > { } export namespace BinaryBufferReadableStream { export function toBuffer ( stream ) { return streams . consumeStream < BinaryBuffer > ( stream , chunks => BinaryBuffer . concat ( chunks ) ) ; } export function fromBuffer ( buffer ) { return streams . toStream < BinaryBuffer > ( buffer , chunks => BinaryBuffer . concat ( chunks ) ) ; } } export interface BinaryBufferReadableBufferedStream extends streams . ReadableBufferedStream < BinaryBuffer > { } export namespace BinaryBufferReadableBufferedStream { export async function toBuffer ( bufferedStream ) { if ( bufferedStream . ended ) { return BinaryBuffer . concat ( bufferedStream . buffer ) ; } return BinaryBuffer . concat ( [ ... bufferedStream . buffer , await BinaryBufferReadableStream . toBuffer ( bufferedStream . stream ) ] ) ; } } export interface BinaryBufferWriteableStream extends streams . WriteableStream < BinaryBuffer > { } export namespace BinaryBufferWriteableStream { export function create ( options ? ) { return streams . newWriteableStream < BinaryBuffer > ( chunks => BinaryBuffer . concat ( chunks ) , options ) ; } }