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