@gmod/bbi 7.1.0 → 8.0.1
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/CHANGELOG.md +8 -0
- package/README.md +59 -0
- package/dist/bbi.d.ts +13 -3
- package/dist/bbi.js +80 -17
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.d.ts +14 -2
- package/dist/bigbed.js +116 -76
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.js +1 -2
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +13 -4
- package/dist/block-view.js +324 -148
- package/dist/block-view.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/parse-bigwig.d.ts +3 -0
- package/dist/parse-bigwig.js +15 -0
- package/dist/parse-bigwig.js.map +1 -0
- package/dist/range.js +11 -24
- package/dist/range.js.map +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/unzip.d.ts +18 -1
- package/dist/unzip.js +33 -4
- package/dist/unzip.js.map +1 -1
- package/dist/util.d.ts +2 -4
- package/dist/util.js +6 -5
- package/dist/util.js.map +1 -1
- package/dist/wasm/inflate-wasm-inlined.d.ts +18 -0
- package/dist/wasm/inflate-wasm-inlined.js +455 -0
- package/dist/wasm/inflate-wasm-inlined.js.map +1 -0
- package/dist/wasm/inflate_wasm.d.ts +1 -0
- package/dist/wasm/inflate_wasm.js +43 -0
- package/dist/wasm/inflate_wasm.js.map +1 -0
- package/dist/wasm/inflate_wasm_bg.d.ts +68 -0
- package/dist/wasm/inflate_wasm_bg.js +307 -0
- package/dist/wasm/inflate_wasm_bg.js.map +1 -0
- package/esm/bbi.d.ts +13 -3
- package/esm/bbi.js +80 -17
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.d.ts +14 -2
- package/esm/bigbed.js +116 -76
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.js +1 -2
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +13 -4
- package/esm/block-view.js +326 -150
- package/esm/block-view.js.map +1 -1
- package/esm/index.d.ts +2 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/parse-bigwig.d.ts +3 -0
- package/esm/parse-bigwig.js +12 -0
- package/esm/parse-bigwig.js.map +1 -0
- package/esm/range.js +11 -24
- package/esm/range.js.map +1 -1
- package/esm/types.d.ts +14 -2
- package/esm/unzip.d.ts +18 -1
- package/esm/unzip.js +30 -3
- package/esm/unzip.js.map +1 -1
- package/esm/util.d.ts +2 -4
- package/esm/util.js +6 -5
- package/esm/util.js.map +1 -1
- package/esm/wasm/inflate-wasm-inlined.d.ts +18 -0
- package/esm/wasm/inflate-wasm-inlined.js +449 -0
- package/esm/wasm/inflate-wasm-inlined.js.map +1 -0
- package/esm/wasm/inflate_wasm.d.ts +1 -0
- package/esm/wasm/inflate_wasm.js +5 -0
- package/esm/wasm/inflate_wasm.js.map +1 -0
- package/esm/wasm/inflate_wasm_bg.d.ts +68 -0
- package/esm/wasm/inflate_wasm_bg.js +296 -0
- package/esm/wasm/inflate_wasm_bg.js.map +1 -0
- package/package.json +17 -12
- package/src/bbi.ts +102 -20
- package/src/bigbed.ts +157 -80
- package/src/bigwig.ts +1 -2
- package/src/block-view.ts +418 -156
- package/src/index.ts +8 -1
- package/src/parse-bigwig.ts +19 -0
- package/src/range.ts +13 -21
- package/src/types.ts +19 -2
- package/src/unzip.ts +86 -3
- package/src/util.ts +9 -10
- package/src/wasm/inflate-wasm-inlined.d.ts +49 -0
- package/src/wasm/inflate-wasm-inlined.js +547 -0
- package/src/wasm/inflate_wasm.d.ts +35 -0
- package/src/wasm/inflate_wasm.js +4 -0
- package/src/wasm/inflate_wasm_bg.js +309 -0
- package/src/wasm/inflate_wasm_bg.wasm +0 -0
- package/src/wasm/inflate_wasm_bg.wasm.d.ts +13 -0
package/src/bigbed.ts
CHANGED
|
@@ -6,6 +6,9 @@ import { map, reduce } from 'rxjs/operators'
|
|
|
6
6
|
import { BBI } from './bbi.ts'
|
|
7
7
|
|
|
8
8
|
import type { Feature, RequestOptions } from './types.ts'
|
|
9
|
+
import type { GenericFilehandle } from 'generic-filehandle2'
|
|
10
|
+
|
|
11
|
+
const decoder = new TextDecoder('utf8')
|
|
9
12
|
|
|
10
13
|
interface Loc {
|
|
11
14
|
key: string
|
|
@@ -25,6 +28,133 @@ export function filterUndef<T>(ts: (T | undefined)[]): T[] {
|
|
|
25
28
|
return ts.filter((t: T | undefined): t is T => !!t)
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
function getTabField(str: string, fieldIndex: number) {
|
|
32
|
+
if (fieldIndex < 0) {
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
let start = 0
|
|
36
|
+
for (let i = 0; i < fieldIndex; i++) {
|
|
37
|
+
start = str.indexOf('\t', start)
|
|
38
|
+
if (start === -1) {
|
|
39
|
+
return undefined
|
|
40
|
+
}
|
|
41
|
+
start++
|
|
42
|
+
}
|
|
43
|
+
const end = str.indexOf('\t', start)
|
|
44
|
+
return end === -1 ? str.slice(start) : str.slice(start, end)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parses a null-terminated string key from a B+ tree node
|
|
48
|
+
function parseKey(buffer: Uint8Array, offset: number, keySize: number) {
|
|
49
|
+
const keyEnd = buffer.indexOf(0, offset)
|
|
50
|
+
const effectiveKeyEnd =
|
|
51
|
+
keyEnd !== -1 && keyEnd < offset + keySize ? keyEnd : offset + keySize
|
|
52
|
+
return decoder.decode(buffer.subarray(offset, effectiveKeyEnd))
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Recursively traverses a B+ tree to search for a specific name in the BigBed extraIndex
|
|
56
|
+
// B+ trees are balanced tree structures optimized for disk-based searches
|
|
57
|
+
async function readBPlusTreeNode(
|
|
58
|
+
bbi: GenericFilehandle,
|
|
59
|
+
nodeOffset: number,
|
|
60
|
+
blockSize: number,
|
|
61
|
+
keySize: number,
|
|
62
|
+
valSize: number,
|
|
63
|
+
name: string,
|
|
64
|
+
field: number,
|
|
65
|
+
opts: RequestOptions,
|
|
66
|
+
): Promise<Loc | undefined> {
|
|
67
|
+
const len = 4 + blockSize * (keySize + valSize)
|
|
68
|
+
const buffer = await bbi.read(len, nodeOffset, opts)
|
|
69
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length)
|
|
70
|
+
let offset = 0
|
|
71
|
+
const nodeType = dataView.getInt8(offset)
|
|
72
|
+
offset += 2 // skip nodeType byte + 1 reserved byte
|
|
73
|
+
const cnt = dataView.getInt16(offset, true)
|
|
74
|
+
offset += 2
|
|
75
|
+
|
|
76
|
+
// Non-leaf node (nodeType === 0): contains keys and child node pointers for navigation
|
|
77
|
+
if (nodeType === 0) {
|
|
78
|
+
const leafkeys = []
|
|
79
|
+
for (let i = 0; i < cnt; i++) {
|
|
80
|
+
const key = parseKey(buffer, offset, keySize)
|
|
81
|
+
offset += keySize
|
|
82
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
83
|
+
offset += 8
|
|
84
|
+
leafkeys.push({
|
|
85
|
+
key,
|
|
86
|
+
offset: dataOffset,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Binary search to find the appropriate child node
|
|
91
|
+
let left = 0
|
|
92
|
+
let right = leafkeys.length - 1
|
|
93
|
+
let targetIndex = leafkeys.length - 1
|
|
94
|
+
|
|
95
|
+
while (left <= right) {
|
|
96
|
+
const mid = Math.floor((left + right) / 2)
|
|
97
|
+
const cmp = name.localeCompare(leafkeys[mid]!.key)
|
|
98
|
+
|
|
99
|
+
if (cmp < 0) {
|
|
100
|
+
targetIndex = mid - 1
|
|
101
|
+
right = mid - 1
|
|
102
|
+
} else {
|
|
103
|
+
left = mid + 1
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const childOffset =
|
|
108
|
+
targetIndex >= 0 ? leafkeys[targetIndex]!.offset : leafkeys[0]!.offset
|
|
109
|
+
return readBPlusTreeNode(
|
|
110
|
+
bbi,
|
|
111
|
+
childOffset,
|
|
112
|
+
blockSize,
|
|
113
|
+
keySize,
|
|
114
|
+
valSize,
|
|
115
|
+
name,
|
|
116
|
+
field,
|
|
117
|
+
opts,
|
|
118
|
+
)
|
|
119
|
+
} else if (nodeType === 1) {
|
|
120
|
+
// Leaf node (nodeType === 1): contains actual key-value data
|
|
121
|
+
const keys = []
|
|
122
|
+
for (let i = 0; i < cnt; i++) {
|
|
123
|
+
const key = parseKey(buffer, offset, keySize)
|
|
124
|
+
offset += keySize
|
|
125
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
126
|
+
offset += 8
|
|
127
|
+
const length = dataView.getUint32(offset, true)
|
|
128
|
+
offset += 4
|
|
129
|
+
offset += 4 // skip reserved
|
|
130
|
+
keys.push({
|
|
131
|
+
key,
|
|
132
|
+
offset: dataOffset,
|
|
133
|
+
length,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Binary search for exact key match in sorted leaf node
|
|
138
|
+
let left = 0
|
|
139
|
+
let right = keys.length - 1
|
|
140
|
+
|
|
141
|
+
while (left <= right) {
|
|
142
|
+
const mid = Math.floor((left + right) / 2)
|
|
143
|
+
const cmp = name.localeCompare(keys[mid]!.key)
|
|
144
|
+
|
|
145
|
+
if (cmp === 0) {
|
|
146
|
+
return { ...keys[mid]!, field }
|
|
147
|
+
} else if (cmp < 0) {
|
|
148
|
+
right = mid - 1
|
|
149
|
+
} else {
|
|
150
|
+
left = mid + 1
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return undefined
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
28
158
|
export class BigBed extends BBI {
|
|
29
159
|
public readIndicesCache = new AbortablePromiseCache<RequestOptions, Index[]>({
|
|
30
160
|
cache: new QuickLRU({ maxSize: 1 }),
|
|
@@ -73,7 +203,7 @@ export class BigBed extends BBI {
|
|
|
73
203
|
const len = blocklen * count
|
|
74
204
|
const buffer = await this.bbi.read(len, dataOffset)
|
|
75
205
|
|
|
76
|
-
const indices
|
|
206
|
+
const indices: Index[] = []
|
|
77
207
|
|
|
78
208
|
for (let i = 0; i < count; i += 1) {
|
|
79
209
|
const b = buffer.subarray(i * blocklen)
|
|
@@ -84,7 +214,7 @@ export class BigBed extends BBI {
|
|
|
84
214
|
const fieldcount = dataView.getInt16(offset, true)
|
|
85
215
|
offset += 2
|
|
86
216
|
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
87
|
-
offset += 8 + 4 //4
|
|
217
|
+
offset += 8 + 4 // skip 8-byte offset + 4 reserved bytes
|
|
88
218
|
const field = dataView.getInt16(offset, true)
|
|
89
219
|
indices.push({
|
|
90
220
|
type,
|
|
@@ -114,7 +244,6 @@ export class BigBed extends BBI {
|
|
|
114
244
|
if (indices.length === 0) {
|
|
115
245
|
return []
|
|
116
246
|
}
|
|
117
|
-
const decoder = new TextDecoder('utf8')
|
|
118
247
|
const locs = indices.map(async index => {
|
|
119
248
|
const { offset: offset2, field } = index
|
|
120
249
|
const b = await this.bbi.read(32, offset2, opts)
|
|
@@ -132,74 +261,16 @@ export class BigBed extends BBI {
|
|
|
132
261
|
// const _itemCount = Number(dataView.getBigUint64(offset, true))
|
|
133
262
|
offset += 8
|
|
134
263
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
offset += 2
|
|
146
|
-
const keys = []
|
|
147
|
-
if (nodeType === 0) {
|
|
148
|
-
const leafkeys = []
|
|
149
|
-
for (let i = 0; i < cnt; i++) {
|
|
150
|
-
const key = decoder
|
|
151
|
-
.decode(b.subarray(offset, offset + keySize))
|
|
152
|
-
.replaceAll('\0', '')
|
|
153
|
-
offset += keySize
|
|
154
|
-
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
155
|
-
offset += 8
|
|
156
|
-
leafkeys.push({
|
|
157
|
-
key,
|
|
158
|
-
offset: dataOffset,
|
|
159
|
-
})
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
let lastOffset = 0
|
|
163
|
-
for (const { key, offset } of leafkeys) {
|
|
164
|
-
if (name.localeCompare(key) < 0 && lastOffset) {
|
|
165
|
-
return bptReadNode(lastOffset)
|
|
166
|
-
}
|
|
167
|
-
lastOffset = offset
|
|
168
|
-
}
|
|
169
|
-
return bptReadNode(lastOffset)
|
|
170
|
-
} else if (nodeType === 1) {
|
|
171
|
-
for (let i = 0; i < cnt; i++) {
|
|
172
|
-
const key = decoder
|
|
173
|
-
.decode(b.subarray(offset, offset + keySize))
|
|
174
|
-
.replaceAll('\0', '')
|
|
175
|
-
offset += keySize
|
|
176
|
-
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
177
|
-
offset += 8
|
|
178
|
-
const length = dataView.getUint32(offset, true)
|
|
179
|
-
offset += 4
|
|
180
|
-
const reserved = dataView.getUint32(offset, true)
|
|
181
|
-
offset += 4
|
|
182
|
-
keys.push({
|
|
183
|
-
key,
|
|
184
|
-
offset: dataOffset,
|
|
185
|
-
length,
|
|
186
|
-
reserved,
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
for (const n of keys) {
|
|
191
|
-
if (n.key === name) {
|
|
192
|
-
return {
|
|
193
|
-
...n,
|
|
194
|
-
field,
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return undefined
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return bptReadNode(offset2 + 32)
|
|
264
|
+
return readBPlusTreeNode(
|
|
265
|
+
this.bbi,
|
|
266
|
+
offset2 + 32,
|
|
267
|
+
blockSize,
|
|
268
|
+
keySize,
|
|
269
|
+
valSize,
|
|
270
|
+
name,
|
|
271
|
+
field,
|
|
272
|
+
opts,
|
|
273
|
+
)
|
|
203
274
|
})
|
|
204
275
|
return filterUndef(await Promise.all(locs))
|
|
205
276
|
}
|
|
@@ -211,7 +282,7 @@ export class BigBed extends BBI {
|
|
|
211
282
|
*
|
|
212
283
|
* @param name - the name to search for
|
|
213
284
|
*
|
|
214
|
-
* @param opts - options object with optional
|
|
285
|
+
* @param opts - options object with optional AbortSignal
|
|
215
286
|
*
|
|
216
287
|
* @return array of Feature
|
|
217
288
|
*/
|
|
@@ -227,16 +298,22 @@ export class BigBed extends BBI {
|
|
|
227
298
|
observer.error(e)
|
|
228
299
|
})
|
|
229
300
|
}).pipe(
|
|
230
|
-
reduce((acc, curr) =>
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return x
|
|
236
|
-
}),
|
|
301
|
+
reduce((acc, curr) => {
|
|
302
|
+
acc.push(...curr)
|
|
303
|
+
return acc
|
|
304
|
+
}, [] as Feature[]),
|
|
305
|
+
map(features => features.map(f => ({ ...f, field: block.field }))),
|
|
237
306
|
)
|
|
238
307
|
})
|
|
239
308
|
const ret = await firstValueFrom(merge(...res))
|
|
240
|
-
|
|
309
|
+
// Filter to features where the indexed field matches the search name
|
|
310
|
+
// field offset is adjusted by -3 to account for chrom, chromStart, chromEnd columns
|
|
311
|
+
return ret.filter(f => {
|
|
312
|
+
if (!f.rest) {
|
|
313
|
+
return false
|
|
314
|
+
}
|
|
315
|
+
const fieldIndex = (f.field || 0) - 3
|
|
316
|
+
return getTabField(f.rest, fieldIndex) === name
|
|
317
|
+
})
|
|
241
318
|
}
|
|
242
319
|
}
|
package/src/bigwig.ts
CHANGED
|
@@ -20,13 +20,12 @@ export class BigWig extends BBI {
|
|
|
20
20
|
|
|
21
21
|
for (let i = maxLevel; i >= 0; i -= 1) {
|
|
22
22
|
const zh = zoomLevels[i]
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
24
23
|
if (zh && zh.reductionLevel <= 2 * basesPerPx) {
|
|
25
24
|
return new BlockView(
|
|
26
25
|
this.bbi,
|
|
27
26
|
refsByName,
|
|
28
27
|
zh.indexOffset,
|
|
29
|
-
uncompressBufSize
|
|
28
|
+
uncompressBufSize,
|
|
30
29
|
'summary',
|
|
31
30
|
)
|
|
32
31
|
}
|