@gmod/bbi 5.0.1 → 6.0.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/CHANGELOG.md +8 -0
- package/README.md +5 -5
- package/dist/bbi.d.ts +15 -16
- package/dist/bbi.js +195 -220
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.js +138 -159
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.js +11 -21
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +2 -3
- package/dist/block-view.js +190 -205
- package/dist/block-view.js.map +1 -1
- package/dist/unzip-pako.d.ts +1 -2
- package/dist/unzip-pako.js.map +1 -1
- package/dist/util.js +3 -14
- package/dist/util.js.map +1 -1
- package/esm/bbi.d.ts +15 -16
- package/esm/bbi.js +66 -70
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.js +47 -40
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.js +3 -2
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +2 -3
- package/esm/block-view.js +36 -41
- package/esm/block-view.js.map +1 -1
- package/esm/unzip-pako.d.ts +1 -2
- package/esm/unzip-pako.js.map +1 -1
- package/package.json +14 -18
- package/src/bbi.ts +78 -99
- package/src/bigbed.ts +47 -62
- package/src/bigwig.ts +2 -2
- package/src/block-view.ts +46 -57
- package/src/unzip-pako.ts +1 -2
package/src/bbi.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle'
|
|
1
|
+
import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle2'
|
|
3
2
|
import { firstValueFrom, Observable } from 'rxjs'
|
|
4
3
|
import { toArray } from 'rxjs/operators'
|
|
5
4
|
import { BlockView } from './block-view'
|
|
@@ -7,7 +6,7 @@ import { BlockView } from './block-view'
|
|
|
7
6
|
const BIG_WIG_MAGIC = -2003829722
|
|
8
7
|
const BIG_BED_MAGIC = -2021002517
|
|
9
8
|
|
|
10
|
-
interface ZoomLevel {
|
|
9
|
+
export interface ZoomLevel {
|
|
11
10
|
reductionLevel: number
|
|
12
11
|
reserved: number
|
|
13
12
|
dataOffset: number
|
|
@@ -27,7 +26,7 @@ export interface Feature {
|
|
|
27
26
|
uniqueId?: string // for bigbed contains uniqueId calculated from file offset
|
|
28
27
|
field?: number // used in bigbed searching
|
|
29
28
|
}
|
|
30
|
-
interface Statistics {
|
|
29
|
+
export interface Statistics {
|
|
31
30
|
scoreSum: number
|
|
32
31
|
basesCovered: number
|
|
33
32
|
scoreSumSquares: number
|
|
@@ -35,7 +34,7 @@ interface Statistics {
|
|
|
35
34
|
scoreMax: number
|
|
36
35
|
}
|
|
37
36
|
|
|
38
|
-
interface RefInfo {
|
|
37
|
+
export interface RefInfo {
|
|
39
38
|
name: string
|
|
40
39
|
id: number
|
|
41
40
|
length: number
|
|
@@ -57,7 +56,6 @@ export interface MainHeader {
|
|
|
57
56
|
uncompressBufSize: number
|
|
58
57
|
chromTreeOffset: number
|
|
59
58
|
extHeaderOffset: number
|
|
60
|
-
isBigEndian: boolean
|
|
61
59
|
fileType: string
|
|
62
60
|
}
|
|
63
61
|
export interface Header extends MainHeader {
|
|
@@ -71,6 +69,11 @@ export interface RequestOptions {
|
|
|
71
69
|
[key: string]: unknown
|
|
72
70
|
}
|
|
73
71
|
|
|
72
|
+
export interface RequestOptions2 extends RequestOptions {
|
|
73
|
+
scale?: number
|
|
74
|
+
basesPerSpan?: number
|
|
75
|
+
}
|
|
76
|
+
|
|
74
77
|
export abstract class BBI {
|
|
75
78
|
protected bbi: GenericFilehandle
|
|
76
79
|
|
|
@@ -80,7 +83,7 @@ export abstract class BBI {
|
|
|
80
83
|
|
|
81
84
|
public getHeader(opts?: RequestOptions) {
|
|
82
85
|
if (!this.headerP) {
|
|
83
|
-
this.headerP = this._getHeader(opts).catch(e => {
|
|
86
|
+
this.headerP = this._getHeader(opts).catch((e: unknown) => {
|
|
84
87
|
this.headerP = undefined
|
|
85
88
|
throw e
|
|
86
89
|
})
|
|
@@ -89,8 +92,7 @@ export abstract class BBI {
|
|
|
89
92
|
}
|
|
90
93
|
|
|
91
94
|
/*
|
|
92
|
-
* @param filehandle - a filehandle from generic-
|
|
93
|
-
* something similar to the node10 fs.promises API
|
|
95
|
+
* @param filehandle - a filehandle from generic-filehandle2
|
|
94
96
|
*
|
|
95
97
|
* @param path - a Local file path as a string
|
|
96
98
|
*
|
|
@@ -121,58 +123,57 @@ export abstract class BBI {
|
|
|
121
123
|
private async _getHeader(opts?: RequestOptions) {
|
|
122
124
|
const header = await this._getMainHeader(opts)
|
|
123
125
|
const chroms = await this._readChromTree(header, opts)
|
|
124
|
-
return {
|
|
126
|
+
return {
|
|
127
|
+
...header,
|
|
128
|
+
...chroms,
|
|
129
|
+
}
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
private async _getMainHeader(
|
|
128
133
|
opts?: RequestOptions,
|
|
129
134
|
requestSize = 2000,
|
|
130
135
|
): Promise<MainHeader> {
|
|
131
|
-
const
|
|
132
|
-
const { buffer } = await this.bbi.read(
|
|
133
|
-
Buffer.alloc(requestSize),
|
|
134
|
-
0,
|
|
135
|
-
requestSize,
|
|
136
|
-
0,
|
|
137
|
-
opts,
|
|
138
|
-
)
|
|
139
|
-
const isBigEndian = this._isBigEndian(buffer)
|
|
140
|
-
const b = buffer
|
|
136
|
+
const b = await this.bbi.read(requestSize, 0, opts)
|
|
141
137
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
138
|
+
|
|
139
|
+
const r1 = dataView.getInt32(0, true)
|
|
140
|
+
if (r1 !== BIG_WIG_MAGIC && r1 !== BIG_BED_MAGIC) {
|
|
141
|
+
throw new Error('not a BigWig/BigBed file')
|
|
142
|
+
}
|
|
142
143
|
let offset = 0
|
|
143
|
-
const magic = dataView.getInt32(offset,
|
|
144
|
+
const magic = dataView.getInt32(offset, true)
|
|
144
145
|
offset += 4
|
|
145
|
-
const version = dataView.getUint16(offset,
|
|
146
|
+
const version = dataView.getUint16(offset, true)
|
|
146
147
|
offset += 2
|
|
147
|
-
const numZoomLevels = dataView.getUint16(offset,
|
|
148
|
+
const numZoomLevels = dataView.getUint16(offset, true)
|
|
148
149
|
offset += 2
|
|
149
|
-
const chromTreeOffset = Number(dataView.getBigUint64(offset,
|
|
150
|
+
const chromTreeOffset = Number(dataView.getBigUint64(offset, true))
|
|
150
151
|
offset += 8
|
|
151
|
-
const unzoomedDataOffset = Number(dataView.getBigUint64(offset,
|
|
152
|
+
const unzoomedDataOffset = Number(dataView.getBigUint64(offset, true))
|
|
152
153
|
offset += 8
|
|
153
|
-
const unzoomedIndexOffset = Number(dataView.getBigUint64(offset,
|
|
154
|
+
const unzoomedIndexOffset = Number(dataView.getBigUint64(offset, true))
|
|
154
155
|
offset += 8
|
|
155
|
-
const fieldCount = dataView.getUint16(offset,
|
|
156
|
+
const fieldCount = dataView.getUint16(offset, true)
|
|
156
157
|
offset += 2
|
|
157
|
-
const definedFieldCount = dataView.getUint16(offset,
|
|
158
|
+
const definedFieldCount = dataView.getUint16(offset, true)
|
|
158
159
|
offset += 2
|
|
159
|
-
const asOffset = Number(dataView.getBigUint64(offset,
|
|
160
|
+
const asOffset = Number(dataView.getBigUint64(offset, true))
|
|
160
161
|
offset += 8
|
|
161
|
-
const totalSummaryOffset = Number(dataView.getBigUint64(offset,
|
|
162
|
+
const totalSummaryOffset = Number(dataView.getBigUint64(offset, true))
|
|
162
163
|
offset += 8
|
|
163
|
-
const uncompressBufSize = dataView.getUint32(offset,
|
|
164
|
+
const uncompressBufSize = dataView.getUint32(offset, true)
|
|
164
165
|
offset += 4
|
|
165
|
-
const extHeaderOffset = Number(dataView.getBigUint64(offset,
|
|
166
|
+
const extHeaderOffset = Number(dataView.getBigUint64(offset, true))
|
|
166
167
|
offset += 8
|
|
167
168
|
const zoomLevels = [] as ZoomLevel[]
|
|
168
169
|
for (let i = 0; i < numZoomLevels; i++) {
|
|
169
|
-
const reductionLevel = dataView.getUint32(offset,
|
|
170
|
+
const reductionLevel = dataView.getUint32(offset, true)
|
|
170
171
|
offset += 4
|
|
171
|
-
const reserved = dataView.getUint32(offset,
|
|
172
|
+
const reserved = dataView.getUint32(offset, true)
|
|
172
173
|
offset += 4
|
|
173
|
-
const dataOffset = Number(dataView.getBigUint64(offset,
|
|
174
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
174
175
|
offset += 8
|
|
175
|
-
const indexOffset = Number(dataView.getBigUint64(offset,
|
|
176
|
+
const indexOffset = Number(dataView.getBigUint64(offset, true))
|
|
176
177
|
offset += 8
|
|
177
178
|
zoomLevels.push({ reductionLevel, reserved, dataOffset, indexOffset })
|
|
178
179
|
}
|
|
@@ -187,18 +188,18 @@ export abstract class BBI {
|
|
|
187
188
|
|
|
188
189
|
let totalSummary: Statistics
|
|
189
190
|
if (totalSummaryOffset) {
|
|
190
|
-
const
|
|
191
|
+
const b2 = b.subarray(Number(totalSummaryOffset))
|
|
191
192
|
let offset = 0
|
|
192
|
-
const dataView = new DataView(
|
|
193
|
-
const basesCovered = Number(dataView.getBigUint64(offset,
|
|
193
|
+
const dataView = new DataView(b2.buffer, b2.byteOffset, b2.length)
|
|
194
|
+
const basesCovered = Number(dataView.getBigUint64(offset, true))
|
|
194
195
|
offset += 8
|
|
195
|
-
const scoreMin = dataView.getFloat64(offset,
|
|
196
|
+
const scoreMin = dataView.getFloat64(offset, true)
|
|
196
197
|
offset += 8
|
|
197
|
-
const scoreMax = dataView.getFloat64(offset,
|
|
198
|
+
const scoreMax = dataView.getFloat64(offset, true)
|
|
198
199
|
offset += 8
|
|
199
|
-
const scoreSum = dataView.getFloat64(offset,
|
|
200
|
+
const scoreSum = dataView.getFloat64(offset, true)
|
|
200
201
|
offset += 8
|
|
201
|
-
const scoreSumSquares = dataView.getFloat64(offset,
|
|
202
|
+
const scoreSumSquares = dataView.getFloat64(offset, true)
|
|
202
203
|
offset += 8
|
|
203
204
|
|
|
204
205
|
totalSummary = {
|
|
@@ -211,6 +212,7 @@ export abstract class BBI {
|
|
|
211
212
|
} else {
|
|
212
213
|
throw new Error('no stats')
|
|
213
214
|
}
|
|
215
|
+
const decoder = new TextDecoder('utf8')
|
|
214
216
|
|
|
215
217
|
return {
|
|
216
218
|
zoomLevels,
|
|
@@ -228,36 +230,17 @@ export abstract class BBI {
|
|
|
228
230
|
unzoomedIndexOffset,
|
|
229
231
|
fileType,
|
|
230
232
|
version,
|
|
231
|
-
isBigEndian,
|
|
232
233
|
autoSql: asOffset
|
|
233
|
-
?
|
|
234
|
+
? decoder.decode(b.subarray(asOffset, b.indexOf(0, asOffset)))
|
|
234
235
|
: '',
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
|
|
238
|
-
private _isBigEndian(buffer: Buffer) {
|
|
239
|
-
let ret = buffer.readInt32LE(0)
|
|
240
|
-
if (ret === BIG_WIG_MAGIC || ret === BIG_BED_MAGIC) {
|
|
241
|
-
return false
|
|
242
|
-
}
|
|
243
|
-
ret = buffer.readInt32BE(0)
|
|
244
|
-
if (ret === BIG_WIG_MAGIC || ret === BIG_BED_MAGIC) {
|
|
245
|
-
return true
|
|
246
|
-
}
|
|
247
|
-
throw new Error('not a BigWig/BigBed file')
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// todo: add progress if long running
|
|
251
239
|
private async _readChromTree(
|
|
252
240
|
header: MainHeader,
|
|
253
241
|
opts?: { signal?: AbortSignal },
|
|
254
242
|
) {
|
|
255
|
-
const
|
|
256
|
-
const le = !isBE
|
|
257
|
-
const refsByNumber: Record<
|
|
258
|
-
number,
|
|
259
|
-
{ name: string; id: number; length: number }
|
|
260
|
-
> = []
|
|
243
|
+
const refsByNumber: Record<number, RefInfo> = []
|
|
261
244
|
const refsByName: Record<string, number> = {}
|
|
262
245
|
|
|
263
246
|
let unzoomedDataOffset = header.unzoomedDataOffset
|
|
@@ -266,60 +249,56 @@ export abstract class BBI {
|
|
|
266
249
|
unzoomedDataOffset += 1
|
|
267
250
|
}
|
|
268
251
|
const off = unzoomedDataOffset - chromTreeOffset
|
|
269
|
-
const
|
|
270
|
-
Buffer.alloc(off),
|
|
271
|
-
0,
|
|
272
|
-
off,
|
|
273
|
-
Number(chromTreeOffset),
|
|
274
|
-
opts,
|
|
275
|
-
)
|
|
252
|
+
const b = await this.bbi.read(off, Number(chromTreeOffset), opts)
|
|
276
253
|
|
|
277
|
-
const b = buffer
|
|
278
254
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
279
255
|
let offset = 0
|
|
280
|
-
// const magic = dataView.getUint32(offset,
|
|
256
|
+
// const magic = dataView.getUint32(offset, true)
|
|
281
257
|
offset += 4
|
|
282
|
-
// const blockSize = dataView.getUint32(offset,
|
|
258
|
+
// const blockSize = dataView.getUint32(offset, true)
|
|
283
259
|
offset += 4
|
|
284
|
-
const keySize = dataView.getUint32(offset,
|
|
260
|
+
const keySize = dataView.getUint32(offset, true)
|
|
285
261
|
offset += 4
|
|
286
|
-
// const valSize = dataView.getUint32(offset,
|
|
262
|
+
// const valSize = dataView.getUint32(offset, true)
|
|
287
263
|
offset += 4
|
|
288
|
-
// const itemCount = dataView.getBigUint64(offset,
|
|
264
|
+
// const itemCount = dataView.getBigUint64(offset, true)
|
|
289
265
|
offset += 8
|
|
290
266
|
|
|
291
267
|
const rootNodeOffset = 32
|
|
268
|
+
const decoder = new TextDecoder('utf8')
|
|
292
269
|
const bptReadNode = async (currentOffset: number) => {
|
|
293
270
|
let offset = currentOffset
|
|
294
|
-
if (offset >=
|
|
271
|
+
if (offset >= b.length) {
|
|
295
272
|
throw new Error('reading beyond end of buffer')
|
|
296
273
|
}
|
|
297
274
|
const isLeafNode = dataView.getUint8(offset)
|
|
298
275
|
offset += 2 //skip 1
|
|
299
|
-
const cnt = dataView.getUint16(offset,
|
|
276
|
+
const cnt = dataView.getUint16(offset, true)
|
|
300
277
|
offset += 2
|
|
301
278
|
if (isLeafNode) {
|
|
302
279
|
for (let n = 0; n < cnt; n++) {
|
|
303
|
-
const key =
|
|
304
|
-
.subarray(offset, offset + keySize)
|
|
305
|
-
.toString()
|
|
280
|
+
const key = decoder
|
|
281
|
+
.decode(b.subarray(offset, offset + keySize))
|
|
306
282
|
.replaceAll('\0', '')
|
|
307
283
|
offset += keySize
|
|
308
|
-
const refId = dataView.getUint32(offset,
|
|
284
|
+
const refId = dataView.getUint32(offset, true)
|
|
309
285
|
offset += 4
|
|
310
|
-
const refSize = dataView.getUint32(offset,
|
|
286
|
+
const refSize = dataView.getUint32(offset, true)
|
|
311
287
|
offset += 4
|
|
312
288
|
|
|
313
|
-
const refRec = { name: key, id: refId, length: refSize }
|
|
314
289
|
refsByName[this.renameRefSeqs(key)] = refId
|
|
315
|
-
refsByNumber[refId] =
|
|
290
|
+
refsByNumber[refId] = {
|
|
291
|
+
name: key,
|
|
292
|
+
id: refId,
|
|
293
|
+
length: refSize,
|
|
294
|
+
}
|
|
316
295
|
}
|
|
317
296
|
} else {
|
|
318
297
|
// parse index node
|
|
319
298
|
const nextNodes = []
|
|
320
299
|
for (let n = 0; n < cnt; n++) {
|
|
321
300
|
offset += keySize
|
|
322
|
-
const childOffset = Number(dataView.getBigUint64(offset,
|
|
301
|
+
const childOffset = Number(dataView.getBigUint64(offset, true))
|
|
323
302
|
offset += 8
|
|
324
303
|
nextNodes.push(
|
|
325
304
|
bptReadNode(Number(childOffset) - Number(chromTreeOffset)),
|
|
@@ -340,18 +319,12 @@ export abstract class BBI {
|
|
|
340
319
|
* @param abortSignal - a signal to optionally abort this operation
|
|
341
320
|
*/
|
|
342
321
|
protected async getUnzoomedView(opts?: RequestOptions) {
|
|
343
|
-
const {
|
|
344
|
-
|
|
345
|
-
refsByName,
|
|
346
|
-
uncompressBufSize,
|
|
347
|
-
isBigEndian,
|
|
348
|
-
fileType,
|
|
349
|
-
} = await this.getHeader(opts)
|
|
322
|
+
const { unzoomedIndexOffset, refsByName, uncompressBufSize, fileType } =
|
|
323
|
+
await this.getHeader(opts)
|
|
350
324
|
return new BlockView(
|
|
351
325
|
this.bbi,
|
|
352
326
|
refsByName,
|
|
353
327
|
unzoomedIndexOffset,
|
|
354
|
-
isBigEndian,
|
|
355
328
|
uncompressBufSize > 0,
|
|
356
329
|
fileType,
|
|
357
330
|
)
|
|
@@ -369,15 +342,19 @@ export abstract class BBI {
|
|
|
369
342
|
* Gets features from a BigWig file
|
|
370
343
|
*
|
|
371
344
|
* @param refName - The chromosome name
|
|
345
|
+
*
|
|
372
346
|
* @param start - The start of a region
|
|
347
|
+
*
|
|
373
348
|
* @param end - The end of a region
|
|
374
|
-
*
|
|
349
|
+
*
|
|
350
|
+
* @param opts - An object containing basesPerSpan (e.g. pixels per basepair)
|
|
351
|
+
* or scale used to infer the zoomLevel to use
|
|
375
352
|
*/
|
|
376
353
|
public async getFeatureStream(
|
|
377
354
|
refName: string,
|
|
378
355
|
start: number,
|
|
379
356
|
end: number,
|
|
380
|
-
opts?:
|
|
357
|
+
opts?: RequestOptions2,
|
|
381
358
|
) {
|
|
382
359
|
await this.getHeader(opts)
|
|
383
360
|
const chrName = this.renameRefSeqs(refName)
|
|
@@ -395,7 +372,9 @@ export abstract class BBI {
|
|
|
395
372
|
return new Observable<Feature[]>(observer => {
|
|
396
373
|
view
|
|
397
374
|
.readWigData(chrName, start, end, observer, opts)
|
|
398
|
-
.catch(e =>
|
|
375
|
+
.catch((e: unknown) => {
|
|
376
|
+
observer.error(e)
|
|
377
|
+
})
|
|
399
378
|
})
|
|
400
379
|
}
|
|
401
380
|
|
|
@@ -403,7 +382,7 @@ export abstract class BBI {
|
|
|
403
382
|
refName: string,
|
|
404
383
|
start: number,
|
|
405
384
|
end: number,
|
|
406
|
-
opts?:
|
|
385
|
+
opts?: RequestOptions2,
|
|
407
386
|
) {
|
|
408
387
|
const ob = await this.getFeatureStream(refName, start, end, opts)
|
|
409
388
|
|
package/src/bigbed.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Buffer } from 'buffer'
|
|
2
1
|
import { Observable, merge, firstValueFrom } from 'rxjs'
|
|
3
2
|
import { map, reduce } from 'rxjs/operators'
|
|
4
3
|
import AbortablePromiseCache from '@gmod/abortable-promise-cache'
|
|
@@ -52,23 +51,16 @@ export class BigBed extends BBI {
|
|
|
52
51
|
* multiple extraIndexes in a bigbed, see bedToBigBed documentation
|
|
53
52
|
*/
|
|
54
53
|
private async _readIndices(opts: RequestOptions) {
|
|
55
|
-
const { extHeaderOffset
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
0,
|
|
59
|
-
64,
|
|
60
|
-
Number(extHeaderOffset),
|
|
61
|
-
)
|
|
62
|
-
const le = !isBigEndian
|
|
63
|
-
|
|
64
|
-
const b = data
|
|
54
|
+
const { extHeaderOffset } = await this.getHeader(opts)
|
|
55
|
+
const b = await this.bbi.read(64, Number(extHeaderOffset))
|
|
56
|
+
|
|
65
57
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
66
58
|
let offset = 0
|
|
67
|
-
// const _size = dataView.getUint16(offset,
|
|
59
|
+
// const _size = dataView.getUint16(offset, true)
|
|
68
60
|
offset += 2
|
|
69
|
-
const count = dataView.getUint16(offset,
|
|
61
|
+
const count = dataView.getUint16(offset, true)
|
|
70
62
|
offset += 2
|
|
71
|
-
const dataOffset = Number(dataView.getBigUint64(offset,
|
|
63
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
72
64
|
offset += 8
|
|
73
65
|
|
|
74
66
|
// no extra index is defined if count==0
|
|
@@ -78,12 +70,7 @@ export class BigBed extends BBI {
|
|
|
78
70
|
|
|
79
71
|
const blocklen = 20
|
|
80
72
|
const len = blocklen * count
|
|
81
|
-
const
|
|
82
|
-
Buffer.alloc(len),
|
|
83
|
-
0,
|
|
84
|
-
len,
|
|
85
|
-
Number(dataOffset),
|
|
86
|
-
)
|
|
73
|
+
const buffer = await this.bbi.read(len, Number(dataOffset))
|
|
87
74
|
|
|
88
75
|
const indices = [] as Index[]
|
|
89
76
|
|
|
@@ -91,13 +78,13 @@ export class BigBed extends BBI {
|
|
|
91
78
|
const b = buffer.subarray(i * blocklen)
|
|
92
79
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
93
80
|
let offset = 0
|
|
94
|
-
const type = dataView.getInt16(offset,
|
|
81
|
+
const type = dataView.getInt16(offset, true)
|
|
95
82
|
offset += 2
|
|
96
|
-
const fieldcount = dataView.getInt16(offset,
|
|
83
|
+
const fieldcount = dataView.getInt16(offset, true)
|
|
97
84
|
offset += 2
|
|
98
|
-
const dataOffset = Number(dataView.getBigUint64(offset,
|
|
85
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
99
86
|
offset += 8 + 4 //4 skip
|
|
100
|
-
const field = dataView.getInt16(offset,
|
|
87
|
+
const field = dataView.getInt16(offset, true)
|
|
101
88
|
indices.push({ type, fieldcount, offset: Number(dataOffset), field })
|
|
102
89
|
}
|
|
103
90
|
return indices
|
|
@@ -108,72 +95,62 @@ export class BigBed extends BBI {
|
|
|
108
95
|
* bigbed data to look for the actual feature data
|
|
109
96
|
*
|
|
110
97
|
* @param name - the name to search for
|
|
98
|
+
*
|
|
111
99
|
* @param opts - a SearchOptions argument with optional signal
|
|
100
|
+
*
|
|
112
101
|
* @return a Promise for an array of bigbed block Loc entries
|
|
113
102
|
*/
|
|
114
103
|
private async searchExtraIndexBlocks(
|
|
115
104
|
name: string,
|
|
116
105
|
opts: RequestOptions = {},
|
|
117
106
|
): Promise<Loc[]> {
|
|
118
|
-
const { isBigEndian } = await this.getHeader(opts)
|
|
119
107
|
const indices = await this.readIndices(opts)
|
|
120
108
|
if (indices.length === 0) {
|
|
121
109
|
return []
|
|
122
110
|
}
|
|
111
|
+
const decoder = new TextDecoder('utf8')
|
|
123
112
|
const locs = indices.map(async (index): Promise<Loc | undefined> => {
|
|
124
113
|
const { offset: offset2, field } = index
|
|
125
|
-
const
|
|
126
|
-
Buffer.alloc(32),
|
|
127
|
-
0,
|
|
128
|
-
32,
|
|
129
|
-
offset2,
|
|
130
|
-
opts,
|
|
131
|
-
)
|
|
132
|
-
const le = !isBigEndian
|
|
133
|
-
const b = data
|
|
114
|
+
const b = await this.bbi.read(32, offset2, opts)
|
|
134
115
|
|
|
135
116
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
136
117
|
let offset = 0
|
|
137
|
-
// const _magic = dataView.getInt32(offset,
|
|
118
|
+
// const _magic = dataView.getInt32(offset, true)
|
|
138
119
|
offset += 4
|
|
139
|
-
const blockSize = dataView.getInt32(offset,
|
|
120
|
+
const blockSize = dataView.getInt32(offset, true)
|
|
140
121
|
offset += 4
|
|
141
|
-
const keySize = dataView.getInt32(offset,
|
|
122
|
+
const keySize = dataView.getInt32(offset, true)
|
|
142
123
|
offset += 4
|
|
143
|
-
const valSize = dataView.getInt32(offset,
|
|
124
|
+
const valSize = dataView.getInt32(offset, true)
|
|
144
125
|
offset += 4
|
|
145
|
-
// const _itemCount = Number(dataView.getBigUint64(offset,
|
|
126
|
+
// const _itemCount = Number(dataView.getBigUint64(offset, true))
|
|
146
127
|
offset += 8
|
|
147
128
|
|
|
148
129
|
const bptReadNode = async (nodeOffset: number) => {
|
|
149
130
|
const val = Number(nodeOffset)
|
|
150
131
|
const len = 4 + blockSize * (keySize + valSize)
|
|
151
|
-
const
|
|
152
|
-
Buffer.alloc(len),
|
|
153
|
-
0,
|
|
154
|
-
len,
|
|
155
|
-
val,
|
|
156
|
-
opts,
|
|
157
|
-
)
|
|
132
|
+
const buffer = await this.bbi.read(len, val, opts)
|
|
158
133
|
const b = buffer
|
|
159
134
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
160
135
|
let offset = 0
|
|
161
136
|
const nodeType = dataView.getInt8(offset)
|
|
162
137
|
offset += 2 //skip 1
|
|
163
|
-
const cnt = dataView.getInt16(offset,
|
|
138
|
+
const cnt = dataView.getInt16(offset, true)
|
|
164
139
|
offset += 2
|
|
165
140
|
const keys = []
|
|
166
141
|
if (nodeType === 0) {
|
|
167
142
|
const leafkeys = []
|
|
168
143
|
for (let i = 0; i < cnt; i++) {
|
|
169
|
-
const key =
|
|
170
|
-
.subarray(offset, offset + keySize)
|
|
171
|
-
.toString()
|
|
144
|
+
const key = decoder
|
|
145
|
+
.decode(b.subarray(offset, offset + keySize))
|
|
172
146
|
.replaceAll('\0', '')
|
|
173
147
|
offset += keySize
|
|
174
|
-
const dataOffset = Number(dataView.getBigUint64(offset,
|
|
148
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
175
149
|
offset += 8
|
|
176
|
-
leafkeys.push({
|
|
150
|
+
leafkeys.push({
|
|
151
|
+
key,
|
|
152
|
+
offset: dataOffset,
|
|
153
|
+
})
|
|
177
154
|
}
|
|
178
155
|
|
|
179
156
|
let lastOffset = 0
|
|
@@ -186,18 +163,22 @@ export class BigBed extends BBI {
|
|
|
186
163
|
return bptReadNode(lastOffset)
|
|
187
164
|
} else if (nodeType === 1) {
|
|
188
165
|
for (let i = 0; i < cnt; i++) {
|
|
189
|
-
const key =
|
|
190
|
-
.subarray(offset, offset + keySize)
|
|
191
|
-
.toString()
|
|
166
|
+
const key = decoder
|
|
167
|
+
.decode(b.subarray(offset, offset + keySize))
|
|
192
168
|
.replaceAll('\0', '')
|
|
193
169
|
offset += keySize
|
|
194
|
-
const dataOffset = Number(dataView.getBigUint64(offset,
|
|
170
|
+
const dataOffset = Number(dataView.getBigUint64(offset, true))
|
|
195
171
|
offset += 8
|
|
196
|
-
const length = dataView.getUint32(offset,
|
|
172
|
+
const length = dataView.getUint32(offset, true)
|
|
197
173
|
offset += 4
|
|
198
|
-
const reserved = dataView.getUint32(offset,
|
|
174
|
+
const reserved = dataView.getUint32(offset, true)
|
|
199
175
|
offset += 4
|
|
200
|
-
keys.push({
|
|
176
|
+
keys.push({
|
|
177
|
+
key,
|
|
178
|
+
offset: dataOffset,
|
|
179
|
+
length,
|
|
180
|
+
reserved,
|
|
181
|
+
})
|
|
201
182
|
}
|
|
202
183
|
|
|
203
184
|
for (const n of keys) {
|
|
@@ -221,8 +202,10 @@ export class BigBed extends BBI {
|
|
|
221
202
|
* the BigBed specification and the -extraIndex argument to bedToBigBed
|
|
222
203
|
*
|
|
223
204
|
* @param name - the name to search for
|
|
224
|
-
*
|
|
225
|
-
* @
|
|
205
|
+
*
|
|
206
|
+
* @param opts - options object with optional AboutSignal
|
|
207
|
+
*
|
|
208
|
+
* @return array of Feature
|
|
226
209
|
*/
|
|
227
210
|
public async searchExtraIndex(name: string, opts: RequestOptions = {}) {
|
|
228
211
|
const blocks = await this.searchExtraIndexBlocks(name, opts)
|
|
@@ -232,7 +215,9 @@ export class BigBed extends BBI {
|
|
|
232
215
|
const view = await this.getUnzoomedView(opts)
|
|
233
216
|
const res = blocks.map(block => {
|
|
234
217
|
return new Observable<Feature[]>(observer => {
|
|
235
|
-
view.readFeatures(observer, [block], opts).catch(e =>
|
|
218
|
+
view.readFeatures(observer, [block], opts).catch((e: unknown) => {
|
|
219
|
+
observer.error(e)
|
|
220
|
+
})
|
|
236
221
|
}).pipe(
|
|
237
222
|
reduce((acc, curr) => acc.concat(curr)),
|
|
238
223
|
map(x => {
|
package/src/bigwig.ts
CHANGED
|
@@ -11,19 +11,19 @@ export class BigWig extends BBI {
|
|
|
11
11
|
* or scale used to infer the zoomLevel to use
|
|
12
12
|
*/
|
|
13
13
|
protected async getView(scale: number, opts: RequestOptions) {
|
|
14
|
-
const { zoomLevels, refsByName,
|
|
14
|
+
const { zoomLevels, refsByName, uncompressBufSize } =
|
|
15
15
|
await this.getHeader(opts)
|
|
16
16
|
const basesPerPx = 1 / scale
|
|
17
17
|
const maxLevel = zoomLevels.length - 1
|
|
18
18
|
|
|
19
19
|
for (let i = maxLevel; i >= 0; i -= 1) {
|
|
20
20
|
const zh = zoomLevels[i]
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
21
22
|
if (zh && zh.reductionLevel <= 2 * basesPerPx) {
|
|
22
23
|
return new BlockView(
|
|
23
24
|
this.bbi,
|
|
24
25
|
refsByName,
|
|
25
26
|
zh.indexOffset,
|
|
26
|
-
isBigEndian,
|
|
27
27
|
uncompressBufSize > 0,
|
|
28
28
|
'summary',
|
|
29
29
|
)
|