@gmod/bbi 9.0.9 → 9.0.12

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.
Files changed (80) hide show
  1. package/README.md +70 -12
  2. package/dist/array-feature-view.d.ts +5 -6
  3. package/dist/array-feature-view.js +7 -13
  4. package/dist/array-feature-view.js.map +1 -1
  5. package/dist/bbi.js +22 -37
  6. package/dist/bbi.js.map +1 -1
  7. package/dist/bigbed.d.ts +1 -2
  8. package/dist/bigbed.js +24 -36
  9. package/dist/bigbed.js.map +1 -1
  10. package/dist/bigwig.js +1 -1
  11. package/dist/bigwig.js.map +1 -1
  12. package/dist/block-view.d.ts +1 -1
  13. package/dist/block-view.js +135 -97
  14. package/dist/block-view.js.map +1 -1
  15. package/dist/range.js +4 -1
  16. package/dist/range.js.map +1 -1
  17. package/dist/types.d.ts +0 -1
  18. package/dist/unzip.d.ts +2 -12
  19. package/dist/unzip.js +7 -15
  20. package/dist/unzip.js.map +1 -1
  21. package/dist/util.d.ts +3 -2
  22. package/dist/util.js +13 -1
  23. package/dist/util.js.map +1 -1
  24. package/dist/wasm/inflate-wasm-inlined.d.ts +21 -18
  25. package/dist/wasm/inflate-wasm-inlined.js +146 -74
  26. package/dist/wasm/package.json +1 -0
  27. package/esm/array-feature-view.d.ts +5 -6
  28. package/esm/array-feature-view.js +7 -13
  29. package/esm/array-feature-view.js.map +1 -1
  30. package/esm/bbi.js +19 -34
  31. package/esm/bbi.js.map +1 -1
  32. package/esm/bigbed.d.ts +1 -2
  33. package/esm/bigbed.js +22 -31
  34. package/esm/bigbed.js.map +1 -1
  35. package/esm/bigwig.js +1 -1
  36. package/esm/bigwig.js.map +1 -1
  37. package/esm/block-view.d.ts +1 -1
  38. package/esm/block-view.js +136 -98
  39. package/esm/block-view.js.map +1 -1
  40. package/esm/range.js +4 -1
  41. package/esm/range.js.map +1 -1
  42. package/esm/types.d.ts +0 -1
  43. package/esm/unzip.d.ts +2 -12
  44. package/esm/unzip.js +7 -15
  45. package/esm/unzip.js.map +1 -1
  46. package/esm/util.d.ts +3 -2
  47. package/esm/util.js +11 -1
  48. package/esm/util.js.map +1 -1
  49. package/esm/wasm/inflate-wasm-inlined.d.ts +21 -18
  50. package/esm/wasm/inflate-wasm-inlined.js +145 -67
  51. package/package.json +23 -22
  52. package/src/array-feature-view.ts +15 -26
  53. package/src/bbi.ts +20 -36
  54. package/src/bigbed.ts +22 -38
  55. package/src/bigwig.ts +2 -2
  56. package/src/block-view.ts +189 -122
  57. package/src/range.ts +4 -3
  58. package/src/types.ts +0 -1
  59. package/src/unzip.ts +11 -24
  60. package/src/util.ts +21 -2
  61. package/src/wasm/inflate-wasm-inlined.d.ts +21 -0
  62. package/src/wasm/inflate-wasm-inlined.js +150 -170
  63. package/src/wasm/inflate_wasm.js +1 -1
  64. package/src/wasm/inflate_wasm_bg.js +2 -3
  65. package/src/wasm/inflate_wasm_bg.wasm +0 -0
  66. package/dist/wasm/inflate-wasm-inlined.js.map +0 -1
  67. package/dist/wasm/inflate_wasm.d.ts +0 -1
  68. package/dist/wasm/inflate_wasm.js +0 -49
  69. package/dist/wasm/inflate_wasm.js.map +0 -1
  70. package/dist/wasm/inflate_wasm_bg.d.ts +0 -68
  71. package/dist/wasm/inflate_wasm_bg.js +0 -306
  72. package/dist/wasm/inflate_wasm_bg.js.map +0 -1
  73. package/esm/wasm/inflate-wasm-inlined.js.map +0 -1
  74. package/esm/wasm/inflate_wasm.d.ts +0 -1
  75. package/esm/wasm/inflate_wasm.js +0 -6
  76. package/esm/wasm/inflate_wasm.js.map +0 -1
  77. package/esm/wasm/inflate_wasm_bg.d.ts +0 -68
  78. package/esm/wasm/inflate_wasm_bg.js +0 -295
  79. package/esm/wasm/inflate_wasm_bg.js.map +0 -1
  80. 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 / (validCnt || 1),
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 featureCache = new AbortablePromiseCache<ReadData, Uint8Array>({
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: ReadData[]; chrId: number } | undefined> {
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.read(48, this.rTreeOffset, opts)
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 = new 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: ReadData[] = []
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.featureCache.get(
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 = new DataView(data.buffer, data.byteOffset, data.length)
468
+ const dv = getDataView(data)
393
469
  const isLeaf = dv.getUint8(0)
394
470
  const count = dv.getUint16(2, true)
395
- let nodeOffset = 4
396
- if (isLeaf === 1) {
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
- blocks.push({ offset: blockOffset, length: blockSize })
411
- }
412
- }
413
- } else if (isLeaf === 0) {
414
- for (let i = 0; i < count; i++) {
415
- const startChrom = dv.getUint32(nodeOffset, true)
416
- const startBase = dv.getUint32(nodeOffset + 4, true)
417
- const endChrom = dv.getUint32(nodeOffset + 8, true)
418
- const endBase = dv.getUint32(nodeOffset + 12, true)
419
- const childOffset = Number(
420
- dv.getBigUint64(nodeOffset + 16, true),
421
- )
422
- nodeOffset += 24
423
- if (
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 this._readSummaryFeaturesAsArrays(blocks, optsWithReq)
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 this._readBigWigFeaturesAsArrays(blocks, optsWithReq)
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: { offset: number; length: number }[] = []
494
- for (const block of subBlocks) {
495
- localBlocks.push({
496
- offset: block.offset - groupOffset,
497
- length: block.length,
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
- decompressedOffsets.push(block.offset - groupOffset)
508
- }
509
- decompressedOffsets.push(data.length)
510
- }
511
-
512
- for (let i = 0; i < subBlocks.length; i++) {
513
- const start = decompressedOffsets[i]!
514
- const end = decompressedOffsets[i + 1]!
515
- const resultData = decompressedData.subarray(start, end)
516
- let features: Feature[]
517
- switch (blockType) {
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, request } = opts
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?.start ?? 0,
572
- request?.end ?? 0,
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, request } = opts
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?.chrId ?? 0,
659
- request?.start ?? 0,
660
- request?.end ?? 0,
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 features = parseSummaryBlock(blockData, 0, request)
677
- if (features.length > 0) {
678
- const starts = new Int32Array(features.length)
679
- const ends = new Int32Array(features.length)
680
- const scores = new Float32Array(features.length)
681
- const minScores = new Float32Array(features.length)
682
- const maxScores = new Float32Array(features.length)
683
- for (let i = 0; i < features.length; i++) {
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
- const sorted = [...ranges].sort((a, b) =>
10
- a.min !== b.min ? a.min - b.min : a.max - b.max,
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
@@ -1,6 +1,5 @@
1
1
  export interface ZoomLevel {
2
2
  reductionLevel: number
3
- reserved: number
4
3
  dataOffset: number
5
4
  indexOffset: number
6
5
  }
package/src/unzip.ts CHANGED
@@ -9,20 +9,23 @@ export interface UnzipBatchResult {
9
9
  offsets: number[]
10
10
  }
11
11
 
12
- export async function unzipBatch(
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 = new Uint32Array(blocks.length)
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 = new Uint32Array(blocks.length)
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
- interface Block {
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 && lastBlockEnd && block.offset - lastBlockEnd <= 2000) {
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 WasmBigWigArrays {
6
+ starts: Int32Array;
7
+ ends: Int32Array;
8
+ scores: Float32Array;
9
+ }
10
+ export interface WasmSummaryArrays {
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<WasmBigWigArrays>;
21
+ export declare function decompressAndParseSummary(inputs: Uint8Array, inputOffsets: Uint32Array, inputLengths: Uint32Array, maxBlockSize: number, reqChrId: number, reqStart: number, reqEnd: number): Promise<WasmSummaryArrays>;