@gmod/tabix 1.5.0 → 1.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +3 -2
  3. package/dist/chunk.js +34 -62
  4. package/dist/chunk.js.map +1 -0
  5. package/dist/csi.d.ts +0 -1
  6. package/dist/csi.js +329 -533
  7. package/dist/csi.js.map +1 -0
  8. package/dist/index.js +12 -36
  9. package/dist/index.js.map +1 -0
  10. package/dist/indexFile.d.ts +1 -1
  11. package/dist/indexFile.js +113 -174
  12. package/dist/indexFile.js.map +1 -0
  13. package/dist/tabixIndexedFile.d.ts +8 -4
  14. package/dist/tabixIndexedFile.js +477 -774
  15. package/dist/tabixIndexedFile.js.map +1 -0
  16. package/dist/tbi.js +313 -485
  17. package/dist/tbi.js.map +1 -0
  18. package/dist/util.js +133 -148
  19. package/dist/util.js.map +1 -0
  20. package/dist/virtualOffset.js +45 -68
  21. package/dist/virtualOffset.js.map +1 -0
  22. package/esm/chunk.d.ts +18 -0
  23. package/esm/chunk.js +33 -0
  24. package/esm/chunk.js.map +1 -0
  25. package/esm/csi.d.ts +71 -0
  26. package/esm/csi.js +230 -0
  27. package/esm/csi.js.map +1 -0
  28. package/esm/index.d.ts +4 -0
  29. package/esm/index.js +5 -0
  30. package/esm/index.js.map +1 -0
  31. package/esm/indexFile.d.ts +32 -0
  32. package/esm/indexFile.js +40 -0
  33. package/esm/indexFile.js.map +1 -0
  34. package/esm/tabixIndexedFile.d.ts +118 -0
  35. package/esm/tabixIndexedFile.js +398 -0
  36. package/esm/tabixIndexedFile.js.map +1 -0
  37. package/esm/tbi.d.ts +45 -0
  38. package/esm/tbi.js +216 -0
  39. package/esm/tbi.js.map +1 -0
  40. package/esm/util.d.ts +25 -0
  41. package/esm/util.js +91 -0
  42. package/esm/util.js.map +1 -0
  43. package/esm/virtualOffset.d.ts +10 -0
  44. package/esm/virtualOffset.js +37 -0
  45. package/esm/virtualOffset.js.map +1 -0
  46. package/package.json +27 -44
  47. package/src/chunk.ts +52 -0
  48. package/src/csi.ts +277 -0
  49. package/src/declare.d.ts +2 -0
  50. package/src/index.ts +5 -0
  51. package/src/indexFile.ts +79 -0
  52. package/src/tabixIndexedFile.ts +519 -0
  53. package/src/tbi.ts +250 -0
  54. package/src/util.ts +103 -0
  55. package/src/virtualOffset.ts +47 -0
  56. package/dist/declare.d.js +0 -2
@@ -0,0 +1,398 @@
1
+ import AbortablePromiseCache from 'abortable-promise-cache';
2
+ import LRU from 'quick-lru';
3
+ import { LocalFile } from 'generic-filehandle';
4
+ import { unzip, unzipChunkSlice } from '@gmod/bgzf-filehandle';
5
+ import { checkAbortSignal } from './util';
6
+ import TBI from './tbi';
7
+ import CSI from './csi';
8
+ function timeout(time) {
9
+ return new Promise(resolve => {
10
+ setTimeout(resolve, time);
11
+ });
12
+ }
13
+ export default class TabixIndexedFile {
14
+ /**
15
+ * @param {object} args
16
+ * @param {string} [args.path]
17
+ * @param {filehandle} [args.filehandle]
18
+ * @param {string} [args.tbiPath]
19
+ * @param {filehandle} [args.tbiFilehandle]
20
+ * @param {string} [args.csiPath]
21
+ * @param {filehandle} [args.csiFilehandle]
22
+ * @param {chunkSizeLimit} default 50MiB
23
+ * @param {function} [args.renameRefSeqs] optional function with sig `string => string` to transform
24
+ * reference sequence names for the purpose of indexing and querying. note that the data that is returned is
25
+ * not altered, just the names of the reference sequences that are used for querying.
26
+ * @param {number} [args.chunkCacheSize] maximum size in bytes of the chunk cache. default 5MB
27
+ * @param {number} [args.blockCacheSize] maximum size in bytes of the block cache. default 5MB
28
+ */
29
+ constructor({ path, filehandle, tbiPath, tbiFilehandle, csiPath, csiFilehandle, chunkSizeLimit = 50000000, renameRefSeqs = n => n, chunkCacheSize = 5 * 2 ** 20, }) {
30
+ if (filehandle) {
31
+ this.filehandle = filehandle;
32
+ }
33
+ else if (path) {
34
+ this.filehandle = new LocalFile(path);
35
+ }
36
+ else {
37
+ throw new TypeError('must provide either filehandle or path');
38
+ }
39
+ if (tbiFilehandle) {
40
+ this.index = new TBI({
41
+ filehandle: tbiFilehandle,
42
+ renameRefSeqs,
43
+ });
44
+ }
45
+ else if (csiFilehandle) {
46
+ this.index = new CSI({
47
+ filehandle: csiFilehandle,
48
+ renameRefSeqs,
49
+ });
50
+ }
51
+ else if (tbiPath) {
52
+ this.index = new TBI({
53
+ filehandle: new LocalFile(tbiPath),
54
+ renameRefSeqs,
55
+ });
56
+ }
57
+ else if (csiPath) {
58
+ this.index = new CSI({
59
+ filehandle: new LocalFile(csiPath),
60
+ renameRefSeqs,
61
+ });
62
+ }
63
+ else if (path) {
64
+ this.index = new TBI({
65
+ filehandle: new LocalFile(`${path}.tbi`),
66
+ renameRefSeqs,
67
+ });
68
+ }
69
+ else {
70
+ throw new TypeError('must provide one of tbiFilehandle, tbiPath, csiFilehandle, or csiPath');
71
+ }
72
+ this.chunkSizeLimit = chunkSizeLimit;
73
+ this.renameRefSeq = renameRefSeqs;
74
+ this.chunkCache = new AbortablePromiseCache({
75
+ cache: new LRU({
76
+ maxSize: Math.floor(chunkCacheSize / (1 << 16)),
77
+ }),
78
+ fill: this.readChunk.bind(this),
79
+ });
80
+ }
81
+ /**
82
+ * @param {string} refName name of the reference sequence
83
+ * @param {number} start start of the region (in 0-based half-open coordinates)
84
+ * @param {number} end end of the region (in 0-based half-open coordinates)
85
+ * @param {function|object} lineCallback callback called for each line in the region. can also pass a object param containing obj.lineCallback, obj.signal, etc
86
+ * @returns {Promise} resolved when the whole read is finished, rejected on error
87
+ */
88
+ async getLines(refName, start, end, opts) {
89
+ let signal;
90
+ let options = {};
91
+ let callback;
92
+ if (typeof opts === 'undefined') {
93
+ throw new TypeError('line callback must be provided');
94
+ }
95
+ if (typeof opts === 'function') {
96
+ callback = opts;
97
+ }
98
+ else {
99
+ options = opts;
100
+ callback = opts.lineCallback;
101
+ }
102
+ if (refName === undefined) {
103
+ throw new TypeError('must provide a reference sequence name');
104
+ }
105
+ if (!callback) {
106
+ throw new TypeError('line callback must be provided');
107
+ }
108
+ const metadata = await this.index.getMetadata(options);
109
+ checkAbortSignal(signal);
110
+ if (!start) {
111
+ start = 0;
112
+ }
113
+ if (!end) {
114
+ end = metadata.maxRefLength;
115
+ }
116
+ if (!(start <= end)) {
117
+ throw new TypeError('invalid start and end coordinates. start must be less than or equal to end');
118
+ }
119
+ if (start === end) {
120
+ return;
121
+ }
122
+ const chunks = await this.index.blocksForRange(refName, start, end, options);
123
+ checkAbortSignal(signal);
124
+ // check the chunks for any that are over the size limit. if
125
+ // any are, don't fetch any of them
126
+ for (let i = 0; i < chunks.length; i += 1) {
127
+ const size = chunks[i].fetchedSize();
128
+ if (size > this.chunkSizeLimit) {
129
+ throw new Error(`Too much data. Chunk size ${size.toLocaleString()} bytes exceeds chunkSizeLimit of ${this.chunkSizeLimit.toLocaleString()}.`);
130
+ }
131
+ }
132
+ // now go through each chunk and parse and filter the lines out of it
133
+ let last = Date.now();
134
+ for (let chunkNum = 0; chunkNum < chunks.length; chunkNum += 1) {
135
+ let previousStartCoordinate;
136
+ const c = chunks[chunkNum];
137
+ const { buffer, cpositions, dpositions } = await this.chunkCache.get(c.toString(), c, signal);
138
+ const lines = (typeof TextDecoder !== 'undefined'
139
+ ? new TextDecoder('utf-8').decode(buffer)
140
+ : buffer.toString()).split('\n');
141
+ lines.pop();
142
+ checkAbortSignal(signal);
143
+ let blockStart = c.minv.dataPosition;
144
+ let pos;
145
+ for (let i = 0; i < lines.length; i += 1) {
146
+ const line = lines[i];
147
+ for (pos = 0; blockStart >= dpositions[pos]; pos += 1) { }
148
+ // filter the line for whether it is within the requested range
149
+ const { startCoordinate, overlaps } = this.checkLine(metadata, refName, start, end, line);
150
+ // do a small check just to make sure that the lines are really sorted by start coordinate
151
+ if (previousStartCoordinate !== undefined &&
152
+ startCoordinate !== undefined &&
153
+ previousStartCoordinate > startCoordinate) {
154
+ throw new Error(`Lines not sorted by start coordinate (${previousStartCoordinate} > ${startCoordinate}), this file is not usable with Tabix.`);
155
+ }
156
+ previousStartCoordinate = startCoordinate;
157
+ if (overlaps) {
158
+ callback(line.trim(),
159
+ // cpositions[pos] refers to actual file offset of a bgzip block boundaries
160
+ //
161
+ // we multiply by (1 <<8) in order to make sure each block has a "unique"
162
+ // address space so that data in that block could never overlap
163
+ //
164
+ // then the blockStart-dpositions is an uncompressed file offset from
165
+ // that bgzip block boundary, and since the cpositions are multiplied by
166
+ // (1 << 8) these uncompressed offsets get a unique space
167
+ cpositions[pos] * (1 << 8) + (blockStart - dpositions[pos]));
168
+ }
169
+ else if (startCoordinate !== undefined && startCoordinate >= end) {
170
+ // the lines were overlapping the region, but now have stopped, so
171
+ // we must be at the end of the relevant data and we can stop
172
+ // processing data now
173
+ return;
174
+ }
175
+ blockStart += line.length + 1;
176
+ // yield if we have emitted beyond the yield limit
177
+ if (last - Date.now() > 500) {
178
+ last = Date.now();
179
+ checkAbortSignal(signal);
180
+ await timeout(1);
181
+ }
182
+ }
183
+ }
184
+ }
185
+ async getMetadata(opts = {}) {
186
+ return this.index.getMetadata(opts);
187
+ }
188
+ /**
189
+ * get a buffer containing the "header" region of
190
+ * the file, which are the bytes up to the first
191
+ * non-meta line
192
+ *
193
+ * @returns {Promise} for a buffer
194
+ */
195
+ async getHeaderBuffer(opts = {}) {
196
+ const { firstDataLine, metaChar, maxBlockSize } = await this.getMetadata(opts);
197
+ checkAbortSignal(opts.signal);
198
+ const maxFetch = firstDataLine && firstDataLine.blockPosition
199
+ ? firstDataLine.blockPosition + maxBlockSize
200
+ : maxBlockSize;
201
+ // TODO: what if we don't have a firstDataLine, and the header
202
+ // actually takes up more than one block? this case is not covered here
203
+ let bytes = await this._readRegion(0, maxFetch, opts);
204
+ checkAbortSignal(opts.signal);
205
+ try {
206
+ bytes = await unzip(bytes);
207
+ }
208
+ catch (e) {
209
+ console.error(e);
210
+ throw new Error(
211
+ //@ts-ignore
212
+ `error decompressing block ${e.code} at 0 (length ${maxFetch}) ${e}`);
213
+ }
214
+ // trim off lines after the last non-meta line
215
+ if (metaChar) {
216
+ // trim backward from the end
217
+ let lastNewline = -1;
218
+ const newlineByte = '\n'.charCodeAt(0);
219
+ const metaByte = metaChar.charCodeAt(0);
220
+ for (let i = 0; i < bytes.length; i += 1) {
221
+ if (i === lastNewline + 1 && bytes[i] !== metaByte) {
222
+ break;
223
+ }
224
+ if (bytes[i] === newlineByte) {
225
+ lastNewline = i;
226
+ }
227
+ }
228
+ bytes = bytes.slice(0, lastNewline + 1);
229
+ }
230
+ return bytes;
231
+ }
232
+ /**
233
+ * get a string containing the "header" region of the
234
+ * file, is the portion up to the first non-meta line
235
+ *
236
+ * @returns {Promise} for a string
237
+ */
238
+ async getHeader(opts = {}) {
239
+ const bytes = await this.getHeaderBuffer(opts);
240
+ checkAbortSignal(opts.signal);
241
+ return bytes.toString('utf8');
242
+ }
243
+ /**
244
+ * get an array of reference sequence names, in the order in which
245
+ * they occur in the file.
246
+ *
247
+ * reference sequence renaming is not applied to these names.
248
+ *
249
+ * @returns {Promise} for an array of string sequence names
250
+ */
251
+ async getReferenceSequenceNames(opts = {}) {
252
+ const metadata = await this.getMetadata(opts);
253
+ return metadata.refIdToName;
254
+ }
255
+ /**
256
+ * @param {object} metadata metadata object from the parsed index,
257
+ * containing columnNumbers, metaChar, and format
258
+ * @param {string} regionRefName
259
+ * @param {number} regionStart region start coordinate (0-based-half-open)
260
+ * @param {number} regionEnd region end coordinate (0-based-half-open)
261
+ * @param {array[string]} line
262
+ * @returns {object} like `{startCoordinate, overlaps}`. overlaps is boolean,
263
+ * true if line is a data line that overlaps the given region
264
+ */
265
+ checkLine({ columnNumbers, metaChar, coordinateType, format, }, regionRefName, regionStart, regionEnd, line) {
266
+ // skip meta lines
267
+ if (line.charAt(0) === metaChar) {
268
+ return { overlaps: false };
269
+ }
270
+ // check ref/start/end using column metadata from index
271
+ let { ref, start, end } = columnNumbers;
272
+ if (!ref) {
273
+ ref = 0;
274
+ }
275
+ if (!start) {
276
+ start = 0;
277
+ }
278
+ if (!end) {
279
+ end = 0;
280
+ }
281
+ if (format === 'VCF') {
282
+ end = 8;
283
+ }
284
+ const maxColumn = Math.max(ref, start, end);
285
+ // this code is kind of complex, but it is fairly fast.
286
+ // basically, we want to avoid doing a split, because if the lines are really long
287
+ // that could lead to us allocating a bunch of extra memory, which is slow
288
+ let currentColumnNumber = 1; // cols are numbered starting at 1 in the index metadata
289
+ let currentColumnStart = 0;
290
+ let refSeq = '';
291
+ let startCoordinate = -Infinity;
292
+ for (let i = 0; i < line.length + 1; i += 1) {
293
+ if (line[i] === '\t' || i === line.length) {
294
+ if (currentColumnNumber === ref) {
295
+ if (this.renameRefSeq(line.slice(currentColumnStart, i)) !==
296
+ regionRefName) {
297
+ return { overlaps: false };
298
+ }
299
+ }
300
+ else if (currentColumnNumber === start) {
301
+ startCoordinate = parseInt(line.slice(currentColumnStart, i), 10);
302
+ // we convert to 0-based-half-open
303
+ if (coordinateType === '1-based-closed') {
304
+ startCoordinate -= 1;
305
+ }
306
+ if (startCoordinate >= regionEnd) {
307
+ return { startCoordinate, overlaps: false };
308
+ }
309
+ if (end === 0 || end === start) {
310
+ // if we have no end, we assume the feature is 1 bp long
311
+ if (startCoordinate + 1 <= regionStart) {
312
+ return { startCoordinate, overlaps: false };
313
+ }
314
+ }
315
+ }
316
+ else if (format === 'VCF' && currentColumnNumber === 4) {
317
+ refSeq = line.slice(currentColumnStart, i);
318
+ }
319
+ else if (currentColumnNumber === end) {
320
+ let endCoordinate;
321
+ // this will never match if there is no end column
322
+ if (format === 'VCF') {
323
+ endCoordinate = this._getVcfEnd(startCoordinate, refSeq, line.slice(currentColumnStart, i));
324
+ }
325
+ else {
326
+ endCoordinate = parseInt(line.slice(currentColumnStart, i), 10);
327
+ }
328
+ if (endCoordinate <= regionStart) {
329
+ return { overlaps: false };
330
+ }
331
+ }
332
+ currentColumnStart = i + 1;
333
+ currentColumnNumber += 1;
334
+ if (currentColumnNumber > maxColumn) {
335
+ break;
336
+ }
337
+ }
338
+ }
339
+ return { startCoordinate, overlaps: true };
340
+ }
341
+ _getVcfEnd(startCoordinate, refSeq, info) {
342
+ let endCoordinate = startCoordinate + refSeq.length;
343
+ // ignore TRA features as they specify CHR2 and END
344
+ // as being on a different chromosome
345
+ // if CHR2 is on the same chromosome, still ignore it
346
+ // because there should be another pairwise feature
347
+ // at the end of this one
348
+ const isTRA = info.indexOf('SVTYPE=TRA') !== -1;
349
+ if (info[0] !== '.' && !isTRA) {
350
+ let prevChar = ';';
351
+ for (let j = 0; j < info.length; j += 1) {
352
+ if (prevChar === ';' && info.slice(j, j + 4) === 'END=') {
353
+ let valueEnd = info.indexOf(';', j);
354
+ if (valueEnd === -1) {
355
+ valueEnd = info.length;
356
+ }
357
+ endCoordinate = parseInt(info.slice(j + 4, valueEnd), 10);
358
+ break;
359
+ }
360
+ prevChar = info[j];
361
+ }
362
+ }
363
+ else if (isTRA) {
364
+ return startCoordinate + 1;
365
+ }
366
+ return endCoordinate;
367
+ }
368
+ /**
369
+ * return the approximate number of data lines in the given reference sequence
370
+ * @param {string} refSeq reference sequence name
371
+ * @returns {Promise} for number of data lines present on that reference sequence
372
+ */
373
+ async lineCount(refName, opts = {}) {
374
+ return this.index.lineCount(refName, opts);
375
+ }
376
+ async _readRegion(position, compressedSize, opts = {}) {
377
+ const { bytesRead, buffer } = await this.filehandle.read(Buffer.alloc(compressedSize), 0, compressedSize, position, opts);
378
+ return bytesRead < compressedSize ? buffer.slice(0, bytesRead) : buffer;
379
+ }
380
+ /**
381
+ * read and uncompress the data in a chunk (composed of one or more
382
+ * contiguous bgzip blocks) of the file
383
+ * @param {Chunk} chunk
384
+ * @returns {Promise} for a string chunk of the file
385
+ */
386
+ async readChunk(chunk, opts = {}) {
387
+ // fetch the uncompressed data, uncompress carefully a block at a time,
388
+ // and stop when done
389
+ const compressedData = await this._readRegion(chunk.minv.blockPosition, chunk.fetchedSize(), opts);
390
+ try {
391
+ return unzipChunkSlice(compressedData, chunk);
392
+ }
393
+ catch (e) {
394
+ throw new Error(`error decompressing chunk ${chunk.toString()} ${e}`);
395
+ }
396
+ }
397
+ }
398
+ //# sourceMappingURL=tabixIndexedFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabixIndexedFile.js","sourceRoot":"","sources":["../src/tabixIndexedFile.ts"],"names":[],"mappings":"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"}
package/esm/tbi.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ /// <reference types="node" />
2
+ import VirtualOffset from './virtualOffset';
3
+ import Chunk from './chunk';
4
+ import IndexFile, { Options } from './indexFile';
5
+ export default class TabixIndex extends IndexFile {
6
+ lineCount(refName: string, opts?: Options): Promise<any>;
7
+ _parse(opts?: Options): Promise<{
8
+ indices: {
9
+ binIndex: {
10
+ [key: number]: Chunk[];
11
+ };
12
+ linearIndex: any[];
13
+ stats: {
14
+ lineCount: number;
15
+ } | undefined;
16
+ }[];
17
+ metaChar: string | null;
18
+ maxBinNumber: number;
19
+ maxRefLength: number;
20
+ skipLines: any;
21
+ firstDataLine: VirtualOffset | undefined;
22
+ columnNumbers: {
23
+ ref: any;
24
+ start: any;
25
+ end: any;
26
+ };
27
+ coordinateType: string;
28
+ format: string;
29
+ refIdToName: string[];
30
+ refNameToId: {
31
+ [key: string]: number;
32
+ };
33
+ maxBlockSize: number;
34
+ }>;
35
+ parsePseudoBin(bytes: Buffer, offset: number): {
36
+ lineCount: number;
37
+ };
38
+ _parseNameBytes(namesBytes: Buffer): {
39
+ refNameToId: {
40
+ [key: string]: number;
41
+ };
42
+ refIdToName: string[];
43
+ };
44
+ blocksForRange(refName: string, min: number, max: number, opts?: Options): Promise<Chunk[]>;
45
+ }
package/esm/tbi.js ADDED
@@ -0,0 +1,216 @@
1
+ import Long from 'long';
2
+ import VirtualOffset, { fromBytes } from './virtualOffset';
3
+ import Chunk from './chunk';
4
+ import { unzip } from '@gmod/bgzf-filehandle';
5
+ import { longToNumber, optimizeChunks, checkAbortSignal } from './util';
6
+ import IndexFile from './indexFile';
7
+ const TBI_MAGIC = 21578324; // TBI\1
8
+ const TAD_LIDX_SHIFT = 14;
9
+ /**
10
+ * calculate the list of bins that may overlap with region [beg,end) (zero-based half-open)
11
+ */
12
+ function reg2bins(beg, end) {
13
+ beg += 1; // < convert to 1-based closed
14
+ end -= 1;
15
+ return [
16
+ [0, 0],
17
+ [1 + (beg >> 26), 1 + (end >> 26)],
18
+ [9 + (beg >> 23), 9 + (end >> 23)],
19
+ [73 + (beg >> 20), 73 + (end >> 20)],
20
+ [585 + (beg >> 17), 585 + (end >> 17)],
21
+ [4681 + (beg >> 14), 4681 + (end >> 14)],
22
+ ];
23
+ }
24
+ export default class TabixIndex extends IndexFile {
25
+ async lineCount(refName, opts = {}) {
26
+ const indexData = await this.parse(opts);
27
+ if (!indexData) {
28
+ return -1;
29
+ }
30
+ const refId = indexData.refNameToId[refName];
31
+ const idx = indexData.indices[refId];
32
+ if (!idx) {
33
+ return -1;
34
+ }
35
+ const { stats } = indexData.indices[refId];
36
+ if (stats) {
37
+ return stats.lineCount;
38
+ }
39
+ return -1;
40
+ }
41
+ // memoize
42
+ // fetch and parse the index
43
+ async _parse(opts = {}) {
44
+ const bytes = await unzip((await this.filehandle.readFile(opts)));
45
+ checkAbortSignal(opts.signal);
46
+ // check TBI magic numbers
47
+ if (bytes.readUInt32LE(0) !== TBI_MAGIC /* "TBI\1" */) {
48
+ throw new Error('Not a TBI file');
49
+ // TODO: do we need to support big-endian TBI files?
50
+ }
51
+ // number of reference sequences in the index
52
+ const refCount = bytes.readInt32LE(4);
53
+ const formatFlags = bytes.readInt32LE(8);
54
+ const coordinateType = formatFlags & 0x10000 ? 'zero-based-half-open' : '1-based-closed';
55
+ const formatOpts = {
56
+ 0: 'generic',
57
+ 1: 'SAM',
58
+ 2: 'VCF',
59
+ };
60
+ const format = formatOpts[formatFlags & 0xf];
61
+ if (!format) {
62
+ throw new Error(`invalid Tabix preset format flags ${formatFlags}`);
63
+ }
64
+ const columnNumbers = {
65
+ ref: bytes.readInt32LE(12),
66
+ start: bytes.readInt32LE(16),
67
+ end: bytes.readInt32LE(20),
68
+ };
69
+ const metaValue = bytes.readInt32LE(24);
70
+ const depth = 5;
71
+ const maxBinNumber = ((1 << ((depth + 1) * 3)) - 1) / 7;
72
+ const maxRefLength = 2 ** (14 + depth * 3);
73
+ const metaChar = metaValue ? String.fromCharCode(metaValue) : null;
74
+ const skipLines = bytes.readInt32LE(28);
75
+ // read sequence dictionary
76
+ const nameSectionLength = bytes.readInt32LE(32);
77
+ const { refNameToId, refIdToName } = this._parseNameBytes(bytes.slice(36, 36 + nameSectionLength));
78
+ // read the indexes for each reference sequence
79
+ let currOffset = 36 + nameSectionLength;
80
+ let firstDataLine;
81
+ const indices = new Array(refCount).fill(0).map(() => {
82
+ // the binning index
83
+ const binCount = bytes.readInt32LE(currOffset);
84
+ currOffset += 4;
85
+ const binIndex = {};
86
+ let stats;
87
+ for (let j = 0; j < binCount; j += 1) {
88
+ const bin = bytes.readUInt32LE(currOffset);
89
+ currOffset += 4;
90
+ if (bin > maxBinNumber + 1) {
91
+ throw new Error('tabix index contains too many bins, please use a CSI index');
92
+ }
93
+ else if (bin === maxBinNumber + 1) {
94
+ const chunkCount = bytes.readInt32LE(currOffset);
95
+ currOffset += 4;
96
+ if (chunkCount === 2) {
97
+ stats = this.parsePseudoBin(bytes, currOffset);
98
+ }
99
+ currOffset += 16 * chunkCount;
100
+ }
101
+ else {
102
+ const chunkCount = bytes.readInt32LE(currOffset);
103
+ currOffset += 4;
104
+ const chunks = new Array(chunkCount);
105
+ for (let k = 0; k < chunkCount; k += 1) {
106
+ const u = fromBytes(bytes, currOffset);
107
+ const v = fromBytes(bytes, currOffset + 8);
108
+ currOffset += 16;
109
+ firstDataLine = this._findFirstData(firstDataLine, u);
110
+ chunks[k] = new Chunk(u, v, bin);
111
+ }
112
+ binIndex[bin] = chunks;
113
+ }
114
+ }
115
+ // the linear index
116
+ const linearCount = bytes.readInt32LE(currOffset);
117
+ currOffset += 4;
118
+ const linearIndex = new Array(linearCount);
119
+ for (let k = 0; k < linearCount; k += 1) {
120
+ linearIndex[k] = fromBytes(bytes, currOffset);
121
+ currOffset += 8;
122
+ firstDataLine = this._findFirstData(firstDataLine, linearIndex[k]);
123
+ }
124
+ return { binIndex, linearIndex, stats };
125
+ });
126
+ return {
127
+ indices,
128
+ metaChar,
129
+ maxBinNumber,
130
+ maxRefLength,
131
+ skipLines,
132
+ firstDataLine,
133
+ columnNumbers,
134
+ coordinateType,
135
+ format,
136
+ refIdToName,
137
+ refNameToId,
138
+ maxBlockSize: 1 << 16,
139
+ };
140
+ }
141
+ parsePseudoBin(bytes, offset) {
142
+ const lineCount = longToNumber(Long.fromBytesLE(bytes.slice(offset + 16, offset + 24), true));
143
+ return { lineCount };
144
+ }
145
+ _parseNameBytes(namesBytes) {
146
+ let currRefId = 0;
147
+ let currNameStart = 0;
148
+ const refIdToName = [];
149
+ const refNameToId = {};
150
+ for (let i = 0; i < namesBytes.length; i += 1) {
151
+ if (!namesBytes[i]) {
152
+ if (currNameStart < i) {
153
+ let refName = namesBytes.toString('utf8', currNameStart, i);
154
+ refName = this.renameRefSeq(refName);
155
+ refIdToName[currRefId] = refName;
156
+ refNameToId[refName] = currRefId;
157
+ }
158
+ currNameStart = i + 1;
159
+ currRefId += 1;
160
+ }
161
+ }
162
+ return { refNameToId, refIdToName };
163
+ }
164
+ async blocksForRange(refName, min, max, opts = {}) {
165
+ if (min < 0) {
166
+ min = 0;
167
+ }
168
+ const indexData = await this.parse(opts);
169
+ if (!indexData) {
170
+ return [];
171
+ }
172
+ const refId = indexData.refNameToId[refName];
173
+ const ba = indexData.indices[refId];
174
+ if (!ba) {
175
+ return [];
176
+ }
177
+ const minOffset = ba.linearIndex.length
178
+ ? ba.linearIndex[min >> TAD_LIDX_SHIFT >= ba.linearIndex.length
179
+ ? ba.linearIndex.length - 1
180
+ : min >> TAD_LIDX_SHIFT]
181
+ : new VirtualOffset(0, 0);
182
+ if (!minOffset) {
183
+ console.warn('querying outside of possible tabix range');
184
+ }
185
+ // const { linearIndex, binIndex } = indexes
186
+ const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max
187
+ const chunks = [];
188
+ // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
189
+ for (const [start, end] of overlappingBins) {
190
+ for (let bin = start; bin <= end; bin++) {
191
+ if (ba.binIndex[bin]) {
192
+ const binChunks = ba.binIndex[bin];
193
+ for (let c = 0; c < binChunks.length; ++c) {
194
+ chunks.push(new Chunk(binChunks[c].minv, binChunks[c].maxv, bin));
195
+ }
196
+ }
197
+ }
198
+ }
199
+ // Use the linear index to find minimum file position of chunks that could
200
+ // contain alignments in the region
201
+ const nintv = ba.linearIndex.length;
202
+ let lowest = null;
203
+ const minLin = Math.min(min >> 14, nintv - 1);
204
+ const maxLin = Math.min(max >> 14, nintv - 1);
205
+ for (let i = minLin; i <= maxLin; ++i) {
206
+ const vp = ba.linearIndex[i];
207
+ if (vp) {
208
+ if (!lowest || vp.compareTo(lowest) < 0) {
209
+ lowest = vp;
210
+ }
211
+ }
212
+ }
213
+ return optimizeChunks(chunks, lowest);
214
+ }
215
+ }
216
+ //# sourceMappingURL=tbi.js.map