@gmod/bam 7.1.0 → 7.1.2

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/src/csi.ts CHANGED
@@ -215,12 +215,13 @@ export default class CSI extends IndexFile {
215
215
 
216
216
  const chunks = []
217
217
  // Find chunks in overlapping bins. Leaf bins (< 4681) are not pruned
218
+ const { binIndex } = ba
218
219
  for (const [start, end] of overlappingBins) {
219
220
  for (let bin = start; bin <= end; bin++) {
220
- if (ba.binIndex[bin]) {
221
- const binChunks = ba.binIndex[bin]!
222
- for (const c of binChunks) {
223
- chunks.push(c)
221
+ const binChunks = binIndex[bin]
222
+ if (binChunks) {
223
+ for (let i = 0, l = binChunks.length; i < l; i++) {
224
+ chunks.push(binChunks[i]!)
224
225
  }
225
226
  }
226
227
  }
package/src/indexFile.ts CHANGED
@@ -1,8 +1,14 @@
1
1
  import Chunk from './chunk.ts'
2
- import { BaseOpts } from './util.ts'
2
+ import { BaseOpts, optimizeChunks } from './util.ts'
3
3
 
4
4
  import type { GenericFilehandle } from 'generic-filehandle2'
5
5
 
6
+ export interface Region {
7
+ refId: number
8
+ start: number
9
+ end: number
10
+ }
11
+
6
12
  export default abstract class IndexFile {
7
13
  public filehandle: GenericFilehandle
8
14
  public renameRefSeq: (s: string) => string
@@ -30,4 +36,19 @@ export default abstract class IndexFile {
30
36
  end: number,
31
37
  opts?: BaseOpts,
32
38
  ): Promise<Chunk[]>
39
+
40
+ async estimatedBytesForRegions(regions: Region[], opts?: BaseOpts) {
41
+ const blockResults = await Promise.all(
42
+ regions.map(r => this.blocksForRange(r.refId, r.start, r.end, opts)),
43
+ )
44
+
45
+ // Deduplicate and merge overlapping blocks across all regions
46
+ const mergedBlocks = optimizeChunks(blockResults.flat())
47
+
48
+ let total = 0
49
+ for (const block of mergedBlocks) {
50
+ total += block.fetchedSize()
51
+ }
52
+ return total
53
+ }
33
54
  }
package/src/util.ts CHANGED
@@ -25,33 +25,68 @@ export function makeOpts(obj: AbortSignal | BaseOpts = {}): BaseOpts {
25
25
  }
26
26
 
27
27
  export function optimizeChunks(chunks: Chunk[], lowest?: Offset) {
28
- const mergedChunks: Chunk[] = []
29
- let lastChunk: Chunk | undefined
30
-
31
- if (chunks.length === 0) {
28
+ const n = chunks.length
29
+ if (n === 0) {
32
30
  return chunks
33
31
  }
34
32
 
35
- chunks.sort((c0, c1) => {
33
+ // Pre-filter chunks below lowest threshold before sorting
34
+ let filtered: Chunk[]
35
+ if (lowest) {
36
+ const lowestBlock = lowest.blockPosition
37
+ const lowestData = lowest.dataPosition
38
+ filtered = []
39
+ for (let i = 0; i < n; i++) {
40
+ const chunk = chunks[i]!
41
+ const maxv = chunk.maxv
42
+ const cmp =
43
+ maxv.blockPosition - lowestBlock || maxv.dataPosition - lowestData
44
+ if (cmp > 0) {
45
+ filtered.push(chunk)
46
+ }
47
+ }
48
+ if (filtered.length === 0) {
49
+ return filtered
50
+ }
51
+ } else {
52
+ filtered = chunks
53
+ }
54
+
55
+ filtered.sort((c0, c1) => {
36
56
  const dif = c0.minv.blockPosition - c1.minv.blockPosition
37
- return dif === 0 ? c0.minv.dataPosition - c1.minv.dataPosition : dif
57
+ return dif !== 0 ? dif : c0.minv.dataPosition - c1.minv.dataPosition
38
58
  })
39
59
 
40
- for (const chunk of chunks) {
41
- if (!lowest || chunk.maxv.compareTo(lowest) > 0) {
42
- if (lastChunk === undefined) {
43
- mergedChunks.push(chunk)
44
- lastChunk = chunk
45
- } else {
46
- if (canMergeBlocks(lastChunk, chunk)) {
47
- if (chunk.maxv.compareTo(lastChunk.maxv) > 0) {
48
- lastChunk.maxv = chunk.maxv
49
- }
50
- } else {
51
- mergedChunks.push(chunk)
52
- lastChunk = chunk
53
- }
60
+ const mergedChunks: Chunk[] = []
61
+ let lastChunk = filtered[0]!
62
+ mergedChunks.push(lastChunk)
63
+
64
+ let lastMinBlock = lastChunk.minv.blockPosition
65
+ let lastMaxBlock = lastChunk.maxv.blockPosition
66
+
67
+ for (let i = 1; i < filtered.length; i++) {
68
+ const chunk = filtered[i]!
69
+ const chunkMinBlock = chunk.minv.blockPosition
70
+ const chunkMaxBlock = chunk.maxv.blockPosition
71
+ // Inlined canMergeBlocks: check if chunks are close enough to merge
72
+ if (
73
+ chunkMinBlock - lastMaxBlock < 65000 &&
74
+ chunkMaxBlock - lastMinBlock < 5000000
75
+ ) {
76
+ const chunkMaxv = chunk.maxv
77
+ const lastMaxv = lastChunk.maxv
78
+ const cmp =
79
+ chunkMaxBlock - lastMaxBlock ||
80
+ chunkMaxv.dataPosition - lastMaxv.dataPosition
81
+ if (cmp > 0) {
82
+ lastChunk.maxv = chunkMaxv
83
+ lastMaxBlock = chunkMaxBlock
54
84
  }
85
+ } else {
86
+ mergedChunks.push(chunk)
87
+ lastChunk = chunk
88
+ lastMinBlock = chunkMinBlock
89
+ lastMaxBlock = chunkMaxBlock
55
90
  }
56
91
  }
57
92