@gmod/bbi 8.1.1 → 9.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 +4 -0
- package/README.md +49 -53
- package/dist/array-feature-view.d.ts +4 -1
- package/dist/array-feature-view.js.map +1 -1
- package/dist/bbi.d.ts +5 -25
- package/dist/bbi.js +31 -133
- package/dist/bbi.js.map +1 -1
- package/dist/bigbed.d.ts +0 -1
- package/dist/bigbed.js +19 -51
- package/dist/bigbed.js.map +1 -1
- package/dist/bigwig.d.ts +1 -2
- package/dist/bigwig.js +1 -2
- package/dist/bigwig.js.map +1 -1
- package/dist/block-view.d.ts +7 -16
- package/dist/block-view.js +370 -440
- package/dist/block-view.js.map +1 -1
- package/dist/parse-bigwig.d.ts +2 -2
- package/dist/parse-bigwig.js +5 -5
- package/dist/parse-bigwig.js.map +1 -1
- package/dist/range.d.ts +1 -15
- package/dist/range.js +16 -49
- package/dist/range.js.map +1 -1
- package/dist/util.d.ts +0 -22
- package/dist/util.js +0 -46
- package/dist/util.js.map +1 -1
- package/dist/wasm/inflate-wasm-inlined.js +85 -86
- package/dist/wasm/inflate-wasm-inlined.js.map +1 -1
- package/dist/wasm/inflate_wasm.d.ts +1 -1
- package/dist/wasm/inflate_wasm.js +10 -4
- package/dist/wasm/inflate_wasm.js.map +1 -1
- package/dist/wasm/inflate_wasm_bg.d.ts +2 -2
- package/dist/wasm/inflate_wasm_bg.js +85 -86
- package/dist/wasm/inflate_wasm_bg.js.map +1 -1
- package/esm/array-feature-view.d.ts +4 -1
- package/esm/array-feature-view.js.map +1 -1
- package/esm/bbi.d.ts +5 -25
- package/esm/bbi.js +31 -133
- package/esm/bbi.js.map +1 -1
- package/esm/bigbed.d.ts +0 -1
- package/esm/bigbed.js +19 -50
- package/esm/bigbed.js.map +1 -1
- package/esm/bigwig.d.ts +1 -2
- package/esm/bigwig.js +1 -2
- package/esm/bigwig.js.map +1 -1
- package/esm/block-view.d.ts +7 -16
- package/esm/block-view.js +370 -440
- package/esm/block-view.js.map +1 -1
- package/esm/parse-bigwig.d.ts +2 -2
- package/esm/parse-bigwig.js +5 -5
- package/esm/parse-bigwig.js.map +1 -1
- package/esm/range.d.ts +1 -15
- package/esm/range.js +15 -48
- package/esm/range.js.map +1 -1
- package/esm/util.d.ts +0 -22
- package/esm/util.js +0 -42
- package/esm/util.js.map +1 -1
- package/esm/wasm/inflate-wasm-inlined.js +85 -86
- package/esm/wasm/inflate-wasm-inlined.js.map +1 -1
- package/esm/wasm/inflate_wasm.d.ts +1 -1
- package/esm/wasm/inflate_wasm.js +2 -1
- package/esm/wasm/inflate_wasm.js.map +1 -1
- package/esm/wasm/inflate_wasm_bg.d.ts +2 -2
- package/esm/wasm/inflate_wasm_bg.js +83 -84
- package/esm/wasm/inflate_wasm_bg.js.map +1 -1
- package/package.json +14 -16
- package/src/array-feature-view.ts +8 -0
- package/src/bbi.ts +50 -153
- package/src/bigbed.ts +22 -55
- package/src/bigwig.ts +1 -3
- package/src/block-view.ts +526 -634
- package/src/parse-bigwig.ts +7 -9
- package/src/range.ts +19 -58
- package/src/util.ts +0 -46
- package/src/wasm/inflate-wasm-inlined.js +101 -102
- package/src/wasm/inflate_wasm.js +7 -2
- package/src/wasm/inflate_wasm_bg.js +99 -100
- package/src/wasm/inflate_wasm_bg.wasm +0 -0
package/dist/block-view.js
CHANGED
|
@@ -6,13 +6,241 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.BlockView = void 0;
|
|
7
7
|
const abortable_promise_cache_1 = __importDefault(require("@gmod/abortable-promise-cache"));
|
|
8
8
|
const quick_lru_1 = __importDefault(require("@jbrowse/quick-lru"));
|
|
9
|
-
const range_ts_1 =
|
|
9
|
+
const range_ts_1 = require("./range.js");
|
|
10
10
|
const unzip_ts_1 = require("./unzip.js");
|
|
11
11
|
const util_ts_1 = require("./util.js");
|
|
12
12
|
const decoder = new TextDecoder('utf8');
|
|
13
|
+
const CIR_TREE_MAGIC = 0x2468ace0;
|
|
13
14
|
function coordFilter(s1, e1, s2, e2) {
|
|
14
15
|
return s1 < e2 && e1 >= s2;
|
|
15
16
|
}
|
|
17
|
+
function parseSummaryBlock(b, startOffset, request) {
|
|
18
|
+
const features = [];
|
|
19
|
+
let offset = startOffset;
|
|
20
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
21
|
+
while (offset < b.byteLength) {
|
|
22
|
+
const chromId = dataView.getUint32(offset, true);
|
|
23
|
+
offset += 4;
|
|
24
|
+
const start = dataView.getUint32(offset, true);
|
|
25
|
+
offset += 4;
|
|
26
|
+
const end = dataView.getUint32(offset, true);
|
|
27
|
+
offset += 4;
|
|
28
|
+
const validCnt = dataView.getUint32(offset, true);
|
|
29
|
+
offset += 4;
|
|
30
|
+
const minScore = dataView.getFloat32(offset, true);
|
|
31
|
+
offset += 4;
|
|
32
|
+
const maxScore = dataView.getFloat32(offset, true);
|
|
33
|
+
offset += 4;
|
|
34
|
+
const sumData = dataView.getFloat32(offset, true);
|
|
35
|
+
offset += 8;
|
|
36
|
+
if (!request ||
|
|
37
|
+
(chromId === request.chrId &&
|
|
38
|
+
coordFilter(start, end, request.start, request.end))) {
|
|
39
|
+
features.push({
|
|
40
|
+
start,
|
|
41
|
+
end,
|
|
42
|
+
maxScore,
|
|
43
|
+
minScore,
|
|
44
|
+
summary: true,
|
|
45
|
+
score: sumData / (validCnt || 1),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return features;
|
|
50
|
+
}
|
|
51
|
+
function parseBigBedBlock(data, startOffset, offset, request) {
|
|
52
|
+
const items = [];
|
|
53
|
+
let currOffset = startOffset;
|
|
54
|
+
const dataView = new DataView(data.buffer, data.byteOffset, data.length);
|
|
55
|
+
while (currOffset < data.byteLength) {
|
|
56
|
+
const c2 = currOffset;
|
|
57
|
+
const chromId = dataView.getUint32(currOffset, true);
|
|
58
|
+
currOffset += 4;
|
|
59
|
+
const start = dataView.getInt32(currOffset, true);
|
|
60
|
+
currOffset += 4;
|
|
61
|
+
const end = dataView.getInt32(currOffset, true);
|
|
62
|
+
currOffset += 4;
|
|
63
|
+
let i = currOffset;
|
|
64
|
+
for (; i < data.length; i++) {
|
|
65
|
+
if (data[i] === 0) {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const b = data.subarray(currOffset, i);
|
|
70
|
+
const rest = decoder.decode(b);
|
|
71
|
+
currOffset = i + 1;
|
|
72
|
+
if (!request ||
|
|
73
|
+
(chromId === request.chrId &&
|
|
74
|
+
coordFilter(start, end, request.start, request.end))) {
|
|
75
|
+
items.push({
|
|
76
|
+
start,
|
|
77
|
+
end,
|
|
78
|
+
rest,
|
|
79
|
+
uniqueId: `bb-${offset + c2}`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return items;
|
|
84
|
+
}
|
|
85
|
+
function parseBigWigBlock(buffer, startOffset, req) {
|
|
86
|
+
const b = buffer.subarray(startOffset);
|
|
87
|
+
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
88
|
+
const blockStart = dataView.getInt32(4, true);
|
|
89
|
+
const itemStep = dataView.getUint32(12, true);
|
|
90
|
+
const itemSpan = dataView.getUint32(16, true);
|
|
91
|
+
const blockType = dataView.getUint8(20);
|
|
92
|
+
const itemCount = dataView.getUint16(22, true);
|
|
93
|
+
let offset = 24;
|
|
94
|
+
const items = [];
|
|
95
|
+
switch (blockType) {
|
|
96
|
+
case 1: {
|
|
97
|
+
for (let i = 0; i < itemCount; i++) {
|
|
98
|
+
const start = dataView.getInt32(offset, true);
|
|
99
|
+
offset += 4;
|
|
100
|
+
const end = dataView.getInt32(offset, true);
|
|
101
|
+
offset += 4;
|
|
102
|
+
const score = dataView.getFloat32(offset, true);
|
|
103
|
+
offset += 4;
|
|
104
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
105
|
+
items.push({ start, end, score });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case 2: {
|
|
111
|
+
for (let i = 0; i < itemCount; i++) {
|
|
112
|
+
const start = dataView.getInt32(offset, true);
|
|
113
|
+
offset += 4;
|
|
114
|
+
const score = dataView.getFloat32(offset, true);
|
|
115
|
+
offset += 4;
|
|
116
|
+
const end = start + itemSpan;
|
|
117
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
118
|
+
items.push({ score, start, end });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
case 3: {
|
|
124
|
+
for (let i = 0; i < itemCount; i++) {
|
|
125
|
+
const score = dataView.getFloat32(offset, true);
|
|
126
|
+
offset += 4;
|
|
127
|
+
const start = blockStart + i * itemStep;
|
|
128
|
+
const end = start + itemSpan;
|
|
129
|
+
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
130
|
+
items.push({ score, start, end });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return items;
|
|
137
|
+
}
|
|
138
|
+
function parseBigWigBlockAsArrays(buffer, startOffset, req) {
|
|
139
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset + startOffset, buffer.length - startOffset);
|
|
140
|
+
const blockStart = dataView.getInt32(4, true);
|
|
141
|
+
const itemStep = dataView.getUint32(12, true);
|
|
142
|
+
const itemSpan = dataView.getUint32(16, true);
|
|
143
|
+
const blockType = dataView.getUint8(20);
|
|
144
|
+
const itemCount = dataView.getUint16(22, true);
|
|
145
|
+
const starts = new Int32Array(itemCount);
|
|
146
|
+
const ends = new Int32Array(itemCount);
|
|
147
|
+
const scores = new Float32Array(itemCount);
|
|
148
|
+
if (!req) {
|
|
149
|
+
switch (blockType) {
|
|
150
|
+
case 1: {
|
|
151
|
+
let offset = 24;
|
|
152
|
+
for (let i = 0; i < itemCount; i++) {
|
|
153
|
+
starts[i] = dataView.getInt32(offset, true);
|
|
154
|
+
ends[i] = dataView.getInt32(offset + 4, true);
|
|
155
|
+
scores[i] = dataView.getFloat32(offset + 8, true);
|
|
156
|
+
offset += 12;
|
|
157
|
+
}
|
|
158
|
+
return { starts, ends, scores };
|
|
159
|
+
}
|
|
160
|
+
case 2: {
|
|
161
|
+
let offset = 24;
|
|
162
|
+
for (let i = 0; i < itemCount; i++) {
|
|
163
|
+
const start = dataView.getInt32(offset, true);
|
|
164
|
+
starts[i] = start;
|
|
165
|
+
ends[i] = start + itemSpan;
|
|
166
|
+
scores[i] = dataView.getFloat32(offset + 4, true);
|
|
167
|
+
offset += 8;
|
|
168
|
+
}
|
|
169
|
+
return { starts, ends, scores };
|
|
170
|
+
}
|
|
171
|
+
case 3: {
|
|
172
|
+
let offset = 24;
|
|
173
|
+
for (let i = 0; i < itemCount; i++) {
|
|
174
|
+
const start = blockStart + i * itemStep;
|
|
175
|
+
starts[i] = start;
|
|
176
|
+
ends[i] = start + itemSpan;
|
|
177
|
+
scores[i] = dataView.getFloat32(offset, true);
|
|
178
|
+
offset += 4;
|
|
179
|
+
}
|
|
180
|
+
return { starts, ends, scores };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return { starts, ends, scores };
|
|
184
|
+
}
|
|
185
|
+
const reqStart = req.start;
|
|
186
|
+
const reqEnd = req.end;
|
|
187
|
+
let idx = 0;
|
|
188
|
+
switch (blockType) {
|
|
189
|
+
case 1: {
|
|
190
|
+
let offset = 24;
|
|
191
|
+
for (let i = 0; i < itemCount; i++) {
|
|
192
|
+
const start = dataView.getInt32(offset, true);
|
|
193
|
+
const end = dataView.getInt32(offset + 4, true);
|
|
194
|
+
if (start < reqEnd && end >= reqStart) {
|
|
195
|
+
starts[idx] = start;
|
|
196
|
+
ends[idx] = end;
|
|
197
|
+
scores[idx] = dataView.getFloat32(offset + 8, true);
|
|
198
|
+
idx++;
|
|
199
|
+
}
|
|
200
|
+
offset += 12;
|
|
201
|
+
}
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case 2: {
|
|
205
|
+
let offset = 24;
|
|
206
|
+
for (let i = 0; i < itemCount; i++) {
|
|
207
|
+
const start = dataView.getInt32(offset, true);
|
|
208
|
+
const end = start + itemSpan;
|
|
209
|
+
if (start < reqEnd && end >= reqStart) {
|
|
210
|
+
starts[idx] = start;
|
|
211
|
+
ends[idx] = end;
|
|
212
|
+
scores[idx] = dataView.getFloat32(offset + 4, true);
|
|
213
|
+
idx++;
|
|
214
|
+
}
|
|
215
|
+
offset += 8;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case 3: {
|
|
220
|
+
let offset = 24;
|
|
221
|
+
for (let i = 0; i < itemCount; i++) {
|
|
222
|
+
const start = blockStart + i * itemStep;
|
|
223
|
+
const end = start + itemSpan;
|
|
224
|
+
if (start < reqEnd && end >= reqStart) {
|
|
225
|
+
starts[idx] = start;
|
|
226
|
+
ends[idx] = end;
|
|
227
|
+
scores[idx] = dataView.getFloat32(offset, true);
|
|
228
|
+
idx++;
|
|
229
|
+
}
|
|
230
|
+
offset += 4;
|
|
231
|
+
}
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (idx < itemCount) {
|
|
236
|
+
return {
|
|
237
|
+
starts: starts.subarray(0, idx),
|
|
238
|
+
ends: ends.subarray(0, idx),
|
|
239
|
+
scores: scores.subarray(0, idx),
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return { starts, ends, scores };
|
|
243
|
+
}
|
|
16
244
|
/**
|
|
17
245
|
* View into a subset of the data in a BigWig file.
|
|
18
246
|
*
|
|
@@ -46,456 +274,158 @@ class BlockView {
|
|
|
46
274
|
throw new Error('invalid rTreeOffset!');
|
|
47
275
|
}
|
|
48
276
|
}
|
|
49
|
-
async
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (recurOffsets.length > 0) {
|
|
110
|
-
traverseRTree(recurOffsets, level + 1);
|
|
111
|
-
}
|
|
112
|
-
};
|
|
113
|
-
const processRTreeNode = (rTreeBlockData, offset2, level) => {
|
|
114
|
-
try {
|
|
115
|
-
const data = rTreeBlockData.subarray(offset2);
|
|
116
|
-
const dataView = new DataView(data.buffer, data.byteOffset, data.length);
|
|
117
|
-
let offset = 0;
|
|
118
|
-
const isLeaf = dataView.getUint8(offset);
|
|
119
|
-
offset += 2; // 1 skip for reserved byte
|
|
120
|
-
const count = dataView.getUint16(offset, true);
|
|
121
|
-
offset += 2;
|
|
122
|
-
if (isLeaf === 1) {
|
|
123
|
-
processLeafNode(dataView, offset, count);
|
|
124
|
-
}
|
|
125
|
-
else if (isLeaf === 0) {
|
|
126
|
-
processNonLeafNode(dataView, offset, count, level);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
130
|
-
observer.error(e);
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
const blockIntersectsQuery = (b) => {
|
|
134
|
-
const { startChrom, startBase, endChrom, endBase } = b;
|
|
135
|
-
return ((startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
136
|
-
(endChrom > chrId || (endChrom === chrId && endBase >= start)));
|
|
137
|
-
};
|
|
138
|
-
const fetchAndProcessRTreeBlocks = async (offsets, range, level) => {
|
|
139
|
-
try {
|
|
140
|
-
const length = range.max - range.min;
|
|
141
|
-
const offset = range.min;
|
|
142
|
-
const resultBuffer = await this.featureCache.get(`${length}_${offset}`, { length, offset }, opts?.signal);
|
|
143
|
-
for (const element of offsets) {
|
|
144
|
-
if (range.contains(element)) {
|
|
145
|
-
processRTreeNode(resultBuffer, element - offset, level);
|
|
146
|
-
outstanding -= 1;
|
|
147
|
-
if (outstanding === 0) {
|
|
148
|
-
this.readFeatures(observer, blocksToFetch, {
|
|
149
|
-
...opts,
|
|
150
|
-
request,
|
|
151
|
-
}).catch((e) => {
|
|
152
|
-
observer.error(e);
|
|
153
|
-
});
|
|
277
|
+
async _collectBlocks(chrName, start, end, opts) {
|
|
278
|
+
const chrId = this.refsByName[chrName];
|
|
279
|
+
if (chrId === undefined) {
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
if (!this.rTreePromise) {
|
|
283
|
+
this.rTreePromise = this.bbi.read(48, this.rTreeOffset, opts);
|
|
284
|
+
}
|
|
285
|
+
const buffer = await this.rTreePromise;
|
|
286
|
+
const dataView = new DataView(buffer.buffer, buffer.byteOffset, buffer.length);
|
|
287
|
+
const magic = dataView.getUint32(0, true);
|
|
288
|
+
if (magic !== CIR_TREE_MAGIC) {
|
|
289
|
+
throw new Error(`invalid cirTree magic: 0x${magic.toString(16)} (expected 0x${CIR_TREE_MAGIC.toString(16)}) at offset ${this.rTreeOffset}, file may be corrupt or unsupported`);
|
|
290
|
+
}
|
|
291
|
+
const rTreeBlockSize = dataView.getUint32(4, true);
|
|
292
|
+
// Upper bound on size, based on a completely full leaf node.
|
|
293
|
+
const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32;
|
|
294
|
+
const blockIntersectsQuery = (startChrom, startBase, endChrom, endBase) => (startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
|
|
295
|
+
(endChrom > chrId || (endChrom === chrId && endBase >= start));
|
|
296
|
+
const blocks = [];
|
|
297
|
+
let currentOffsets = [this.rTreeOffset + 48];
|
|
298
|
+
while (currentOffsets.length > 0) {
|
|
299
|
+
const spans = (0, range_ts_1.mergeRanges)(currentOffsets.map(o => ({ min: o, max: o + maxRTreeBlockSpan })));
|
|
300
|
+
const nextOffsets = [];
|
|
301
|
+
for (const { min, max } of spans) {
|
|
302
|
+
const length = max - min;
|
|
303
|
+
const offset = min;
|
|
304
|
+
const resultBuffer = await this.featureCache.get(`${length}_${offset}`, { length, offset }, opts?.signal);
|
|
305
|
+
for (const element of currentOffsets) {
|
|
306
|
+
if (min <= element && element <= max) {
|
|
307
|
+
const data = resultBuffer.subarray(element - offset);
|
|
308
|
+
const dv = new DataView(data.buffer, data.byteOffset, data.length);
|
|
309
|
+
const isLeaf = dv.getUint8(0);
|
|
310
|
+
const count = dv.getUint16(2, true);
|
|
311
|
+
let nodeOffset = 4;
|
|
312
|
+
if (isLeaf === 1) {
|
|
313
|
+
for (let i = 0; i < count; i++) {
|
|
314
|
+
const startChrom = dv.getUint32(nodeOffset, true);
|
|
315
|
+
const startBase = dv.getUint32(nodeOffset + 4, true);
|
|
316
|
+
const endChrom = dv.getUint32(nodeOffset + 8, true);
|
|
317
|
+
const endBase = dv.getUint32(nodeOffset + 12, true);
|
|
318
|
+
const blockOffset = Number(dv.getBigUint64(nodeOffset + 16, true));
|
|
319
|
+
const blockSize = Number(dv.getBigUint64(nodeOffset + 24, true));
|
|
320
|
+
nodeOffset += 32;
|
|
321
|
+
if (blockIntersectsQuery(startChrom, startBase, endChrom, endBase)) {
|
|
322
|
+
blocks.push({ offset: blockOffset, length: blockSize });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else if (isLeaf === 0) {
|
|
327
|
+
for (let i = 0; i < count; i++) {
|
|
328
|
+
const startChrom = dv.getUint32(nodeOffset, true);
|
|
329
|
+
const startBase = dv.getUint32(nodeOffset + 4, true);
|
|
330
|
+
const endChrom = dv.getUint32(nodeOffset + 8, true);
|
|
331
|
+
const endBase = dv.getUint32(nodeOffset + 12, true);
|
|
332
|
+
const childOffset = Number(dv.getBigUint64(nodeOffset + 16, true));
|
|
333
|
+
nodeOffset += 24;
|
|
334
|
+
if (blockIntersectsQuery(startChrom, startBase, endChrom, endBase)) {
|
|
335
|
+
nextOffsets.push(childOffset);
|
|
336
|
+
}
|
|
154
337
|
}
|
|
155
338
|
}
|
|
156
339
|
}
|
|
157
340
|
}
|
|
158
|
-
catch (e) {
|
|
159
|
-
observer.error(e);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
const traverseRTree = (offsets, level) => {
|
|
163
|
-
try {
|
|
164
|
-
outstanding += offsets.length;
|
|
165
|
-
// Upper bound on size, based on a completely full leaf node.
|
|
166
|
-
const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32;
|
|
167
|
-
let spans = new range_ts_1.default([
|
|
168
|
-
{
|
|
169
|
-
min: offsets[0],
|
|
170
|
-
max: offsets[0] + maxRTreeBlockSpan,
|
|
171
|
-
},
|
|
172
|
-
]);
|
|
173
|
-
for (let i = 1; i < offsets.length; i += 1) {
|
|
174
|
-
const blockSpan = new range_ts_1.default([
|
|
175
|
-
{
|
|
176
|
-
min: offsets[i],
|
|
177
|
-
max: offsets[i] + maxRTreeBlockSpan,
|
|
178
|
-
},
|
|
179
|
-
]);
|
|
180
|
-
spans = spans.union(blockSpan);
|
|
181
|
-
}
|
|
182
|
-
spans.getRanges().forEach(range => {
|
|
183
|
-
fetchAndProcessRTreeBlocks(offsets, range, level).catch((e) => {
|
|
184
|
-
observer.error(e);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
catch (e) {
|
|
189
|
-
observer.error(e);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
traverseRTree([this.rTreeOffset + 48], 1);
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
catch (e) {
|
|
196
|
-
observer.error(e);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
parseSummaryBlock(b, startOffset, request) {
|
|
200
|
-
const features = [];
|
|
201
|
-
let offset = startOffset;
|
|
202
|
-
const dataView = new DataView(b.buffer, b.byteOffset, b.length);
|
|
203
|
-
while (offset < b.byteLength) {
|
|
204
|
-
const chromId = dataView.getUint32(offset, true);
|
|
205
|
-
offset += 4;
|
|
206
|
-
const start = dataView.getUint32(offset, true);
|
|
207
|
-
offset += 4;
|
|
208
|
-
const end = dataView.getUint32(offset, true);
|
|
209
|
-
offset += 4;
|
|
210
|
-
const validCnt = dataView.getUint32(offset, true);
|
|
211
|
-
offset += 4;
|
|
212
|
-
const minScore = dataView.getFloat32(offset, true);
|
|
213
|
-
offset += 4;
|
|
214
|
-
const maxScore = dataView.getFloat32(offset, true);
|
|
215
|
-
offset += 4;
|
|
216
|
-
const sumData = dataView.getFloat32(offset, true);
|
|
217
|
-
offset += 8;
|
|
218
|
-
if (!request ||
|
|
219
|
-
(chromId === request.chrId &&
|
|
220
|
-
coordFilter(start, end, request.start, request.end))) {
|
|
221
|
-
features.push({
|
|
222
|
-
start,
|
|
223
|
-
end,
|
|
224
|
-
maxScore,
|
|
225
|
-
minScore,
|
|
226
|
-
summary: true,
|
|
227
|
-
score: sumData / (validCnt || 1),
|
|
228
|
-
});
|
|
229
341
|
}
|
|
342
|
+
currentOffsets = nextOffsets;
|
|
230
343
|
}
|
|
231
|
-
return
|
|
344
|
+
return { blocks, chrId };
|
|
232
345
|
}
|
|
233
|
-
|
|
234
|
-
const
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
while (currOffset < data.byteLength) {
|
|
238
|
-
const c2 = currOffset;
|
|
239
|
-
const chromId = dataView.getUint32(currOffset, true);
|
|
240
|
-
currOffset += 4;
|
|
241
|
-
const start = dataView.getInt32(currOffset, true);
|
|
242
|
-
currOffset += 4;
|
|
243
|
-
const end = dataView.getInt32(currOffset, true);
|
|
244
|
-
currOffset += 4;
|
|
245
|
-
let i = currOffset;
|
|
246
|
-
for (; i < data.length; i++) {
|
|
247
|
-
if (data[i] === 0) {
|
|
248
|
-
break;
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
const b = data.subarray(currOffset, i);
|
|
252
|
-
const rest = decoder.decode(b);
|
|
253
|
-
currOffset = i + 1;
|
|
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
|
-
}
|
|
346
|
+
async readWigData(chrName, start, end, opts) {
|
|
347
|
+
const collected = await this._collectBlocks(chrName, start, end, opts);
|
|
348
|
+
if (!collected) {
|
|
349
|
+
return [];
|
|
264
350
|
}
|
|
265
|
-
|
|
351
|
+
const { blocks, chrId } = collected;
|
|
352
|
+
return this.readFeatures(blocks, {
|
|
353
|
+
...opts,
|
|
354
|
+
request: { chrId, start, end },
|
|
355
|
+
});
|
|
266
356
|
}
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const itemSpan = dataView.getUint32(offset, true);
|
|
277
|
-
offset += 4;
|
|
278
|
-
const blockType = dataView.getUint8(offset);
|
|
279
|
-
offset += 2;
|
|
280
|
-
const itemCount = dataView.getUint16(offset, true);
|
|
281
|
-
offset += 2;
|
|
282
|
-
const items = [];
|
|
283
|
-
switch (blockType) {
|
|
284
|
-
case 1: {
|
|
285
|
-
for (let i = 0; i < itemCount; i++) {
|
|
286
|
-
const start = dataView.getInt32(offset, true);
|
|
287
|
-
offset += 4;
|
|
288
|
-
const end = dataView.getInt32(offset, true);
|
|
289
|
-
offset += 4;
|
|
290
|
-
const score = dataView.getFloat32(offset, true);
|
|
291
|
-
offset += 4;
|
|
292
|
-
if (!req || coordFilter(start, end, req.start, req.end)) {
|
|
293
|
-
items.push({
|
|
294
|
-
start,
|
|
295
|
-
end,
|
|
296
|
-
score,
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
case 2: {
|
|
303
|
-
for (let i = 0; i < itemCount; i++) {
|
|
304
|
-
const start = dataView.getInt32(offset, true);
|
|
305
|
-
offset += 4;
|
|
306
|
-
const score = dataView.getFloat32(offset, true);
|
|
307
|
-
offset += 4;
|
|
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
|
-
}
|
|
316
|
-
}
|
|
317
|
-
break;
|
|
318
|
-
}
|
|
319
|
-
case 3: {
|
|
320
|
-
for (let i = 0; i < itemCount; i++) {
|
|
321
|
-
const score = dataView.getFloat32(offset, true);
|
|
322
|
-
offset += 4;
|
|
323
|
-
const start = blockStart + i * itemStep;
|
|
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
|
-
}
|
|
332
|
-
}
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
357
|
+
async readWigDataAsArrays(chrName, start, end, opts) {
|
|
358
|
+
const collected = await this._collectBlocks(chrName, start, end, opts);
|
|
359
|
+
const blocks = collected?.blocks ?? [];
|
|
360
|
+
const request = collected
|
|
361
|
+
? { chrId: collected.chrId, start, end }
|
|
362
|
+
: undefined;
|
|
363
|
+
const optsWithReq = { ...opts, request };
|
|
364
|
+
if (this.blockType === 'summary') {
|
|
365
|
+
return this._readSummaryFeaturesAsArrays(blocks, optsWithReq);
|
|
335
366
|
}
|
|
336
|
-
return
|
|
367
|
+
return this._readBigWigFeaturesAsArrays(blocks, optsWithReq);
|
|
337
368
|
}
|
|
338
|
-
|
|
339
|
-
const
|
|
340
|
-
const
|
|
341
|
-
const
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}
|
|
358
|
-
return { starts, ends, scores };
|
|
359
|
-
}
|
|
360
|
-
case 2: {
|
|
361
|
-
let offset = 24;
|
|
362
|
-
for (let i = 0; i < itemCount; i++) {
|
|
363
|
-
const start = dataView.getInt32(offset, true);
|
|
364
|
-
starts[i] = start;
|
|
365
|
-
ends[i] = start + itemSpan;
|
|
366
|
-
scores[i] = dataView.getFloat32(offset + 4, true);
|
|
367
|
-
offset += 8;
|
|
368
|
-
}
|
|
369
|
-
return { starts, ends, scores };
|
|
370
|
-
}
|
|
371
|
-
case 3: {
|
|
372
|
-
let offset = 24;
|
|
373
|
-
for (let i = 0; i < itemCount; i++) {
|
|
374
|
-
const start = blockStart + i * itemStep;
|
|
375
|
-
starts[i] = start;
|
|
376
|
-
ends[i] = start + itemSpan;
|
|
377
|
-
scores[i] = dataView.getFloat32(offset, true);
|
|
378
|
-
offset += 4;
|
|
379
|
-
}
|
|
380
|
-
return { starts, ends, scores };
|
|
369
|
+
async readFeatures(blocks, opts = {}) {
|
|
370
|
+
const { blockType, uncompressBufSize } = this;
|
|
371
|
+
const { signal, request } = opts;
|
|
372
|
+
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
373
|
+
const allFeatures = [];
|
|
374
|
+
for (const blockGroup of blockGroupsToFetch) {
|
|
375
|
+
const data = await this.bbi.read(blockGroup.length, blockGroup.offset, {
|
|
376
|
+
signal,
|
|
377
|
+
});
|
|
378
|
+
const groupOffset = blockGroup.offset;
|
|
379
|
+
const subBlocks = blockGroup.blocks;
|
|
380
|
+
let decompressedData;
|
|
381
|
+
let decompressedOffsets;
|
|
382
|
+
if (uncompressBufSize > 0) {
|
|
383
|
+
const localBlocks = [];
|
|
384
|
+
for (const block of subBlocks) {
|
|
385
|
+
localBlocks.push({
|
|
386
|
+
offset: block.offset - groupOffset,
|
|
387
|
+
length: block.length,
|
|
388
|
+
});
|
|
381
389
|
}
|
|
390
|
+
const result = await (0, unzip_ts_1.unzipBatch)(data, localBlocks, uncompressBufSize);
|
|
391
|
+
decompressedData = result.data;
|
|
392
|
+
decompressedOffsets = result.offsets;
|
|
382
393
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
switch (blockType) {
|
|
389
|
-
case 1: {
|
|
390
|
-
let offset = 24;
|
|
391
|
-
for (let i = 0; i < itemCount; i++) {
|
|
392
|
-
const start = dataView.getInt32(offset, true);
|
|
393
|
-
const end = dataView.getInt32(offset + 4, true);
|
|
394
|
-
if (start < reqEnd && end >= reqStart) {
|
|
395
|
-
starts[idx] = start;
|
|
396
|
-
ends[idx] = end;
|
|
397
|
-
scores[idx] = dataView.getFloat32(offset + 8, true);
|
|
398
|
-
idx++;
|
|
399
|
-
}
|
|
400
|
-
offset += 12;
|
|
394
|
+
else {
|
|
395
|
+
decompressedData = data;
|
|
396
|
+
decompressedOffsets = [];
|
|
397
|
+
for (const block of subBlocks) {
|
|
398
|
+
decompressedOffsets.push(block.offset - groupOffset);
|
|
401
399
|
}
|
|
402
|
-
|
|
400
|
+
decompressedOffsets.push(data.length);
|
|
403
401
|
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
402
|
+
for (let i = 0; i < subBlocks.length; i++) {
|
|
403
|
+
const start = decompressedOffsets[i];
|
|
404
|
+
const end = decompressedOffsets[i + 1];
|
|
405
|
+
const resultData = decompressedData.subarray(start, end);
|
|
406
|
+
let features;
|
|
407
|
+
switch (blockType) {
|
|
408
|
+
case 'summary':
|
|
409
|
+
features = parseSummaryBlock(resultData, 0, request);
|
|
410
|
+
break;
|
|
411
|
+
case 'bigwig':
|
|
412
|
+
features = parseBigWigBlock(resultData, 0, request);
|
|
413
|
+
break;
|
|
414
|
+
case 'bigbed':
|
|
415
|
+
features = parseBigBedBlock(resultData, 0, subBlocks[i].offset * (1 << 8), request);
|
|
416
|
+
break;
|
|
417
|
+
default:
|
|
418
|
+
features = [];
|
|
419
|
+
console.warn(`Don't know what to do with ${blockType}`);
|
|
416
420
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
case 3: {
|
|
420
|
-
let offset = 24;
|
|
421
|
-
for (let i = 0; i < itemCount; i++) {
|
|
422
|
-
const start = blockStart + i * itemStep;
|
|
423
|
-
const end = start + itemSpan;
|
|
424
|
-
if (start < reqEnd && end >= reqStart) {
|
|
425
|
-
starts[idx] = start;
|
|
426
|
-
ends[idx] = end;
|
|
427
|
-
scores[idx] = dataView.getFloat32(offset, true);
|
|
428
|
-
idx++;
|
|
429
|
-
}
|
|
430
|
-
offset += 4;
|
|
421
|
+
for (const f of features) {
|
|
422
|
+
allFeatures.push(f);
|
|
431
423
|
}
|
|
432
|
-
break;
|
|
433
424
|
}
|
|
434
425
|
}
|
|
435
|
-
|
|
436
|
-
return {
|
|
437
|
-
starts: starts.subarray(0, idx),
|
|
438
|
-
ends: ends.subarray(0, idx),
|
|
439
|
-
scores: scores.subarray(0, idx),
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
return { starts, ends, scores };
|
|
443
|
-
}
|
|
444
|
-
async readFeatures(observer, blocks, opts = {}) {
|
|
445
|
-
try {
|
|
446
|
-
const { blockType, uncompressBufSize } = this;
|
|
447
|
-
const { signal, request } = opts;
|
|
448
|
-
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
449
|
-
await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
|
|
450
|
-
const { length, offset } = blockGroup;
|
|
451
|
-
const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
|
|
452
|
-
const localBlocks = blockGroup.blocks.map(block => ({
|
|
453
|
-
offset: block.offset - blockGroup.offset,
|
|
454
|
-
length: block.length,
|
|
455
|
-
}));
|
|
456
|
-
let decompressedData;
|
|
457
|
-
let decompressedOffsets;
|
|
458
|
-
if (uncompressBufSize > 0) {
|
|
459
|
-
const result = await (0, unzip_ts_1.unzipBatch)(data, localBlocks, uncompressBufSize);
|
|
460
|
-
decompressedData = result.data;
|
|
461
|
-
decompressedOffsets = result.offsets;
|
|
462
|
-
}
|
|
463
|
-
else {
|
|
464
|
-
decompressedData = data;
|
|
465
|
-
decompressedOffsets = localBlocks.map(b => b.offset);
|
|
466
|
-
decompressedOffsets.push(data.length);
|
|
467
|
-
}
|
|
468
|
-
for (let i = 0; i < blockGroup.blocks.length; i++) {
|
|
469
|
-
const block = blockGroup.blocks[i];
|
|
470
|
-
const start = decompressedOffsets[i];
|
|
471
|
-
const end = decompressedOffsets[i + 1];
|
|
472
|
-
const resultData = decompressedData.subarray(start, end);
|
|
473
|
-
switch (blockType) {
|
|
474
|
-
case 'summary': {
|
|
475
|
-
observer.next(this.parseSummaryBlock(resultData, 0, request));
|
|
476
|
-
break;
|
|
477
|
-
}
|
|
478
|
-
case 'bigwig': {
|
|
479
|
-
observer.next(this.parseBigWigBlock(resultData, 0, request));
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
case 'bigbed': {
|
|
483
|
-
observer.next(this.parseBigBedBlock(resultData, 0, block.offset * (1 << 8), request));
|
|
484
|
-
break;
|
|
485
|
-
}
|
|
486
|
-
default: {
|
|
487
|
-
console.warn(`Don't know what to do with ${blockType}`);
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}));
|
|
492
|
-
observer.complete();
|
|
493
|
-
}
|
|
494
|
-
catch (e) {
|
|
495
|
-
observer.error(e);
|
|
496
|
-
}
|
|
426
|
+
return allFeatures;
|
|
497
427
|
}
|
|
498
|
-
async
|
|
428
|
+
async _readBigWigFeaturesAsArrays(blocks, opts = {}) {
|
|
499
429
|
const { uncompressBufSize } = this;
|
|
500
430
|
const { signal, request } = opts;
|
|
501
431
|
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
@@ -503,9 +433,9 @@ class BlockView {
|
|
|
503
433
|
const allEnds = [];
|
|
504
434
|
const allScores = [];
|
|
505
435
|
let totalCount = 0;
|
|
506
|
-
|
|
436
|
+
for (const blockGroup of blockGroupsToFetch) {
|
|
507
437
|
const { length, offset } = blockGroup;
|
|
508
|
-
const data = await this.
|
|
438
|
+
const data = await this.bbi.read(length, offset, { signal });
|
|
509
439
|
const localBlocks = blockGroup.blocks.map(block => ({
|
|
510
440
|
offset: block.offset - blockGroup.offset,
|
|
511
441
|
length: block.length,
|
|
@@ -522,7 +452,7 @@ class BlockView {
|
|
|
522
452
|
else {
|
|
523
453
|
for (const block of localBlocks) {
|
|
524
454
|
const blockData = data.subarray(block.offset, block.offset + block.length);
|
|
525
|
-
const result =
|
|
455
|
+
const result = parseBigWigBlockAsArrays(blockData, 0, request);
|
|
526
456
|
if (result.starts.length > 0) {
|
|
527
457
|
allStarts.push(result.starts);
|
|
528
458
|
allEnds.push(result.ends);
|
|
@@ -531,7 +461,7 @@ class BlockView {
|
|
|
531
461
|
}
|
|
532
462
|
}
|
|
533
463
|
}
|
|
534
|
-
}
|
|
464
|
+
}
|
|
535
465
|
if (allStarts.length === 0) {
|
|
536
466
|
return {
|
|
537
467
|
starts: new Int32Array(0),
|
|
@@ -560,7 +490,7 @@ class BlockView {
|
|
|
560
490
|
}
|
|
561
491
|
return { starts, ends, scores, isSummary: false };
|
|
562
492
|
}
|
|
563
|
-
async
|
|
493
|
+
async _readSummaryFeaturesAsArrays(blocks, opts = {}) {
|
|
564
494
|
const { uncompressBufSize } = this;
|
|
565
495
|
const { signal, request } = opts;
|
|
566
496
|
const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
|
|
@@ -570,9 +500,9 @@ class BlockView {
|
|
|
570
500
|
const allMinScores = [];
|
|
571
501
|
const allMaxScores = [];
|
|
572
502
|
let totalCount = 0;
|
|
573
|
-
|
|
503
|
+
for (const blockGroup of blockGroupsToFetch) {
|
|
574
504
|
const { length, offset } = blockGroup;
|
|
575
|
-
const data = await this.
|
|
505
|
+
const data = await this.bbi.read(length, offset, { signal });
|
|
576
506
|
const localBlocks = blockGroup.blocks.map(block => ({
|
|
577
507
|
offset: block.offset - blockGroup.offset,
|
|
578
508
|
length: block.length,
|
|
@@ -591,7 +521,7 @@ class BlockView {
|
|
|
591
521
|
else {
|
|
592
522
|
for (const block of localBlocks) {
|
|
593
523
|
const blockData = data.subarray(block.offset, block.offset + block.length);
|
|
594
|
-
const features =
|
|
524
|
+
const features = parseSummaryBlock(blockData, 0, request);
|
|
595
525
|
if (features.length > 0) {
|
|
596
526
|
const starts = new Int32Array(features.length);
|
|
597
527
|
const ends = new Int32Array(features.length);
|
|
@@ -615,7 +545,7 @@ class BlockView {
|
|
|
615
545
|
}
|
|
616
546
|
}
|
|
617
547
|
}
|
|
618
|
-
}
|
|
548
|
+
}
|
|
619
549
|
if (allStarts.length === 0) {
|
|
620
550
|
return {
|
|
621
551
|
starts: new Int32Array(0),
|