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