@gmod/bbi 9.0.9 → 9.0.11
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 +70 -12
- package/dist/array-feature-view.d.ts +9 -11
- package/dist/array-feature-view.js +7 -13
- package/dist/array-feature-view.js.map +1 -1
- package/dist/bbi.js +22 -37
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.d.ts +1 -2
- package/dist/bigbed.js +24 -36
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.js +1 -1
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +1 -1
- package/dist/block-view.js +135 -97
- package/dist/block-view.js.map +1 -1
- package/dist/range.js +4 -1
- package/dist/range.js.map +1 -1
- package/dist/types.d.ts +0 -1
- package/dist/unzip.d.ts +2 -12
- package/dist/unzip.js +7 -15
- package/dist/unzip.js.map +1 -1
- package/dist/util.d.ts +3 -2
- package/dist/util.js +13 -1
- package/dist/util.js.map +1 -1
- package/esm/array-feature-view.d.ts +9 -11
- package/esm/array-feature-view.js +7 -13
- package/esm/array-feature-view.js.map +1 -1
- package/esm/bbi.js +19 -34
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.d.ts +1 -2
- package/esm/bigbed.js +22 -31
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.js +1 -1
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +1 -1
- package/esm/block-view.js +136 -98
- package/esm/block-view.js.map +1 -1
- package/esm/range.js +4 -1
- package/esm/range.js.map +1 -1
- package/esm/types.d.ts +0 -1
- package/esm/unzip.d.ts +2 -12
- package/esm/unzip.js +7 -15
- package/esm/unzip.js.map +1 -1
- package/esm/util.d.ts +3 -2
- package/esm/util.js +11 -1
- package/esm/util.js.map +1 -1
- package/package.json +18 -20
- package/src/array-feature-view.ts +11 -20
- package/src/bbi.ts +20 -36
- package/src/bigbed.ts +22 -38
- package/src/bigwig.ts +2 -2
- package/src/block-view.ts +189 -122
- package/src/range.ts +4 -3
- package/src/types.ts +0 -1
- package/src/unzip.ts +11 -24
- package/src/util.ts +21 -2
- package/src/wasm/inflate-wasm-inlined.d.ts +21 -0
- package/src/wasm/inflate-wasm-inlined.js +150 -170
- package/src/wasm/inflate_wasm.js +1 -1
- package/src/wasm/inflate_wasm_bg.js +2 -3
- package/src/wasm/inflate_wasm_bg.wasm +0 -0
- package/dist/wasm/inflate-wasm-inlined.d.ts +0 -18
- package/dist/wasm/inflate-wasm-inlined.js +0 -454
- package/dist/wasm/inflate-wasm-inlined.js.map +0 -1
- package/dist/wasm/inflate_wasm.d.ts +0 -1
- package/dist/wasm/inflate_wasm.js +0 -49
- package/dist/wasm/inflate_wasm.js.map +0 -1
- package/dist/wasm/inflate_wasm_bg.d.ts +0 -68
- package/dist/wasm/inflate_wasm_bg.js +0 -306
- package/dist/wasm/inflate_wasm_bg.js.map +0 -1
- package/esm/wasm/inflate-wasm-inlined.d.ts +0 -18
- package/esm/wasm/inflate-wasm-inlined.js +0 -448
- package/esm/wasm/inflate-wasm-inlined.js.map +0 -1
- package/esm/wasm/inflate_wasm.d.ts +0 -1
- package/esm/wasm/inflate_wasm.js +0 -6
- package/esm/wasm/inflate_wasm.js.map +0 -1
- package/esm/wasm/inflate_wasm_bg.d.ts +0 -68
- package/esm/wasm/inflate_wasm_bg.js +0 -295
- package/esm/wasm/inflate_wasm_bg.js.map +0 -1
- package/src/wasm/inflate_wasm_bg.wasm.d.ts +0 -13
package/src/block-view.ts
CHANGED
|
@@ -7,10 +7,11 @@ import {
|
|
|
7
7
|
decompressAndParseSummaryBlocks,
|
|
8
8
|
unzipBatch,
|
|
9
9
|
} from './unzip.ts'
|
|
10
|
-
import { groupBlocks } from './util.ts'
|
|
10
|
+
import { getDataView, groupBlocks } from './util.ts'
|
|
11
11
|
|
|
12
12
|
import type { Feature } from './types.ts'
|
|
13
13
|
import type { BigWigFeatureArrays, SummaryFeatureArrays } from './unzip.ts'
|
|
14
|
+
import type { Block } from './util.ts'
|
|
14
15
|
import type { GenericFilehandle } from 'generic-filehandle2'
|
|
15
16
|
|
|
16
17
|
const decoder = new TextDecoder('utf8')
|
|
@@ -22,11 +23,6 @@ interface CoordRequest {
|
|
|
22
23
|
end: number
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
interface ReadData {
|
|
26
|
-
offset: number
|
|
27
|
-
length: number
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
interface Options {
|
|
31
27
|
signal?: AbortSignal
|
|
32
28
|
request?: CoordRequest
|
|
@@ -71,7 +67,7 @@ function parseSummaryBlock(
|
|
|
71
67
|
maxScore,
|
|
72
68
|
minScore,
|
|
73
69
|
summary: true,
|
|
74
|
-
score: sumData /
|
|
70
|
+
score: validCnt ? sumData / validCnt : 0,
|
|
75
71
|
})
|
|
76
72
|
}
|
|
77
73
|
}
|
|
@@ -298,6 +294,85 @@ function parseBigWigBlockAsArrays(
|
|
|
298
294
|
return { starts, ends, scores }
|
|
299
295
|
}
|
|
300
296
|
|
|
297
|
+
function parseSummaryBlockAsArrays(
|
|
298
|
+
b: Uint8Array,
|
|
299
|
+
startOffset: number,
|
|
300
|
+
request?: CoordRequest,
|
|
301
|
+
): {
|
|
302
|
+
starts: Int32Array
|
|
303
|
+
ends: Int32Array
|
|
304
|
+
scores: Float32Array
|
|
305
|
+
minScores: Float32Array
|
|
306
|
+
maxScores: Float32Array
|
|
307
|
+
} {
|
|
308
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
309
|
+
const maxItems = Math.floor((b.byteLength - startOffset) / 32)
|
|
310
|
+
const starts = new Int32Array(maxItems)
|
|
311
|
+
const ends = new Int32Array(maxItems)
|
|
312
|
+
const scores = new Float32Array(maxItems)
|
|
313
|
+
const minScores = new Float32Array(maxItems)
|
|
314
|
+
const maxScores = new Float32Array(maxItems)
|
|
315
|
+
let idx = 0
|
|
316
|
+
let offset = startOffset
|
|
317
|
+
while (offset < b.byteLength) {
|
|
318
|
+
const chromId = dataView.getUint32(offset, true)
|
|
319
|
+
offset += 4
|
|
320
|
+
const start = dataView.getUint32(offset, true)
|
|
321
|
+
offset += 4
|
|
322
|
+
const end = dataView.getUint32(offset, true)
|
|
323
|
+
offset += 4
|
|
324
|
+
const validCnt = dataView.getUint32(offset, true)
|
|
325
|
+
offset += 4
|
|
326
|
+
const minScore = dataView.getFloat32(offset, true)
|
|
327
|
+
offset += 4
|
|
328
|
+
const maxScore = dataView.getFloat32(offset, true)
|
|
329
|
+
offset += 4
|
|
330
|
+
const sumData = dataView.getFloat32(offset, true)
|
|
331
|
+
offset += 8
|
|
332
|
+
if (
|
|
333
|
+
!request ||
|
|
334
|
+
(chromId === request.chrId &&
|
|
335
|
+
coordFilter(start, end, request.start, request.end))
|
|
336
|
+
) {
|
|
337
|
+
starts[idx] = start
|
|
338
|
+
ends[idx] = end
|
|
339
|
+
scores[idx] = validCnt ? sumData / validCnt : 0
|
|
340
|
+
minScores[idx] = minScore
|
|
341
|
+
maxScores[idx] = maxScore
|
|
342
|
+
idx++
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (idx < maxItems) {
|
|
346
|
+
return {
|
|
347
|
+
starts: starts.subarray(0, idx),
|
|
348
|
+
ends: ends.subarray(0, idx),
|
|
349
|
+
scores: scores.subarray(0, idx),
|
|
350
|
+
minScores: minScores.subarray(0, idx),
|
|
351
|
+
maxScores: maxScores.subarray(0, idx),
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return { starts, ends, scores, minScores, maxScores }
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function parseBlock(
|
|
358
|
+
blockType: string,
|
|
359
|
+
data: Uint8Array,
|
|
360
|
+
blockOffset: number,
|
|
361
|
+
request?: CoordRequest,
|
|
362
|
+
): Feature[] {
|
|
363
|
+
switch (blockType) {
|
|
364
|
+
case 'summary':
|
|
365
|
+
return parseSummaryBlock(data, 0, request)
|
|
366
|
+
case 'bigwig':
|
|
367
|
+
return parseBigWigBlock(data, 0, request)
|
|
368
|
+
case 'bigbed':
|
|
369
|
+
return parseBigBedBlock(data, 0, blockOffset * (1 << 8), request)
|
|
370
|
+
default:
|
|
371
|
+
console.warn(`Don't know what to do with ${blockType}`)
|
|
372
|
+
return []
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
301
376
|
/**
|
|
302
377
|
* View into a subset of the data in a BigWig file.
|
|
303
378
|
*
|
|
@@ -310,7 +385,7 @@ export class BlockView {
|
|
|
310
385
|
// efficiently query genomic intervals by chromosome and position
|
|
311
386
|
private rTreePromise?: Promise<Uint8Array>
|
|
312
387
|
|
|
313
|
-
private
|
|
388
|
+
private rTreeNodeCache = new AbortablePromiseCache<Block, Uint8Array>({
|
|
314
389
|
cache: new QuickLRU({ maxSize: 1000 }),
|
|
315
390
|
|
|
316
391
|
fill: async ({ length, offset }, signal) =>
|
|
@@ -337,20 +412,21 @@ export class BlockView {
|
|
|
337
412
|
start: number,
|
|
338
413
|
end: number,
|
|
339
414
|
opts?: Options,
|
|
340
|
-
): Promise<{ blocks:
|
|
415
|
+
): Promise<{ blocks: Block[]; chrId: number } | undefined> {
|
|
341
416
|
const chrId = this.refsByName[chrName]
|
|
342
417
|
if (chrId === undefined) {
|
|
343
418
|
return undefined
|
|
344
419
|
}
|
|
345
420
|
if (!this.rTreePromise) {
|
|
346
|
-
this.rTreePromise = this.bbi
|
|
421
|
+
this.rTreePromise = this.bbi
|
|
422
|
+
.read(48, this.rTreeOffset, opts)
|
|
423
|
+
.catch((e: unknown) => {
|
|
424
|
+
this.rTreePromise = undefined
|
|
425
|
+
throw e
|
|
426
|
+
})
|
|
347
427
|
}
|
|
348
428
|
const buffer = await this.rTreePromise
|
|
349
|
-
const dataView =
|
|
350
|
-
buffer.buffer,
|
|
351
|
-
buffer.byteOffset,
|
|
352
|
-
buffer.length,
|
|
353
|
-
)
|
|
429
|
+
const dataView = getDataView(buffer)
|
|
354
430
|
const magic = dataView.getUint32(0, true)
|
|
355
431
|
if (magic !== CIR_TREE_MAGIC) {
|
|
356
432
|
throw new Error(
|
|
@@ -370,7 +446,7 @@ export class BlockView {
|
|
|
370
446
|
(startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
371
447
|
(endChrom > chrId || (endChrom === chrId && endBase >= start))
|
|
372
448
|
|
|
373
|
-
const blocks:
|
|
449
|
+
const blocks: Block[] = []
|
|
374
450
|
let currentOffsets = [this.rTreeOffset + 48]
|
|
375
451
|
|
|
376
452
|
while (currentOffsets.length > 0) {
|
|
@@ -381,7 +457,7 @@ export class BlockView {
|
|
|
381
457
|
for (const { min, max } of spans) {
|
|
382
458
|
const length = max - min
|
|
383
459
|
const offset = min
|
|
384
|
-
const resultBuffer = await this.
|
|
460
|
+
const resultBuffer = await this.rTreeNodeCache.get(
|
|
385
461
|
`${length}_${offset}`,
|
|
386
462
|
{ length, offset },
|
|
387
463
|
opts?.signal,
|
|
@@ -389,42 +465,36 @@ export class BlockView {
|
|
|
389
465
|
for (const element of currentOffsets) {
|
|
390
466
|
if (min <= element && element <= max) {
|
|
391
467
|
const data = resultBuffer.subarray(element - offset)
|
|
392
|
-
const dv =
|
|
468
|
+
const dv = getDataView(data)
|
|
393
469
|
const isLeaf = dv.getUint8(0)
|
|
394
470
|
const count = dv.getUint16(2, true)
|
|
395
|
-
|
|
396
|
-
|
|
471
|
+
if (isLeaf === 1 || isLeaf === 0) {
|
|
472
|
+
const entrySize = isLeaf === 1 ? 32 : 24
|
|
473
|
+
let nodeOffset = 4
|
|
397
474
|
for (let i = 0; i < count; i++) {
|
|
398
475
|
const startChrom = dv.getUint32(nodeOffset, true)
|
|
399
476
|
const startBase = dv.getUint32(nodeOffset + 4, true)
|
|
400
477
|
const endChrom = dv.getUint32(nodeOffset + 8, true)
|
|
401
478
|
const endBase = dv.getUint32(nodeOffset + 12, true)
|
|
402
|
-
const blockOffset = Number(
|
|
403
|
-
dv.getBigUint64(nodeOffset + 16, true),
|
|
404
|
-
)
|
|
405
|
-
const blockSize = Number(dv.getBigUint64(nodeOffset + 24, true))
|
|
406
|
-
nodeOffset += 32
|
|
407
479
|
if (
|
|
408
480
|
blockIntersectsQuery(startChrom, startBase, endChrom, endBase)
|
|
409
481
|
) {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
blockIntersectsQuery(startChrom, startBase, endChrom, endBase)
|
|
425
|
-
) {
|
|
426
|
-
nextOffsets.push(childOffset)
|
|
482
|
+
const childOrBlockOffset = Number(
|
|
483
|
+
dv.getBigUint64(nodeOffset + 16, true),
|
|
484
|
+
)
|
|
485
|
+
if (isLeaf === 1) {
|
|
486
|
+
const blockSize = Number(
|
|
487
|
+
dv.getBigUint64(nodeOffset + 24, true),
|
|
488
|
+
)
|
|
489
|
+
blocks.push({
|
|
490
|
+
offset: childOrBlockOffset,
|
|
491
|
+
length: blockSize,
|
|
492
|
+
})
|
|
493
|
+
} else {
|
|
494
|
+
nextOffsets.push(childOrBlockOffset)
|
|
495
|
+
}
|
|
427
496
|
}
|
|
497
|
+
nodeOffset += entrySize
|
|
428
498
|
}
|
|
429
499
|
}
|
|
430
500
|
}
|
|
@@ -460,15 +530,34 @@ export class BlockView {
|
|
|
460
530
|
opts?: Options,
|
|
461
531
|
): Promise<BigWigFeatureArrays | SummaryFeatureArrays> {
|
|
462
532
|
const collected = await this._collectBlocks(chrName, start, end, opts)
|
|
463
|
-
const blocks = collected?.blocks ?? []
|
|
464
|
-
const request = collected
|
|
465
|
-
? { chrId: collected.chrId, start, end }
|
|
466
|
-
: undefined
|
|
467
|
-
const optsWithReq = { ...opts, request }
|
|
468
533
|
if (this.blockType === 'summary') {
|
|
469
|
-
return
|
|
534
|
+
return collected
|
|
535
|
+
? this._readSummaryFeaturesAsArrays(
|
|
536
|
+
collected.blocks,
|
|
537
|
+
{ chrId: collected.chrId, start, end },
|
|
538
|
+
opts,
|
|
539
|
+
)
|
|
540
|
+
: {
|
|
541
|
+
starts: new Int32Array(0),
|
|
542
|
+
ends: new Int32Array(0),
|
|
543
|
+
scores: new Float32Array(0),
|
|
544
|
+
minScores: new Float32Array(0),
|
|
545
|
+
maxScores: new Float32Array(0),
|
|
546
|
+
isSummary: true as const,
|
|
547
|
+
}
|
|
470
548
|
}
|
|
471
|
-
return
|
|
549
|
+
return collected
|
|
550
|
+
? this._readBigWigFeaturesAsArrays(
|
|
551
|
+
collected.blocks,
|
|
552
|
+
{ chrId: collected.chrId, start, end },
|
|
553
|
+
opts,
|
|
554
|
+
)
|
|
555
|
+
: {
|
|
556
|
+
starts: new Int32Array(0),
|
|
557
|
+
ends: new Int32Array(0),
|
|
558
|
+
scores: new Float32Array(0),
|
|
559
|
+
isSummary: false as const,
|
|
560
|
+
}
|
|
472
561
|
}
|
|
473
562
|
|
|
474
563
|
public async readFeatures(
|
|
@@ -486,55 +575,44 @@ export class BlockView {
|
|
|
486
575
|
const groupOffset = blockGroup.offset
|
|
487
576
|
const subBlocks = blockGroup.blocks
|
|
488
577
|
|
|
489
|
-
let decompressedData: Uint8Array
|
|
490
|
-
let decompressedOffsets: number[]
|
|
491
|
-
|
|
492
578
|
if (uncompressBufSize > 0) {
|
|
493
|
-
const localBlocks
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
579
|
+
const localBlocks = subBlocks.map(block => ({
|
|
580
|
+
offset: block.offset - groupOffset,
|
|
581
|
+
length: block.length,
|
|
582
|
+
}))
|
|
583
|
+
const { data: decompressedData, offsets } = await unzipBatch(
|
|
584
|
+
data,
|
|
585
|
+
localBlocks,
|
|
586
|
+
uncompressBufSize,
|
|
587
|
+
)
|
|
588
|
+
for (let i = 0; i < subBlocks.length; i++) {
|
|
589
|
+
const resultData = decompressedData.subarray(
|
|
590
|
+
offsets[i],
|
|
591
|
+
offsets[i + 1],
|
|
592
|
+
)
|
|
593
|
+
const features = parseBlock(
|
|
594
|
+
blockType,
|
|
595
|
+
resultData,
|
|
596
|
+
subBlocks[i]!.offset,
|
|
597
|
+
request,
|
|
598
|
+
)
|
|
599
|
+
for (const f of features) {
|
|
600
|
+
allFeatures.push(f)
|
|
601
|
+
}
|
|
499
602
|
}
|
|
500
|
-
const result = await unzipBatch(data, localBlocks, uncompressBufSize)
|
|
501
|
-
decompressedData = result.data
|
|
502
|
-
decompressedOffsets = result.offsets
|
|
503
603
|
} else {
|
|
504
|
-
decompressedData = data
|
|
505
|
-
decompressedOffsets = []
|
|
506
604
|
for (const block of subBlocks) {
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
case 'summary':
|
|
519
|
-
features = parseSummaryBlock(resultData, 0, request)
|
|
520
|
-
break
|
|
521
|
-
case 'bigwig':
|
|
522
|
-
features = parseBigWigBlock(resultData, 0, request)
|
|
523
|
-
break
|
|
524
|
-
case 'bigbed':
|
|
525
|
-
features = parseBigBedBlock(
|
|
526
|
-
resultData,
|
|
527
|
-
0,
|
|
528
|
-
subBlocks[i]!.offset * (1 << 8),
|
|
529
|
-
request,
|
|
530
|
-
)
|
|
531
|
-
break
|
|
532
|
-
default:
|
|
533
|
-
features = []
|
|
534
|
-
console.warn(`Don't know what to do with ${blockType}`)
|
|
535
|
-
}
|
|
536
|
-
for (const f of features) {
|
|
537
|
-
allFeatures.push(f)
|
|
605
|
+
const start = block.offset - groupOffset
|
|
606
|
+
const resultData = data.subarray(start, start + block.length)
|
|
607
|
+
const features = parseBlock(
|
|
608
|
+
blockType,
|
|
609
|
+
resultData,
|
|
610
|
+
block.offset,
|
|
611
|
+
request,
|
|
612
|
+
)
|
|
613
|
+
for (const f of features) {
|
|
614
|
+
allFeatures.push(f)
|
|
615
|
+
}
|
|
538
616
|
}
|
|
539
617
|
}
|
|
540
618
|
}
|
|
@@ -543,10 +621,11 @@ export class BlockView {
|
|
|
543
621
|
|
|
544
622
|
private async _readBigWigFeaturesAsArrays(
|
|
545
623
|
blocks: { offset: number; length: number }[],
|
|
624
|
+
request: CoordRequest,
|
|
546
625
|
opts: Options = {},
|
|
547
626
|
): Promise<BigWigFeatureArrays> {
|
|
548
627
|
const { uncompressBufSize } = this
|
|
549
|
-
const { signal
|
|
628
|
+
const { signal } = opts
|
|
550
629
|
const blockGroupsToFetch = groupBlocks(blocks)
|
|
551
630
|
|
|
552
631
|
const allStarts: Int32Array[] = []
|
|
@@ -568,8 +647,8 @@ export class BlockView {
|
|
|
568
647
|
data,
|
|
569
648
|
localBlocks,
|
|
570
649
|
uncompressBufSize,
|
|
571
|
-
request
|
|
572
|
-
request
|
|
650
|
+
request.start,
|
|
651
|
+
request.end,
|
|
573
652
|
)
|
|
574
653
|
if (result.starts.length > 0) {
|
|
575
654
|
allStarts.push(result.starts)
|
|
@@ -628,10 +707,11 @@ export class BlockView {
|
|
|
628
707
|
|
|
629
708
|
private async _readSummaryFeaturesAsArrays(
|
|
630
709
|
blocks: { offset: number; length: number }[],
|
|
710
|
+
request: CoordRequest,
|
|
631
711
|
opts: Options = {},
|
|
632
712
|
): Promise<SummaryFeatureArrays> {
|
|
633
713
|
const { uncompressBufSize } = this
|
|
634
|
-
const { signal
|
|
714
|
+
const { signal } = opts
|
|
635
715
|
const blockGroupsToFetch = groupBlocks(blocks)
|
|
636
716
|
|
|
637
717
|
const allStarts: Int32Array[] = []
|
|
@@ -655,9 +735,9 @@ export class BlockView {
|
|
|
655
735
|
data,
|
|
656
736
|
localBlocks,
|
|
657
737
|
uncompressBufSize,
|
|
658
|
-
request
|
|
659
|
-
request
|
|
660
|
-
request
|
|
738
|
+
request.chrId,
|
|
739
|
+
request.start,
|
|
740
|
+
request.end,
|
|
661
741
|
)
|
|
662
742
|
if (result.starts.length > 0) {
|
|
663
743
|
allStarts.push(result.starts)
|
|
@@ -673,27 +753,14 @@ export class BlockView {
|
|
|
673
753
|
block.offset,
|
|
674
754
|
block.offset + block.length,
|
|
675
755
|
)
|
|
676
|
-
const
|
|
677
|
-
if (
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const f = features[i]!
|
|
685
|
-
starts[i] = f.start
|
|
686
|
-
ends[i] = f.end
|
|
687
|
-
scores[i] = f.score ?? 0
|
|
688
|
-
minScores[i] = f.minScore ?? 0
|
|
689
|
-
maxScores[i] = f.maxScore ?? 0
|
|
690
|
-
}
|
|
691
|
-
allStarts.push(starts)
|
|
692
|
-
allEnds.push(ends)
|
|
693
|
-
allScores.push(scores)
|
|
694
|
-
allMinScores.push(minScores)
|
|
695
|
-
allMaxScores.push(maxScores)
|
|
696
|
-
totalCount += features.length
|
|
756
|
+
const result = parseSummaryBlockAsArrays(blockData, 0, request)
|
|
757
|
+
if (result.starts.length > 0) {
|
|
758
|
+
allStarts.push(result.starts)
|
|
759
|
+
allEnds.push(result.ends)
|
|
760
|
+
allScores.push(result.scores)
|
|
761
|
+
allMinScores.push(result.minScores)
|
|
762
|
+
allMaxScores.push(result.maxScores)
|
|
763
|
+
totalCount += result.starts.length
|
|
697
764
|
}
|
|
698
765
|
}
|
|
699
766
|
}
|
package/src/range.ts
CHANGED
|
@@ -6,9 +6,10 @@ export interface IRange {
|
|
|
6
6
|
// Merges overlapping or adjacent byte ranges so that nearby R-tree nodes can
|
|
7
7
|
// be fetched in a single read rather than many small reads
|
|
8
8
|
export function mergeRanges(ranges: IRange[]) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
if (ranges.length === 0) {
|
|
10
|
+
return []
|
|
11
|
+
}
|
|
12
|
+
const sorted = ranges.toSorted((a, b) => a.min - b.min)
|
|
12
13
|
|
|
13
14
|
const merged: IRange[] = []
|
|
14
15
|
let current = sorted[0]!
|
package/src/types.ts
CHANGED
package/src/unzip.ts
CHANGED
|
@@ -9,20 +9,23 @@ export interface UnzipBatchResult {
|
|
|
9
9
|
offsets: number[]
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
data: Uint8Array,
|
|
14
|
-
blocks: { offset: number; length: number }[],
|
|
15
|
-
maxOutputSize: number,
|
|
16
|
-
): Promise<UnzipBatchResult> {
|
|
12
|
+
function blocksToTypedArrays(blocks: { offset: number; length: number }[]) {
|
|
17
13
|
const inputOffsets = new Uint32Array(blocks.length)
|
|
18
14
|
const inputLengths = new Uint32Array(blocks.length)
|
|
19
|
-
|
|
20
15
|
for (let i = 0; i < blocks.length; i++) {
|
|
21
16
|
const block = blocks[i]!
|
|
22
17
|
inputOffsets[i] = block.offset
|
|
23
18
|
inputLengths[i] = block.length
|
|
24
19
|
}
|
|
20
|
+
return { inputOffsets, inputLengths }
|
|
21
|
+
}
|
|
25
22
|
|
|
23
|
+
export async function unzipBatch(
|
|
24
|
+
data: Uint8Array,
|
|
25
|
+
blocks: { offset: number; length: number }[],
|
|
26
|
+
maxOutputSize: number,
|
|
27
|
+
): Promise<UnzipBatchResult> {
|
|
28
|
+
const { inputOffsets, inputLengths } = blocksToTypedArrays(blocks)
|
|
26
29
|
return inflateRawBatch(data, inputOffsets, inputLengths, maxOutputSize)
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -33,15 +36,7 @@ export async function decompressAndParseBigWigBlocks(
|
|
|
33
36
|
reqStart: number,
|
|
34
37
|
reqEnd: number,
|
|
35
38
|
) {
|
|
36
|
-
const inputOffsets =
|
|
37
|
-
const inputLengths = new Uint32Array(blocks.length)
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
40
|
-
const block = blocks[i]!
|
|
41
|
-
inputOffsets[i] = block.offset
|
|
42
|
-
inputLengths[i] = block.length
|
|
43
|
-
}
|
|
44
|
-
|
|
39
|
+
const { inputOffsets, inputLengths } = blocksToTypedArrays(blocks)
|
|
45
40
|
return decompressAndParseBigWig(
|
|
46
41
|
data,
|
|
47
42
|
inputOffsets,
|
|
@@ -60,15 +55,7 @@ export async function decompressAndParseSummaryBlocks(
|
|
|
60
55
|
reqStart: number,
|
|
61
56
|
reqEnd: number,
|
|
62
57
|
) {
|
|
63
|
-
const inputOffsets =
|
|
64
|
-
const inputLengths = new Uint32Array(blocks.length)
|
|
65
|
-
|
|
66
|
-
for (let i = 0; i < blocks.length; i++) {
|
|
67
|
-
const block = blocks[i]!
|
|
68
|
-
inputOffsets[i] = block.offset
|
|
69
|
-
inputLengths[i] = block.length
|
|
70
|
-
}
|
|
71
|
-
|
|
58
|
+
const { inputOffsets, inputLengths } = blocksToTypedArrays(blocks)
|
|
72
59
|
return decompressAndParseSummary(
|
|
73
60
|
data,
|
|
74
61
|
inputOffsets,
|
package/src/util.ts
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
const decoder = new TextDecoder('utf8')
|
|
2
|
+
|
|
3
|
+
export interface Block {
|
|
2
4
|
offset: number
|
|
3
5
|
length: number
|
|
4
6
|
}
|
|
7
|
+
|
|
8
|
+
export function getDataView(buffer: Uint8Array, byteOffset = 0) {
|
|
9
|
+
return new DataView(
|
|
10
|
+
buffer.buffer,
|
|
11
|
+
buffer.byteOffset + byteOffset,
|
|
12
|
+
buffer.length - byteOffset,
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Decode a null-terminated fixed-width key from a B+ tree node
|
|
17
|
+
export function parseKey(buffer: Uint8Array, offset: number, keySize: number) {
|
|
18
|
+
const nullPos = buffer.indexOf(0, offset)
|
|
19
|
+
const end =
|
|
20
|
+
nullPos !== -1 && nullPos < offset + keySize ? nullPos : offset + keySize
|
|
21
|
+
return decoder.decode(buffer.subarray(offset, end))
|
|
22
|
+
}
|
|
23
|
+
|
|
5
24
|
// sort blocks by file offset and
|
|
6
25
|
// group blocks that are within 2KB of eachother
|
|
7
26
|
export function groupBlocks(blocks: Block[]) {
|
|
@@ -11,7 +30,7 @@ export function groupBlocks(blocks: Block[]) {
|
|
|
11
30
|
let lastBlock: (Block & { blocks: Block[] }) | undefined
|
|
12
31
|
let lastBlockEnd: number | undefined
|
|
13
32
|
for (const block of blocks) {
|
|
14
|
-
if (lastBlock &&
|
|
33
|
+
if (lastBlock && block.offset - lastBlockEnd! <= 2000) {
|
|
15
34
|
lastBlock.length = block.offset + block.length - lastBlock.offset
|
|
16
35
|
lastBlock.blocks.push(block)
|
|
17
36
|
lastBlockEnd = block.offset + block.length
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface BatchResult {
|
|
2
|
+
data: Uint8Array;
|
|
3
|
+
offsets: number[];
|
|
4
|
+
}
|
|
5
|
+
export interface BigWigFeatureArrays {
|
|
6
|
+
starts: Int32Array;
|
|
7
|
+
ends: Int32Array;
|
|
8
|
+
scores: Float32Array;
|
|
9
|
+
}
|
|
10
|
+
export interface SummaryFeatureArrays {
|
|
11
|
+
starts: Int32Array;
|
|
12
|
+
ends: Int32Array;
|
|
13
|
+
scores: Float32Array;
|
|
14
|
+
minScores: Float32Array;
|
|
15
|
+
maxScores: Float32Array;
|
|
16
|
+
}
|
|
17
|
+
export declare function inflateRaw(input: Uint8Array, outputSize: number): Promise<Uint8Array>;
|
|
18
|
+
export declare function inflateRawUnknownSize(input: Uint8Array): Promise<Uint8Array>;
|
|
19
|
+
export declare function inflateRawBatch(inputs: Uint8Array, inputOffsets: Uint32Array, inputLengths: Uint32Array, maxOutputSize: number): Promise<BatchResult>;
|
|
20
|
+
export declare function decompressAndParseBigWig(inputs: Uint8Array, inputOffsets: Uint32Array, inputLengths: Uint32Array, maxBlockSize: number, reqStart: number, reqEnd: number): Promise<BigWigFeatureArrays>;
|
|
21
|
+
export declare function decompressAndParseSummary(inputs: Uint8Array, inputOffsets: Uint32Array, inputLengths: Uint32Array, maxBlockSize: number, reqChrId: number, reqStart: number, reqEnd: number): Promise<SummaryFeatureArrays>;
|