@gmod/bgzf-filehandle
Version:
read from a compressed bgzip file (with .gzi) as if it were uncompressed
75 lines • 2.52 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const long_ts_1 = require("./long.js");
const ENTRY_SIZE = 16;
function parseEntries(buf, numEntries) {
const entries = new Array(numEntries + 1);
entries[0] = [0, 0];
const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
for (let i = 0; i < numEntries; i += 1) {
const offset = i * ENTRY_SIZE;
entries[i + 1] = [(0, long_ts_1.readUint64LE)(view, offset), (0, long_ts_1.readUint64LE)(view, offset + 8)];
}
return entries;
}
// lower-bound binary search: first index whose uncompressed start > position
function findUpperBound(entries, position) {
let lo = 0;
let hi = entries.length;
while (lo < hi) {
const mid = (lo + hi) >>> 1;
if (entries[mid][1] <= position) {
lo = mid + 1;
}
else {
hi = mid;
}
}
return lo;
}
class GziIndex {
filehandle;
index;
constructor({ filehandle }) {
this.filehandle = filehandle;
}
_getIndex() {
if (!this.index) {
this.index = this._readIndex().catch((error) => {
this.index = undefined;
throw error;
});
}
return this.index;
}
async _readIndex() {
const header = await this.filehandle.read(8, 0);
const numEntries = (0, long_ts_1.readUint64LE)(new DataView(header.buffer, header.byteOffset, header.byteLength));
if (numEntries === 0) {
return [[0, 0]];
}
if (numEntries > Number.MAX_SAFE_INTEGER / ENTRY_SIZE) {
throw new TypeError('integer overflow');
}
const body = await this.filehandle.read(ENTRY_SIZE * numEntries, 8);
return parseEntries(body, numEntries);
}
async getRelevantBlocksForRead(length, position) {
if (length === 0) {
return { blocks: [], nextCompressedPosition: undefined };
}
const entries = await this._getIndex();
const readEnd = position + length;
const startBlock = findUpperBound(entries, position) - 1;
let endBlock = startBlock + 1;
while (endBlock < entries.length && entries[endBlock][1] < readEnd) {
endBlock += 1;
}
return {
blocks: entries.slice(startBlock, endBlock),
nextCompressedPosition: entries[endBlock]?.[0],
};
}
}
exports.default = GziIndex;
//# sourceMappingURL=gziIndex.js.map