@gmod/tabix 3.5.3 → 3.5.4
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.
- package/README.md +12 -0
- package/dist/csi.d.ts +5 -9
- package/dist/csi.js +17 -54
- package/dist/csi.js.map +1 -1
- package/dist/indexFile.d.ts +43 -6
- package/dist/indexFile.js +48 -0
- package/dist/indexFile.js.map +1 -1
- package/dist/tabix-bundle.js +1 -1
- package/dist/tabixIndexedFile.d.ts +42 -7
- package/dist/tabixIndexedFile.js +104 -27
- package/dist/tabixIndexedFile.js.map +1 -1
- package/dist/tbi.d.ts +14 -2
- package/dist/tbi.js +36 -58
- package/dist/tbi.js.map +1 -1
- package/esm/csi.d.ts +5 -9
- package/esm/csi.js +18 -55
- package/esm/csi.js.map +1 -1
- package/esm/indexFile.d.ts +43 -6
- package/esm/indexFile.js +48 -0
- package/esm/indexFile.js.map +1 -1
- package/esm/tabixIndexedFile.d.ts +42 -7
- package/esm/tabixIndexedFile.js +104 -27
- package/esm/tabixIndexedFile.js.map +1 -1
- package/esm/tbi.d.ts +14 -2
- package/esm/tbi.js +37 -59
- package/esm/tbi.js.map +1 -1
- package/package.json +2 -2
- package/src/csi.ts +26 -74
- package/src/indexFile.ts +86 -8
- package/src/tabixIndexedFile.ts +120 -44
- package/src/tbi.ts +48 -82
package/esm/tbi.d.ts
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
import Chunk from './chunk.ts';
|
|
2
1
|
import IndexFile from './indexFile.ts';
|
|
3
2
|
import type { Options, RefIndex } from './indexFile.ts';
|
|
4
3
|
import type VirtualOffset from './virtualOffset.ts';
|
|
5
4
|
export default class TabixIndex extends IndexFile {
|
|
5
|
+
/**
|
|
6
|
+
* The bins that may overlap [beg, end) (zero-based half-open). TBI's scheme
|
|
7
|
+
* is CSI's with minShift/depth fixed, so this is CSI's reg2bins with the
|
|
8
|
+
* levels unrolled and its float shifts replaced by int32 ones.
|
|
9
|
+
*/
|
|
10
|
+
protected reg2bins(beg: number, end: number): (readonly [number, number])[];
|
|
11
|
+
/**
|
|
12
|
+
* The linear index is monotonically non-decreasing, so the minimum virtual
|
|
13
|
+
* offset for chunks that could overlap [beg, ...) is its entry for beg.
|
|
14
|
+
* SYNC: ~/src/gmod/bam-js/src/bai.ts getLowestChunk
|
|
15
|
+
*/
|
|
16
|
+
protected lowestOffset(ref: RefIndex, beg: number): VirtualOffset | undefined;
|
|
6
17
|
/** @internal */
|
|
7
18
|
_parse(opts?: Options): Promise<{
|
|
8
19
|
indices: (refId: number) => RefIndex | undefined;
|
|
9
20
|
metaChar: string | undefined;
|
|
21
|
+
minShift: number;
|
|
22
|
+
depth: number;
|
|
10
23
|
maxBinNumber: number;
|
|
11
24
|
maxRefLength: number;
|
|
12
25
|
skipLines: number;
|
|
@@ -22,5 +35,4 @@ export default class TabixIndex extends IndexFile {
|
|
|
22
35
|
refNameToId: Record<string, number>;
|
|
23
36
|
maxBlockSize: number;
|
|
24
37
|
}>;
|
|
25
|
-
blocksForRange(refName: string, min: number, max: number, opts?: Options): Promise<Chunk[]>;
|
|
26
38
|
}
|
package/esm/tbi.js
CHANGED
|
@@ -1,36 +1,48 @@
|
|
|
1
|
-
import { unzip } from '@gmod/bgzf-filehandle';
|
|
2
1
|
import Chunk from "./chunk.js";
|
|
3
2
|
import IndexFile from "./indexFile.js";
|
|
4
|
-
import { clampChunkEnds, memoizeByRefId, minVirtualOffset,
|
|
3
|
+
import { clampChunkEnds, memoizeByRefId, minVirtualOffset, parseAuxData, parsePseudoBin, } from "./util.js";
|
|
5
4
|
import { fromBytes } from "./virtualOffset.js";
|
|
6
5
|
const TBI_MAGIC = 21_578_324; // TBI\1
|
|
7
6
|
// TBI is always the fixed depth-5, 14-bit-leaf binning scheme
|
|
8
7
|
const TBI_DEPTH = 5;
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
// truncate to int32 and go negative, which would silently yield no bins.
|
|
17
|
-
const b = Math.min(Math.max(beg, 0), TBI_MAX_REF_LENGTH);
|
|
18
|
-
const e = Math.min(end, TBI_MAX_REF_LENGTH) - 1;
|
|
19
|
-
const bins = [];
|
|
20
|
-
if (b <= e) {
|
|
21
|
-
bins.push([0, 0], [1 + (b >> 26), 1 + (e >> 26)], [9 + (b >> 23), 9 + (e >> 23)], [73 + (b >> 20), 73 + (e >> 20)], [585 + (b >> 17), 585 + (e >> 17)], [4681 + (b >> 14), 4681 + (e >> 14)]);
|
|
22
|
-
}
|
|
23
|
-
return bins;
|
|
8
|
+
const TBI_MIN_SHIFT = 14;
|
|
9
|
+
const TBI_MAX_REF_LENGTH = 2 ** (TBI_MIN_SHIFT + TBI_DEPTH * 3);
|
|
10
|
+
// Clamp a query coordinate into the binning scheme's range. Past 2**31 the
|
|
11
|
+
// int32 shifts below truncate and go negative, which would silently yield no
|
|
12
|
+
// bins.
|
|
13
|
+
function clampToRefLength(pos) {
|
|
14
|
+
return Math.min(Math.max(pos, 0), TBI_MAX_REF_LENGTH);
|
|
24
15
|
}
|
|
25
16
|
export default class TabixIndex extends IndexFile {
|
|
17
|
+
/**
|
|
18
|
+
* The bins that may overlap [beg, end) (zero-based half-open). TBI's scheme
|
|
19
|
+
* is CSI's with minShift/depth fixed, so this is CSI's reg2bins with the
|
|
20
|
+
* levels unrolled and its float shifts replaced by int32 ones.
|
|
21
|
+
*/
|
|
22
|
+
reg2bins(beg, end) {
|
|
23
|
+
if (beg > TBI_MAX_REF_LENGTH) {
|
|
24
|
+
console.warn('querying outside of possible tabix range');
|
|
25
|
+
}
|
|
26
|
+
const b = clampToRefLength(beg);
|
|
27
|
+
const e = Math.min(end, TBI_MAX_REF_LENGTH) - 1;
|
|
28
|
+
const bins = [];
|
|
29
|
+
if (b <= e) {
|
|
30
|
+
bins.push([0, 0], [1 + (b >> 26), 1 + (e >> 26)], [9 + (b >> 23), 9 + (e >> 23)], [73 + (b >> 20), 73 + (e >> 20)], [585 + (b >> 17), 585 + (e >> 17)], [4681 + (b >> 14), 4681 + (e >> 14)]);
|
|
31
|
+
}
|
|
32
|
+
return bins;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The linear index is monotonically non-decreasing, so the minimum virtual
|
|
36
|
+
* offset for chunks that could overlap [beg, ...) is its entry for beg.
|
|
37
|
+
* SYNC: ~/src/gmod/bam-js/src/bai.ts getLowestChunk
|
|
38
|
+
*/
|
|
39
|
+
lowestOffset(ref, beg) {
|
|
40
|
+
const linearIndex = ref.linearIndex;
|
|
41
|
+
return linearIndex?.[Math.min(clampToRefLength(beg) >> TBI_MIN_SHIFT, linearIndex.length - 1)];
|
|
42
|
+
}
|
|
26
43
|
/** @internal */
|
|
27
44
|
async _parse(opts = {}) {
|
|
28
|
-
const
|
|
29
|
-
signal: opts.signal,
|
|
30
|
-
onProgress: opts.onProgress,
|
|
31
|
-
});
|
|
32
|
-
const bytes = (await unzip(buf));
|
|
33
|
-
const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
45
|
+
const { bytes, dataView } = await this.readIndexBytes(opts);
|
|
34
46
|
if (dataView.getUint32(0, true) !== TBI_MAGIC) {
|
|
35
47
|
throw new Error('Not a TBI file');
|
|
36
48
|
}
|
|
@@ -119,6 +131,8 @@ export default class TabixIndex extends IndexFile {
|
|
|
119
131
|
return {
|
|
120
132
|
indices: memoizeByRefId(getIndices),
|
|
121
133
|
metaChar,
|
|
134
|
+
minShift: TBI_MIN_SHIFT,
|
|
135
|
+
depth: TBI_DEPTH,
|
|
122
136
|
maxBinNumber,
|
|
123
137
|
maxRefLength: TBI_MAX_REF_LENGTH,
|
|
124
138
|
skipLines,
|
|
@@ -131,41 +145,5 @@ export default class TabixIndex extends IndexFile {
|
|
|
131
145
|
maxBlockSize: 1 << 16,
|
|
132
146
|
};
|
|
133
147
|
}
|
|
134
|
-
async blocksForRange(refName, min, max, opts = {}) {
|
|
135
|
-
const indexData = await this.parse(opts);
|
|
136
|
-
const refId = indexData.refNameToId[refName];
|
|
137
|
-
if (refId === undefined) {
|
|
138
|
-
return [];
|
|
139
|
-
}
|
|
140
|
-
const ba = indexData.indices(refId);
|
|
141
|
-
if (!ba) {
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
|
-
if (min > TBI_MAX_REF_LENGTH) {
|
|
145
|
-
console.warn('querying outside of possible tabix range');
|
|
146
|
-
}
|
|
147
|
-
// clamp before the >> 14 below, which would truncate to int32 and go
|
|
148
|
-
// negative past 2**31
|
|
149
|
-
const beg = Math.min(Math.max(min, 0), TBI_MAX_REF_LENGTH);
|
|
150
|
-
const overlappingBins = reg2bins(min, max); // List of bin #s that overlap min, max
|
|
151
|
-
const chunks = [];
|
|
152
|
-
// Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
|
|
153
|
-
for (const [start, end] of overlappingBins) {
|
|
154
|
-
for (let bin = start; bin <= end; bin++) {
|
|
155
|
-
const binChunks = ba.binIndex[bin];
|
|
156
|
-
if (binChunks) {
|
|
157
|
-
for (const c of binChunks) {
|
|
158
|
-
chunks.push(c);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// The linear index is monotonically non-decreasing, so the minimum virtual
|
|
164
|
-
// offset for chunks that could overlap [min, ...) is at index minLin.
|
|
165
|
-
// SYNC: ~/src/gmod/bam-js/src/bai.ts getLowestChunk
|
|
166
|
-
const linearIndex = ba.linearIndex;
|
|
167
|
-
const lowest = linearIndex?.[Math.min(beg >> 14, linearIndex.length - 1)];
|
|
168
|
-
return optimizeChunks(chunks, lowest);
|
|
169
|
-
}
|
|
170
148
|
}
|
|
171
149
|
//# sourceMappingURL=tbi.js.map
|
package/esm/tbi.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tbi.js","sourceRoot":"","sources":["../src/tbi.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"tbi.js","sourceRoot":"","sources":["../src/tbi.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gmod/tabix",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.4",
|
|
4
4
|
"packageManager": "pnpm@11.15.1",
|
|
5
5
|
"description": "Read Tabix-indexed files, supports both .tbi and .csi indexes",
|
|
6
6
|
"type": "module",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@gmod/abortable-promise-cache": "^3.0.4",
|
|
53
|
-
"@gmod/bgzf-filehandle": "^6.2
|
|
53
|
+
"@gmod/bgzf-filehandle": "^6.3.2",
|
|
54
54
|
"@jbrowse/quick-lru": "^7.3.5",
|
|
55
55
|
"generic-filehandle2": "^2.2.0"
|
|
56
56
|
},
|
package/src/csi.ts
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
import { unzip } from '@gmod/bgzf-filehandle'
|
|
2
|
-
|
|
3
1
|
import Chunk from './chunk.ts'
|
|
4
2
|
import IndexFile from './indexFile.ts'
|
|
5
3
|
import {
|
|
6
4
|
clampChunkEnds,
|
|
7
5
|
memoizeByRefId,
|
|
8
6
|
minVirtualOffset,
|
|
9
|
-
optimizeChunks,
|
|
10
7
|
parseAuxData,
|
|
11
8
|
parsePseudoBin,
|
|
12
9
|
} from './util.ts'
|
|
13
10
|
import { fromBytes } from './virtualOffset.ts'
|
|
14
11
|
|
|
15
|
-
import type { Options, RefIndex } from './indexFile.ts'
|
|
12
|
+
import type { IndexData, Options, RefIndex } from './indexFile.ts'
|
|
16
13
|
import type VirtualOffset from './virtualOffset.ts'
|
|
17
14
|
|
|
18
15
|
const CSI1_MAGIC = 21_582_659 // CSI\1
|
|
@@ -28,22 +25,9 @@ function rshift(num: number, bits: number) {
|
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
export default class CSI extends IndexFile {
|
|
31
|
-
private maxBinNumber = 0
|
|
32
|
-
private depth = 0
|
|
33
|
-
private minShift = 0
|
|
34
|
-
|
|
35
28
|
/** @internal */
|
|
36
29
|
async _parse(opts: Options = {}) {
|
|
37
|
-
const
|
|
38
|
-
signal: opts.signal,
|
|
39
|
-
onProgress: opts.onProgress,
|
|
40
|
-
})
|
|
41
|
-
const bytes = (await unzip(buf)) as Uint8Array
|
|
42
|
-
const dataView = new DataView(
|
|
43
|
-
bytes.buffer,
|
|
44
|
-
bytes.byteOffset,
|
|
45
|
-
bytes.byteLength,
|
|
46
|
-
)
|
|
30
|
+
const { bytes, dataView } = await this.readIndexBytes(opts)
|
|
47
31
|
|
|
48
32
|
const magic = dataView.getUint32(0, true)
|
|
49
33
|
let csiVersion
|
|
@@ -55,11 +39,10 @@ export default class CSI extends IndexFile {
|
|
|
55
39
|
throw new Error(`Not a CSI file (magic=${magic})`)
|
|
56
40
|
}
|
|
57
41
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
const maxRefLength = 2 ** (this.minShift + this.depth * 3)
|
|
42
|
+
const minShift = dataView.getInt32(4, true)
|
|
43
|
+
const depth = dataView.getInt32(8, true)
|
|
44
|
+
const maxBinNumber = ((1 << ((depth + 1) * 3)) - 1) / 7
|
|
45
|
+
const maxRefLength = 2 ** (minShift + depth * 3)
|
|
63
46
|
const auxLength = dataView.getInt32(12, true)
|
|
64
47
|
const aux =
|
|
65
48
|
auxLength >= 30
|
|
@@ -144,51 +127,13 @@ export default class CSI extends IndexFile {
|
|
|
144
127
|
firstDataLine,
|
|
145
128
|
csiVersion,
|
|
146
129
|
indices: memoizeByRefId(getIndices),
|
|
147
|
-
|
|
130
|
+
minShift,
|
|
131
|
+
depth,
|
|
148
132
|
maxBinNumber,
|
|
149
133
|
maxRefLength,
|
|
150
134
|
}
|
|
151
135
|
}
|
|
152
136
|
|
|
153
|
-
async blocksForRange(
|
|
154
|
-
refName: string,
|
|
155
|
-
min: number,
|
|
156
|
-
max: number,
|
|
157
|
-
opts: Options = {},
|
|
158
|
-
) {
|
|
159
|
-
if (min < 0) {
|
|
160
|
-
min = 0
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const indexData = await this.parse(opts)
|
|
164
|
-
const refId = indexData.refNameToId[refName]
|
|
165
|
-
if (refId === undefined) {
|
|
166
|
-
return []
|
|
167
|
-
}
|
|
168
|
-
const ba = indexData.indices(refId)
|
|
169
|
-
if (!ba) {
|
|
170
|
-
return []
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// List of bin #s that overlap min, max
|
|
174
|
-
const overlappingBins = this.reg2bins(min, max)
|
|
175
|
-
const chunks: Chunk[] = []
|
|
176
|
-
|
|
177
|
-
// Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
|
|
178
|
-
for (const [start, end] of overlappingBins) {
|
|
179
|
-
for (let bin = start; bin <= end; bin++) {
|
|
180
|
-
const binChunks = ba.binIndex[bin]
|
|
181
|
-
if (binChunks) {
|
|
182
|
-
for (const c of binChunks) {
|
|
183
|
-
chunks.push(c)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return optimizeChunks(chunks, this.minOffset(ba.loffsets, min))
|
|
190
|
-
}
|
|
191
|
-
|
|
192
137
|
/**
|
|
193
138
|
* CSI's equivalent of the BAI/TBI linear index: the loffset of the deepest
|
|
194
139
|
* indexed bin covering `beg` is the earliest virtual offset any record
|
|
@@ -196,14 +141,17 @@ export default class CSI extends IndexFile {
|
|
|
196
141
|
* Walks leaf -> previous sibling -> parent until an indexed bin is found.
|
|
197
142
|
* SYNC: htslib hts_itr_query min_off computation
|
|
198
143
|
*/
|
|
199
|
-
|
|
200
|
-
|
|
144
|
+
protected lowestOffset(
|
|
145
|
+
ref: RefIndex,
|
|
201
146
|
beg: number,
|
|
147
|
+
{ minShift, depth }: IndexData,
|
|
202
148
|
) {
|
|
149
|
+
const { loffsets } = ref
|
|
203
150
|
let found: VirtualOffset | undefined
|
|
204
151
|
if (loffsets) {
|
|
205
152
|
// first bin of the deepest level, i.e. (8**depth - 1) / 7
|
|
206
|
-
let bin =
|
|
153
|
+
let bin =
|
|
154
|
+
(lshift(1, 3 * depth) - 1) / 7 + rshift(Math.max(beg, 0), minShift)
|
|
207
155
|
while (found === undefined && bin > 0) {
|
|
208
156
|
found = loffsets[bin]
|
|
209
157
|
if (found === undefined) {
|
|
@@ -217,23 +165,27 @@ export default class CSI extends IndexFile {
|
|
|
217
165
|
return found
|
|
218
166
|
}
|
|
219
167
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
168
|
+
protected reg2bins(
|
|
169
|
+
beg: number,
|
|
170
|
+
end: number,
|
|
171
|
+
{ minShift, depth, maxBinNumber }: IndexData,
|
|
172
|
+
) {
|
|
173
|
+
beg = Math.max(beg, 0)
|
|
174
|
+
const maxPos = 2 ** (minShift + depth * 3)
|
|
223
175
|
if (end > maxPos) {
|
|
224
176
|
end = maxPos
|
|
225
177
|
}
|
|
226
178
|
end -= 1
|
|
227
179
|
let l = 0
|
|
228
180
|
let t = 0
|
|
229
|
-
let s =
|
|
230
|
-
const bins = []
|
|
231
|
-
for (; l <=
|
|
181
|
+
let s = minShift + depth * 3
|
|
182
|
+
const bins: (readonly [number, number])[] = []
|
|
183
|
+
for (; l <= depth; s -= 3, t += lshift(1, l * 3), l += 1) {
|
|
232
184
|
const b = t + rshift(beg, s)
|
|
233
185
|
const e = t + rshift(end, s)
|
|
234
|
-
if (e - b + bins.length >
|
|
186
|
+
if (e - b + bins.length > maxBinNumber) {
|
|
235
187
|
throw new Error(
|
|
236
|
-
`query ${beg}-${end} is too large for current binning scheme (shift ${
|
|
188
|
+
`query ${beg}-${end} is too large for current binning scheme (shift ${minShift}, depth ${depth}), try a smaller query or a coarser index binning scheme`,
|
|
237
189
|
)
|
|
238
190
|
}
|
|
239
191
|
bins.push([b, e] as const)
|
package/src/indexFile.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { unzip } from '@gmod/bgzf-filehandle'
|
|
2
|
+
|
|
3
|
+
import { optimizeChunks } from './util.ts'
|
|
4
|
+
|
|
1
5
|
import type Chunk from './chunk.ts'
|
|
2
6
|
import type VirtualOffset from './virtualOffset.ts'
|
|
3
7
|
import type { GenericFilehandle } from 'generic-filehandle2'
|
|
@@ -30,13 +34,19 @@ export interface IndexData {
|
|
|
30
34
|
indices: (refId: number) => RefIndex | undefined
|
|
31
35
|
maxRefLength: number
|
|
32
36
|
skipLines?: number
|
|
33
|
-
maxBinNumber?: number
|
|
34
37
|
maxBlockSize: number
|
|
35
38
|
firstDataLine?: VirtualOffset
|
|
36
39
|
refCount?: number
|
|
37
40
|
csi?: boolean
|
|
38
41
|
csiVersion?: number
|
|
39
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The binning scheme. TBI reports these too, though its own are fixed by the
|
|
44
|
+
* format (minShift 14, depth 5) rather than read from the file: they are the
|
|
45
|
+
* same scheme, and CSI exists to let a file choose other values for them.
|
|
46
|
+
*/
|
|
47
|
+
minShift: number
|
|
48
|
+
depth: number
|
|
49
|
+
maxBinNumber: number
|
|
40
50
|
}
|
|
41
51
|
|
|
42
52
|
export default abstract class IndexFile {
|
|
@@ -49,6 +59,43 @@ export default abstract class IndexFile {
|
|
|
49
59
|
|
|
50
60
|
protected abstract _parse(opts: Options): Promise<IndexData>
|
|
51
61
|
|
|
62
|
+
/**
|
|
63
|
+
* The bins that may overlap [beg, end), as one inclusive [first, last] range
|
|
64
|
+
* per level of the binning scheme.
|
|
65
|
+
*/
|
|
66
|
+
protected abstract reg2bins(
|
|
67
|
+
beg: number,
|
|
68
|
+
end: number,
|
|
69
|
+
indexData: IndexData,
|
|
70
|
+
): (readonly [number, number])[]
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The earliest virtual offset any record overlapping `beg` can have, so that
|
|
74
|
+
* chunks ending at or before it can be dropped. TBI reads it off the linear
|
|
75
|
+
* index; CSI, which has none, off the bins' loffsets.
|
|
76
|
+
*/
|
|
77
|
+
protected abstract lowestOffset(
|
|
78
|
+
ref: RefIndex,
|
|
79
|
+
beg: number,
|
|
80
|
+
indexData: IndexData,
|
|
81
|
+
): VirtualOffset | undefined
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The whole index file, decompressed, with a DataView over it. Both .tbi and
|
|
85
|
+
* .csi are bgzf-compressed and read whole, so both parsers start here.
|
|
86
|
+
*/
|
|
87
|
+
protected async readIndexBytes(opts: Options) {
|
|
88
|
+
const buf = await this.filehandle.readFile({
|
|
89
|
+
signal: opts.signal,
|
|
90
|
+
onProgress: opts.onProgress,
|
|
91
|
+
})
|
|
92
|
+
const bytes = await unzip(buf)
|
|
93
|
+
return {
|
|
94
|
+
bytes,
|
|
95
|
+
dataView: new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength),
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
52
99
|
/** @internal */
|
|
53
100
|
public async lineCount(refName: string, opts: Options = {}) {
|
|
54
101
|
const indexData = await this.parse(opts)
|
|
@@ -65,13 +112,44 @@ export default abstract class IndexFile {
|
|
|
65
112
|
return rest
|
|
66
113
|
}
|
|
67
114
|
|
|
68
|
-
/**
|
|
69
|
-
|
|
115
|
+
/**
|
|
116
|
+
* The chunks of the data file that may hold records overlapping the region.
|
|
117
|
+
* The two index formats differ only in their binning scheme and in where
|
|
118
|
+
* they keep the pruning floor, which is what the two hooks above supply.
|
|
119
|
+
*
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
122
|
+
public async blocksForRange(
|
|
70
123
|
refName: string,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
opts: Options,
|
|
74
|
-
)
|
|
124
|
+
min: number,
|
|
125
|
+
max: number,
|
|
126
|
+
opts: Options = {},
|
|
127
|
+
) {
|
|
128
|
+
const indexData = await this.parse(opts)
|
|
129
|
+
const refId = indexData.refNameToId[refName]
|
|
130
|
+
if (refId === undefined) {
|
|
131
|
+
return []
|
|
132
|
+
}
|
|
133
|
+
const ba = indexData.indices(refId)
|
|
134
|
+
if (!ba) {
|
|
135
|
+
return []
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Find chunks in overlapping bins. Leaf bins are not pruned.
|
|
139
|
+
const chunks: Chunk[] = []
|
|
140
|
+
for (const [start, end] of this.reg2bins(min, max, indexData)) {
|
|
141
|
+
for (let bin = start; bin <= end; bin++) {
|
|
142
|
+
const binChunks = ba.binIndex[bin]
|
|
143
|
+
if (binChunks) {
|
|
144
|
+
for (const c of binChunks) {
|
|
145
|
+
chunks.push(c)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return optimizeChunks(chunks, this.lowestOffset(ba, min, indexData))
|
|
152
|
+
}
|
|
75
153
|
|
|
76
154
|
/** @internal */
|
|
77
155
|
async parse(opts: Options = {}) {
|