UNPKG

genomic-reader

Version:

A Typescript library for reading BigWig, BigBed, 2bit, and Bam files. Capable of streaming. For use in the browser or on Node.js.

209 lines 8.28 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.BufferedDataLoader = exports.FileFormatError = exports.IOError = exports.DataMissingError = exports.OutOfRangeError = exports.ErrorType = void 0; var ErrorType; (function (ErrorType) { ErrorType["OUT_OF_RANGE"] = "OUT_OF_RANGE"; ErrorType["DATA_MISSING"] = "DATA_MISSING"; ErrorType["IO"] = "IO"; ErrorType["FILE_FORMAT"] = "FILE_FORMAT"; })(ErrorType = exports.ErrorType || (exports.ErrorType = {})); ; class OutOfRangeError extends Error { constructor(resource, start, size) { super(`Request on ${resource} out of range. Range given: ${start}-${size || ''}`); this.resource = resource; this.start = start; this.size = size; this.errortype = ErrorType.OUT_OF_RANGE; } } exports.OutOfRangeError = OutOfRangeError; class DataMissingError extends Error { constructor(chromosome) { super(`Given chromosome ${chromosome} not found in file header chromosome tree`); this.chromosome = chromosome; this.errortype = ErrorType.DATA_MISSING; } } exports.DataMissingError = DataMissingError; class IOError extends Error { constructor(message) { super(message); this.message = message; this.errortype = ErrorType.IO; } } exports.IOError = IOError; class FileFormatError extends Error { constructor(message) { super(message); this.message = message; this.errortype = ErrorType.FILE_FORMAT; } } exports.FileFormatError = FileFormatError; class BufferedDataLoader { constructor(dataLoader, bufferSize, streamMode = false) { this.dataLoader = dataLoader; this.bufferSize = bufferSize; this.streamMode = streamMode; } load(start, size) { return __awaiter(this, void 0, void 0, function* () { if (!this.bufferContainsData(start, size)) { if (!this.streamMode) { yield this.loadDataIntoBuffer(start, size); } else { yield this.streamDataIntoBuffer(start, size); } } return yield this.getDataFromBuffer(start, size); }); } loadDataIntoBuffer(start, size) { return __awaiter(this, void 0, void 0, function* () { let data; try { const loadEnd = this.bufferSize !== undefined ? Math.max(this.bufferSize, size) : undefined; data = yield this.dataLoader.load(start, loadEnd); } catch (e) { if (e instanceof OutOfRangeError) { data = yield this.dataLoader.load(start); } else { throw e; } } this.buffer = { data: data, start: start }; }); } streamDataIntoBuffer(start, size) { return __awaiter(this, void 0, void 0, function* () { if (this.dataLoader.loadStream === undefined) { throw Error("Stream mode enabled, but DataLoader loadStream function not defined"); } if (this.stream !== undefined) { this.stream.destroy(); this.stream = undefined; } try { const loadEnd = this.bufferSize !== undefined ? Math.max(this.bufferSize, size) : undefined; this.stream = yield this.dataLoader.loadStream(start, loadEnd); } catch (e) { if (e instanceof OutOfRangeError) { this.stream = yield this.dataLoader.loadStream(start); } else { throw e; } } const buffer = { data: new ArrayBuffer(0), start: start, remainingBytes: this.bufferSize }; this.buffer = buffer; this.stream.on('data', (chunk) => { buffer.data = appendBuffer(buffer.data, chunk); if (buffer.remainingBytes !== undefined) { buffer.remainingBytes = buffer.remainingBytes -= chunk.byteLength; } if (this.streamCaughtUpLock !== undefined) { const dataEndPos = buffer.start + buffer.data.byteLength; this.streamCaughtUpLock.updatePosition(dataEndPos); } }); this.stream.on('end', () => { if (this.streamCaughtUpLock !== undefined) { this.streamCaughtUpLock.endStream(); } }); }); } bufferContainsData(start, size) { if (this.buffer === undefined) return false; if (this.bufferSize === undefined) return true; const end = start + size; let bufferEnd = this.buffer.start + this.buffer.data.byteLength; if (this.buffer.remainingBytes !== undefined) { bufferEnd += this.buffer.remainingBytes; } return start >= this.buffer.start && end <= bufferEnd; } getDataFromBuffer(start, size) { return __awaiter(this, void 0, void 0, function* () { if (this.buffer === undefined) { throw new Error("Invalid State. Buffer should not be empty"); } const sliceStart = start - this.buffer.start; const sliceEnd = sliceStart + size; if (this.streamMode === false) { if (size > this.buffer.data.byteLength) { throw new IOError(`Requested ${size} bytes but only got back ${this.buffer.data.byteLength}`); } return this.buffer.data.slice(sliceStart, sliceEnd); } const currentDataEnd = this.buffer.start + this.buffer.data.byteLength; const requiredEnd = start + size; this.streamCaughtUpLock = new StreamCaughtUpLock(currentDataEnd, requiredEnd); yield this.streamCaughtUpLock.waitForStream(); const response = this.buffer.data.slice(sliceStart, sliceEnd); this.buffer.data = this.buffer.data.slice(sliceEnd, this.buffer.data.byteLength); this.buffer.start = this.buffer.start + sliceEnd; return response; }); } } exports.BufferedDataLoader = BufferedDataLoader; class StreamCaughtUpLock { constructor(currentPos, caughtUpPos) { this.currentPos = currentPos; this.caughtUpPos = caughtUpPos; this.promise = new Promise((resolve, reject) => { if (this.currentPos >= this.caughtUpPos) resolve(); this.promiseResolve = resolve; this.promiseReject = reject; }); } waitForStream() { return this.promise; } updatePosition(position) { this.currentPos = position; if (this.promiseResolve !== undefined && this.currentPos >= this.caughtUpPos) { this.promiseResolve(); } } endStream() { if (this.promiseReject !== undefined && this.currentPos < this.caughtUpPos) { this.promiseReject("Stream ended prematurely"); } } } function appendBuffer(buffer1, buffer2) { var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength); tmp.set(new Uint8Array(buffer1), 0); tmp.set(new Uint8Array(buffer2), buffer1.byteLength); return tmp.buffer; } ; //# sourceMappingURL=DataLoader.js.map