@gmod/tabix 3.4.2 → 3.5.0
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/dist/csi.d.ts +8 -6
- package/dist/csi.js +33 -17
- package/dist/csi.js.map +1 -1
- package/dist/indexFile.d.ts +2 -0
- package/dist/indexFile.js.map +1 -1
- package/dist/long.d.ts +0 -2
- package/dist/long.js +2 -4
- package/dist/long.js.map +1 -1
- package/dist/tabix-bundle.js +1 -1
- package/dist/tabixIndexedFile.js +49 -35
- package/dist/tabixIndexedFile.js.map +1 -1
- package/dist/tbi.js +21 -23
- package/dist/tbi.js.map +1 -1
- package/dist/util.js +4 -1
- package/dist/util.js.map +1 -1
- package/esm/csi.d.ts +8 -6
- package/esm/csi.js +33 -17
- package/esm/csi.js.map +1 -1
- package/esm/indexFile.d.ts +2 -0
- package/esm/indexFile.js.map +1 -1
- package/esm/long.d.ts +0 -2
- package/esm/long.js +1 -2
- package/esm/long.js.map +1 -1
- package/esm/tabixIndexedFile.js +49 -35
- package/esm/tabixIndexedFile.js.map +1 -1
- package/esm/tbi.js +21 -23
- package/esm/tbi.js.map +1 -1
- package/esm/util.js +4 -1
- package/esm/util.js.map +1 -1
- package/package.json +3 -1
- package/src/csi.ts +41 -18
- package/src/indexFile.ts +2 -0
- package/src/long.ts +1 -2
- package/src/tabixIndexedFile.ts +70 -30
- package/src/tbi.ts +33 -25
- package/src/util.ts +12 -2
package/src/tbi.ts
CHANGED
|
@@ -17,21 +17,31 @@ import type VirtualOffset from './virtualOffset.ts'
|
|
|
17
17
|
|
|
18
18
|
const TBI_MAGIC = 21_578_324 // TBI\1
|
|
19
19
|
|
|
20
|
+
// TBI is always the fixed depth-5, 14-bit-leaf binning scheme
|
|
21
|
+
const TBI_DEPTH = 5
|
|
22
|
+
const TBI_MAX_REF_LENGTH = 2 ** (14 + TBI_DEPTH * 3)
|
|
23
|
+
|
|
20
24
|
/**
|
|
21
25
|
* calculate the list of bins that may overlap with region [beg,end)
|
|
22
26
|
* (zero-based half-open)
|
|
23
27
|
*/
|
|
24
28
|
function reg2bins(beg: number, end: number) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
// Clamp into the binning scheme's range. Past 2**31 the shifts below
|
|
30
|
+
// truncate to int32 and go negative, which would silently yield no bins.
|
|
31
|
+
const b = Math.min(Math.max(beg, 0), TBI_MAX_REF_LENGTH)
|
|
32
|
+
const e = Math.min(end, TBI_MAX_REF_LENGTH) - 1
|
|
33
|
+
const bins: (readonly [number, number])[] = []
|
|
34
|
+
if (b <= e) {
|
|
35
|
+
bins.push(
|
|
36
|
+
[0, 0],
|
|
37
|
+
[1 + (b >> 26), 1 + (e >> 26)],
|
|
38
|
+
[9 + (b >> 23), 9 + (e >> 23)],
|
|
39
|
+
[73 + (b >> 20), 73 + (e >> 20)],
|
|
40
|
+
[585 + (b >> 17), 585 + (e >> 17)],
|
|
41
|
+
[4681 + (b >> 14), 4681 + (e >> 14)],
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
return bins
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
export default class TabixIndex extends IndexFile {
|
|
@@ -42,16 +52,18 @@ export default class TabixIndex extends IndexFile {
|
|
|
42
52
|
onProgress: opts.onProgress,
|
|
43
53
|
})
|
|
44
54
|
const bytes = (await unzip(buf)) as Uint8Array
|
|
45
|
-
const dataView = new DataView(
|
|
55
|
+
const dataView = new DataView(
|
|
56
|
+
bytes.buffer,
|
|
57
|
+
bytes.byteOffset,
|
|
58
|
+
bytes.byteLength,
|
|
59
|
+
)
|
|
46
60
|
|
|
47
61
|
if (dataView.getUint32(0, true) !== TBI_MAGIC) {
|
|
48
62
|
throw new Error('Not a TBI file')
|
|
49
63
|
}
|
|
50
64
|
|
|
51
65
|
const refCount = dataView.getUint32(4, true)
|
|
52
|
-
const
|
|
53
|
-
const maxBinNumber = ((1 << ((depth + 1) * 3)) - 1) / 7
|
|
54
|
-
const maxRefLength = 2 ** (14 + depth * 3)
|
|
66
|
+
const maxBinNumber = ((1 << ((TBI_DEPTH + 1) * 3)) - 1) / 7
|
|
55
67
|
|
|
56
68
|
// TBI header layout matches CSI aux data; parseAuxData handles both
|
|
57
69
|
const {
|
|
@@ -159,7 +171,7 @@ export default class TabixIndex extends IndexFile {
|
|
|
159
171
|
indices: memoizeByRefId(getIndices),
|
|
160
172
|
metaChar,
|
|
161
173
|
maxBinNumber,
|
|
162
|
-
maxRefLength,
|
|
174
|
+
maxRefLength: TBI_MAX_REF_LENGTH,
|
|
163
175
|
skipLines,
|
|
164
176
|
firstDataLine,
|
|
165
177
|
columnNumbers,
|
|
@@ -177,10 +189,6 @@ export default class TabixIndex extends IndexFile {
|
|
|
177
189
|
max: number,
|
|
178
190
|
opts: Options = {},
|
|
179
191
|
) {
|
|
180
|
-
if (min < 0) {
|
|
181
|
-
min = 0
|
|
182
|
-
}
|
|
183
|
-
|
|
184
192
|
const indexData = await this.parse(opts)
|
|
185
193
|
const refId = indexData.refNameToId[refName]
|
|
186
194
|
if (refId === undefined) {
|
|
@@ -191,13 +199,12 @@ export default class TabixIndex extends IndexFile {
|
|
|
191
199
|
return []
|
|
192
200
|
}
|
|
193
201
|
|
|
194
|
-
|
|
195
|
-
// min >= 2**31 overflows int32 and produces a negative >> 14 result —
|
|
196
|
-
// query is well beyond the indexed range
|
|
197
|
-
if (linearIndex.length > 0 && min >= 2 ** 31) {
|
|
202
|
+
if (min > TBI_MAX_REF_LENGTH) {
|
|
198
203
|
console.warn('querying outside of possible tabix range')
|
|
199
204
|
}
|
|
200
|
-
|
|
205
|
+
// clamp before the >> 14 below, which would truncate to int32 and go
|
|
206
|
+
// negative past 2**31
|
|
207
|
+
const beg = Math.min(Math.max(min, 0), TBI_MAX_REF_LENGTH)
|
|
201
208
|
const overlappingBins = reg2bins(min, max) // List of bin #s that overlap min, max
|
|
202
209
|
const chunks: Chunk[] = []
|
|
203
210
|
|
|
@@ -216,7 +223,8 @@ export default class TabixIndex extends IndexFile {
|
|
|
216
223
|
// The linear index is monotonically non-decreasing, so the minimum virtual
|
|
217
224
|
// offset for chunks that could overlap [min, ...) is at index minLin.
|
|
218
225
|
// SYNC: ~/src/gmod/bam-js/src/bai.ts getLowestChunk
|
|
219
|
-
const
|
|
226
|
+
const linearIndex = ba.linearIndex
|
|
227
|
+
const lowest = linearIndex?.[Math.min(beg >> 14, linearIndex.length - 1)]
|
|
220
228
|
|
|
221
229
|
return optimizeChunks(chunks, lowest)
|
|
222
230
|
}
|
package/src/util.ts
CHANGED
|
@@ -86,7 +86,13 @@ export function optimizeChunks(chunks: Chunk[], lowest?: VirtualOffset) {
|
|
|
86
86
|
// maxv.blockPosition is an upper bound on where that final block ends — always at
|
|
87
87
|
// least the true block end, so the clamped fetch still contains the whole block.
|
|
88
88
|
// Shrinks both the byte estimate and the actual fetch with no extra I/O.
|
|
89
|
-
export function clampChunkEnds(
|
|
89
|
+
export function clampChunkEnds(
|
|
90
|
+
chunks: Chunk[],
|
|
91
|
+
extraBoundaries: number[] = [],
|
|
92
|
+
) {
|
|
93
|
+
// Array#sort with a comparator beats a Float64Array numeric sort here:
|
|
94
|
+
// extraBoundaries (the linear index) arrives already sorted and TimSort
|
|
95
|
+
// exploits the existing run, which introsort cannot.
|
|
90
96
|
const boundaries = [...extraBoundaries]
|
|
91
97
|
for (const c of chunks) {
|
|
92
98
|
boundaries.push(c.minv.blockPosition, c.maxv.blockPosition)
|
|
@@ -150,7 +156,11 @@ const tabixFormats: Record<number, string> = {
|
|
|
150
156
|
}
|
|
151
157
|
|
|
152
158
|
export function parseAuxData(bytes: Uint8Array, offset: number) {
|
|
153
|
-
const dataView = new DataView(
|
|
159
|
+
const dataView = new DataView(
|
|
160
|
+
bytes.buffer,
|
|
161
|
+
bytes.byteOffset,
|
|
162
|
+
bytes.byteLength,
|
|
163
|
+
)
|
|
154
164
|
const formatFlags = dataView.getInt32(offset, true)
|
|
155
165
|
const coordinateType =
|
|
156
166
|
formatFlags & 0x1_00_00 ? 'zero-based-half-open' : '1-based-closed'
|