/**
* @author [Tristan Valcke]{@link https://github.com/Itee}
* @license [BSD-3-Clause]{@link https://opensource.org/licenses/BSD-3-Clause}
*
* @file Todo
*
* @example Todo
*
*/
import * as globalBuffer from 'buffer'
import fs from 'fs'
import {
isNull,
isString,
isUndefined
} from 'itee-validators'
import { Writable } from 'stream'
/* Writable memory stream */
class MemoryWriteStream extends Writable {
constructor ( options ) {
super( options )
const bufferSize = options.bufferSize || globalBuffer.kStringMaxLength
this.memoryBuffer = Buffer.alloc( bufferSize )
this.offset = 0
}
_final ( callback ) {
callback()
}
_write ( chunk, encoding, callback ) {
// our memory store stores things in buffers
const buffer = ( Buffer.isBuffer( chunk ) ) ? chunk : new Buffer( chunk, encoding )
// concat to the buffer already there
for ( let byteIndex = 0, numberOfByte = buffer.length ; byteIndex < numberOfByte ; byteIndex++ ) {
this.memoryBuffer[ this.offset ] = buffer[ byteIndex ]
this.offset++
}
// Next
callback()
}
_writev ( chunks, callback ) {
for ( let chunkIndex = 0, numberOfChunks = chunks.length ; chunkIndex < numberOfChunks ; chunkIndex++ ) {
this.memoryBuffer = Buffer.concat( [ this.memoryBuffer, chunks[ chunkIndex ] ] )
}
// Next
callback()
}
_releaseMemory () {
this.memoryBuffer = null
}
toArrayBuffer () {
const buffer = this.memoryBuffer
const arrayBuffer = new ArrayBuffer( buffer.length )
const view = new Uint8Array( arrayBuffer )
for ( let i = 0 ; i < buffer.length ; ++i ) {
view[ i ] = buffer[ i ]
}
this._releaseMemory()
return arrayBuffer
}
toJSON () {
return JSON.parse( this.toString() )
}
toString () {
const string = this.memoryBuffer.toString()
this._releaseMemory()
return string
}
}
////////
class TAbstractFileConverter {
constructor ( parameters = {} ) {
const _parameters = {
...{
dumpType: TAbstractFileConverter.DumpType.ArrayBuffer
}, ...parameters
}
this.dumpType = _parameters.dumpType
this._isProcessing = false
this._queue = []
}
get dumpType () {
return this._dumpType
}
set dumpType ( value ) {
if ( isNull( value ) ) { throw new TypeError( 'Dump type cannot be null ! Expect a non empty string.' ) }
if ( isUndefined( value ) ) { throw new TypeError( 'Dump type cannot be undefined ! Expect a non empty string.' ) }
this._dumpType = value
}
setDumpType ( value ) {
this.dumpType = value
return this
}
convert ( file, parameters, onSuccess, onProgress, onError ) {
if ( !file ) {
onError( 'File cannot be null or empty, aborting file convertion !!!' )
return
}
this._queue.push( {
file,
parameters,
onSuccess,
onProgress,
onError
} )
this._processQueue()
}
_processQueue () {
if ( this._queue.length === 0 || this._isProcessing ) { return }
this._isProcessing = true
const self = this
const dataBloc = this._queue.shift()
const file = dataBloc.file
const parameters = dataBloc.parameters
const onSuccess = dataBloc.onSuccess
const onProgress = dataBloc.onProgress
const onError = dataBloc.onError
if ( isString( file ) ) {
self._dumpFileInMemoryAs(
self._dumpType,
file,
parameters,
_onDumpSuccess,
_onProcessProgress,
_onProcessError
)
} else {
const data = file.data
switch ( self._dumpType ) {
case TAbstractFileConverter.DumpType.ArrayBuffer: {
const bufferSize = data.length
const arrayBuffer = new ArrayBuffer( bufferSize )
const view = new Uint8Array( arrayBuffer )
for ( let i = 0 ; i < bufferSize ; ++i ) {
view[ i ] = data[ i ]
}
_onDumpSuccess( arrayBuffer )
}
break
case TAbstractFileConverter.DumpType.JSON:
_onDumpSuccess( JSON.parse( data.toString() ) )
break
case TAbstractFileConverter.DumpType.String:
_onDumpSuccess( data.toString() )
break
default:
throw new RangeError( `Invalid switch parameter: ${ self._dumpType }` )
}
}
function _onDumpSuccess ( data ) {
self._convert(
data,
parameters,
_onProcessSuccess,
_onProcessProgress,
_onProcessError
)
}
function _onProcessSuccess ( threeData ) {
onSuccess( threeData )
self._isProcessing = false
self._processQueue()
}
function _onProcessProgress ( progress ) {
onProgress( progress )
}
function _onProcessError ( error ) {
onError( error )
self._isProcessing = false
self._processQueue()
}
}
_dumpFileInMemoryAs ( dumpType, file, parameters, onSuccess, onProgress, onError ) {
let isOnError = false
const fileReadStream = fs.createReadStream( file )
fileReadStream.on( 'error', ( error ) => {
isOnError = true
onError( error )
} )
const fileSize = parseInt( parameters.fileSize )
const memoryWriteStream = new MemoryWriteStream( { bufferSize: fileSize } )
memoryWriteStream.on( 'error', ( error ) => {
isOnError = true
onError( error )
} )
memoryWriteStream.on( 'finish', () => {
if ( isOnError ) {
return
}
switch ( dumpType ) {
case TAbstractFileConverter.DumpType.ArrayBuffer:
onSuccess( memoryWriteStream.toArrayBuffer() )
break
case TAbstractFileConverter.DumpType.String:
onSuccess( memoryWriteStream.toString() )
break
case TAbstractFileConverter.DumpType.JSON:
onSuccess( memoryWriteStream.toJSON() )
break
default:
throw new RangeError( `Invalid switch parameter: ${ dumpType }` )
}
fileReadStream.unpipe()
fileReadStream.close()
memoryWriteStream.end()
} )
fileReadStream.pipe( memoryWriteStream )
}
_convert ( /*data, parameters, onSuccess, onProgress, onError*/ ) {}
}
TAbstractFileConverter.MAX_FILE_SIZE = 67108864
TAbstractFileConverter.DumpType = /*#__PURE__*/Object.freeze( {
ArrayBuffer: 0,
String: 1,
JSON: 2
} )
export {
TAbstractFileConverter,
MemoryWriteStream
}