@gmod/bam 7.1.19 → 7.1.21
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 +22 -13
- package/dist/bai.d.ts +2 -2
- package/dist/bai.js +1 -0
- package/dist/bai.js.map +1 -1
- package/dist/bamFile.d.ts +2 -1
- package/dist/bamFile.js +15 -6
- package/dist/bamFile.js.map +1 -1
- package/dist/chunk.d.ts +1 -1
- package/dist/chunk.js +5 -0
- package/dist/chunk.js.map +1 -1
- package/dist/csi.js +4 -6
- package/dist/csi.js.map +1 -1
- package/dist/htsget.d.ts +1 -1
- package/dist/htsget.js +15 -9
- package/dist/htsget.js.map +1 -1
- package/dist/indexFile.d.ts +2 -2
- package/dist/indexFile.js +2 -0
- package/dist/indexFile.js.map +1 -1
- package/dist/nullFilehandle.d.ts +4 -4
- package/dist/nullIndex.d.ts +4 -4
- package/dist/record.d.ts +11 -14
- package/dist/record.js +119 -117
- package/dist/record.js.map +1 -1
- package/dist/util.d.ts +2 -2
- package/dist/util.js +4 -7
- package/dist/util.js.map +1 -1
- package/dist/virtualOffset.js +2 -0
- package/dist/virtualOffset.js.map +1 -1
- package/esm/bai.d.ts +2 -2
- package/esm/bai.js +2 -1
- package/esm/bai.js.map +1 -1
- package/esm/bamFile.d.ts +2 -1
- package/esm/bamFile.js +15 -6
- package/esm/bamFile.js.map +1 -1
- package/esm/chunk.d.ts +1 -1
- package/esm/chunk.js +5 -0
- package/esm/chunk.js.map +1 -1
- package/esm/csi.js +4 -6
- package/esm/csi.js.map +1 -1
- package/esm/htsget.d.ts +1 -1
- package/esm/htsget.js +12 -9
- package/esm/htsget.js.map +1 -1
- package/esm/indexFile.d.ts +2 -2
- package/esm/indexFile.js +2 -0
- package/esm/indexFile.js.map +1 -1
- package/esm/nullFilehandle.d.ts +4 -4
- package/esm/nullIndex.d.ts +4 -4
- package/esm/record.d.ts +11 -14
- package/esm/record.js +119 -117
- package/esm/record.js.map +1 -1
- package/esm/util.d.ts +2 -2
- package/esm/util.js +4 -7
- package/esm/util.js.map +1 -1
- package/esm/virtualOffset.js +2 -0
- package/esm/virtualOffset.js.map +1 -1
- package/package.json +28 -32
- package/src/bai.ts +5 -7
- package/src/bamFile.ts +3 -1
- package/src/chunk.ts +1 -1
- package/src/htsget.ts +18 -14
- package/src/indexFile.ts +3 -2
- package/src/nullFilehandle.ts +4 -4
- package/src/nullIndex.ts +4 -4
- package/src/record.ts +137 -140
- package/src/util.ts +11 -11
- package/CHANGELOG.md +0 -530
package/src/record.ts
CHANGED
|
@@ -17,6 +17,8 @@ const ASCII_CIGAR_CODES = [
|
|
|
17
17
|
77, 73, 68, 78, 83, 72, 80, 61, 88, 63, 63, 63, 63, 63, 63, 63,
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
+
const textDecoder = new TextDecoder()
|
|
21
|
+
|
|
20
22
|
// Bitmask for ops that consume ref: M=0, D=2, N=3, P=6, ==7, X=8
|
|
21
23
|
// Binary: 0b111001101 = 0x1CD
|
|
22
24
|
const CIGAR_CONSUMES_REF_MASK = 0x1cd
|
|
@@ -27,55 +29,42 @@ export interface Bytes {
|
|
|
27
29
|
byteArray: Uint8Array
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
interface CIGAR_AND_LENGTH {
|
|
31
|
-
length_on_ref: number
|
|
32
|
-
NUMERIC_CIGAR: Uint32Array | number[]
|
|
33
|
-
}
|
|
34
|
-
|
|
35
32
|
export default class BamRecord {
|
|
36
33
|
public fileOffset: number
|
|
37
|
-
private
|
|
34
|
+
private _byteArray: Uint8Array
|
|
35
|
+
private _start: number
|
|
36
|
+
private _end: number
|
|
38
37
|
private _dataView: DataView
|
|
39
38
|
|
|
40
|
-
private _cachedFlags?: number
|
|
41
|
-
private _cachedRefId?: number
|
|
42
|
-
private _cachedStart?: number
|
|
43
39
|
private _cachedEnd?: number
|
|
44
40
|
private _cachedTags?: Record<string, unknown>
|
|
45
|
-
private
|
|
41
|
+
private _cachedLengthOnRef?: number
|
|
42
|
+
private _cachedNumericCigar?: Uint32Array | number[]
|
|
46
43
|
private _cachedNUMERIC_MD?: Uint8Array | null
|
|
47
44
|
private _cachedTagsStart?: number
|
|
48
45
|
|
|
49
|
-
constructor(args: { bytes: Bytes; fileOffset: number }) {
|
|
50
|
-
this.
|
|
46
|
+
constructor(args: { bytes: Bytes; fileOffset: number; dataView: DataView }) {
|
|
47
|
+
this._byteArray = args.bytes.byteArray
|
|
48
|
+
this._start = args.bytes.start
|
|
49
|
+
this._end = args.bytes.end
|
|
51
50
|
this.fileOffset = args.fileOffset
|
|
52
|
-
this._dataView =
|
|
51
|
+
this._dataView = args.dataView
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
get byteArray() {
|
|
56
|
-
return this.
|
|
55
|
+
return this._byteArray
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
get flags() {
|
|
60
|
-
|
|
61
|
-
this._cachedFlags =
|
|
62
|
-
(this._dataView.getInt32(this.bytes.start + 16, true) & 0xffff0000) >>
|
|
63
|
-
16
|
|
64
|
-
}
|
|
65
|
-
return this._cachedFlags
|
|
59
|
+
return (this._dataView.getInt32(this._start + 16, true) & 0xffff0000) >> 16
|
|
66
60
|
}
|
|
61
|
+
|
|
67
62
|
get ref_id() {
|
|
68
|
-
|
|
69
|
-
this._cachedRefId = this._dataView.getInt32(this.bytes.start + 4, true)
|
|
70
|
-
}
|
|
71
|
-
return this._cachedRefId
|
|
63
|
+
return this._dataView.getInt32(this._start + 4, true)
|
|
72
64
|
}
|
|
73
65
|
|
|
74
66
|
get start() {
|
|
75
|
-
|
|
76
|
-
this._cachedStart = this._dataView.getInt32(this.bytes.start + 8, true)
|
|
77
|
-
}
|
|
78
|
-
return this._cachedStart
|
|
67
|
+
return this._dataView.getInt32(this._start + 8, true)
|
|
79
68
|
}
|
|
80
69
|
|
|
81
70
|
get end() {
|
|
@@ -98,12 +87,13 @@ export default class BamRecord {
|
|
|
98
87
|
if (this.isSegmentUnmapped()) {
|
|
99
88
|
return null
|
|
100
89
|
} else {
|
|
90
|
+
const seqLen = this.seq_length
|
|
101
91
|
const p =
|
|
102
92
|
this.b0 +
|
|
103
93
|
this.read_name_length +
|
|
104
94
|
this.num_cigar_bytes +
|
|
105
|
-
|
|
106
|
-
return this.
|
|
95
|
+
((seqLen + 1) >> 1)
|
|
96
|
+
return this._byteArray.subarray(p, p + seqLen)
|
|
107
97
|
}
|
|
108
98
|
}
|
|
109
99
|
|
|
@@ -112,25 +102,27 @@ export default class BamRecord {
|
|
|
112
102
|
}
|
|
113
103
|
|
|
114
104
|
get b0() {
|
|
115
|
-
return this.
|
|
105
|
+
return this._start + 36
|
|
116
106
|
}
|
|
117
107
|
|
|
118
108
|
get tagsStart() {
|
|
119
109
|
if (this._cachedTagsStart === undefined) {
|
|
110
|
+
const seqLen = this.seq_length
|
|
120
111
|
this._cachedTagsStart =
|
|
121
112
|
this.b0 +
|
|
122
113
|
this.read_name_length +
|
|
123
114
|
this.num_cigar_bytes +
|
|
124
|
-
|
|
125
|
-
|
|
115
|
+
((seqLen + 1) >> 1) +
|
|
116
|
+
seqLen
|
|
126
117
|
}
|
|
127
118
|
return this._cachedTagsStart
|
|
128
119
|
}
|
|
120
|
+
|
|
129
121
|
// batch fromCharCode: fastest for typical name lengths (see benchmarks/string-building.bench.ts)
|
|
130
122
|
get name() {
|
|
131
123
|
const len = this.read_name_length - 1
|
|
132
124
|
const start = this.b0
|
|
133
|
-
const ba = this.
|
|
125
|
+
const ba = this._byteArray
|
|
134
126
|
const codes = new Array(len)
|
|
135
127
|
for (let i = 0; i < len; i++) {
|
|
136
128
|
codes[i] = ba[start + i]!
|
|
@@ -170,12 +162,12 @@ export default class BamRecord {
|
|
|
170
162
|
|
|
171
163
|
let p = this.tagsStart
|
|
172
164
|
|
|
173
|
-
const blockEnd = this.
|
|
174
|
-
const ba = this.
|
|
165
|
+
const blockEnd = this._end
|
|
166
|
+
const ba = this._byteArray
|
|
175
167
|
while (p < blockEnd) {
|
|
176
|
-
const currentTag1 = ba[p]
|
|
177
|
-
const currentTag2 = ba[p + 1]
|
|
178
|
-
const type = ba[p + 2]
|
|
168
|
+
const currentTag1 = ba[p]
|
|
169
|
+
const currentTag2 = ba[p + 1]
|
|
170
|
+
const type = ba[p + 2]
|
|
179
171
|
p += 3
|
|
180
172
|
|
|
181
173
|
const isMatch = currentTag1 === tag1 && currentTag2 === tag2
|
|
@@ -232,26 +224,21 @@ export default class BamRecord {
|
|
|
232
224
|
case 0x5a: // 'Z'
|
|
233
225
|
case 0x48: {
|
|
234
226
|
// 'H'
|
|
227
|
+
const start = p
|
|
228
|
+
while (p < blockEnd && ba[p] !== 0) {
|
|
229
|
+
p++
|
|
230
|
+
}
|
|
235
231
|
if (isMatch) {
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
p
|
|
239
|
-
}
|
|
240
|
-
if (raw) {
|
|
241
|
-
return ba.subarray(start, p)
|
|
242
|
-
}
|
|
243
|
-
const value = []
|
|
244
|
-
for (let i = start; i < p; i++) {
|
|
245
|
-
value.push(String.fromCharCode(ba[i]!))
|
|
246
|
-
}
|
|
247
|
-
return value.join('')
|
|
232
|
+
return raw
|
|
233
|
+
? ba.subarray(start, p)
|
|
234
|
+
: textDecoder.decode(ba.subarray(start, p))
|
|
248
235
|
}
|
|
249
|
-
|
|
236
|
+
p++ // advance past null terminator
|
|
250
237
|
break
|
|
251
238
|
}
|
|
252
239
|
case 0x42: {
|
|
253
240
|
// 'B'
|
|
254
|
-
const Btype = ba[p++]
|
|
241
|
+
const Btype = ba[p++]
|
|
255
242
|
const limit = this._dataView.getInt32(p, true)
|
|
256
243
|
p += 4
|
|
257
244
|
const absOffset = ba.byteOffset + p
|
|
@@ -331,9 +318,9 @@ export default class BamRecord {
|
|
|
331
318
|
private _computeTags() {
|
|
332
319
|
let p = this.tagsStart
|
|
333
320
|
|
|
334
|
-
const blockEnd = this.
|
|
335
|
-
const ba = this.
|
|
336
|
-
const tags
|
|
321
|
+
const blockEnd = this._end
|
|
322
|
+
const ba = this._byteArray
|
|
323
|
+
const tags: Record<string, unknown> = {}
|
|
337
324
|
while (p < blockEnd) {
|
|
338
325
|
const tag = String.fromCharCode(ba[p]!, ba[p + 1]!)
|
|
339
326
|
const type = ba[p + 2]!
|
|
@@ -375,21 +362,17 @@ export default class BamRecord {
|
|
|
375
362
|
case 0x5a: // 'Z'
|
|
376
363
|
case 0x48: {
|
|
377
364
|
// 'H'
|
|
378
|
-
const
|
|
379
|
-
while (p
|
|
380
|
-
|
|
381
|
-
if (cc !== 0) {
|
|
382
|
-
value.push(String.fromCharCode(cc))
|
|
383
|
-
} else {
|
|
384
|
-
break
|
|
385
|
-
}
|
|
365
|
+
const start = p
|
|
366
|
+
while (p < blockEnd && ba[p] !== 0) {
|
|
367
|
+
p++
|
|
386
368
|
}
|
|
387
|
-
tags[tag] =
|
|
369
|
+
tags[tag] = textDecoder.decode(ba.subarray(start, p))
|
|
370
|
+
p++ // advance past null terminator
|
|
388
371
|
break
|
|
389
372
|
}
|
|
390
373
|
case 0x42: {
|
|
391
374
|
// 'B'
|
|
392
|
-
const Btype = ba[p++]
|
|
375
|
+
const Btype = ba[p++]
|
|
393
376
|
const limit = this._dataView.getInt32(p, true)
|
|
394
377
|
p += 4
|
|
395
378
|
const absOffset = ba.byteOffset + p
|
|
@@ -520,13 +503,6 @@ export default class BamRecord {
|
|
|
520
503
|
return !!(this.flags & Constants.BAM_FSUPPLEMENTARY)
|
|
521
504
|
}
|
|
522
505
|
|
|
523
|
-
get cigarAndLength() {
|
|
524
|
-
if (this._cachedCigarAndLength === undefined) {
|
|
525
|
-
this._cachedCigarAndLength = this._computeCigarAndLength()
|
|
526
|
-
}
|
|
527
|
-
return this._cachedCigarAndLength
|
|
528
|
-
}
|
|
529
|
-
|
|
530
506
|
// Benchmark results for CIGAR parsing strategies (see benchmarks/cigar-lifecycle.bench.ts):
|
|
531
507
|
//
|
|
532
508
|
// Aligned data:
|
|
@@ -545,78 +521,94 @@ export default class BamRecord {
|
|
|
545
521
|
//
|
|
546
522
|
// Strategy: use plain array with |0 for small aligned (≤50 ops) and all unaligned,
|
|
547
523
|
// Uint32Array view only for large aligned CIGARs.
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
524
|
+
|
|
525
|
+
// CG tag pattern: first op is soft-clip consuming entire sequence, second op is N encoding length-on-ref
|
|
526
|
+
private _isCGTagPattern(p: number) {
|
|
527
|
+
const cigop = this._dataView.getInt32(p, true)
|
|
528
|
+
return (cigop & 0xf) === CIGAR_SOFT_CLIP && cigop >> 4 === this.seq_length
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private _computeLengthOnRef(): number {
|
|
532
|
+
const flag_nc = this._dataView.getInt32(this._start + 16, true)
|
|
533
|
+
if (flag_nc & (Constants.BAM_FUNMAP << 16)) {
|
|
534
|
+
return 0
|
|
554
535
|
}
|
|
555
536
|
|
|
556
|
-
const numCigarOps =
|
|
557
|
-
|
|
537
|
+
const numCigarOps = flag_nc & 0xffff
|
|
538
|
+
const p = this.b0 + this.read_name_length
|
|
558
539
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
const lop = cigop >> 4
|
|
563
|
-
const op = cigop & 0xf
|
|
564
|
-
if (op === CIGAR_SOFT_CLIP && lop === this.seq_length) {
|
|
565
|
-
// if there is a CG the second CIGAR field will be a N tag the represents
|
|
566
|
-
// the length on ref
|
|
567
|
-
p += 4
|
|
568
|
-
const cigop = this._dataView.getInt32(p, true)
|
|
569
|
-
const lop = cigop >> 4
|
|
570
|
-
const op = cigop & 0xf
|
|
571
|
-
if (op !== CIGAR_REF_SKIP) {
|
|
540
|
+
if (this._isCGTagPattern(p)) {
|
|
541
|
+
const cigop2 = this._dataView.getInt32(p + 4, true)
|
|
542
|
+
if ((cigop2 & 0xf) !== CIGAR_REF_SKIP) {
|
|
572
543
|
console.warn('CG tag with no N tag')
|
|
573
544
|
}
|
|
574
|
-
|
|
575
|
-
return {
|
|
576
|
-
NUMERIC_CIGAR: cgArray,
|
|
577
|
-
length_on_ref: lop,
|
|
578
|
-
}
|
|
545
|
+
return cigop2 >> 4
|
|
579
546
|
}
|
|
580
547
|
|
|
581
|
-
const absOffset = this.
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (isAligned && numCigarOps > 50) {
|
|
548
|
+
const absOffset = this._byteArray.byteOffset + p
|
|
549
|
+
if (absOffset % 4 === 0 && numCigarOps > 50) {
|
|
585
550
|
const cigarView = new Uint32Array(
|
|
586
|
-
this.
|
|
551
|
+
this._byteArray.buffer,
|
|
587
552
|
absOffset,
|
|
588
553
|
numCigarOps,
|
|
589
554
|
)
|
|
555
|
+
this._cachedNumericCigar = cigarView
|
|
590
556
|
let lref = 0
|
|
591
557
|
for (let c = 0; c < numCigarOps; ++c) {
|
|
592
|
-
const
|
|
593
|
-
lref += (
|
|
594
|
-
}
|
|
595
|
-
return {
|
|
596
|
-
NUMERIC_CIGAR: cigarView,
|
|
597
|
-
length_on_ref: lref,
|
|
558
|
+
const co = cigarView[c]!
|
|
559
|
+
lref += (co >> 4) * ((CIGAR_CONSUMES_REF_MASK >> (co & 0xf)) & 1)
|
|
598
560
|
}
|
|
561
|
+
return lref
|
|
599
562
|
}
|
|
600
563
|
|
|
601
|
-
const cigarArray: number[] = new Array(numCigarOps)
|
|
602
564
|
let lref = 0
|
|
603
565
|
for (let c = 0; c < numCigarOps; ++c) {
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
lref += (cigop >> 4) * ((CIGAR_CONSUMES_REF_MASK >> (cigop & 0xf)) & 1)
|
|
566
|
+
const co = this._dataView.getInt32(p + c * 4, true)
|
|
567
|
+
lref += (co >> 4) * ((CIGAR_CONSUMES_REF_MASK >> (co & 0xf)) & 1)
|
|
607
568
|
}
|
|
608
|
-
return
|
|
609
|
-
|
|
610
|
-
|
|
569
|
+
return lref
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
private _computeNumericCigar(): Uint32Array | number[] {
|
|
573
|
+
const flag_nc = this._dataView.getInt32(this._start + 16, true)
|
|
574
|
+
if (flag_nc & (Constants.BAM_FUNMAP << 16)) {
|
|
575
|
+
return new Uint32Array(0)
|
|
611
576
|
}
|
|
577
|
+
|
|
578
|
+
const numCigarOps = flag_nc & 0xffff
|
|
579
|
+
const p = this.b0 + this.read_name_length
|
|
580
|
+
|
|
581
|
+
if (this._isCGTagPattern(p)) {
|
|
582
|
+
return (
|
|
583
|
+
(this.tags.CG as Uint32Array | number[] | undefined) ??
|
|
584
|
+
new Uint32Array(0)
|
|
585
|
+
)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const absOffset = this._byteArray.byteOffset + p
|
|
589
|
+
if (absOffset % 4 === 0 && numCigarOps > 50) {
|
|
590
|
+
return new Uint32Array(this._byteArray.buffer, absOffset, numCigarOps)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const cigarArray: number[] = new Array(numCigarOps)
|
|
594
|
+
for (let c = 0; c < numCigarOps; ++c) {
|
|
595
|
+
cigarArray[c] = this._dataView.getInt32(p + c * 4, true) | 0
|
|
596
|
+
}
|
|
597
|
+
return cigarArray
|
|
612
598
|
}
|
|
613
599
|
|
|
614
600
|
get length_on_ref() {
|
|
615
|
-
|
|
601
|
+
if (this._cachedLengthOnRef === undefined) {
|
|
602
|
+
this._cachedLengthOnRef = this._computeLengthOnRef()
|
|
603
|
+
}
|
|
604
|
+
return this._cachedLengthOnRef
|
|
616
605
|
}
|
|
617
606
|
|
|
618
607
|
get NUMERIC_CIGAR() {
|
|
619
|
-
|
|
608
|
+
if (this._cachedNumericCigar === undefined) {
|
|
609
|
+
this._cachedNumericCigar = this._computeNumericCigar()
|
|
610
|
+
}
|
|
611
|
+
return this._cachedNumericCigar
|
|
620
612
|
}
|
|
621
613
|
|
|
622
614
|
get CIGAR() {
|
|
@@ -649,35 +641,40 @@ export default class BamRecord {
|
|
|
649
641
|
|
|
650
642
|
get NUMERIC_SEQ() {
|
|
651
643
|
const p = this.b0 + this.read_name_length + this.num_cigar_bytes
|
|
652
|
-
return this.
|
|
644
|
+
return this._byteArray.subarray(p, p + this.num_seq_bytes)
|
|
653
645
|
}
|
|
654
646
|
|
|
655
647
|
get seq() {
|
|
656
|
-
const numeric = this.NUMERIC_SEQ
|
|
657
648
|
const len = this.seq_length
|
|
649
|
+
const seqStart = this.b0 + this.read_name_length + this.num_cigar_bytes
|
|
650
|
+
const numeric = this._byteArray
|
|
658
651
|
const buf = new Array(len)
|
|
659
652
|
let i = 0
|
|
660
653
|
const fullBytes = len >> 1
|
|
661
654
|
|
|
662
655
|
for (let j = 0; j < fullBytes; ++j) {
|
|
663
|
-
const sb = numeric[j]!
|
|
664
|
-
buf[i++] = SEQRET_DECODER[(sb & 0xf0) >> 4]
|
|
665
|
-
buf[i++] = SEQRET_DECODER[sb & 0x0f]
|
|
656
|
+
const sb = numeric[seqStart + j]!
|
|
657
|
+
buf[i++] = SEQRET_DECODER[(sb & 0xf0) >> 4]!
|
|
658
|
+
buf[i++] = SEQRET_DECODER[sb & 0x0f]!
|
|
666
659
|
}
|
|
667
660
|
|
|
668
661
|
if (i < len) {
|
|
669
|
-
const sb = numeric[fullBytes]!
|
|
670
|
-
buf[i] = SEQRET_DECODER[(sb & 0xf0) >> 4]
|
|
662
|
+
const sb = numeric[seqStart + fullBytes]!
|
|
663
|
+
buf[i] = SEQRET_DECODER[(sb & 0xf0) >> 4]!
|
|
671
664
|
}
|
|
672
665
|
|
|
673
666
|
return buf.join('')
|
|
674
667
|
}
|
|
675
668
|
|
|
676
669
|
// adapted from igv.js
|
|
677
|
-
// uses precomputed lookup table indexed by flag bits + isize sign
|
|
670
|
+
// uses precomputed lookup table indexed by flag bits + isize sign.
|
|
671
|
+
// the BAM spec defines tlen as positive for the leftmost segment and
|
|
672
|
+
// negative for the rightmost, so tlen > 0 reliably indicates which
|
|
673
|
+
// read comes first without needing position-based correction
|
|
674
|
+
// (see also: gmod/cram-js src/cramFile/record.ts getPairOrientation)
|
|
678
675
|
get pair_orientation() {
|
|
679
676
|
const f = this.flags
|
|
680
|
-
//
|
|
677
|
+
// unmapped (0x4) or mate unmapped (0x8) -> undefined
|
|
681
678
|
if (f & 0xc || this.ref_id !== this.next_refid) {
|
|
682
679
|
return undefined
|
|
683
680
|
}
|
|
@@ -687,52 +684,52 @@ export default class BamRecord {
|
|
|
687
684
|
}
|
|
688
685
|
|
|
689
686
|
get bin_mq_nl() {
|
|
690
|
-
return this._dataView.getInt32(this.
|
|
687
|
+
return this._dataView.getInt32(this._start + 12, true)
|
|
691
688
|
}
|
|
692
689
|
|
|
693
690
|
get flag_nc() {
|
|
694
|
-
return this._dataView.getInt32(this.
|
|
691
|
+
return this._dataView.getInt32(this._start + 16, true)
|
|
695
692
|
}
|
|
696
693
|
|
|
697
694
|
get seq_length() {
|
|
698
|
-
return this._dataView.getInt32(this.
|
|
695
|
+
return this._dataView.getInt32(this._start + 20, true)
|
|
699
696
|
}
|
|
700
697
|
|
|
701
698
|
get next_refid() {
|
|
702
|
-
return this._dataView.getInt32(this.
|
|
699
|
+
return this._dataView.getInt32(this._start + 24, true)
|
|
703
700
|
}
|
|
704
701
|
|
|
705
702
|
get next_pos() {
|
|
706
|
-
return this._dataView.getInt32(this.
|
|
703
|
+
return this._dataView.getInt32(this._start + 28, true)
|
|
707
704
|
}
|
|
708
705
|
|
|
709
706
|
get template_length() {
|
|
710
|
-
return this._dataView.getInt32(this.
|
|
707
|
+
return this._dataView.getInt32(this._start + 32, true)
|
|
711
708
|
}
|
|
712
709
|
|
|
713
710
|
seqAt(idx: number): string | undefined {
|
|
714
711
|
if (idx < this.seq_length) {
|
|
715
712
|
const byteIndex = idx >> 1
|
|
716
713
|
const sb =
|
|
717
|
-
this.
|
|
714
|
+
this._byteArray[
|
|
718
715
|
this.b0 + this.read_name_length + this.num_cigar_bytes + byteIndex
|
|
719
716
|
]!
|
|
720
717
|
|
|
721
718
|
return idx % 2 === 0
|
|
722
|
-
? SEQRET_DECODER[(sb & 0xf0) >> 4]
|
|
723
|
-
: SEQRET_DECODER[sb & 0x0f]
|
|
719
|
+
? SEQRET_DECODER[(sb & 0xf0) >> 4]!
|
|
720
|
+
: SEQRET_DECODER[sb & 0x0f]!
|
|
724
721
|
} else {
|
|
725
722
|
return undefined
|
|
726
723
|
}
|
|
727
724
|
}
|
|
728
725
|
|
|
729
726
|
toJSON() {
|
|
730
|
-
const data: Record<string,
|
|
727
|
+
const data: Record<string, unknown> = {}
|
|
731
728
|
for (const k of Object.keys(this)) {
|
|
732
|
-
if (k.startsWith('_')
|
|
729
|
+
if (k.startsWith('_')) {
|
|
733
730
|
continue
|
|
734
731
|
}
|
|
735
|
-
// @ts-
|
|
732
|
+
// @ts-expect-error
|
|
736
733
|
data[k] = this[k]
|
|
737
734
|
}
|
|
738
735
|
|
package/src/util.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import Chunk from './chunk.ts'
|
|
2
1
|
import { longFromBytesToUnsigned } from './long.ts'
|
|
3
|
-
|
|
2
|
+
|
|
3
|
+
import type Chunk from './chunk.ts'
|
|
4
|
+
import type { Offset, VirtualOffset } from './virtualOffset.ts'
|
|
4
5
|
|
|
5
6
|
export function canMergeBlocks(chunk1: Chunk, chunk2: Chunk) {
|
|
6
7
|
return (
|
|
@@ -33,7 +34,7 @@ export interface BaseOpts {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
export function makeOpts(obj: AbortSignal | BaseOpts = {}): BaseOpts {
|
|
36
|
-
return 'aborted' in obj ?
|
|
37
|
+
return 'aborted' in obj ? { signal: obj } : obj
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export function optimizeChunks(chunks: Chunk[], lowest?: Offset) {
|
|
@@ -126,23 +127,22 @@ export function parseNameBytes(
|
|
|
126
127
|
namesBytes: Uint8Array,
|
|
127
128
|
renameRefSeq: (arg: string) => string = s => s,
|
|
128
129
|
) {
|
|
130
|
+
const decoder = new TextDecoder()
|
|
129
131
|
let currRefId = 0
|
|
130
132
|
let currNameStart = 0
|
|
131
|
-
const refIdToName = []
|
|
133
|
+
const refIdToName: string[] = []
|
|
132
134
|
const refNameToId: Record<string, number> = {}
|
|
133
|
-
for (let i = 0; i < namesBytes.length; i
|
|
135
|
+
for (let i = 0; i < namesBytes.length; i++) {
|
|
134
136
|
if (!namesBytes[i]) {
|
|
135
137
|
if (currNameStart < i) {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
refName = renameRefSeq(refName)
|
|
138
|
+
const refName = renameRefSeq(
|
|
139
|
+
decoder.decode(namesBytes.subarray(currNameStart, i)),
|
|
140
|
+
)
|
|
141
141
|
refIdToName[currRefId] = refName
|
|
142
142
|
refNameToId[refName] = currRefId
|
|
143
143
|
}
|
|
144
144
|
currNameStart = i + 1
|
|
145
|
-
currRefId
|
|
145
|
+
currRefId++
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
return { refNameToId, refIdToName }
|