@gmod/bbi 7.1.0 → 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +80 -17
  5. package/dist/bbi.js.map +1 -1
  6. package/dist/bigbed.d.ts +14 -2
  7. package/dist/bigbed.js +116 -76
  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 +324 -148
  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 +11 -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 +6 -5
  28. package/dist/util.js.map +1 -1
  29. package/dist/wasm/inflate-wasm-inlined.d.ts +18 -0
  30. package/dist/wasm/inflate-wasm-inlined.js +455 -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 +80 -17
  40. package/esm/bbi.js.map +1 -1
  41. package/esm/bigbed.d.ts +14 -2
  42. package/esm/bigbed.js +116 -76
  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 +326 -150
  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 +11 -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 +6 -5
  63. package/esm/util.js.map +1 -1
  64. package/esm/wasm/inflate-wasm-inlined.d.ts +18 -0
  65. package/esm/wasm/inflate-wasm-inlined.js +449 -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 +17 -12
  74. package/src/bbi.ts +102 -20
  75. package/src/bigbed.ts +157 -80
  76. package/src/bigwig.ts +1 -2
  77. package/src/block-view.ts +418 -156
  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 +86 -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 +547 -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
  }
@@ -16,22 +16,28 @@ function coordFilter(s1, e1, s2, e2) {
16
16
  export class BlockView {
17
17
  bbi;
18
18
  refsByName;
19
- cirTreeOffset;
20
- isCompressed;
19
+ rTreeOffset;
20
+ uncompressBufSize;
21
21
  blockType;
22
- cirTreePromise;
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;
23
25
  featureCache = new AbortablePromiseCache({
24
26
  cache: new QuickLRU({ maxSize: 1000 }),
25
27
  fill: async ({ length, offset }, signal) => this.bbi.read(length, offset, { signal }),
26
28
  });
27
- constructor(bbi, refsByName, cirTreeOffset, isCompressed, blockType) {
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) {
28
34
  this.bbi = bbi;
29
35
  this.refsByName = refsByName;
30
- this.cirTreeOffset = cirTreeOffset;
31
- this.isCompressed = isCompressed;
36
+ this.rTreeOffset = rTreeOffset;
37
+ this.uncompressBufSize = uncompressBufSize;
32
38
  this.blockType = blockType;
33
- if (!(cirTreeOffset >= 0)) {
34
- throw new Error('invalid cirTreeOffset!');
39
+ if (!(rTreeOffset >= 0)) {
40
+ throw new Error('invalid rTreeOffset!');
35
41
  }
36
42
  }
37
43
  async readWigData(chrName, start, end, observer, opts) {
@@ -39,105 +45,98 @@ export class BlockView {
39
45
  const chrId = this.refsByName[chrName];
40
46
  if (chrId === undefined) {
41
47
  observer.complete();
48
+ return;
42
49
  }
43
50
  const request = { chrId, start, end };
44
- if (!this.cirTreePromise) {
45
- this.cirTreePromise = this.bbi.read(48, this.cirTreeOffset, opts);
51
+ if (!this.rTreePromise) {
52
+ this.rTreePromise = this.bbi.read(48, this.rTreeOffset, opts);
46
53
  }
47
- const buffer = await this.cirTreePromise;
48
- const dataView = new DataView(buffer.buffer);
49
- const cirBlockSize = dataView.getUint32(4, true);
50
- 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 = [];
51
59
  let outstanding = 0;
52
- 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) => {
53
108
  try {
54
- const data = cirBlockData.subarray(offset2);
55
- const b = data;
56
- 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);
57
111
  let offset = 0;
58
112
  const isLeaf = dataView.getUint8(offset);
59
- offset += 2; // 1 skip
60
- const cnt = dataView.getUint16(offset, true);
113
+ offset += 2; // 1 skip for reserved byte
114
+ const count = dataView.getUint16(offset, true);
61
115
  offset += 2;
62
116
  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
- })));
117
+ processLeafNode(dataView, offset, count);
93
118
  }
94
119
  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
- }
120
+ processNonLeafNode(dataView, offset, count, level);
122
121
  }
123
122
  }
124
123
  catch (e) {
125
124
  observer.error(e);
126
125
  }
127
126
  };
128
- const filterFeats = (b) => {
127
+ const blockIntersectsQuery = (b) => {
129
128
  const { startChrom, startBase, endChrom, endBase } = b;
130
129
  return ((startChrom < chrId || (startChrom === chrId && startBase <= end)) &&
131
130
  (endChrom > chrId || (endChrom === chrId && endBase >= start)));
132
131
  };
133
- const cirFobStartFetch = async (off, fr, level) => {
132
+ const fetchAndProcessRTreeBlocks = async (offsets, range, level) => {
134
133
  try {
135
- const length = fr.max - fr.min;
136
- const offset = fr.min;
134
+ const length = range.max - range.min;
135
+ const offset = range.min;
137
136
  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);
137
+ for (const element of offsets) {
138
+ if (range.contains(element)) {
139
+ processRTreeNode(resultBuffer, element - offset, level);
141
140
  outstanding -= 1;
142
141
  if (outstanding === 0) {
143
142
  this.readFeatures(observer, blocksToFetch, {
@@ -154,34 +153,37 @@ export class BlockView {
154
153
  observer.error(e);
155
154
  }
156
155
  };
157
- const cirFobRecur = (offset, level) => {
156
+ const traverseRTree = (offsets, level) => {
158
157
  try {
159
- outstanding += offset.length;
158
+ outstanding += offsets.length;
160
159
  // Upper bound on size, based on a completely full leaf node.
161
- const maxCirBlockSpan = 4 + cirBlockSize * 32;
160
+ const maxRTreeBlockSpan = 4 + rTreeBlockSize * 32;
162
161
  let spans = new Range([
163
162
  {
164
- min: offset[0],
165
- max: offset[0] + maxCirBlockSpan,
163
+ min: offsets[0],
164
+ max: offsets[0] + maxRTreeBlockSpan,
166
165
  },
167
166
  ]);
168
- for (let i = 1; i < offset.length; i += 1) {
167
+ for (let i = 1; i < offsets.length; i += 1) {
169
168
  const blockSpan = new Range([
170
169
  {
171
- min: offset[i],
172
- max: offset[i] + maxCirBlockSpan,
170
+ min: offsets[i],
171
+ max: offsets[i] + maxRTreeBlockSpan,
173
172
  },
174
173
  ]);
175
174
  spans = spans.union(blockSpan);
176
175
  }
177
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
178
- 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
+ });
179
181
  }
180
182
  catch (e) {
181
183
  observer.error(e);
182
184
  }
183
185
  };
184
- cirFobRecur([this.cirTreeOffset + 48], 1);
186
+ traverseRTree([this.rTreeOffset + 48], 1);
185
187
  return;
186
188
  }
187
189
  catch (e) {
@@ -193,8 +195,6 @@ export class BlockView {
193
195
  let offset = startOffset;
194
196
  const dataView = new DataView(b.buffer, b.byteOffset, b.length);
195
197
  while (offset < b.byteLength) {
196
- // this was extracted from looking at the runtime code generated by
197
- // binary-parser
198
198
  const chromId = dataView.getUint32(offset, true);
199
199
  offset += 4;
200
200
  const start = dataView.getUint32(offset, true);
@@ -208,14 +208,10 @@ export class BlockView {
208
208
  const maxScore = dataView.getFloat32(offset, true);
209
209
  offset += 4;
210
210
  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) {
211
+ offset += 8;
212
+ if (!request ||
213
+ (chromId === request.chrId &&
214
+ coordFilter(start, end, request.start, request.end))) {
219
215
  features.push({
220
216
  start,
221
217
  end,
@@ -231,8 +227,7 @@ export class BlockView {
231
227
  parseBigBedBlock(data, startOffset, offset, request) {
232
228
  const items = [];
233
229
  let currOffset = startOffset;
234
- const b = data;
235
- const dataView = new DataView(b.buffer, b.byteOffset, b.length);
230
+ const dataView = new DataView(data.buffer, data.byteOffset, data.length);
236
231
  while (currOffset < data.byteLength) {
237
232
  const c2 = currOffset;
238
233
  const chromId = dataView.getUint32(currOffset, true);
@@ -248,19 +243,20 @@ export class BlockView {
248
243
  }
249
244
  }
250
245
  const b = data.subarray(currOffset, i);
251
- const rest = decoder?.decode(b) ?? b.toString();
246
+ const rest = decoder.decode(b);
252
247
  currOffset = i + 1;
253
- items.push({
254
- chromId,
255
- start,
256
- end,
257
- rest,
258
- uniqueId: `bb-${offset + c2}`,
259
- });
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
+ }
260
258
  }
261
- return request
262
- ? items.filter((f) => coordFilter(f.start, f.end, request.start, request.end))
263
- : items;
259
+ return items;
264
260
  }
265
261
  parseBigWigBlock(buffer, startOffset, req) {
266
262
  const b = buffer.subarray(startOffset);
@@ -277,7 +273,7 @@ export class BlockView {
277
273
  offset += 2;
278
274
  const itemCount = dataView.getUint16(offset, true);
279
275
  offset += 2;
280
- const items = new Array(itemCount);
276
+ const items = [];
281
277
  switch (blockType) {
282
278
  case 1: {
283
279
  for (let i = 0; i < itemCount; i++) {
@@ -287,11 +283,13 @@ export class BlockView {
287
283
  offset += 4;
288
284
  const score = dataView.getFloat32(offset, true);
289
285
  offset += 4;
290
- items[i] = {
291
- start,
292
- end,
293
- score,
294
- };
286
+ if (!req || coordFilter(start, end, req.start, req.end)) {
287
+ items.push({
288
+ start,
289
+ end,
290
+ score,
291
+ });
292
+ }
295
293
  }
296
294
  break;
297
295
  }
@@ -301,11 +299,14 @@ export class BlockView {
301
299
  offset += 4;
302
300
  const score = dataView.getFloat32(offset, true);
303
301
  offset += 4;
304
- items[i] = {
305
- score,
306
- start,
307
- end: start + itemSpan,
308
- };
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
+ }
309
310
  }
310
311
  break;
311
312
  }
@@ -314,36 +315,49 @@ export class BlockView {
314
315
  const score = dataView.getFloat32(offset, true);
315
316
  offset += 4;
316
317
  const start = blockStart + i * itemStep;
317
- items[i] = {
318
- score,
319
- start,
320
- end: start + itemSpan,
321
- };
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
+ }
322
326
  }
323
327
  break;
324
328
  }
325
329
  }
326
- return req
327
- ? items.filter(f => coordFilter(f.start, f.end, req.start, req.end))
328
- : items;
330
+ return items;
329
331
  }
330
332
  async readFeatures(observer, blocks, opts = {}) {
331
333
  try {
332
- const { blockType, isCompressed } = this;
334
+ const { blockType, uncompressBufSize } = this;
333
335
  const { signal, request } = opts;
334
336
  const blockGroupsToFetch = groupBlocks(blocks);
335
- checkAbortSignal(signal);
336
337
  await Promise.all(blockGroupsToFetch.map(async (blockGroup) => {
337
- checkAbortSignal(signal);
338
338
  const { length, offset } = blockGroup;
339
339
  const data = await this.featureCache.get(`${length}_${offset}`, blockGroup, signal);
340
- for (const block of blockGroup.blocks) {
341
- checkAbortSignal(signal);
342
- let resultData = data.subarray(block.offset - blockGroup.offset);
343
- if (isCompressed) {
344
- resultData = unzip(resultData);
345
- }
346
- 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);
347
361
  switch (blockType) {
348
362
  case 'summary': {
349
363
  observer.next(this.parseSummaryBlock(resultData, 0, request));
@@ -369,5 +383,167 @@ export class BlockView {
369
383
  observer.error(e);
370
384
  }
371
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
+ }
372
548
  }
373
549
  //# sourceMappingURL=block-view.js.map