@gmod/bbi 7.0.5 → 8.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 (90) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +59 -0
  3. package/dist/bbi.d.ts +13 -3
  4. package/dist/bbi.js +81 -18
  5. package/dist/bbi.js.map +1 -1
  6. package/dist/bigbed.d.ts +14 -2
  7. package/dist/bigbed.js +127 -86
  8. package/dist/bigbed.js.map +1 -1
  9. package/dist/bigwig.js +1 -2
  10. package/dist/bigwig.js.map +1 -1
  11. package/dist/block-view.d.ts +13 -4
  12. package/dist/block-view.js +332 -150
  13. package/dist/block-view.js.map +1 -1
  14. package/dist/index.d.ts +2 -1
  15. package/dist/index.js +3 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/parse-bigwig.d.ts +3 -0
  18. package/dist/parse-bigwig.js +15 -0
  19. package/dist/parse-bigwig.js.map +1 -0
  20. package/dist/range.js +12 -24
  21. package/dist/range.js.map +1 -1
  22. package/dist/types.d.ts +14 -2
  23. package/dist/unzip.d.ts +18 -1
  24. package/dist/unzip.js +33 -4
  25. package/dist/unzip.js.map +1 -1
  26. package/dist/util.d.ts +2 -4
  27. package/dist/util.js +7 -5
  28. package/dist/util.js.map +1 -1
  29. package/dist/wasm/inflate-wasm-inlined.d.ts +19 -0
  30. package/dist/wasm/inflate-wasm-inlined.js +117 -0
  31. package/dist/wasm/inflate-wasm-inlined.js.map +1 -0
  32. package/dist/wasm/inflate_wasm.d.ts +1 -0
  33. package/dist/wasm/inflate_wasm.js +43 -0
  34. package/dist/wasm/inflate_wasm.js.map +1 -0
  35. package/dist/wasm/inflate_wasm_bg.d.ts +68 -0
  36. package/dist/wasm/inflate_wasm_bg.js +307 -0
  37. package/dist/wasm/inflate_wasm_bg.js.map +1 -0
  38. package/esm/bbi.d.ts +13 -3
  39. package/esm/bbi.js +81 -18
  40. package/esm/bbi.js.map +1 -1
  41. package/esm/bigbed.d.ts +14 -2
  42. package/esm/bigbed.js +127 -86
  43. package/esm/bigbed.js.map +1 -1
  44. package/esm/bigwig.js +1 -2
  45. package/esm/bigwig.js.map +1 -1
  46. package/esm/block-view.d.ts +13 -4
  47. package/esm/block-view.js +334 -152
  48. package/esm/block-view.js.map +1 -1
  49. package/esm/index.d.ts +2 -1
  50. package/esm/index.js +1 -0
  51. package/esm/index.js.map +1 -1
  52. package/esm/parse-bigwig.d.ts +3 -0
  53. package/esm/parse-bigwig.js +12 -0
  54. package/esm/parse-bigwig.js.map +1 -0
  55. package/esm/range.js +12 -24
  56. package/esm/range.js.map +1 -1
  57. package/esm/types.d.ts +14 -2
  58. package/esm/unzip.d.ts +18 -1
  59. package/esm/unzip.js +30 -3
  60. package/esm/unzip.js.map +1 -1
  61. package/esm/util.d.ts +2 -4
  62. package/esm/util.js +7 -5
  63. package/esm/util.js.map +1 -1
  64. package/esm/wasm/inflate-wasm-inlined.d.ts +19 -0
  65. package/esm/wasm/inflate-wasm-inlined.js +111 -0
  66. package/esm/wasm/inflate-wasm-inlined.js.map +1 -0
  67. package/esm/wasm/inflate_wasm.d.ts +1 -0
  68. package/esm/wasm/inflate_wasm.js +5 -0
  69. package/esm/wasm/inflate_wasm.js.map +1 -0
  70. package/esm/wasm/inflate_wasm_bg.d.ts +68 -0
  71. package/esm/wasm/inflate_wasm_bg.js +296 -0
  72. package/esm/wasm/inflate_wasm_bg.js.map +1 -0
  73. package/package.json +21 -17
  74. package/src/bbi.ts +101 -21
  75. package/src/bigbed.ts +165 -83
  76. package/src/bigwig.ts +1 -2
  77. package/src/block-view.ts +415 -158
  78. package/src/index.ts +8 -1
  79. package/src/parse-bigwig.ts +19 -0
  80. package/src/range.ts +13 -21
  81. package/src/types.ts +19 -2
  82. package/src/unzip.ts +88 -3
  83. package/src/util.ts +9 -10
  84. package/src/wasm/inflate-wasm-inlined.d.ts +49 -0
  85. package/src/wasm/inflate-wasm-inlined.js +1 -0
  86. package/src/wasm/inflate_wasm.d.ts +35 -0
  87. package/src/wasm/inflate_wasm.js +4 -0
  88. package/src/wasm/inflate_wasm_bg.js +309 -0
  89. package/src/wasm/inflate_wasm_bg.wasm +0 -0
  90. package/src/wasm/inflate_wasm_bg.wasm.d.ts +13 -0
@@ -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 = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf8') : undefined;
12
+ const decoder = new TextDecoder('utf8');
13
13
  function coordFilter(s1, e1, s2, e2) {
14
14
  return s1 < e2 && e1 >= s2;
15
15
  }
@@ -20,18 +20,30 @@ function coordFilter(s1, e1, s2, e2) {
20
20
  * Genome Explorer by Thomas Down.
21
21
  */
22
22
  class BlockView {
23
- constructor(bbi, refsByName, cirTreeOffset, isCompressed, blockType) {
23
+ bbi;
24
+ refsByName;
25
+ rTreeOffset;
26
+ uncompressBufSize;
27
+ blockType;
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;
31
+ featureCache = new abortable_promise_cache_1.default({
32
+ cache: new quick_lru_1.default({ maxSize: 1000 }),
33
+ fill: async ({ length, offset }, signal) => this.bbi.read(length, offset, { signal }),
34
+ });
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) {
24
40
  this.bbi = bbi;
25
41
  this.refsByName = refsByName;
26
- this.cirTreeOffset = cirTreeOffset;
27
- this.isCompressed = isCompressed;
42
+ this.rTreeOffset = rTreeOffset;
43
+ this.uncompressBufSize = uncompressBufSize;
28
44
  this.blockType = blockType;
29
- this.featureCache = new abortable_promise_cache_1.default({
30
- cache: new quick_lru_1.default({ maxSize: 1000 }),
31
- fill: async ({ length, offset }, signal) => this.bbi.read(length, offset, { signal }),
32
- });
33
- if (!(cirTreeOffset >= 0)) {
34
- throw new Error('invalid cirTreeOffset!');
45
+ if (!(rTreeOffset >= 0)) {
46
+ throw new Error('invalid rTreeOffset!');
35
47
  }
36
48
  }
37
49
  async readWigData(chrName, start, end, observer, opts) {
@@ -39,105 +51,98 @@ class BlockView {
39
51
  const chrId = this.refsByName[chrName];
40
52
  if (chrId === undefined) {
41
53
  observer.complete();
54
+ return;
42
55
  }
43
56
  const request = { chrId, start, end };
44
- if (!this.cirTreePromise) {
45
- this.cirTreePromise = this.bbi.read(48, this.cirTreeOffset, opts);
57
+ if (!this.rTreePromise) {
58
+ this.rTreePromise = this.bbi.read(48, this.rTreeOffset, opts);
46
59
  }
47
- const buffer = await this.cirTreePromise;
48
- const dataView = new DataView(buffer.buffer);
49
- const cirBlockSize = dataView.getUint32(4, true);
50
- let blocksToFetch = [];
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 = [];
51
65
  let outstanding = 0;
52
- const cirFobRecur2 = (cirBlockData, offset2, level) => {
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) => {
53
114
  try {
54
- const data = cirBlockData.subarray(offset2);
55
- const b = data;
56
- 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);
57
117
  let offset = 0;
58
118
  const isLeaf = dataView.getUint8(offset);
59
- offset += 2; // 1 skip
60
- const cnt = dataView.getUint16(offset, true);
119
+ offset += 2; // 1 skip for reserved byte
120
+ const count = dataView.getUint16(offset, true);
61
121
  offset += 2;
62
122
  if (isLeaf === 1) {
63
- const blocksToFetch2 = [];
64
- for (let i = 0; i < cnt; i++) {
65
- const startChrom = dataView.getUint32(offset, true);
66
- offset += 4;
67
- const startBase = dataView.getUint32(offset, true);
68
- offset += 4;
69
- const endChrom = dataView.getUint32(offset, true);
70
- offset += 4;
71
- const endBase = dataView.getUint32(offset, true);
72
- offset += 4;
73
- const blockOffset = Number(dataView.getBigUint64(offset, true));
74
- offset += 8;
75
- const blockSize = Number(dataView.getBigUint64(offset, true));
76
- offset += 8;
77
- blocksToFetch2.push({
78
- startChrom,
79
- startBase,
80
- endBase,
81
- endChrom,
82
- blockOffset,
83
- blockSize,
84
- offset,
85
- });
86
- }
87
- blocksToFetch = blocksToFetch.concat(blocksToFetch2
88
- .filter(f => filterFeats(f))
89
- .map(l => ({
90
- offset: l.blockOffset,
91
- length: l.blockSize,
92
- })));
123
+ processLeafNode(dataView, offset, count);
93
124
  }
94
125
  else if (isLeaf === 0) {
95
- const recurOffsets = [];
96
- for (let i = 0; i < cnt; i++) {
97
- const startChrom = dataView.getUint32(offset, true);
98
- offset += 4;
99
- const startBase = dataView.getUint32(offset, true);
100
- offset += 4;
101
- const endChrom = dataView.getUint32(offset, true);
102
- offset += 4;
103
- const endBase = dataView.getUint32(offset, true);
104
- offset += 4;
105
- const blockOffset = Number(dataView.getBigUint64(offset, true));
106
- offset += 8;
107
- recurOffsets.push({
108
- startChrom,
109
- startBase,
110
- endChrom,
111
- endBase,
112
- blockOffset,
113
- offset,
114
- });
115
- }
116
- const recurOffsets2 = recurOffsets
117
- .filter(f => filterFeats(f))
118
- .map(l => l.blockOffset);
119
- if (recurOffsets2.length > 0) {
120
- cirFobRecur(recurOffsets2, level + 1);
121
- }
126
+ processNonLeafNode(dataView, offset, count, level);
122
127
  }
123
128
  }
124
129
  catch (e) {
125
130
  observer.error(e);
126
131
  }
127
132
  };
128
- const filterFeats = (b) => {
133
+ const blockIntersectsQuery = (b) => {
129
134
  const { startChrom, startBase, endChrom, endBase } = b;
130
135
  return ((startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
131
136
  (endChrom > chrId || (endChrom === chrId && endBase >= start)));
132
137
  };
133
- const cirFobStartFetch = async (off, fr, level) => {
138
+ const fetchAndProcessRTreeBlocks = async (offsets, range, level) => {
134
139
  try {
135
- const length = fr.max - fr.min;
136
- const offset = fr.min;
140
+ const length = range.max - range.min;
141
+ const offset = range.min;
137
142
  const resultBuffer = await this.featureCache.get(`${length}_${offset}`, { length, offset }, opts?.signal);
138
- for (const element of off) {
139
- if (fr.contains(element)) {
140
- cirFobRecur2(resultBuffer, element - offset, level);
143
+ for (const element of offsets) {
144
+ if (range.contains(element)) {
145
+ processRTreeNode(resultBuffer, element - offset, level);
141
146
  outstanding -= 1;
142
147
  if (outstanding === 0) {
143
148
  this.readFeatures(observer, blocksToFetch, {
@@ -154,34 +159,37 @@ class BlockView {
154
159
  observer.error(e);
155
160
  }
156
161
  };
157
- const cirFobRecur = (offset, level) => {
162
+ const traverseRTree = (offsets, level) => {
158
163
  try {
159
- outstanding += offset.length;
164
+ outstanding += offsets.length;
160
165
  // Upper bound on size, based on a completely full leaf node.
161
- const maxCirBlockSpan = 4 + cirBlockSize * 32;
166
+ const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32;
162
167
  let spans = new range_ts_1.default([
163
168
  {
164
- min: offset[0],
165
- max: offset[0] + maxCirBlockSpan,
169
+ min: offsets[0],
170
+ max: offsets[0] + maxRTreeBlockSpan,
166
171
  },
167
172
  ]);
168
- for (let i = 1; i < offset.length; i += 1) {
173
+ for (let i = 1; i < offsets.length; i += 1) {
169
174
  const blockSpan = new range_ts_1.default([
170
175
  {
171
- min: offset[i],
172
- max: offset[i] + maxCirBlockSpan,
176
+ min: offsets[i],
177
+ max: offsets[i] + maxRTreeBlockSpan,
173
178
  },
174
179
  ]);
175
180
  spans = spans.union(blockSpan);
176
181
  }
177
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
178
- spans.getRanges().map(fr => cirFobStartFetch(offset, fr, level));
182
+ spans.getRanges().forEach(range => {
183
+ fetchAndProcessRTreeBlocks(offsets, range, level).catch((e) => {
184
+ observer.error(e);
185
+ });
186
+ });
179
187
  }
180
188
  catch (e) {
181
189
  observer.error(e);
182
190
  }
183
191
  };
184
- cirFobRecur([Number(this.cirTreeOffset) + 48], 1);
192
+ traverseRTree([this.rTreeOffset + 48], 1);
185
193
  return;
186
194
  }
187
195
  catch (e) {
@@ -193,8 +201,6 @@ class BlockView {
193
201
  let offset = startOffset;
194
202
  const dataView = new DataView(b.buffer, b.byteOffset, b.length);
195
203
  while (offset < b.byteLength) {
196
- // this was extracted from looking at the runtime code generated by
197
- // binary-parser
198
204
  const chromId = dataView.getUint32(offset, true);
199
205
  offset += 4;
200
206
  const start = dataView.getUint32(offset, true);
@@ -208,14 +214,10 @@ class BlockView {
208
214
  const maxScore = dataView.getFloat32(offset, true);
209
215
  offset += 4;
210
216
  const sumData = dataView.getFloat32(offset, true);
211
- offset += 4;
212
- // unused
213
- // const sumSqData = dataView.getFloat32(offset, true)
214
- offset += 4;
215
- if (request
216
- ? chromId === request.chrId &&
217
- coordFilter(start, end, request.start, request.end)
218
- : true) {
217
+ offset += 8;
218
+ if (!request ||
219
+ (chromId === request.chrId &&
220
+ coordFilter(start, end, request.start, request.end))) {
219
221
  features.push({
220
222
  start,
221
223
  end,
@@ -231,8 +233,7 @@ class BlockView {
231
233
  parseBigBedBlock(data, startOffset, offset, request) {
232
234
  const items = [];
233
235
  let currOffset = startOffset;
234
- const b = data;
235
- const dataView = new DataView(b.buffer, b.byteOffset, b.length);
236
+ const dataView = new DataView(data.buffer, data.byteOffset, data.length);
236
237
  while (currOffset < data.byteLength) {
237
238
  const c2 = currOffset;
238
239
  const chromId = dataView.getUint32(currOffset, true);
@@ -248,19 +249,20 @@ class BlockView {
248
249
  }
249
250
  }
250
251
  const b = data.subarray(currOffset, i);
251
- const rest = decoder?.decode(b) ?? b.toString();
252
+ const rest = decoder.decode(b);
252
253
  currOffset = i + 1;
253
- items.push({
254
- chromId,
255
- start,
256
- end,
257
- rest,
258
- uniqueId: `bb-${offset + c2}`,
259
- });
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
+ }
260
264
  }
261
- return request
262
- ? items.filter((f) => coordFilter(f.start, f.end, request.start, request.end))
263
- : items;
265
+ return items;
264
266
  }
265
267
  parseBigWigBlock(buffer, startOffset, req) {
266
268
  const b = buffer.subarray(startOffset);
@@ -277,7 +279,7 @@ class BlockView {
277
279
  offset += 2;
278
280
  const itemCount = dataView.getUint16(offset, true);
279
281
  offset += 2;
280
- const items = new Array(itemCount);
282
+ const items = [];
281
283
  switch (blockType) {
282
284
  case 1: {
283
285
  for (let i = 0; i < itemCount; i++) {
@@ -287,11 +289,13 @@ class BlockView {
287
289
  offset += 4;
288
290
  const score = dataView.getFloat32(offset, true);
289
291
  offset += 4;
290
- items[i] = {
291
- start,
292
- end,
293
- score,
294
- };
292
+ if (!req || coordFilter(start, end, req.start, req.end)) {
293
+ items.push({
294
+ start,
295
+ end,
296
+ score,
297
+ });
298
+ }
295
299
  }
296
300
  break;
297
301
  }
@@ -301,11 +305,14 @@ class BlockView {
301
305
  offset += 4;
302
306
  const score = dataView.getFloat32(offset, true);
303
307
  offset += 4;
304
- items[i] = {
305
- score,
306
- start,
307
- end: start + itemSpan,
308
- };
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
+ }
309
316
  }
310
317
  break;
311
318
  }
@@ -314,36 +321,49 @@ class BlockView {
314
321
  const score = dataView.getFloat32(offset, true);
315
322
  offset += 4;
316
323
  const start = blockStart + i * itemStep;
317
- items[i] = {
318
- score,
319
- start,
320
- end: start + itemSpan,
321
- };
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
+ }
322
332
  }
323
333
  break;
324
334
  }
325
335
  }
326
- return req
327
- ? items.filter(f => coordFilter(f.start, f.end, req.start, req.end))
328
- : items;
336
+ return items;
329
337
  }
330
338
  async readFeatures(observer, blocks, opts = {}) {
331
339
  try {
332
- const { blockType, isCompressed } = this;
340
+ const { blockType, uncompressBufSize } = this;
333
341
  const { signal, request } = opts;
334
342
  const blockGroupsToFetch = (0, util_ts_1.groupBlocks)(blocks);
335
- (0, util_ts_1.checkAbortSignal)(signal);
336
343
  await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
337
- (0, util_ts_1.checkAbortSignal)(signal);
338
344
  const { length, offset } = blockGroup;
339
345
  const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
340
- for (const block of blockGroup.blocks) {
341
- (0, util_ts_1.checkAbortSignal)(signal);
342
- let resultData = data.subarray(Number(block.offset) - Number(blockGroup.offset));
343
- if (isCompressed) {
344
- resultData = (0, unzip_ts_1.unzip)(resultData);
345
- }
346
- (0, util_ts_1.checkAbortSignal)(signal);
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);
347
367
  switch (blockType) {
348
368
  case 'summary': {
349
369
  observer.next(this.parseSummaryBlock(resultData, 0, request));
@@ -354,7 +374,7 @@ class BlockView {
354
374
  break;
355
375
  }
356
376
  case 'bigbed': {
357
- observer.next(this.parseBigBedBlock(resultData, 0, Number(block.offset) * (1 << 8), request));
377
+ observer.next(this.parseBigBedBlock(resultData, 0, block.offset * (1 << 8), request));
358
378
  break;
359
379
  }
360
380
  default: {
@@ -369,6 +389,168 @@ class BlockView {
369
389
  observer.error(e);
370
390
  }
371
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
+ }
372
554
  }
373
555
  exports.BlockView = BlockView;
374
556
  //# sourceMappingURL=block-view.js.map