@gmod/bbi 4.0.6 → 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.
- package/CHANGELOG.md +6 -1
- package/dist/bbi.d.ts +20 -4
- package/dist/bbi.js +124 -104
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.js +88 -69
- package/dist/bigbed.js.map +1 -1
- package/dist/bigint-polyfill/polyfill.js +0 -10
- package/dist/bigint-polyfill/polyfill.js.map +1 -1
- package/dist/bigint-polyfill/pure.d.ts +0 -2
- package/dist/bigint-polyfill/pure.js +0 -26
- package/dist/bigint-polyfill/pure.js.map +1 -1
- package/dist/bigwig.js +3 -8
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +4 -6
- package/dist/block-view.js +122 -131
- package/dist/block-view.js.map +1 -1
- package/dist/range.js +1 -1
- package/dist/range.js.map +1 -1
- package/dist/util.d.ts +12 -14
- package/dist/util.js +8 -14
- package/dist/util.js.map +1 -1
- package/esm/bbi.d.ts +20 -4
- package/esm/bbi.js +124 -104
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.js +88 -69
- package/esm/bigbed.js.map +1 -1
- package/esm/bigint-polyfill/polyfill.js +1 -11
- package/esm/bigint-polyfill/polyfill.js.map +1 -1
- package/esm/bigint-polyfill/pure.d.ts +0 -2
- package/esm/bigint-polyfill/pure.js +0 -24
- package/esm/bigint-polyfill/pure.js.map +1 -1
- package/esm/bigwig.js +3 -8
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +4 -6
- package/esm/block-view.js +122 -131
- package/esm/block-view.js.map +1 -1
- package/esm/range.js +1 -1
- package/esm/range.js.map +1 -1
- package/esm/util.d.ts +12 -14
- package/esm/util.js +8 -14
- package/esm/util.js.map +1 -1
- package/package.json +4 -5
- package/src/bbi.ts +151 -115
- package/src/bigbed.ts +99 -80
- package/src/bigint-polyfill/polyfill.ts +1 -13
- package/src/bigint-polyfill/pure.ts +0 -36
- package/src/bigwig.ts +3 -9
- package/src/block-view.ts +133 -168
- package/src/range.ts +1 -1
- package/src/util.ts +16 -21
package/src/bbi.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Buffer } from 'buffer'
|
|
2
|
-
import { Parser } from 'binary-parser'
|
|
3
2
|
import { LocalFile, RemoteFile, GenericFilehandle } from 'generic-filehandle'
|
|
4
3
|
import { firstValueFrom, Observable } from 'rxjs'
|
|
5
4
|
import { toArray } from 'rxjs/operators'
|
|
@@ -8,14 +7,22 @@ import { BlockView } from './block-view'
|
|
|
8
7
|
const BIG_WIG_MAGIC = -2003829722
|
|
9
8
|
const BIG_BED_MAGIC = -2021002517
|
|
10
9
|
|
|
11
|
-
function
|
|
10
|
+
function myToString(arr: Uint8Array) {
|
|
12
11
|
return new TextDecoder().decode(arr)
|
|
13
12
|
}
|
|
13
|
+
interface ZoomLevel {
|
|
14
|
+
reductionLevel: number
|
|
15
|
+
reserved: number
|
|
16
|
+
dataOffset: number
|
|
17
|
+
indexOffset: number
|
|
18
|
+
}
|
|
14
19
|
|
|
15
20
|
export interface Feature {
|
|
21
|
+
offset?: number
|
|
22
|
+
chromId: number
|
|
16
23
|
start: number
|
|
17
24
|
end: number
|
|
18
|
-
score
|
|
25
|
+
score?: number
|
|
19
26
|
rest?: string // for bigbed line
|
|
20
27
|
minScore?: number // for summary line
|
|
21
28
|
maxScore?: number // for summary line
|
|
@@ -27,6 +34,8 @@ interface Statistics {
|
|
|
27
34
|
scoreSum: number
|
|
28
35
|
basesCovered: number
|
|
29
36
|
scoreSumSquares: number
|
|
37
|
+
scoreMin: number
|
|
38
|
+
scoreMax: number
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
interface RefInfo {
|
|
@@ -34,87 +43,31 @@ interface RefInfo {
|
|
|
34
43
|
id: number
|
|
35
44
|
length: number
|
|
36
45
|
}
|
|
37
|
-
|
|
46
|
+
|
|
47
|
+
export interface MainHeader {
|
|
48
|
+
magic: number
|
|
38
49
|
version: number
|
|
39
50
|
autoSql: string
|
|
40
51
|
totalSummary: Statistics
|
|
41
|
-
|
|
52
|
+
asOffset: number
|
|
53
|
+
zoomLevels: ZoomLevel[]
|
|
54
|
+
fieldCount: number
|
|
55
|
+
numZoomLevels: number
|
|
42
56
|
unzoomedIndexOffset: number
|
|
57
|
+
totalSummaryOffset: number
|
|
43
58
|
unzoomedDataOffset: number
|
|
44
59
|
definedFieldCount: number
|
|
45
60
|
uncompressBufSize: number
|
|
46
61
|
chromTreeOffset: number
|
|
47
|
-
fileSize: number
|
|
48
62
|
extHeaderOffset: number
|
|
49
63
|
isBigEndian: boolean
|
|
50
64
|
fileType: string
|
|
65
|
+
}
|
|
66
|
+
export interface Header extends MainHeader {
|
|
51
67
|
refsByName: Record<string, number>
|
|
52
68
|
refsByNumber: Record<number, RefInfo>
|
|
53
69
|
}
|
|
54
70
|
|
|
55
|
-
/**
|
|
56
|
-
* get the compiled parsers for different sections of the bigwig file
|
|
57
|
-
*
|
|
58
|
-
* @param isBE - is big endian, typically false
|
|
59
|
-
* @return an object with compiled parsers
|
|
60
|
-
*/
|
|
61
|
-
function getParsers(isBE: boolean) {
|
|
62
|
-
const le = isBE ? 'big' : 'little'
|
|
63
|
-
const headerParser = new Parser()
|
|
64
|
-
.endianess(le)
|
|
65
|
-
.int32('magic')
|
|
66
|
-
.uint16('version')
|
|
67
|
-
.uint16('numZoomLevels')
|
|
68
|
-
.uint64('chromTreeOffset')
|
|
69
|
-
.uint64('unzoomedDataOffset')
|
|
70
|
-
.uint64('unzoomedIndexOffset')
|
|
71
|
-
.uint16('fieldCount')
|
|
72
|
-
.uint16('definedFieldCount')
|
|
73
|
-
.uint64('asOffset') // autoSql offset, used in bigbed
|
|
74
|
-
.uint64('totalSummaryOffset')
|
|
75
|
-
.uint32('uncompressBufSize')
|
|
76
|
-
.uint64('extHeaderOffset') // name index offset, used in bigbed
|
|
77
|
-
.array('zoomLevels', {
|
|
78
|
-
length: 'numZoomLevels',
|
|
79
|
-
type: new Parser()
|
|
80
|
-
.endianess(le)
|
|
81
|
-
.uint32('reductionLevel')
|
|
82
|
-
.uint32('reserved')
|
|
83
|
-
.uint64('dataOffset')
|
|
84
|
-
.uint64('indexOffset'),
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
const totalSummaryParser = new Parser()
|
|
88
|
-
.endianess(le)
|
|
89
|
-
.uint64('basesCovered')
|
|
90
|
-
.doublele('scoreMin')
|
|
91
|
-
.doublele('scoreMax')
|
|
92
|
-
.doublele('scoreSum')
|
|
93
|
-
.doublele('scoreSumSquares')
|
|
94
|
-
|
|
95
|
-
const chromTreeParser = new Parser()
|
|
96
|
-
.endianess(le)
|
|
97
|
-
.uint32('magic')
|
|
98
|
-
.uint32('blockSize')
|
|
99
|
-
.uint32('keySize')
|
|
100
|
-
.uint32('valSize')
|
|
101
|
-
.uint64('itemCount')
|
|
102
|
-
|
|
103
|
-
const isLeafNode = new Parser()
|
|
104
|
-
.endianess(le)
|
|
105
|
-
.uint8('isLeafNode')
|
|
106
|
-
.skip(1)
|
|
107
|
-
.uint16('cnt')
|
|
108
|
-
.saveOffset('offset')
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
chromTreeParser,
|
|
112
|
-
totalSummaryParser,
|
|
113
|
-
headerParser,
|
|
114
|
-
isLeafNode,
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
71
|
export interface RequestOptions {
|
|
119
72
|
signal?: AbortSignal
|
|
120
73
|
headers?: Record<string, string>
|
|
@@ -139,7 +92,8 @@ export abstract class BBI {
|
|
|
139
92
|
}
|
|
140
93
|
|
|
141
94
|
/*
|
|
142
|
-
* @param filehandle - a filehandle from generic-filehandle or implementing
|
|
95
|
+
* @param filehandle - a filehandle from generic-filehandle or implementing
|
|
96
|
+
* something similar to the node10 fs.promises API
|
|
143
97
|
*
|
|
144
98
|
* @param path - a Local file path as a string
|
|
145
99
|
*
|
|
@@ -176,7 +130,8 @@ export abstract class BBI {
|
|
|
176
130
|
private async _getMainHeader(
|
|
177
131
|
opts?: RequestOptions,
|
|
178
132
|
requestSize = 2000,
|
|
179
|
-
): Promise<
|
|
133
|
+
): Promise<MainHeader> {
|
|
134
|
+
const le = true
|
|
180
135
|
const { buffer } = await this.bbi.read(
|
|
181
136
|
Buffer.alloc(requestSize),
|
|
182
137
|
0,
|
|
@@ -185,30 +140,102 @@ export abstract class BBI {
|
|
|
185
140
|
opts,
|
|
186
141
|
)
|
|
187
142
|
const isBigEndian = this._isBigEndian(buffer)
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
143
|
+
const b = buffer
|
|
144
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
145
|
+
let offset = 0
|
|
146
|
+
const magic = dataView.getInt32(offset, le)
|
|
147
|
+
offset += 4
|
|
148
|
+
const version = dataView.getUint16(offset, le)
|
|
149
|
+
offset += 2
|
|
150
|
+
const numZoomLevels = dataView.getUint16(offset, le)
|
|
151
|
+
offset += 2
|
|
152
|
+
const chromTreeOffset = Number(dataView.getBigUint64(offset, le))
|
|
153
|
+
offset += 8
|
|
154
|
+
const unzoomedDataOffset = Number(dataView.getBigUint64(offset, le))
|
|
155
|
+
offset += 8
|
|
156
|
+
const unzoomedIndexOffset = Number(dataView.getBigUint64(offset, le))
|
|
157
|
+
offset += 8
|
|
158
|
+
const fieldCount = dataView.getUint16(offset, le)
|
|
159
|
+
offset += 2
|
|
160
|
+
const definedFieldCount = dataView.getUint16(offset, le)
|
|
161
|
+
offset += 2
|
|
162
|
+
const asOffset = Number(dataView.getBigUint64(offset, le))
|
|
163
|
+
offset += 8
|
|
164
|
+
const totalSummaryOffset = Number(dataView.getBigUint64(offset, le))
|
|
165
|
+
offset += 8
|
|
166
|
+
const uncompressBufSize = dataView.getUint32(offset, le)
|
|
167
|
+
offset += 4
|
|
168
|
+
const extHeaderOffset = Number(dataView.getBigUint64(offset, le))
|
|
169
|
+
offset += 8
|
|
170
|
+
const zoomLevels = [] as ZoomLevel[]
|
|
171
|
+
for (let i = 0; i < numZoomLevels; i++) {
|
|
172
|
+
const reductionLevel = dataView.getUint32(offset, le)
|
|
173
|
+
offset += 4
|
|
174
|
+
const reserved = dataView.getUint32(offset, le)
|
|
175
|
+
offset += 4
|
|
176
|
+
const dataOffset = Number(dataView.getBigUint64(offset, le))
|
|
177
|
+
offset += 8
|
|
178
|
+
const indexOffset = Number(dataView.getBigUint64(offset, le))
|
|
179
|
+
offset += 8
|
|
180
|
+
zoomLevels.push({ reductionLevel, reserved, dataOffset, indexOffset })
|
|
198
181
|
}
|
|
199
182
|
|
|
183
|
+
const fileType = magic === BIG_BED_MAGIC ? 'bigbed' : 'bigwig'
|
|
184
|
+
|
|
200
185
|
// refetch header if it is too large on first pass,
|
|
201
186
|
// 8*5 is the sizeof the totalSummary struct
|
|
202
|
-
if (
|
|
187
|
+
if (asOffset > requestSize || totalSummaryOffset > requestSize - 8 * 5) {
|
|
203
188
|
return this._getMainHeader(opts, requestSize * 2)
|
|
204
189
|
}
|
|
205
190
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
191
|
+
let totalSummary: Statistics
|
|
192
|
+
if (totalSummaryOffset) {
|
|
193
|
+
const b = buffer.subarray(Number(totalSummaryOffset))
|
|
194
|
+
let offset = 0
|
|
195
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
196
|
+
const basesCovered = Number(dataView.getBigUint64(offset, le))
|
|
197
|
+
offset += 8
|
|
198
|
+
const scoreMin = dataView.getFloat64(offset, le)
|
|
199
|
+
offset += 8
|
|
200
|
+
const scoreMax = dataView.getFloat64(offset, le)
|
|
201
|
+
offset += 8
|
|
202
|
+
const scoreSum = dataView.getFloat64(offset, le)
|
|
203
|
+
offset += 8
|
|
204
|
+
const scoreSumSquares = dataView.getFloat64(offset, le)
|
|
205
|
+
offset += 8
|
|
206
|
+
|
|
207
|
+
totalSummary = {
|
|
208
|
+
scoreMin,
|
|
209
|
+
scoreMax,
|
|
210
|
+
scoreSum,
|
|
211
|
+
scoreSumSquares,
|
|
212
|
+
basesCovered,
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
throw new Error('no stats')
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
zoomLevels,
|
|
220
|
+
magic,
|
|
221
|
+
extHeaderOffset,
|
|
222
|
+
numZoomLevels,
|
|
223
|
+
fieldCount,
|
|
224
|
+
totalSummary,
|
|
225
|
+
definedFieldCount,
|
|
226
|
+
uncompressBufSize,
|
|
227
|
+
asOffset,
|
|
228
|
+
chromTreeOffset,
|
|
229
|
+
totalSummaryOffset,
|
|
230
|
+
unzoomedDataOffset,
|
|
231
|
+
unzoomedIndexOffset,
|
|
232
|
+
fileType,
|
|
233
|
+
version,
|
|
234
|
+
isBigEndian,
|
|
235
|
+
autoSql: asOffset
|
|
236
|
+
? myToString(buffer.subarray(asOffset, buffer.indexOf(0, asOffset)))
|
|
237
|
+
: '',
|
|
210
238
|
}
|
|
211
|
-
return { ...header, isBigEndian }
|
|
212
239
|
}
|
|
213
240
|
|
|
214
241
|
private _isBigEndian(buffer: Buffer) {
|
|
@@ -225,19 +252,19 @@ export abstract class BBI {
|
|
|
225
252
|
|
|
226
253
|
// todo: add progress if long running
|
|
227
254
|
private async _readChromTree(
|
|
228
|
-
header:
|
|
255
|
+
header: MainHeader,
|
|
229
256
|
opts?: { signal?: AbortSignal },
|
|
230
257
|
) {
|
|
231
258
|
const isBE = header.isBigEndian
|
|
232
|
-
const le = isBE
|
|
259
|
+
const le = !isBE
|
|
233
260
|
const refsByNumber: Record<
|
|
234
261
|
number,
|
|
235
262
|
{ name: string; id: number; length: number }
|
|
236
263
|
> = []
|
|
237
264
|
const refsByName: Record<string, number> = {}
|
|
238
265
|
|
|
239
|
-
let unzoomedDataOffset =
|
|
240
|
-
const chromTreeOffset =
|
|
266
|
+
let unzoomedDataOffset = header.unzoomedDataOffset
|
|
267
|
+
const chromTreeOffset = header.chromTreeOffset
|
|
241
268
|
while (unzoomedDataOffset % 4 !== 0) {
|
|
242
269
|
unzoomedDataOffset += 1
|
|
243
270
|
}
|
|
@@ -250,33 +277,42 @@ export abstract class BBI {
|
|
|
250
277
|
opts,
|
|
251
278
|
)
|
|
252
279
|
|
|
253
|
-
const
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
280
|
+
const b = buffer
|
|
281
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
282
|
+
let offset = 0
|
|
283
|
+
// const magic = dataView.getUint32(offset, le)
|
|
284
|
+
offset += 4
|
|
285
|
+
// const blockSize = dataView.getUint32(offset, le)
|
|
286
|
+
offset += 4
|
|
287
|
+
const keySize = dataView.getUint32(offset, le)
|
|
288
|
+
offset += 4
|
|
289
|
+
// const valSize = dataView.getUint32(offset, le)
|
|
290
|
+
offset += 4
|
|
291
|
+
// const itemCount = dataView.getBigUint64(offset, le)
|
|
292
|
+
offset += 8
|
|
293
|
+
|
|
266
294
|
const rootNodeOffset = 32
|
|
267
295
|
const bptReadNode = async (currentOffset: number) => {
|
|
268
296
|
let offset = currentOffset
|
|
269
297
|
if (offset >= buffer.length) {
|
|
270
298
|
throw new Error('reading beyond end of buffer')
|
|
271
299
|
}
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
300
|
+
const isLeafNode = dataView.getUint8(offset)
|
|
301
|
+
offset += 2 //skip 1
|
|
302
|
+
const cnt = dataView.getUint16(offset, le)
|
|
303
|
+
offset += 2
|
|
275
304
|
if (isLeafNode) {
|
|
276
|
-
for (let n = 0; n < cnt; n
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
305
|
+
for (let n = 0; n < cnt; n++) {
|
|
306
|
+
const key = buffer
|
|
307
|
+
.subarray(offset, offset + keySize)
|
|
308
|
+
.toString()
|
|
309
|
+
.replaceAll('\0', '')
|
|
310
|
+
offset += keySize
|
|
311
|
+
const refId = dataView.getUint32(offset, le)
|
|
312
|
+
offset += 4
|
|
313
|
+
const refSize = dataView.getUint32(offset, le)
|
|
314
|
+
offset += 4
|
|
315
|
+
|
|
280
316
|
const refRec = { name: key, id: refId, length: refSize }
|
|
281
317
|
refsByName[this.renameRefSeqs(key)] = refId
|
|
282
318
|
refsByNumber[refId] = refRec
|
|
@@ -284,10 +320,10 @@ export abstract class BBI {
|
|
|
284
320
|
} else {
|
|
285
321
|
// parse index node
|
|
286
322
|
const nextNodes = []
|
|
287
|
-
for (let n = 0; n < cnt; n
|
|
288
|
-
|
|
289
|
-
const
|
|
290
|
-
offset +=
|
|
323
|
+
for (let n = 0; n < cnt; n++) {
|
|
324
|
+
offset += keySize
|
|
325
|
+
const childOffset = Number(dataView.getBigUint64(offset, le))
|
|
326
|
+
offset += 8
|
|
291
327
|
nextNodes.push(
|
|
292
328
|
bptReadNode(Number(childOffset) - Number(chromTreeOffset)),
|
|
293
329
|
)
|
package/src/bigbed.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { Buffer } from 'buffer'
|
|
2
|
-
import { Parser } from 'binary-parser'
|
|
3
2
|
import { Observable, merge, firstValueFrom } from 'rxjs'
|
|
4
3
|
import { map, reduce } from 'rxjs/operators'
|
|
5
4
|
import AbortablePromiseCache from '@gmod/abortable-promise-cache'
|
|
6
5
|
import QuickLRU from 'quick-lru'
|
|
7
6
|
|
|
7
|
+
// locals
|
|
8
8
|
import { BBI, Feature, RequestOptions } from './bbi'
|
|
9
9
|
|
|
10
10
|
interface Loc {
|
|
11
11
|
key: string
|
|
12
|
-
offset:
|
|
13
|
-
length:
|
|
12
|
+
offset: number
|
|
13
|
+
length: number
|
|
14
14
|
field?: number
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -46,8 +46,10 @@ export class BigBed extends BBI {
|
|
|
46
46
|
|
|
47
47
|
/*
|
|
48
48
|
* parse the bigbed extraIndex fields
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
*
|
|
50
|
+
*
|
|
51
|
+
* @return a Promise for an array of Index data structure since there can be
|
|
52
|
+
* multiple extraIndexes in a bigbed, see bedToBigBed documentation
|
|
51
53
|
*/
|
|
52
54
|
private async _readIndices(opts: RequestOptions) {
|
|
53
55
|
const { extHeaderOffset, isBigEndian } = await this.getHeader(opts)
|
|
@@ -57,15 +59,17 @@ export class BigBed extends BBI {
|
|
|
57
59
|
64,
|
|
58
60
|
Number(extHeaderOffset),
|
|
59
61
|
)
|
|
60
|
-
const le = isBigEndian
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
const le = !isBigEndian
|
|
63
|
+
|
|
64
|
+
const b = data
|
|
65
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
66
|
+
let offset = 0
|
|
67
|
+
// const _size = dataView.getUint16(offset, le)
|
|
68
|
+
offset += 2
|
|
69
|
+
const count = dataView.getUint16(offset, le)
|
|
70
|
+
offset += 2
|
|
71
|
+
const dataOffset = Number(dataView.getBigUint64(offset, le))
|
|
72
|
+
offset += 8
|
|
69
73
|
|
|
70
74
|
// no extra index is defined if count==0
|
|
71
75
|
if (count === 0) {
|
|
@@ -78,26 +82,30 @@ export class BigBed extends BBI {
|
|
|
78
82
|
Buffer.alloc(len),
|
|
79
83
|
0,
|
|
80
84
|
len,
|
|
81
|
-
Number(
|
|
85
|
+
Number(dataOffset),
|
|
82
86
|
)
|
|
83
|
-
|
|
84
|
-
.endianess(le)
|
|
85
|
-
.int16('type')
|
|
86
|
-
.int16('fieldcount')
|
|
87
|
-
.uint64('offset')
|
|
88
|
-
.skip(4)
|
|
89
|
-
.int16('field')
|
|
87
|
+
|
|
90
88
|
const indices = [] as Index[]
|
|
91
89
|
|
|
92
90
|
for (let i = 0; i < count; i += 1) {
|
|
93
|
-
|
|
91
|
+
const b = buffer.subarray(i * blocklen)
|
|
92
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
93
|
+
let offset = 0
|
|
94
|
+
const type = dataView.getInt16(offset, le)
|
|
95
|
+
offset += 2
|
|
96
|
+
const fieldcount = dataView.getInt16(offset, le)
|
|
97
|
+
offset += 2
|
|
98
|
+
const dataOffset = Number(dataView.getBigUint64(offset, le))
|
|
99
|
+
offset += 8 + 4 //4 skip
|
|
100
|
+
const field = dataView.getInt16(offset, le)
|
|
101
|
+
indices.push({ type, fieldcount, offset: Number(dataOffset), field })
|
|
94
102
|
}
|
|
95
103
|
return indices
|
|
96
104
|
}
|
|
97
105
|
|
|
98
106
|
/*
|
|
99
|
-
* perform a search in the bigbed extraIndex to find which blocks in the
|
|
100
|
-
* actual feature data
|
|
107
|
+
* perform a search in the bigbed extraIndex to find which blocks in the
|
|
108
|
+
* bigbed data to look for the actual feature data
|
|
101
109
|
*
|
|
102
110
|
* @param name - the name to search for
|
|
103
111
|
* @param opts - a SearchOptions argument with optional signal
|
|
@@ -112,56 +120,32 @@ export class BigBed extends BBI {
|
|
|
112
120
|
if (indices.length === 0) {
|
|
113
121
|
return []
|
|
114
122
|
}
|
|
115
|
-
const locs = indices.map(async (index
|
|
116
|
-
const { offset, field } = index
|
|
123
|
+
const locs = indices.map(async (index): Promise<Loc | undefined> => {
|
|
124
|
+
const { offset: offset2, field } = index
|
|
117
125
|
const { buffer: data } = await this.bbi.read(
|
|
118
126
|
Buffer.alloc(32),
|
|
119
127
|
0,
|
|
120
128
|
32,
|
|
121
|
-
|
|
129
|
+
offset2,
|
|
122
130
|
opts,
|
|
123
131
|
)
|
|
124
|
-
const le = isBigEndian
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
tag: 'nodeType',
|
|
142
|
-
choices: {
|
|
143
|
-
0: new Parser().array('leafkeys', {
|
|
144
|
-
length: 'cnt',
|
|
145
|
-
type: new Parser()
|
|
146
|
-
.endianess(le)
|
|
147
|
-
.string('key', { length: keySize, stripNull: true })
|
|
148
|
-
.uint64('offset'),
|
|
149
|
-
}),
|
|
150
|
-
1: new Parser().array('keys', {
|
|
151
|
-
length: 'cnt',
|
|
152
|
-
type: new Parser()
|
|
153
|
-
.endianess(le)
|
|
154
|
-
.string('key', { length: keySize, stripNull: true })
|
|
155
|
-
.uint64('offset')
|
|
156
|
-
.uint32('length')
|
|
157
|
-
.uint32('reserved'),
|
|
158
|
-
}),
|
|
159
|
-
},
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
const bptReadNode = async (
|
|
163
|
-
nodeOffset: number,
|
|
164
|
-
): Promise<Loc | undefined> => {
|
|
132
|
+
const le = !isBigEndian
|
|
133
|
+
const b = data
|
|
134
|
+
|
|
135
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
136
|
+
let offset = 0
|
|
137
|
+
// const _magic = dataView.getInt32(offset, le)
|
|
138
|
+
offset += 4
|
|
139
|
+
const blockSize = dataView.getInt32(offset, le)
|
|
140
|
+
offset += 4
|
|
141
|
+
const keySize = dataView.getInt32(offset, le)
|
|
142
|
+
offset += 4
|
|
143
|
+
const valSize = dataView.getInt32(offset, le)
|
|
144
|
+
offset += 4
|
|
145
|
+
// const _itemCount = Number(dataView.getBigUint64(offset, le))
|
|
146
|
+
offset += 8
|
|
147
|
+
|
|
148
|
+
const bptReadNode = async (nodeOffset: number) => {
|
|
165
149
|
const val = Number(nodeOffset)
|
|
166
150
|
const len = 4 + blockSize * (keySize + valSize)
|
|
167
151
|
const { buffer } = await this.bbi.read(
|
|
@@ -171,27 +155,62 @@ export class BigBed extends BBI {
|
|
|
171
155
|
val,
|
|
172
156
|
opts,
|
|
173
157
|
)
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
158
|
+
const b = buffer
|
|
159
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length)
|
|
160
|
+
let offset = 0
|
|
161
|
+
const nodeType = dataView.getInt8(offset)
|
|
162
|
+
offset += 2 //skip 1
|
|
163
|
+
const cnt = dataView.getInt16(offset, le)
|
|
164
|
+
offset += 2
|
|
165
|
+
const keys = []
|
|
166
|
+
if (nodeType === 0) {
|
|
167
|
+
const leafkeys = []
|
|
168
|
+
for (let i = 0; i < cnt; i++) {
|
|
169
|
+
const key = b
|
|
170
|
+
.subarray(offset, offset + keySize)
|
|
171
|
+
.toString()
|
|
172
|
+
.replaceAll('\0', '')
|
|
173
|
+
offset += keySize
|
|
174
|
+
const dataOffset = Number(dataView.getBigUint64(offset, le))
|
|
175
|
+
offset += 8
|
|
176
|
+
leafkeys.push({ key, offset: dataOffset })
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
let lastOffset = 0
|
|
180
|
+
for (const { key, offset } of leafkeys) {
|
|
178
181
|
if (name.localeCompare(key) < 0 && lastOffset) {
|
|
179
182
|
return bptReadNode(lastOffset)
|
|
180
183
|
}
|
|
181
184
|
lastOffset = offset
|
|
182
185
|
}
|
|
183
186
|
return bptReadNode(lastOffset)
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
187
|
+
} else if (nodeType === 1) {
|
|
188
|
+
for (let i = 0; i < cnt; i++) {
|
|
189
|
+
const key = b
|
|
190
|
+
.subarray(offset, offset + keySize)
|
|
191
|
+
.toString()
|
|
192
|
+
.replaceAll('\0', '')
|
|
193
|
+
offset += keySize
|
|
194
|
+
const dataOffset = Number(dataView.getBigUint64(offset, le))
|
|
195
|
+
offset += 8
|
|
196
|
+
const length = dataView.getUint32(offset, le)
|
|
197
|
+
offset += 4
|
|
198
|
+
const reserved = dataView.getUint32(offset, le)
|
|
199
|
+
offset += 4
|
|
200
|
+
keys.push({ key, offset: dataOffset, length, reserved })
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (const n of keys) {
|
|
204
|
+
if (n.key === name) {
|
|
205
|
+
return { ...n, field }
|
|
206
|
+
}
|
|
188
207
|
}
|
|
189
|
-
}
|
|
190
208
|
|
|
191
|
-
|
|
209
|
+
return undefined
|
|
210
|
+
}
|
|
192
211
|
}
|
|
193
212
|
const rootNodeOffset = 32
|
|
194
|
-
return bptReadNode(
|
|
213
|
+
return bptReadNode(offset2 + rootNodeOffset)
|
|
195
214
|
})
|
|
196
215
|
return filterUndef(await Promise.all(locs))
|
|
197
216
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { getBigInt64, getBigUint64
|
|
1
|
+
import { getBigInt64, getBigUint64 } from './pure'
|
|
2
2
|
|
|
3
3
|
if (!('getBigInt64' in DataView)) {
|
|
4
4
|
DataView.prototype.getBigInt64 = function (byteOffset, littleEndian) {
|
|
@@ -11,15 +11,3 @@ if (!('getBigUint64' in DataView)) {
|
|
|
11
11
|
return getBigUint64(this, byteOffset, littleEndian)
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
if (!('setBigInt64' in DataView)) {
|
|
16
|
-
DataView.prototype.setBigInt64 = function (byteOffset, value, littleEndian) {
|
|
17
|
-
setBigInt64(this, byteOffset, value, littleEndian)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
if (!('setBigUint64' in DataView)) {
|
|
22
|
-
DataView.prototype.setBigUint64 = function (byteOffset, value, littleEndian) {
|
|
23
|
-
setBigUint64(this, byteOffset, value, littleEndian)
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -40,39 +40,3 @@ export function getBigUint64(
|
|
|
40
40
|
BigInt(a * littleEndianMask + b * bigEndianMask)
|
|
41
41
|
)
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
export function setBigInt64(
|
|
45
|
-
dataView: DataView,
|
|
46
|
-
byteOffset: number,
|
|
47
|
-
value: bigint,
|
|
48
|
-
littleEndian: boolean | undefined,
|
|
49
|
-
) {
|
|
50
|
-
const hi = Number(value >> BigInt32)
|
|
51
|
-
const lo = Number(value & BigInt(0xffffffff))
|
|
52
|
-
|
|
53
|
-
if (littleEndian) {
|
|
54
|
-
dataView.setInt32(byteOffset + 4, hi, littleEndian)
|
|
55
|
-
dataView.setUint32(byteOffset, lo, littleEndian)
|
|
56
|
-
} else {
|
|
57
|
-
dataView.setInt32(byteOffset, hi, littleEndian)
|
|
58
|
-
dataView.setUint32(byteOffset + 4, lo, littleEndian)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function setBigUint64(
|
|
63
|
-
dataView: DataView,
|
|
64
|
-
byteOffset: number,
|
|
65
|
-
value: bigint,
|
|
66
|
-
littleEndian: boolean | undefined,
|
|
67
|
-
) {
|
|
68
|
-
const hi = Number(value >> BigInt32)
|
|
69
|
-
const lo = Number(value & BigInt(0xffffffff))
|
|
70
|
-
|
|
71
|
-
if (littleEndian) {
|
|
72
|
-
dataView.setUint32(byteOffset + 4, hi, littleEndian)
|
|
73
|
-
dataView.setUint32(byteOffset, lo, littleEndian)
|
|
74
|
-
} else {
|
|
75
|
-
dataView.setUint32(byteOffset, hi, littleEndian)
|
|
76
|
-
dataView.setUint32(byteOffset + 4, lo, littleEndian)
|
|
77
|
-
}
|
|
78
|
-
}
|