@gmod/bam 4.0.0 → 5.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +4 -4
  2. package/README.md +7 -11
  3. package/dist/bai.d.ts +1 -1
  4. package/dist/bai.js +151 -169
  5. package/dist/bai.js.map +1 -1
  6. package/dist/bamFile.d.ts +4 -5
  7. package/dist/bamFile.js +271 -350
  8. package/dist/bamFile.js.map +1 -1
  9. package/dist/chunk.d.ts +1 -1
  10. package/dist/chunk.js +5 -0
  11. package/dist/chunk.js.map +1 -1
  12. package/dist/csi.d.ts +1 -1
  13. package/dist/csi.js +140 -145
  14. package/dist/csi.js.map +1 -1
  15. package/dist/htsget.d.ts +1 -2
  16. package/dist/htsget.js +131 -161
  17. package/dist/htsget.js.map +1 -1
  18. package/dist/indexFile.d.ts +1 -1
  19. package/dist/indexFile.js +2 -0
  20. package/dist/indexFile.js.map +1 -1
  21. package/dist/nullIndex.js +2 -13
  22. package/dist/nullIndex.js.map +1 -1
  23. package/dist/record.d.ts +5 -5
  24. package/dist/record.js +44 -37
  25. package/dist/record.js.map +1 -1
  26. package/dist/util.d.ts +4 -2
  27. package/dist/util.js +25 -15
  28. package/dist/util.js.map +1 -1
  29. package/dist/virtualOffset.d.ts +1 -1
  30. package/dist/virtualOffset.js +2 -0
  31. package/dist/virtualOffset.js.map +1 -1
  32. package/esm/bai.d.ts +1 -1
  33. package/esm/bai.js +13 -13
  34. package/esm/bai.js.map +1 -1
  35. package/esm/bamFile.d.ts +4 -5
  36. package/esm/bamFile.js +49 -50
  37. package/esm/bamFile.js.map +1 -1
  38. package/esm/chunk.d.ts +1 -1
  39. package/esm/chunk.js +5 -0
  40. package/esm/chunk.js.map +1 -1
  41. package/esm/csi.d.ts +1 -1
  42. package/esm/csi.js +26 -28
  43. package/esm/csi.js.map +1 -1
  44. package/esm/htsget.d.ts +1 -2
  45. package/esm/htsget.js +21 -11
  46. package/esm/htsget.js.map +1 -1
  47. package/esm/indexFile.d.ts +1 -1
  48. package/esm/indexFile.js +2 -0
  49. package/esm/indexFile.js.map +1 -1
  50. package/esm/record.d.ts +5 -5
  51. package/esm/record.js +44 -37
  52. package/esm/record.js.map +1 -1
  53. package/esm/util.d.ts +4 -2
  54. package/esm/util.js +20 -1
  55. package/esm/util.js.map +1 -1
  56. package/esm/virtualOffset.d.ts +1 -1
  57. package/esm/virtualOffset.js +2 -0
  58. package/esm/virtualOffset.js.map +1 -1
  59. package/package.json +6 -6
  60. package/src/bai.ts +11 -8
  61. package/src/bamFile.ts +22 -41
  62. package/src/chunk.ts +1 -1
  63. package/src/csi.ts +22 -19
  64. package/src/htsget.ts +18 -9
  65. package/src/indexFile.ts +1 -1
  66. package/src/record.ts +44 -43
  67. package/src/util.ts +23 -3
  68. package/src/virtualOffset.ts +1 -1
package/src/bamFile.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { Buffer } from 'buffer'
2
1
  import crc32 from 'crc/crc32'
3
2
  import { unzip, unzipChunkSlice } from '@gmod/bgzf-filehandle'
4
- import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle'
3
+ import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle2'
5
4
  import AbortablePromiseCache from '@gmod/abortable-promise-cache'
6
5
  import QuickLRU from 'quick-lru'
7
6
 
@@ -148,23 +147,21 @@ export default class BamFile {
148
147
  let buffer
149
148
  if (ret) {
150
149
  const s = ret + blockLen
151
- const res = await this.bam.read(Buffer.alloc(s), 0, s, 0, opts)
152
- if (!res.bytesRead) {
153
- throw new Error('Error reading header')
154
- }
155
- buffer = res.buffer.subarray(0, Math.min(res.bytesRead, ret))
150
+ buffer = await this.bam.read(s, 0)
156
151
  } else {
157
152
  buffer = await this.bam.readFile(opts)
158
153
  }
159
154
 
160
155
  const uncba = await unzip(buffer)
156
+ const dataView = new DataView(uncba.buffer)
161
157
 
162
- if (uncba.readInt32LE(0) !== BAM_MAGIC) {
158
+ if (dataView.getInt32(0, true) !== BAM_MAGIC) {
163
159
  throw new Error('Not a BAM file')
164
160
  }
165
- const headLen = uncba.readInt32LE(4)
161
+ const headLen = dataView.getInt32(4, true)
166
162
 
167
- this.header = uncba.toString('utf8', 8, 8 + headLen)
163
+ const decoder = new TextDecoder('utf8')
164
+ this.header = decoder.decode(uncba.subarray(8, 8 + headLen))
168
165
  const { chrToIndex, indexToChr } = await this._readRefSeqs(
169
166
  headLen + 8,
170
167
  65535,
@@ -204,30 +201,21 @@ export default class BamFile {
204
201
  if (start > refSeqBytes) {
205
202
  return this._readRefSeqs(start, refSeqBytes * 2, opts)
206
203
  }
207
- const size = refSeqBytes + blockLen
208
- const { bytesRead, buffer } = await this.bam.read(
209
- Buffer.alloc(size),
210
- 0,
211
- refSeqBytes,
212
- 0,
213
- opts,
214
- )
215
- if (!bytesRead) {
216
- throw new Error('Error reading refseqs from header')
217
- }
218
- const uncba = await unzip(
219
- buffer.subarray(0, Math.min(bytesRead, refSeqBytes)),
220
- )
221
- const nRef = uncba.readInt32LE(start)
204
+ // const size = refSeqBytes + blockLen <-- use this?
205
+ const buffer = await this.bam.read(refSeqBytes, 0, opts)
206
+ const uncba = await unzip(buffer)
207
+ const dataView = new DataView(uncba.buffer)
208
+ const nRef = dataView.getInt32(start, true)
222
209
  let p = start + 4
223
210
  const chrToIndex: Record<string, number> = {}
224
211
  const indexToChr: { refName: string; length: number }[] = []
212
+ const decoder = new TextDecoder('utf8')
225
213
  for (let i = 0; i < nRef; i += 1) {
226
- const lName = uncba.readInt32LE(p)
214
+ const lName = dataView.getInt32(p, true)
227
215
  const refName = this.renameRefSeq(
228
- uncba.toString('utf8', p + 4, p + 4 + lName - 1),
216
+ decoder.decode(uncba.subarray(p + 4, p + 4 + lName - 1)),
229
217
  )
230
- const lRef = uncba.readInt32LE(p + lName + 4)
218
+ const lRef = dataView.getInt32(p + lName + 4, true)
231
219
 
232
220
  chrToIndex[refName] = i
233
221
  indexToChr.push({ refName, length: lRef })
@@ -388,15 +376,7 @@ export default class BamFile {
388
376
  }
389
377
 
390
378
  async _readRegion(position: number, size: number, opts: BaseOpts = {}) {
391
- const { bytesRead, buffer } = await this.bam.read(
392
- Buffer.alloc(size),
393
- 0,
394
- size,
395
- position,
396
- opts,
397
- )
398
-
399
- return buffer.subarray(0, Math.min(bytesRead, size))
379
+ return this.bam.read(size, position, opts)
400
380
  }
401
381
 
402
382
  async _readChunk({ chunk, opts }: { chunk: Chunk; opts: BaseOpts }) {
@@ -415,7 +395,7 @@ export default class BamFile {
415
395
  }
416
396
 
417
397
  async readBamFeatures(
418
- ba: Buffer,
398
+ ba: Uint8Array,
419
399
  cpositions: number[],
420
400
  dpositions: number[],
421
401
  chunk: Chunk,
@@ -425,8 +405,9 @@ export default class BamFile {
425
405
  let pos = 0
426
406
  let last = +Date.now()
427
407
 
408
+ const dataView = new DataView(ba.buffer)
428
409
  while (blockStart + 4 < ba.length) {
429
- const blockSize = ba.readInt32LE(blockStart)
410
+ const blockSize = dataView.getInt32(blockStart, true)
430
411
  const blockEnd = blockStart + 4 + blockSize - 1
431
412
 
432
413
  // increment position to the current decompressed status
@@ -471,8 +452,8 @@ export default class BamFile {
471
452
  chunk.minv.dataPosition +
472
453
  1
473
454
  : // must be slice, not subarray for buffer polyfill on web
474
- // eslint-disable-next-line @typescript-eslint/no-deprecated
475
- crc32.signed(ba.slice(blockStart, blockEnd)),
455
+ // @ts-expect-error
456
+ crc32.signed(ba.subarray(blockStart, blockEnd)),
476
457
  })
477
458
 
478
459
  sink.push(feature)
package/src/chunk.ts CHANGED
@@ -2,7 +2,7 @@ import VirtualOffset from './virtualOffset'
2
2
 
3
3
  // little class representing a chunk in the index
4
4
  export default class Chunk {
5
- public buffer?: Buffer
5
+ public buffer?: Uint8Array
6
6
 
7
7
  constructor(
8
8
  public minv: VirtualOffset,
package/src/csi.ts CHANGED
@@ -37,8 +37,9 @@ export default class CSI extends IndexFile {
37
37
  return []
38
38
  }
39
39
 
40
- parseAuxData(bytes: Buffer, offset: number) {
41
- const formatFlags = bytes.readInt32LE(offset)
40
+ parseAuxData(bytes: Uint8Array, offset: number) {
41
+ const dataView = new DataView(bytes.buffer)
42
+ const formatFlags = dataView.getUint32(offset, true)
42
43
  const coordinateType =
43
44
  formatFlags & 0x10000 ? 'zero-based-half-open' : '1-based-closed'
44
45
  const format = (
@@ -48,14 +49,14 @@ export default class CSI extends IndexFile {
48
49
  throw new Error(`invalid Tabix preset format flags ${formatFlags}`)
49
50
  }
50
51
  const columnNumbers = {
51
- ref: bytes.readInt32LE(offset + 4),
52
- start: bytes.readInt32LE(offset + 8),
53
- end: bytes.readInt32LE(offset + 12),
52
+ ref: dataView.getInt32(offset + 4, true),
53
+ start: dataView.getInt32(offset + 8, true),
54
+ end: dataView.getInt32(offset + 12, true),
54
55
  }
55
- const metaValue = bytes.readInt32LE(offset + 16)
56
+ const metaValue = dataView.getInt32(offset + 16, true)
56
57
  const metaChar = metaValue ? String.fromCharCode(metaValue) : ''
57
- const skipLines = bytes.readInt32LE(offset + 20)
58
- const nameSectionLength = bytes.readInt32LE(offset + 24)
58
+ const skipLines = dataView.getInt32(offset + 20, true)
59
+ const nameSectionLength = dataView.getInt32(offset + 24, true)
59
60
 
60
61
  return {
61
62
  columnNumbers,
@@ -77,23 +78,25 @@ export default class CSI extends IndexFile {
77
78
  const buffer = await this.filehandle.readFile(opts)
78
79
  const bytes = await unzip(buffer)
79
80
 
81
+ const dataView = new DataView(bytes.buffer)
80
82
  let csiVersion
81
- // check TBI magic numbers
82
- if (bytes.readUInt32LE(0) === CSI1_MAGIC) {
83
+ const magic = dataView.getUint32(0, true)
84
+
85
+ if (magic === CSI1_MAGIC) {
83
86
  csiVersion = 1
84
- } else if (bytes.readUInt32LE(0) === CSI2_MAGIC) {
87
+ } else if (magic === CSI2_MAGIC) {
85
88
  csiVersion = 2
86
89
  } else {
87
- throw new Error('Not a CSI file')
90
+ throw new Error(`Not a CSI file ${magic}`)
88
91
  // TODO: do we need to support big-endian CSI files?
89
92
  }
90
93
 
91
- this.minShift = bytes.readInt32LE(4)
92
- this.depth = bytes.readInt32LE(8)
94
+ this.minShift = dataView.getInt32(4, true)
95
+ this.depth = dataView.getInt32(8, true)
93
96
  this.maxBinNumber = ((1 << ((this.depth + 1) * 3)) - 1) / 7
94
- const auxLength = bytes.readInt32LE(12)
97
+ const auxLength = dataView.getInt32(12, true)
95
98
  const aux = auxLength >= 30 ? this.parseAuxData(bytes, 16) : undefined
96
- const refCount = bytes.readInt32LE(16 + auxLength)
99
+ const refCount = dataView.getInt32(16 + auxLength, true)
97
100
 
98
101
  type BinIndex = Record<string, Chunk[]>
99
102
 
@@ -106,12 +109,12 @@ export default class CSI extends IndexFile {
106
109
  }>(refCount)
107
110
  for (let i = 0; i < refCount; i++) {
108
111
  // the binning index
109
- const binCount = bytes.readInt32LE(curr)
112
+ const binCount = dataView.getInt32(curr, true)
110
113
  curr += 4
111
114
  const binIndex: Record<string, Chunk[]> = {}
112
115
  let stats // < provided by parsing a pseudo-bin, if present
113
116
  for (let j = 0; j < binCount; j++) {
114
- const bin = bytes.readUInt32LE(curr)
117
+ const bin = dataView.getUint32(curr, true)
115
118
  curr += 4
116
119
  if (bin > this.maxBinNumber) {
117
120
  stats = parsePseudoBin(bytes, curr + 28)
@@ -119,7 +122,7 @@ export default class CSI extends IndexFile {
119
122
  } else {
120
123
  firstDataLine = findFirstData(firstDataLine, fromBytes(bytes, curr))
121
124
  curr += 8
122
- const chunkCount = bytes.readInt32LE(curr)
125
+ const chunkCount = dataView.getInt32(curr, true)
123
126
  curr += 4
124
127
  const chunks = new Array<Chunk>(chunkCount)
125
128
  for (let k = 0; k < chunkCount; k += 1) {
package/src/htsget.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { unzip } from '@gmod/bgzf-filehandle'
2
- import { Buffer } from 'buffer'
3
- import { BaseOpts, BamOpts } from './util'
2
+ import { BaseOpts, BamOpts, concatUint8Array } from './util'
4
3
  import BamFile, { BAM_MAGIC } from './bamFile'
5
4
  import Chunk from './chunk'
6
5
  import { parseHeaderText } from './sam'
@@ -14,7 +13,8 @@ async function concat(arr: HtsgetChunk[], opts?: Record<string, any>) {
14
13
  arr.map(async chunk => {
15
14
  const { url, headers } = chunk
16
15
  if (url.startsWith('data:')) {
17
- return Buffer.from(url.split(',')[1], 'base64')
16
+ // @ts-expect-error
17
+ return Uint8Array.fromBase64(url.split(',')[1], 'base64') as Uint8Array
18
18
  } else {
19
19
  //remove referer header, it is not even allowed to be specified
20
20
  // @ts-expect-error
@@ -29,12 +29,12 @@ async function concat(arr: HtsgetChunk[], opts?: Record<string, any>) {
29
29
  `HTTP ${res.status} fetching ${url}: ${await res.text()}`,
30
30
  )
31
31
  }
32
- return Buffer.from(await res.arrayBuffer())
32
+ return new Uint8Array(await res.arrayBuffer())
33
33
  }
34
34
  }),
35
35
  )
36
36
 
37
- return Buffer.concat(await Promise.all(res.map(elt => unzip(elt))))
37
+ return concatUint8Array(await Promise.all(res.map(elt => unzip(elt))))
38
38
  }
39
39
 
40
40
  export default class HtsgetFile extends BamFile {
@@ -108,11 +108,17 @@ export default class HtsgetFile extends BamFile {
108
108
  }
109
109
  }
110
110
 
111
+ // @ts-expect-error
111
112
  async _readChunk({ chunk }: { chunk: Chunk; opts: BaseOpts }) {
112
113
  if (!chunk.buffer) {
113
114
  throw new Error('expected chunk.buffer in htsget')
114
115
  }
115
- return { data: chunk.buffer, cpositions: [], dpositions: [], chunk }
116
+ return {
117
+ data: chunk.buffer,
118
+ cpositions: [],
119
+ dpositions: [],
120
+ chunk,
121
+ }
116
122
  }
117
123
 
118
124
  async getHeader(opts: BaseOpts = {}) {
@@ -125,12 +131,15 @@ export default class HtsgetFile extends BamFile {
125
131
  }
126
132
  const data = await result.json()
127
133
  const uncba = await concat(data.htsget.urls, opts)
134
+ const dataView = new DataView(uncba.buffer)
128
135
 
129
- if (uncba.readInt32LE(0) !== BAM_MAGIC) {
136
+ if (dataView.getInt32(0, true) !== BAM_MAGIC) {
130
137
  throw new Error('Not a BAM file')
131
138
  }
132
- const headLen = uncba.readInt32LE(4)
133
- const headerText = uncba.toString('utf8', 8, 8 + headLen)
139
+ const headLen = dataView.getInt32(4, true)
140
+
141
+ const decoder = new TextDecoder('utf8')
142
+ const headerText = decoder.decode(uncba.subarray(8, 8 + headLen))
134
143
  const samHeader = parseHeaderText(headerText)
135
144
 
136
145
  // use the @SQ lines in the header to figure out the
package/src/indexFile.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { GenericFilehandle } from 'generic-filehandle'
1
+ import { GenericFilehandle } from 'generic-filehandle2'
2
2
  import Chunk from './chunk'
3
3
  import { BaseOpts } from './util'
4
4
 
package/src/record.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import Constants from './constants'
2
- import type { Buffer } from 'buffer'
3
2
 
4
3
  const SEQRET_DECODER = '=ACMGRSVTWYHKDBN'.split('')
5
4
  const CIGAR_DECODER = 'MIDNSHP=X???????'.split('')
@@ -7,15 +6,18 @@ const CIGAR_DECODER = 'MIDNSHP=X???????'.split('')
7
6
  interface Bytes {
8
7
  start: number
9
8
  end: number
10
- byteArray: Buffer
9
+ byteArray: Uint8Array
11
10
  }
11
+
12
12
  export default class BamRecord {
13
13
  public fileOffset: number
14
14
  private bytes: Bytes
15
+ #dataView: DataView
15
16
 
16
17
  constructor(args: { bytes: Bytes; fileOffset: number }) {
17
18
  this.bytes = args.bytes
18
19
  this.fileOffset = args.fileOffset
20
+ this.#dataView = new DataView(this.bytes.byteArray.buffer)
19
21
  }
20
22
 
21
23
  get byteArray() {
@@ -24,15 +26,15 @@ export default class BamRecord {
24
26
 
25
27
  get flags() {
26
28
  return (
27
- (this.byteArray.readInt32LE(this.bytes.start + 16) & 0xffff0000) >> 16
29
+ (this.#dataView.getInt32(this.bytes.start + 16, true) & 0xffff0000) >> 16
28
30
  )
29
31
  }
30
32
  get ref_id() {
31
- return this.byteArray.readInt32LE(this.bytes.start + 4)
33
+ return this.#dataView.getInt32(this.bytes.start + 4, true)
32
34
  }
33
35
 
34
36
  get start() {
35
- return this.byteArray.readInt32LE(this.bytes.start + 8)
37
+ return this.#dataView.getInt32(this.bytes.start + 8, true)
36
38
  }
37
39
 
38
40
  get end() {
@@ -73,15 +75,14 @@ export default class BamRecord {
73
75
  return this.bytes.start + 36
74
76
  }
75
77
  get name() {
76
- return this.byteArray.toString(
77
- 'ascii',
78
- this.b0,
79
- this.b0 + this.read_name_length - 1,
80
- )
78
+ let str = ''
79
+ for (let i = 0; i < this.read_name_length - 1; i++) {
80
+ str += String.fromCharCode(this.byteArray[this.b0 + i])
81
+ }
82
+ return str
81
83
  }
82
84
 
83
85
  get tags() {
84
- const { byteArray } = this.bytes
85
86
  let p =
86
87
  this.b0 +
87
88
  this.read_name_length +
@@ -92,38 +93,38 @@ export default class BamRecord {
92
93
  const blockEnd = this.bytes.end
93
94
  const tags = {} as Record<string, unknown>
94
95
  while (p < blockEnd) {
95
- const tag = String.fromCharCode(byteArray[p], byteArray[p + 1])
96
- const type = String.fromCharCode(byteArray[p + 2])
96
+ const tag = String.fromCharCode(this.byteArray[p], this.byteArray[p + 1])
97
+ const type = String.fromCharCode(this.byteArray[p + 2])
97
98
  p += 3
98
99
 
99
100
  if (type === 'A') {
100
- tags[tag] = String.fromCharCode(byteArray[p])
101
+ tags[tag] = String.fromCharCode(this.byteArray[p])
101
102
  p += 1
102
103
  } else if (type === 'i') {
103
- tags[tag] = byteArray.readInt32LE(p)
104
+ tags[tag] = this.#dataView.getInt32(p, true)
104
105
  p += 4
105
106
  } else if (type === 'I') {
106
- tags[tag] = byteArray.readUInt32LE(p)
107
+ tags[tag] = this.#dataView.getUint32(p, true)
107
108
  p += 4
108
109
  } else if (type === 'c') {
109
- tags[tag] = byteArray.readInt8(p)
110
+ tags[tag] = this.#dataView.getInt8(p)
110
111
  p += 1
111
112
  } else if (type === 'C') {
112
- tags[tag] = byteArray.readUInt8(p)
113
+ tags[tag] = this.#dataView.getUint8(p)
113
114
  p += 1
114
115
  } else if (type === 's') {
115
- tags[tag] = byteArray.readInt16LE(p)
116
+ tags[tag] = this.#dataView.getInt16(p, true)
116
117
  p += 2
117
118
  } else if (type === 'S') {
118
- tags[tag] = byteArray.readUInt16LE(p)
119
+ tags[tag] = this.#dataView.getUint16(p, true)
119
120
  p += 2
120
121
  } else if (type === 'f') {
121
- tags[tag] = byteArray.readFloatLE(p)
122
+ tags[tag] = this.#dataView.getFloat32(p, true)
122
123
  p += 4
123
124
  } else if (type === 'Z' || type === 'H') {
124
125
  const value = []
125
126
  while (p <= blockEnd) {
126
- const cc = byteArray[p++]
127
+ const cc = this.byteArray[p++]
127
128
  if (cc !== 0) {
128
129
  value.push(String.fromCharCode(cc))
129
130
  } else {
@@ -132,15 +133,15 @@ export default class BamRecord {
132
133
  }
133
134
  tags[tag] = value.join('')
134
135
  } else if (type === 'B') {
135
- const cc = byteArray[p++]
136
+ const cc = this.byteArray[p++]
136
137
  const Btype = String.fromCharCode(cc)
137
- const limit = byteArray.readInt32LE(p)
138
+ const limit = this.#dataView.getInt32(p, true)
138
139
  p += 4
139
140
  if (Btype === 'i') {
140
141
  if (tag === 'CG') {
141
142
  const value = []
142
143
  for (let k = 0; k < limit; k++) {
143
- const cigop = byteArray.readInt32LE(p)
144
+ const cigop = this.#dataView.getInt32(p, true)
144
145
  const lop = cigop >> 4
145
146
  const op = CIGAR_DECODER[cigop & 0xf]
146
147
  value.push(lop + op)
@@ -150,7 +151,7 @@ export default class BamRecord {
150
151
  } else {
151
152
  const value = []
152
153
  for (let k = 0; k < limit; k++) {
153
- value.push(byteArray.readInt32LE(p))
154
+ value.push(this.#dataView.getInt32(p, true))
154
155
  p += 4
155
156
  }
156
157
  tags[tag] = value
@@ -159,7 +160,7 @@ export default class BamRecord {
159
160
  if (tag === 'CG') {
160
161
  const value = []
161
162
  for (let k = 0; k < limit; k++) {
162
- const cigop = byteArray.readUInt32LE(p)
163
+ const cigop = this.#dataView.getUint32(p, true)
163
164
  const lop = cigop >> 4
164
165
  const op = CIGAR_DECODER[cigop & 0xf]
165
166
  value.push(lop + op)
@@ -169,7 +170,7 @@ export default class BamRecord {
169
170
  } else {
170
171
  const value = []
171
172
  for (let k = 0; k < limit; k++) {
172
- value.push(byteArray.readUInt32LE(p))
173
+ value.push(this.#dataView.getUint32(p, true))
173
174
  p += 4
174
175
  }
175
176
  tags[tag] = value
@@ -177,35 +178,35 @@ export default class BamRecord {
177
178
  } else if (Btype === 's') {
178
179
  const value = []
179
180
  for (let k = 0; k < limit; k++) {
180
- value.push(byteArray.readInt16LE(p))
181
+ value.push(this.#dataView.getInt16(p, true))
181
182
  p += 2
182
183
  }
183
184
  tags[tag] = value
184
185
  } else if (Btype === 'S') {
185
186
  const value = []
186
187
  for (let k = 0; k < limit; k++) {
187
- value.push(byteArray.readUInt16LE(p))
188
+ value.push(this.#dataView.getUint16(p, true))
188
189
  p += 2
189
190
  }
190
191
  tags[tag] = value
191
192
  } else if (Btype === 'c') {
192
193
  const value = []
193
194
  for (let k = 0; k < limit; k++) {
194
- value.push(byteArray.readInt8(p))
195
+ value.push(this.#dataView.getInt8(p))
195
196
  p += 1
196
197
  }
197
198
  tags[tag] = value
198
199
  } else if (Btype === 'C') {
199
200
  const value = []
200
201
  for (let k = 0; k < limit; k++) {
201
- value.push(byteArray.readUInt8(p))
202
+ value.push(this.#dataView.getUint8(p))
202
203
  p += 1
203
204
  }
204
205
  tags[tag] = value
205
206
  } else if (Btype === 'f') {
206
207
  const value = []
207
208
  for (let k = 0; k < limit; k++) {
208
- value.push(byteArray.readFloatLE(p))
209
+ value.push(this.#dataView.getFloat32(p, true))
209
210
  p += 4
210
211
  }
211
212
  tags[tag] = value
@@ -295,14 +296,14 @@ export default class BamRecord {
295
296
 
296
297
  // check for CG tag by inspecting whether the CIGAR field contains a clip
297
298
  // that consumes entire seqLen
298
- let cigop = this.byteArray.readInt32LE(p)
299
+ let cigop = this.#dataView.getInt32(p, true)
299
300
  let lop = cigop >> 4
300
301
  let op = CIGAR_DECODER[cigop & 0xf]
301
302
  if (op === 'S' && lop === this.seq_length) {
302
303
  // if there is a CG the second CIGAR field will be a N tag the represents
303
304
  // the length on ref
304
305
  p += 4
305
- cigop = this.byteArray.readInt32LE(p)
306
+ cigop = this.#dataView.getInt32(p, true)
306
307
  lop = cigop >> 4
307
308
  op = CIGAR_DECODER[cigop & 0xf]
308
309
  if (op !== 'N') {
@@ -315,7 +316,7 @@ export default class BamRecord {
315
316
  } else {
316
317
  let lref = 0
317
318
  for (let c = 0; c < numCigarOps; ++c) {
318
- cigop = this.byteArray.readInt32LE(p)
319
+ cigop = this.#dataView.getInt32(p, true)
319
320
  lop = cigop >> 4
320
321
  op = CIGAR_DECODER[cigop & 0xf]
321
322
  CIGAR.push(lop + op)
@@ -408,31 +409,31 @@ export default class BamRecord {
408
409
  }
409
410
  return tmp.join('')
410
411
  }
411
- return ''
412
+ return undefined
412
413
  }
413
414
 
414
415
  get bin_mq_nl() {
415
- return this.byteArray.readInt32LE(this.bytes.start + 12)
416
+ return this.#dataView.getInt32(this.bytes.start + 12, true)
416
417
  }
417
418
 
418
419
  get flag_nc() {
419
- return this.byteArray.readInt32LE(this.bytes.start + 16)
420
+ return this.#dataView.getInt32(this.bytes.start + 16, true)
420
421
  }
421
422
 
422
423
  get seq_length() {
423
- return this.byteArray.readInt32LE(this.bytes.start + 20)
424
+ return this.#dataView.getInt32(this.bytes.start + 20, true)
424
425
  }
425
426
 
426
427
  get next_refid() {
427
- return this.byteArray.readInt32LE(this.bytes.start + 24)
428
+ return this.#dataView.getInt32(this.bytes.start + 24, true)
428
429
  }
429
430
 
430
431
  get next_pos() {
431
- return this.byteArray.readInt32LE(this.bytes.start + 28)
432
+ return this.#dataView.getInt32(this.bytes.start + 28, true)
432
433
  }
433
434
 
434
435
  get template_length() {
435
- return this.byteArray.readInt32LE(this.bytes.start + 32)
436
+ return this.#dataView.getInt32(this.bytes.start + 32, true)
436
437
  }
437
438
 
438
439
  toJSON() {
package/src/util.ts CHANGED
@@ -102,7 +102,7 @@ export function optimizeChunks(chunks: Chunk[], lowest?: VirtualOffset) {
102
102
  return mergedChunks
103
103
  }
104
104
 
105
- export function parsePseudoBin(bytes: Buffer, offset: number) {
105
+ export function parsePseudoBin(bytes: Uint8Array, offset: number) {
106
106
  return {
107
107
  lineCount: Long.fromBytesLE(
108
108
  Array.prototype.slice.call(bytes, offset, offset + 8),
@@ -123,7 +123,7 @@ export function findFirstData(
123
123
  }
124
124
 
125
125
  export function parseNameBytes(
126
- namesBytes: Buffer,
126
+ namesBytes: Uint8Array,
127
127
  renameRefSeq: (arg: string) => string = s => s,
128
128
  ) {
129
129
  let currRefId = 0
@@ -133,7 +133,10 @@ export function parseNameBytes(
133
133
  for (let i = 0; i < namesBytes.length; i += 1) {
134
134
  if (!namesBytes[i]) {
135
135
  if (currNameStart < i) {
136
- let refName = namesBytes.toString('utf8', currNameStart, i)
136
+ let refName = ''
137
+ for (let j = currNameStart; j < i; j++) {
138
+ refName += String.fromCharCode(namesBytes[j])
139
+ }
137
140
  refName = renameRefSeq(refName)
138
141
  refIdToName[currRefId] = refName
139
142
  refNameToId[refName] = currRefId
@@ -144,3 +147,20 @@ export function parseNameBytes(
144
147
  }
145
148
  return { refNameToId, refIdToName }
146
149
  }
150
+
151
+ export function sum(array: Uint8Array[]) {
152
+ let sum = 0
153
+ for (const entry of array) {
154
+ sum += entry.length
155
+ }
156
+ return sum
157
+ }
158
+ export function concatUint8Array(args: Uint8Array[]) {
159
+ const mergedArray = new Uint8Array(sum(args))
160
+ let offset = 0
161
+ for (const entry of args) {
162
+ mergedArray.set(entry, offset)
163
+ offset += entry.length
164
+ }
165
+ return mergedArray
166
+ }
@@ -30,7 +30,7 @@ export default class VirtualOffset {
30
30
  return min
31
31
  }
32
32
  }
33
- export function fromBytes(bytes: Buffer, offset = 0, bigendian = false) {
33
+ export function fromBytes(bytes: Uint8Array, offset = 0, bigendian = false) {
34
34
  if (bigendian) {
35
35
  throw new Error('big-endian virtual file offsets not implemented')
36
36
  }