@gmod/bbi 7.1.0 → 8.0.1
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 +8 -0
- package/README.md +59 -0
- package/dist/bbi.d.ts +13 -3
- package/dist/bbi.js +80 -17
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.d.ts +14 -2
- package/dist/bigbed.js +116 -76
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.js +1 -2
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +13 -4
- package/dist/block-view.js +324 -148
- package/dist/block-view.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/parse-bigwig.d.ts +3 -0
- package/dist/parse-bigwig.js +15 -0
- package/dist/parse-bigwig.js.map +1 -0
- package/dist/range.js +11 -24
- package/dist/range.js.map +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/unzip.d.ts +18 -1
- package/dist/unzip.js +33 -4
- package/dist/unzip.js.map +1 -1
- package/dist/util.d.ts +2 -4
- package/dist/util.js +6 -5
- package/dist/util.js.map +1 -1
- package/dist/wasm/inflate-wasm-inlined.d.ts +18 -0
- package/dist/wasm/inflate-wasm-inlined.js +455 -0
- package/dist/wasm/inflate-wasm-inlined.js.map +1 -0
- package/dist/wasm/inflate_wasm.d.ts +1 -0
- package/dist/wasm/inflate_wasm.js +43 -0
- package/dist/wasm/inflate_wasm.js.map +1 -0
- package/dist/wasm/inflate_wasm_bg.d.ts +68 -0
- package/dist/wasm/inflate_wasm_bg.js +307 -0
- package/dist/wasm/inflate_wasm_bg.js.map +1 -0
- package/esm/bbi.d.ts +13 -3
- package/esm/bbi.js +80 -17
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.d.ts +14 -2
- package/esm/bigbed.js +116 -76
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.js +1 -2
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +13 -4
- package/esm/block-view.js +326 -150
- package/esm/block-view.js.map +1 -1
- package/esm/index.d.ts +2 -1
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/parse-bigwig.d.ts +3 -0
- package/esm/parse-bigwig.js +12 -0
- package/esm/parse-bigwig.js.map +1 -0
- package/esm/range.js +11 -24
- package/esm/range.js.map +1 -1
- package/esm/types.d.ts +14 -2
- package/esm/unzip.d.ts +18 -1
- package/esm/unzip.js +30 -3
- package/esm/unzip.js.map +1 -1
- package/esm/util.d.ts +2 -4
- package/esm/util.js +6 -5
- package/esm/util.js.map +1 -1
- package/esm/wasm/inflate-wasm-inlined.d.ts +18 -0
- package/esm/wasm/inflate-wasm-inlined.js +449 -0
- package/esm/wasm/inflate-wasm-inlined.js.map +1 -0
- package/esm/wasm/inflate_wasm.d.ts +1 -0
- package/esm/wasm/inflate_wasm.js +5 -0
- package/esm/wasm/inflate_wasm.js.map +1 -0
- package/esm/wasm/inflate_wasm_bg.d.ts +68 -0
- package/esm/wasm/inflate_wasm_bg.js +296 -0
- package/esm/wasm/inflate_wasm_bg.js.map +1 -0
- package/package.json +17 -12
- package/src/bbi.ts +102 -20
- package/src/bigbed.ts +157 -80
- package/src/bigwig.ts +1 -2
- package/src/block-view.ts +418 -156
- package/src/index.ts +8 -1
- package/src/parse-bigwig.ts +19 -0
- package/src/range.ts +13 -21
- package/src/types.ts +19 -2
- package/src/unzip.ts +86 -3
- package/src/util.ts +9 -10
- package/src/wasm/inflate-wasm-inlined.d.ts +49 -0
- package/src/wasm/inflate-wasm-inlined.js +547 -0
- package/src/wasm/inflate_wasm.d.ts +35 -0
- package/src/wasm/inflate_wasm.js +4 -0
- package/src/wasm/inflate_wasm_bg.js +309 -0
- package/src/wasm/inflate_wasm_bg.wasm +0 -0
- package/src/wasm/inflate_wasm_bg.wasm.d.ts +13 -0
package/dist/block-view.js
CHANGED
|
@@ -9,7 +9,7 @@ const quick_lru_1 = __importDefault(require("quick-lru"));
|
|
|
9
9
|
const range_ts_1 = __importDefault(require("./range.js"));
|
|
10
10
|
const unzip_ts_1 = require("./unzip.js");
|
|
11
11
|
const util_ts_1 = require("./util.js");
|
|
12
|
-
const decoder =
|
|
12
|
+
const decoder = new TextDecoder('utf8');
|
|
13
13
|
function coordFilter(s1, e1, s2, e2) {
|
|
14
14
|
return s1 < e2 && e1 >= s2;
|
|
15
15
|
}
|
|
@@ -22,22 +22,28 @@ function coordFilter(s1, e1, s2, e2) {
|
|
|
22
22
|
class BlockView {
|
|
23
23
|
bbi;
|
|
24
24
|
refsByName;
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
rTreeOffset;
|
|
26
|
+
uncompressBufSize;
|
|
27
27
|
blockType;
|
|
28
|
-
|
|
28
|
+
// R-tree index header cache - R-trees are spatial data structures used to
|
|
29
|
+
// efficiently query genomic intervals by chromosome and position
|
|
30
|
+
rTreePromise;
|
|
29
31
|
featureCache = new abortable_promise_cache_1.default({
|
|
30
32
|
cache: new quick_lru_1.default({ maxSize: 1000 }),
|
|
31
33
|
fill: async ({ length, offset }, signal) => this.bbi.read(length, offset, { signal }),
|
|
32
34
|
});
|
|
33
|
-
constructor(bbi, refsByName,
|
|
35
|
+
constructor(bbi, refsByName,
|
|
36
|
+
// Offset to the R-tree index in the file - this is part of the "cirTree"
|
|
37
|
+
// (combined ID R-tree), which combines a B+ tree for chromosome names
|
|
38
|
+
// with an R-tree for efficient spatial queries
|
|
39
|
+
rTreeOffset, uncompressBufSize, blockType) {
|
|
34
40
|
this.bbi = bbi;
|
|
35
41
|
this.refsByName = refsByName;
|
|
36
|
-
this.
|
|
37
|
-
this.
|
|
42
|
+
this.rTreeOffset = rTreeOffset;
|
|
43
|
+
this.uncompressBufSize = uncompressBufSize;
|
|
38
44
|
this.blockType = blockType;
|
|
39
|
-
if (!(
|
|
40
|
-
throw new Error('invalid
|
|
45
|
+
if (!(rTreeOffset >= 0)) {
|
|
46
|
+
throw new Error('invalid rTreeOffset!');
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
49
|
async readWigData(chrName, start, end, observer, opts) {
|
|
@@ -45,105 +51,98 @@ class BlockView {
|
|
|
45
51
|
const chrId = this.refsByName[chrName];
|
|
46
52
|
if (chrId === undefined) {
|
|
47
53
|
observer.complete();
|
|
54
|
+
return;
|
|
48
55
|
}
|
|
49
56
|
const request = { chrId, start, end };
|
|
50
|
-
if (!this.
|
|
51
|
-
this.
|
|
57
|
+
if (!this.rTreePromise) {
|
|
58
|
+
this.rTreePromise = this.bbi.read(48, this.rTreeOffset, opts);
|
|
52
59
|
}
|
|
53
|
-
const buffer = await this.
|
|
54
|
-
const dataView = new DataView(buffer.buffer);
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
const buffer = await this.rTreePromise;
|
|
61
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);
|
|
62
|
+
// Maximum number of children per R-tree node - used to calculate memory bounds
|
|
63
|
+
const rTreeBlockSize = dataView.getUint32(4, true);
|
|
64
|
+
const blocksToFetch = [];
|
|
57
65
|
let outstanding = 0;
|
|
58
|
-
|
|
66
|
+
// R-tree leaf nodes contain the actual data blocks to fetch
|
|
67
|
+
const processLeafNode = (dataView, startOffset, count) => {
|
|
68
|
+
let offset = startOffset;
|
|
69
|
+
for (let i = 0; i < count; i++) {
|
|
70
|
+
const startChrom = dataView.getUint32(offset, true);
|
|
71
|
+
offset += 4;
|
|
72
|
+
const startBase = dataView.getUint32(offset, true);
|
|
73
|
+
offset += 4;
|
|
74
|
+
const endChrom = dataView.getUint32(offset, true);
|
|
75
|
+
offset += 4;
|
|
76
|
+
const endBase = dataView.getUint32(offset, true);
|
|
77
|
+
offset += 4;
|
|
78
|
+
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
79
|
+
offset += 8;
|
|
80
|
+
const blockSize = Number(dataView.getBigUint64(offset, true));
|
|
81
|
+
offset += 8;
|
|
82
|
+
if (blockIntersectsQuery({ startChrom, startBase, endBase, endChrom })) {
|
|
83
|
+
blocksToFetch.push({
|
|
84
|
+
offset: blockOffset,
|
|
85
|
+
length: blockSize,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
// R-tree non-leaf nodes contain pointers to child nodes
|
|
91
|
+
const processNonLeafNode = (dataView, startOffset, count, level) => {
|
|
92
|
+
const recurOffsets = [];
|
|
93
|
+
let offset = startOffset;
|
|
94
|
+
for (let i = 0; i < count; i++) {
|
|
95
|
+
const startChrom = dataView.getUint32(offset, true);
|
|
96
|
+
offset += 4;
|
|
97
|
+
const startBase = dataView.getUint32(offset, true);
|
|
98
|
+
offset += 4;
|
|
99
|
+
const endChrom = dataView.getUint32(offset, true);
|
|
100
|
+
offset += 4;
|
|
101
|
+
const endBase = dataView.getUint32(offset, true);
|
|
102
|
+
offset += 4;
|
|
103
|
+
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
104
|
+
offset += 8;
|
|
105
|
+
if (blockIntersectsQuery({ startChrom, startBase, endChrom, endBase })) {
|
|
106
|
+
recurOffsets.push(blockOffset);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (recurOffsets.length > 0) {
|
|
110
|
+
traverseRTree(recurOffsets, level + 1);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const processRTreeNode = (rTreeBlockData, offset2, level) => {
|
|
59
114
|
try {
|
|
60
|
-
const data =
|
|
61
|
-
const
|
|
62
|
-
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
115
|
+
const data = rTreeBlockData.subarray(offset2);
|
|
116
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.length);
|
|
63
117
|
let offset = 0;
|
|
64
118
|
const isLeaf = dataView.getUint8(offset);
|
|
65
|
-
offset += 2; // 1 skip
|
|
66
|
-
const
|
|
119
|
+
offset += 2; // 1 skip for reserved byte
|
|
120
|
+
const count = dataView.getUint16(offset, true);
|
|
67
121
|
offset += 2;
|
|
68
122
|
if (isLeaf === 1) {
|
|
69
|
-
|
|
70
|
-
for (let i = 0; i < cnt; i++) {
|
|
71
|
-
const startChrom = dataView.getUint32(offset, true);
|
|
72
|
-
offset += 4;
|
|
73
|
-
const startBase = dataView.getUint32(offset, true);
|
|
74
|
-
offset += 4;
|
|
75
|
-
const endChrom = dataView.getUint32(offset, true);
|
|
76
|
-
offset += 4;
|
|
77
|
-
const endBase = dataView.getUint32(offset, true);
|
|
78
|
-
offset += 4;
|
|
79
|
-
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
80
|
-
offset += 8;
|
|
81
|
-
const blockSize = Number(dataView.getBigUint64(offset, true));
|
|
82
|
-
offset += 8;
|
|
83
|
-
blocksToFetch2.push({
|
|
84
|
-
startChrom,
|
|
85
|
-
startBase,
|
|
86
|
-
endBase,
|
|
87
|
-
endChrom,
|
|
88
|
-
blockOffset,
|
|
89
|
-
blockSize,
|
|
90
|
-
offset,
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
blocksToFetch = blocksToFetch.concat(blocksToFetch2
|
|
94
|
-
.filter(f => filterFeats(f))
|
|
95
|
-
.map(l => ({
|
|
96
|
-
offset: l.blockOffset,
|
|
97
|
-
length: l.blockSize,
|
|
98
|
-
})));
|
|
123
|
+
processLeafNode(dataView, offset, count);
|
|
99
124
|
}
|
|
100
125
|
else if (isLeaf === 0) {
|
|
101
|
-
|
|
102
|
-
for (let i = 0; i < cnt; i++) {
|
|
103
|
-
const startChrom = dataView.getUint32(offset, true);
|
|
104
|
-
offset += 4;
|
|
105
|
-
const startBase = dataView.getUint32(offset, true);
|
|
106
|
-
offset += 4;
|
|
107
|
-
const endChrom = dataView.getUint32(offset, true);
|
|
108
|
-
offset += 4;
|
|
109
|
-
const endBase = dataView.getUint32(offset, true);
|
|
110
|
-
offset += 4;
|
|
111
|
-
const blockOffset = Number(dataView.getBigUint64(offset, true));
|
|
112
|
-
offset += 8;
|
|
113
|
-
recurOffsets.push({
|
|
114
|
-
startChrom,
|
|
115
|
-
startBase,
|
|
116
|
-
endChrom,
|
|
117
|
-
endBase,
|
|
118
|
-
blockOffset,
|
|
119
|
-
offset,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
const recurOffsets2 = recurOffsets
|
|
123
|
-
.filter(f => filterFeats(f))
|
|
124
|
-
.map(l => l.blockOffset);
|
|
125
|
-
if (recurOffsets2.length > 0) {
|
|
126
|
-
cirFobRecur(recurOffsets2, level + 1);
|
|
127
|
-
}
|
|
126
|
+
processNonLeafNode(dataView, offset, count, level);
|
|
128
127
|
}
|
|
129
128
|
}
|
|
130
129
|
catch (e) {
|
|
131
130
|
observer.error(e);
|
|
132
131
|
}
|
|
133
132
|
};
|
|
134
|
-
const
|
|
133
|
+
const blockIntersectsQuery = (b) => {
|
|
135
134
|
const { startChrom, startBase, endChrom, endBase } = b;
|
|
136
135
|
return ((startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
137
136
|
(endChrom > chrId || (endChrom === chrId && endBase >= start)));
|
|
138
137
|
};
|
|
139
|
-
const
|
|
138
|
+
const fetchAndProcessRTreeBlocks = async (offsets, range, level) => {
|
|
140
139
|
try {
|
|
141
|
-
const length =
|
|
142
|
-
const offset =
|
|
140
|
+
const length = range.max - range.min;
|
|
141
|
+
const offset = range.min;
|
|
143
142
|
const resultBuffer = await this.featureCache.get(`${length}_${offset}`, { length, offset }, opts?.signal);
|
|
144
|
-
for (const element of
|
|
145
|
-
if (
|
|
146
|
-
|
|
143
|
+
for (const element of offsets) {
|
|
144
|
+
if (range.contains(element)) {
|
|
145
|
+
processRTreeNode(resultBuffer, element - offset, level);
|
|
147
146
|
outstanding -= 1;
|
|
148
147
|
if (outstanding === 0) {
|
|
149
148
|
this.readFeatures(observer, blocksToFetch, {
|
|
@@ -160,34 +159,37 @@ class BlockView {
|
|
|
160
159
|
observer.error(e);
|
|
161
160
|
}
|
|
162
161
|
};
|
|
163
|
-
const
|
|
162
|
+
const traverseRTree = (offsets, level) => {
|
|
164
163
|
try {
|
|
165
|
-
outstanding +=
|
|
164
|
+
outstanding += offsets.length;
|
|
166
165
|
// Upper bound on size, based on a completely full leaf node.
|
|
167
|
-
const
|
|
166
|
+
const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32;
|
|
168
167
|
let spans = new range_ts_1.default([
|
|
169
168
|
{
|
|
170
|
-
min:
|
|
171
|
-
max:
|
|
169
|
+
min: offsets[0],
|
|
170
|
+
max: offsets[0] + maxRTreeBlockSpan,
|
|
172
171
|
},
|
|
173
172
|
]);
|
|
174
|
-
for (let i = 1; i <
|
|
173
|
+
for (let i = 1; i < offsets.length; i += 1) {
|
|
175
174
|
const blockSpan = new range_ts_1.default([
|
|
176
175
|
{
|
|
177
|
-
min:
|
|
178
|
-
max:
|
|
176
|
+
min: offsets[i],
|
|
177
|
+
max: offsets[i] + maxRTreeBlockSpan,
|
|
179
178
|
},
|
|
180
179
|
]);
|
|
181
180
|
spans = spans.union(blockSpan);
|
|
182
181
|
}
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
spans.getRanges().forEach(range => {
|
|
183
|
+
fetchAndProcessRTreeBlocks(offsets, range, level).catch((e) => {
|
|
184
|
+
observer.error(e);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
185
187
|
}
|
|
186
188
|
catch (e) {
|
|
187
189
|
observer.error(e);
|
|
188
190
|
}
|
|
189
191
|
};
|
|
190
|
-
|
|
192
|
+
traverseRTree([this.rTreeOffset + 48], 1);
|
|
191
193
|
return;
|
|
192
194
|
}
|
|
193
195
|
catch (e) {
|
|
@@ -199,8 +201,6 @@ class BlockView {
|
|
|
199
201
|
let offset = startOffset;
|
|
200
202
|
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
201
203
|
while (offset < b.byteLength) {
|
|
202
|
-
// this was extracted from looking at the runtime code generated by
|
|
203
|
-
// binary-parser
|
|
204
204
|
const chromId = dataView.getUint32(offset, true);
|
|
205
205
|
offset += 4;
|
|
206
206
|
const start = dataView.getUint32(offset, true);
|
|
@@ -214,14 +214,10 @@ class BlockView {
|
|
|
214
214
|
const maxScore = dataView.getFloat32(offset, true);
|
|
215
215
|
offset += 4;
|
|
216
216
|
const sumData = dataView.getFloat32(offset, true);
|
|
217
|
-
offset +=
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (request
|
|
222
|
-
? chromId === request.chrId &&
|
|
223
|
-
coordFilter(start, end, request.start, request.end)
|
|
224
|
-
: true) {
|
|
217
|
+
offset += 8;
|
|
218
|
+
if (!request ||
|
|
219
|
+
(chromId === request.chrId &&
|
|
220
|
+
coordFilter(start, end, request.start, request.end))) {
|
|
225
221
|
features.push({
|
|
226
222
|
start,
|
|
227
223
|
end,
|
|
@@ -237,8 +233,7 @@ class BlockView {
|
|
|
237
233
|
parseBigBedBlock(data, startOffset, offset, request) {
|
|
238
234
|
const items = [];
|
|
239
235
|
let currOffset = startOffset;
|
|
240
|
-
const
|
|
241
|
-
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
236
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.length);
|
|
242
237
|
while (currOffset < data.byteLength) {
|
|
243
238
|
const c2 = currOffset;
|
|
244
239
|
const chromId = dataView.getUint32(currOffset, true);
|
|
@@ -254,19 +249,20 @@ class BlockView {
|
|
|
254
249
|
}
|
|
255
250
|
}
|
|
256
251
|
const b = data.subarray(currOffset, i);
|
|
257
|
-
const rest = decoder
|
|
252
|
+
const rest = decoder.decode(b);
|
|
258
253
|
currOffset = i + 1;
|
|
259
|
-
|
|
260
|
-
chromId
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
254
|
+
if (!request ||
|
|
255
|
+
(chromId === request.chrId &&
|
|
256
|
+
coordFilter(start, end, request.start, request.end))) {
|
|
257
|
+
items.push({
|
|
258
|
+
start,
|
|
259
|
+
end,
|
|
260
|
+
rest,
|
|
261
|
+
uniqueId: `bb-${offset + c2}`,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
266
264
|
}
|
|
267
|
-
return
|
|
268
|
-
? items.filter((f) => coordFilter(f.start, f.end, request.start, request.end))
|
|
269
|
-
: items;
|
|
265
|
+
return items;
|
|
270
266
|
}
|
|
271
267
|
parseBigWigBlock(buffer, startOffset, req) {
|
|
272
268
|
const b = buffer.subarray(startOffset);
|
|
@@ -283,7 +279,7 @@ class BlockView {
|
|
|
283
279
|
offset += 2;
|
|
284
280
|
const itemCount = dataView.getUint16(offset, true);
|
|
285
281
|
offset += 2;
|
|
286
|
-
const items =
|
|
282
|
+
const items = [];
|
|
287
283
|
switch (blockType) {
|
|
288
284
|
case 1: {
|
|
289
285
|
for (let i = 0; i < itemCount; i++) {
|
|
@@ -293,11 +289,13 @@ class BlockView {
|
|
|
293
289
|
offset += 4;
|
|
294
290
|
const score = dataView.getFloat32(offset, true);
|
|
295
291
|
offset += 4;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
292
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
293
|
+
items.push({
|
|
294
|
+
start,
|
|
295
|
+
end,
|
|
296
|
+
score,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
301
299
|
}
|
|
302
300
|
break;
|
|
303
301
|
}
|
|
@@ -307,11 +305,14 @@ class BlockView {
|
|
|
307
305
|
offset += 4;
|
|
308
306
|
const score = dataView.getFloat32(offset, true);
|
|
309
307
|
offset += 4;
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
308
|
+
const end = start + itemSpan;
|
|
309
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
310
|
+
items.push({
|
|
311
|
+
score,
|
|
312
|
+
start,
|
|
313
|
+
end,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
315
316
|
}
|
|
316
317
|
break;
|
|
317
318
|
}
|
|
@@ -320,36 +321,49 @@ class BlockView {
|
|
|
320
321
|
const score = dataView.getFloat32(offset, true);
|
|
321
322
|
offset += 4;
|
|
322
323
|
const start = blockStart + i * itemStep;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
324
|
+
const end = start + itemSpan;
|
|
325
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
326
|
+
items.push({
|
|
327
|
+
score,
|
|
328
|
+
start,
|
|
329
|
+
end,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
328
332
|
}
|
|
329
333
|
break;
|
|
330
334
|
}
|
|
331
335
|
}
|
|
332
|
-
return
|
|
333
|
-
? items.filter(f => coordFilter(f.start, f.end, req.start, req.end))
|
|
334
|
-
: items;
|
|
336
|
+
return items;
|
|
335
337
|
}
|
|
336
338
|
async readFeatures(observer, blocks, opts = {}) {
|
|
337
339
|
try {
|
|
338
|
-
const { blockType,
|
|
340
|
+
const { blockType, uncompressBufSize } = this;
|
|
339
341
|
const { signal, request } = opts;
|
|
340
342
|
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
341
|
-
(0, util_ts_1.checkAbortSignal)(signal);
|
|
342
343
|
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
343
|
-
(0, util_ts_1.checkAbortSignal)(signal);
|
|
344
344
|
const { length, offset } = blockGroup;
|
|
345
345
|
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
346
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
347
|
+
offset: block.offset - blockGroup.offset,
|
|
348
|
+
length: block.length,
|
|
349
|
+
}));
|
|
350
|
+
let decompressedData;
|
|
351
|
+
let decompressedOffsets;
|
|
352
|
+
if (uncompressBufSize > 0) {
|
|
353
|
+
const result = await (0, unzip_ts_1.unzipBatch)(data, localBlocks, uncompressBufSize);
|
|
354
|
+
decompressedData = result.data;
|
|
355
|
+
decompressedOffsets = result.offsets;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
decompressedData = data;
|
|
359
|
+
decompressedOffsets = localBlocks.map(b => b.offset);
|
|
360
|
+
decompressedOffsets.push(data.length);
|
|
361
|
+
}
|
|
362
|
+
for (let i = 0; i < blockGroup.blocks.length; i++) {
|
|
363
|
+
const block = blockGroup.blocks[i];
|
|
364
|
+
const start = decompressedOffsets[i];
|
|
365
|
+
const end = decompressedOffsets[i + 1];
|
|
366
|
+
const resultData = decompressedData.subarray(start, end);
|
|
353
367
|
switch (blockType) {
|
|
354
368
|
case 'summary': {
|
|
355
369
|
observer.next(this.parseSummaryBlock(resultData, 0, request));
|
|
@@ -375,6 +389,168 @@ class BlockView {
|
|
|
375
389
|
observer.error(e);
|
|
376
390
|
}
|
|
377
391
|
}
|
|
392
|
+
async readBigWigFeaturesAsArrays(blocks, opts = {}) {
|
|
393
|
+
const { uncompressBufSize } = this;
|
|
394
|
+
const { signal, request } = opts;
|
|
395
|
+
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
396
|
+
const allStarts = [];
|
|
397
|
+
const allEnds = [];
|
|
398
|
+
const allScores = [];
|
|
399
|
+
let totalCount = 0;
|
|
400
|
+
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
401
|
+
const { length, offset } = blockGroup;
|
|
402
|
+
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
403
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
404
|
+
offset: block.offset - blockGroup.offset,
|
|
405
|
+
length: block.length,
|
|
406
|
+
}));
|
|
407
|
+
if (uncompressBufSize > 0) {
|
|
408
|
+
const result = await (0, unzip_ts_1.decompressAndParseBigWigBlocks)(data, localBlocks, uncompressBufSize, request?.start ?? 0, request?.end ?? 0);
|
|
409
|
+
if (result.starts.length > 0) {
|
|
410
|
+
allStarts.push(result.starts);
|
|
411
|
+
allEnds.push(result.ends);
|
|
412
|
+
allScores.push(result.scores);
|
|
413
|
+
totalCount += result.starts.length;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
for (const block of localBlocks) {
|
|
418
|
+
const blockData = data.subarray(block.offset, block.offset + block.length);
|
|
419
|
+
const features = this.parseBigWigBlock(blockData, 0, request);
|
|
420
|
+
if (features.length > 0) {
|
|
421
|
+
const starts = new Int32Array(features.length);
|
|
422
|
+
const ends = new Int32Array(features.length);
|
|
423
|
+
const scores = new Float32Array(features.length);
|
|
424
|
+
for (let i = 0; i < features.length; i++) {
|
|
425
|
+
const f = features[i];
|
|
426
|
+
starts[i] = f.start;
|
|
427
|
+
ends[i] = f.end;
|
|
428
|
+
scores[i] = f.score;
|
|
429
|
+
}
|
|
430
|
+
allStarts.push(starts);
|
|
431
|
+
allEnds.push(ends);
|
|
432
|
+
allScores.push(scores);
|
|
433
|
+
totalCount += features.length;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}));
|
|
438
|
+
if (allStarts.length === 0) {
|
|
439
|
+
return {
|
|
440
|
+
starts: new Int32Array(0),
|
|
441
|
+
ends: new Int32Array(0),
|
|
442
|
+
scores: new Float32Array(0),
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
if (allStarts.length === 1) {
|
|
446
|
+
return {
|
|
447
|
+
starts: allStarts[0],
|
|
448
|
+
ends: allEnds[0],
|
|
449
|
+
scores: allScores[0],
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
const starts = new Int32Array(totalCount);
|
|
453
|
+
const ends = new Int32Array(totalCount);
|
|
454
|
+
const scores = new Float32Array(totalCount);
|
|
455
|
+
let offset = 0;
|
|
456
|
+
for (let i = 0; i < allStarts.length; i++) {
|
|
457
|
+
starts.set(allStarts[i], offset);
|
|
458
|
+
ends.set(allEnds[i], offset);
|
|
459
|
+
scores.set(allScores[i], offset);
|
|
460
|
+
offset += allStarts[i].length;
|
|
461
|
+
}
|
|
462
|
+
return { starts, ends, scores };
|
|
463
|
+
}
|
|
464
|
+
async readSummaryFeaturesAsArrays(blocks, opts = {}) {
|
|
465
|
+
const { uncompressBufSize } = this;
|
|
466
|
+
const { signal, request } = opts;
|
|
467
|
+
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
468
|
+
const allStarts = [];
|
|
469
|
+
const allEnds = [];
|
|
470
|
+
const allScores = [];
|
|
471
|
+
const allMinScores = [];
|
|
472
|
+
const allMaxScores = [];
|
|
473
|
+
let totalCount = 0;
|
|
474
|
+
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
475
|
+
const { length, offset } = blockGroup;
|
|
476
|
+
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
477
|
+
const localBlocks = blockGroup.blocks.map(block => ({
|
|
478
|
+
offset: block.offset - blockGroup.offset,
|
|
479
|
+
length: block.length,
|
|
480
|
+
}));
|
|
481
|
+
if (uncompressBufSize > 0) {
|
|
482
|
+
const result = await (0, unzip_ts_1.decompressAndParseSummaryBlocks)(data, localBlocks, uncompressBufSize, request?.chrId ?? 0, request?.start ?? 0, request?.end ?? 0);
|
|
483
|
+
if (result.starts.length > 0) {
|
|
484
|
+
allStarts.push(result.starts);
|
|
485
|
+
allEnds.push(result.ends);
|
|
486
|
+
allScores.push(result.scores);
|
|
487
|
+
allMinScores.push(result.minScores);
|
|
488
|
+
allMaxScores.push(result.maxScores);
|
|
489
|
+
totalCount += result.starts.length;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
for (const block of localBlocks) {
|
|
494
|
+
const blockData = data.subarray(block.offset, block.offset + block.length);
|
|
495
|
+
const features = this.parseSummaryBlock(blockData, 0, request);
|
|
496
|
+
if (features.length > 0) {
|
|
497
|
+
const starts = new Int32Array(features.length);
|
|
498
|
+
const ends = new Int32Array(features.length);
|
|
499
|
+
const scores = new Float32Array(features.length);
|
|
500
|
+
const minScores = new Float32Array(features.length);
|
|
501
|
+
const maxScores = new Float32Array(features.length);
|
|
502
|
+
for (let i = 0; i < features.length; i++) {
|
|
503
|
+
const f = features[i];
|
|
504
|
+
starts[i] = f.start;
|
|
505
|
+
ends[i] = f.end;
|
|
506
|
+
scores[i] = f.score ?? 0;
|
|
507
|
+
minScores[i] = f.minScore ?? 0;
|
|
508
|
+
maxScores[i] = f.maxScore ?? 0;
|
|
509
|
+
}
|
|
510
|
+
allStarts.push(starts);
|
|
511
|
+
allEnds.push(ends);
|
|
512
|
+
allScores.push(scores);
|
|
513
|
+
allMinScores.push(minScores);
|
|
514
|
+
allMaxScores.push(maxScores);
|
|
515
|
+
totalCount += features.length;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}));
|
|
520
|
+
if (allStarts.length === 0) {
|
|
521
|
+
return {
|
|
522
|
+
starts: new Int32Array(0),
|
|
523
|
+
ends: new Int32Array(0),
|
|
524
|
+
scores: new Float32Array(0),
|
|
525
|
+
minScores: new Float32Array(0),
|
|
526
|
+
maxScores: new Float32Array(0),
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
if (allStarts.length === 1) {
|
|
530
|
+
return {
|
|
531
|
+
starts: allStarts[0],
|
|
532
|
+
ends: allEnds[0],
|
|
533
|
+
scores: allScores[0],
|
|
534
|
+
minScores: allMinScores[0],
|
|
535
|
+
maxScores: allMaxScores[0],
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
const starts = new Int32Array(totalCount);
|
|
539
|
+
const ends = new Int32Array(totalCount);
|
|
540
|
+
const scores = new Float32Array(totalCount);
|
|
541
|
+
const minScores = new Float32Array(totalCount);
|
|
542
|
+
const maxScores = new Float32Array(totalCount);
|
|
543
|
+
let offset = 0;
|
|
544
|
+
for (let i = 0; i < allStarts.length; i++) {
|
|
545
|
+
starts.set(allStarts[i], offset);
|
|
546
|
+
ends.set(allEnds[i], offset);
|
|
547
|
+
scores.set(allScores[i], offset);
|
|
548
|
+
minScores.set(allMinScores[i], offset);
|
|
549
|
+
maxScores.set(allMaxScores[i], offset);
|
|
550
|
+
offset += allStarts[i].length;
|
|
551
|
+
}
|
|
552
|
+
return { starts, ends, scores, minScores, maxScores };
|
|
553
|
+
}
|
|
378
554
|
}
|
|
379
555
|
exports.BlockView = BlockView;
|
|
380
556
|
//# sourceMappingURL=block-view.js.map
|