/** * Copyright (c) 2018-2019 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author Alexander Rose * @author David Sehnal */ import { defaults, noop } from 'mol-util'; import { SimpleBuffer } from './simple-buffer'; // only import 'fs' in node.js const fs = typeof document === 'undefined' ? require('fs') as typeof import('fs') : void 0; export interface FileHandle { /** * Asynchronously reads data, returning buffer and number of bytes read * * @param position The offset from the beginning of the file from which data should be read. * @param sizeOrBuffer The buffer the data will be read from. * @param length The number of bytes to read. * @param byteOffset The offset in the buffer at which to start writing. */ readBuffer(position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number): Promise<{ bytesRead: number, buffer: SimpleBuffer }> /** * Asynchronously writes buffer, returning the number of bytes written. * * @param position — The offset from the beginning of the file where this data should be written. * @param buffer - The buffer data to be written. * @param length — The number of bytes to write. If not supplied, defaults to buffer.length */ writeBuffer(position: number, buffer: SimpleBuffer, length?: number): Promise /** * Synchronously writes buffer, returning the number of bytes written. * * @param position — The offset from the beginning of the file where this data should be written. * @param buffer - The buffer data to be written. * @param length — The number of bytes to write. If not supplied, defaults to buffer.length */ writeBufferSync(position: number, buffer: SimpleBuffer, length?: number): number /** Closes a file handle */ close(): void } export namespace FileHandle { export function fromBuffer(buffer: SimpleBuffer): FileHandle { return { readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, size?: number, byteOffset?: number) => { let bytesRead: number let outBuffer: SimpleBuffer if (typeof sizeOrBuffer === 'number') { size = defaults(size, sizeOrBuffer) const start = position const end = Math.min(buffer.length, start + size) bytesRead = end - start outBuffer = SimpleBuffer.fromUint8Array(new Uint8Array(buffer.buffer, start, end - start)) } else { size = defaults(size, sizeOrBuffer.length) const start = position const end = Math.min(buffer.length, start + size) sizeOrBuffer.set(buffer.subarray(start, end), byteOffset) bytesRead = end - start outBuffer = sizeOrBuffer } if (size !== bytesRead) { console.warn(`byteCount ${size} and bytesRead ${bytesRead} differ`) } return Promise.resolve({ bytesRead, buffer: outBuffer }) }, writeBuffer: (position: number, buffer: SimpleBuffer, length?: number) => { length = defaults(length, buffer.length) console.error('.writeBuffer not implemented for FileHandle.fromBuffer') return Promise.resolve(0) }, writeBufferSync: (position: number, buffer: SimpleBuffer, length?: number, ) => { length = defaults(length, buffer.length) console.error('.writeSync not implemented for FileHandle.fromBuffer') return 0 }, close: noop } } export function fromDescriptor(file: number): FileHandle { if (fs === undefined) throw new Error('fs module not available') return { readBuffer: (position: number, sizeOrBuffer: SimpleBuffer | number, length?: number, byteOffset?: number) => { return new Promise((res, rej) => { let outBuffer: SimpleBuffer if (typeof sizeOrBuffer === 'number') { byteOffset = defaults(byteOffset, 0) length = defaults(length, sizeOrBuffer) outBuffer = SimpleBuffer.fromArrayBuffer(new ArrayBuffer(sizeOrBuffer)); } else { byteOffset = defaults(byteOffset, 0) length = defaults(length, sizeOrBuffer.length) outBuffer = sizeOrBuffer } fs.read(file, outBuffer, byteOffset, length, position, (err, bytesRead, buffer) => { if (err) { rej(err); return; } if (length !== bytesRead) { console.warn(`byteCount ${length} and bytesRead ${bytesRead} differ`) } res({ bytesRead, buffer }); }); }) }, writeBuffer: (position: number, buffer: SimpleBuffer, length?: number) => { length = defaults(length, buffer.length) return new Promise((res, rej) => { fs.write(file, buffer, 0, length, position, (err, written) => { if (err) rej(err); else res(written); }) }) }, writeBufferSync: (position: number, buffer: Uint8Array, length?: number) => { length = defaults(length, buffer.length) return fs.writeSync(file, buffer, 0, length, position); }, close: () => { try { if (file !== void 0) fs.close(file, noop); } catch (e) { } } } } }