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