@gmod/tabix 1.5.3 → 1.5.5
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.
- package/CHANGELOG.md +12 -0
- package/README.md +37 -37
- package/dist/chunk.js +12 -14
- package/dist/chunk.js.map +1 -1
- package/dist/csi.js +170 -253
- package/dist/csi.js.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/indexFile.js +27 -73
- package/dist/indexFile.js.map +1 -1
- package/dist/tabixIndexedFile.d.ts +1 -1
- package/dist/tabixIndexedFile.js +205 -306
- package/dist/tabixIndexedFile.js.map +1 -1
- package/dist/tbi.js +185 -256
- package/dist/tbi.js.map +1 -1
- package/dist/util.js +10 -64
- package/dist/util.js.map +1 -1
- package/dist/virtualOffset.js +13 -20
- package/dist/virtualOffset.js.map +1 -1
- package/esm/tabixIndexedFile.d.ts +1 -1
- package/package.json +14 -14
package/dist/tabixIndexedFile.js
CHANGED
|
@@ -8,50 +8,23 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
-
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
-
function step(op) {
|
|
16
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
-
while (_) try {
|
|
18
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
-
switch (op[0]) {
|
|
21
|
-
case 0: case 1: t = op; break;
|
|
22
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
-
default:
|
|
26
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
-
if (t[2]) _.ops.pop();
|
|
31
|
-
_.trys.pop(); continue;
|
|
32
|
-
}
|
|
33
|
-
op = body.call(thisArg, _);
|
|
34
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
11
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
13
|
};
|
|
41
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
15
|
+
const abortable_promise_cache_1 = __importDefault(require("abortable-promise-cache"));
|
|
16
|
+
const quick_lru_1 = __importDefault(require("quick-lru"));
|
|
17
|
+
const generic_filehandle_1 = require("generic-filehandle");
|
|
18
|
+
const bgzf_filehandle_1 = require("@gmod/bgzf-filehandle");
|
|
19
|
+
const util_1 = require("./util");
|
|
20
|
+
const tbi_1 = __importDefault(require("./tbi"));
|
|
21
|
+
const csi_1 = __importDefault(require("./csi"));
|
|
49
22
|
function timeout(time) {
|
|
50
|
-
return new Promise(
|
|
23
|
+
return new Promise(resolve => {
|
|
51
24
|
setTimeout(resolve, time);
|
|
52
25
|
});
|
|
53
26
|
}
|
|
54
|
-
|
|
27
|
+
class TabixIndexedFile {
|
|
55
28
|
/**
|
|
56
29
|
* @param {object} args
|
|
57
30
|
* @param {string} [args.path]
|
|
@@ -67,8 +40,7 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
67
40
|
* @param {number} [args.chunkCacheSize] maximum size in bytes of the chunk cache. default 5MB
|
|
68
41
|
* @param {number} [args.blockCacheSize] maximum size in bytes of the block cache. default 5MB
|
|
69
42
|
*/
|
|
70
|
-
|
|
71
|
-
var path = _a.path, filehandle = _a.filehandle, tbiPath = _a.tbiPath, tbiFilehandle = _a.tbiFilehandle, csiPath = _a.csiPath, csiFilehandle = _a.csiFilehandle, _b = _a.chunkSizeLimit, chunkSizeLimit = _b === void 0 ? 50000000 : _b, _c = _a.renameRefSeqs, renameRefSeqs = _c === void 0 ? function (n) { return n; } : _c, _d = _a.chunkCacheSize, chunkCacheSize = _d === void 0 ? 5 * Math.pow(2, 20) : _d;
|
|
43
|
+
constructor({ path, filehandle, tbiPath, tbiFilehandle, csiPath, csiFilehandle, chunkSizeLimit = 50000000, renameRefSeqs = n => n, chunkCacheSize = 5 * Math.pow(2, 20), }) {
|
|
72
44
|
if (filehandle) {
|
|
73
45
|
this.filehandle = filehandle;
|
|
74
46
|
}
|
|
@@ -81,31 +53,31 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
81
53
|
if (tbiFilehandle) {
|
|
82
54
|
this.index = new tbi_1.default({
|
|
83
55
|
filehandle: tbiFilehandle,
|
|
84
|
-
renameRefSeqs
|
|
56
|
+
renameRefSeqs,
|
|
85
57
|
});
|
|
86
58
|
}
|
|
87
59
|
else if (csiFilehandle) {
|
|
88
60
|
this.index = new csi_1.default({
|
|
89
61
|
filehandle: csiFilehandle,
|
|
90
|
-
renameRefSeqs
|
|
62
|
+
renameRefSeqs,
|
|
91
63
|
});
|
|
92
64
|
}
|
|
93
65
|
else if (tbiPath) {
|
|
94
66
|
this.index = new tbi_1.default({
|
|
95
67
|
filehandle: new generic_filehandle_1.LocalFile(tbiPath),
|
|
96
|
-
renameRefSeqs
|
|
68
|
+
renameRefSeqs,
|
|
97
69
|
});
|
|
98
70
|
}
|
|
99
71
|
else if (csiPath) {
|
|
100
72
|
this.index = new csi_1.default({
|
|
101
73
|
filehandle: new generic_filehandle_1.LocalFile(csiPath),
|
|
102
|
-
renameRefSeqs
|
|
74
|
+
renameRefSeqs,
|
|
103
75
|
});
|
|
104
76
|
}
|
|
105
77
|
else if (path) {
|
|
106
78
|
this.index = new tbi_1.default({
|
|
107
|
-
filehandle: new generic_filehandle_1.LocalFile(
|
|
108
|
-
renameRefSeqs
|
|
79
|
+
filehandle: new generic_filehandle_1.LocalFile(`${path}.tbi`),
|
|
80
|
+
renameRefSeqs,
|
|
109
81
|
});
|
|
110
82
|
}
|
|
111
83
|
else {
|
|
@@ -127,133 +99,110 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
127
99
|
* @param {function|object} lineCallback callback called for each line in the region. can also pass a object param containing obj.lineCallback, obj.signal, etc
|
|
128
100
|
* @returns {Promise} resolved when the whole read is finished, rejected on error
|
|
129
101
|
*/
|
|
130
|
-
|
|
131
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// (1 << 8) these uncompressed offsets get a unique space
|
|
222
|
-
cpositions[pos] * (1 << 8) + (blockStart - dpositions[pos]));
|
|
223
|
-
}
|
|
224
|
-
else if (startCoordinate !== undefined && startCoordinate >= end) {
|
|
225
|
-
// the lines were overlapping the region, but now have stopped, so
|
|
226
|
-
// we must be at the end of the relevant data and we can stop
|
|
227
|
-
// processing data now
|
|
228
|
-
return [2 /*return*/];
|
|
229
|
-
}
|
|
230
|
-
blockStart += line.length + 1;
|
|
231
|
-
if (!(last - Date.now() > 500)) return [3 /*break*/, 7];
|
|
102
|
+
getLines(refName, start, end, opts) {
|
|
103
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
104
|
+
let signal;
|
|
105
|
+
let options = {};
|
|
106
|
+
let callback;
|
|
107
|
+
if (typeof opts === 'undefined') {
|
|
108
|
+
throw new TypeError('line callback must be provided');
|
|
109
|
+
}
|
|
110
|
+
if (typeof opts === 'function') {
|
|
111
|
+
callback = opts;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
options = opts;
|
|
115
|
+
callback = opts.lineCallback;
|
|
116
|
+
}
|
|
117
|
+
if (refName === undefined) {
|
|
118
|
+
throw new TypeError('must provide a reference sequence name');
|
|
119
|
+
}
|
|
120
|
+
if (!callback) {
|
|
121
|
+
throw new TypeError('line callback must be provided');
|
|
122
|
+
}
|
|
123
|
+
const metadata = yield this.index.getMetadata(options);
|
|
124
|
+
(0, util_1.checkAbortSignal)(signal);
|
|
125
|
+
if (!start) {
|
|
126
|
+
start = 0;
|
|
127
|
+
}
|
|
128
|
+
if (!end) {
|
|
129
|
+
end = metadata.maxRefLength;
|
|
130
|
+
}
|
|
131
|
+
if (!(start <= end)) {
|
|
132
|
+
throw new TypeError('invalid start and end coordinates. start must be less than or equal to end');
|
|
133
|
+
}
|
|
134
|
+
if (start === end) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const chunks = yield this.index.blocksForRange(refName, start, end, options);
|
|
138
|
+
(0, util_1.checkAbortSignal)(signal);
|
|
139
|
+
// check the chunks for any that are over the size limit. if
|
|
140
|
+
// any are, don't fetch any of them
|
|
141
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
142
|
+
const size = chunks[i].fetchedSize();
|
|
143
|
+
if (size > this.chunkSizeLimit) {
|
|
144
|
+
throw new Error(`Too much data. Chunk size ${size.toLocaleString()} bytes exceeds chunkSizeLimit of ${this.chunkSizeLimit.toLocaleString()}.`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// now go through each chunk and parse and filter the lines out of it
|
|
148
|
+
let last = Date.now();
|
|
149
|
+
for (let chunkNum = 0; chunkNum < chunks.length; chunkNum += 1) {
|
|
150
|
+
let previousStartCoordinate;
|
|
151
|
+
const c = chunks[chunkNum];
|
|
152
|
+
const { buffer, cpositions, dpositions } = yield this.chunkCache.get(c.toString(), c, signal);
|
|
153
|
+
const lines = (typeof TextDecoder !== 'undefined'
|
|
154
|
+
? new TextDecoder('utf-8').decode(buffer)
|
|
155
|
+
: buffer.toString()).split('\n');
|
|
156
|
+
lines.pop();
|
|
157
|
+
(0, util_1.checkAbortSignal)(signal);
|
|
158
|
+
let blockStart = c.minv.dataPosition;
|
|
159
|
+
let pos;
|
|
160
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
161
|
+
const line = lines[i];
|
|
162
|
+
for (pos = 0; blockStart >= dpositions[pos]; pos += 1) { }
|
|
163
|
+
// filter the line for whether it is within the requested range
|
|
164
|
+
const { startCoordinate, overlaps } = this.checkLine(metadata, refName, start, end, line);
|
|
165
|
+
// do a small check just to make sure that the lines are really sorted by start coordinate
|
|
166
|
+
if (previousStartCoordinate !== undefined &&
|
|
167
|
+
startCoordinate !== undefined &&
|
|
168
|
+
previousStartCoordinate > startCoordinate) {
|
|
169
|
+
throw new Error(`Lines not sorted by start coordinate (${previousStartCoordinate} > ${startCoordinate}), this file is not usable with Tabix.`);
|
|
170
|
+
}
|
|
171
|
+
previousStartCoordinate = startCoordinate;
|
|
172
|
+
if (overlaps) {
|
|
173
|
+
callback(line.trim(),
|
|
174
|
+
// cpositions[pos] refers to actual file offset of a bgzip block boundaries
|
|
175
|
+
//
|
|
176
|
+
// we multiply by (1 <<8) in order to make sure each block has a "unique"
|
|
177
|
+
// address space so that data in that block could never overlap
|
|
178
|
+
//
|
|
179
|
+
// then the blockStart-dpositions is an uncompressed file offset from
|
|
180
|
+
// that bgzip block boundary, and since the cpositions are multiplied by
|
|
181
|
+
// (1 << 8) these uncompressed offsets get a unique space
|
|
182
|
+
cpositions[pos] * (1 << 8) + (blockStart - dpositions[pos]));
|
|
183
|
+
}
|
|
184
|
+
else if (startCoordinate !== undefined && startCoordinate >= end) {
|
|
185
|
+
// the lines were overlapping the region, but now have stopped, so
|
|
186
|
+
// we must be at the end of the relevant data and we can stop
|
|
187
|
+
// processing data now
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
blockStart += line.length + 1;
|
|
191
|
+
// yield if we have emitted beyond the yield limit
|
|
192
|
+
if (last - Date.now() > 500) {
|
|
232
193
|
last = Date.now();
|
|
233
194
|
(0, util_1.checkAbortSignal)(signal);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
_c.sent();
|
|
237
|
-
_c.label = 7;
|
|
238
|
-
case 7:
|
|
239
|
-
i += 1;
|
|
240
|
-
return [3 /*break*/, 5];
|
|
241
|
-
case 8:
|
|
242
|
-
chunkNum += 1;
|
|
243
|
-
return [3 /*break*/, 3];
|
|
244
|
-
case 9: return [2 /*return*/];
|
|
195
|
+
yield timeout(1);
|
|
196
|
+
}
|
|
245
197
|
}
|
|
246
|
-
}
|
|
198
|
+
}
|
|
247
199
|
});
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return __generator(this, function (_a) {
|
|
253
|
-
return [2 /*return*/, this.index.getMetadata(opts)];
|
|
254
|
-
});
|
|
200
|
+
}
|
|
201
|
+
getMetadata(opts = {}) {
|
|
202
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
203
|
+
return this.index.getMetadata(opts);
|
|
255
204
|
});
|
|
256
|
-
}
|
|
205
|
+
}
|
|
257
206
|
/**
|
|
258
207
|
* get a buffer containing the "header" region of
|
|
259
208
|
* the file, which are the bytes up to the first
|
|
@@ -261,78 +210,58 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
261
210
|
*
|
|
262
211
|
* @returns {Promise} for a buffer
|
|
263
212
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
lastNewline = -1;
|
|
298
|
-
newlineByte = '\n'.charCodeAt(0);
|
|
299
|
-
metaByte = metaChar.charCodeAt(0);
|
|
300
|
-
for (i = 0; i < bytes.length; i += 1) {
|
|
301
|
-
if (i === lastNewline + 1 && bytes[i] !== metaByte) {
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
if (bytes[i] === newlineByte) {
|
|
305
|
-
lastNewline = i;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
bytes = bytes.slice(0, lastNewline + 1);
|
|
309
|
-
}
|
|
310
|
-
return [2 /*return*/, bytes];
|
|
213
|
+
getHeaderBuffer(opts = {}) {
|
|
214
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
215
|
+
const { firstDataLine, metaChar, maxBlockSize } = yield this.getMetadata(opts);
|
|
216
|
+
(0, util_1.checkAbortSignal)(opts.signal);
|
|
217
|
+
const maxFetch = firstDataLine && firstDataLine.blockPosition
|
|
218
|
+
? firstDataLine.blockPosition + maxBlockSize
|
|
219
|
+
: maxBlockSize;
|
|
220
|
+
// TODO: what if we don't have a firstDataLine, and the header
|
|
221
|
+
// actually takes up more than one block? this case is not covered here
|
|
222
|
+
let bytes = yield this._readRegion(0, maxFetch, opts);
|
|
223
|
+
(0, util_1.checkAbortSignal)(opts.signal);
|
|
224
|
+
try {
|
|
225
|
+
bytes = yield (0, bgzf_filehandle_1.unzip)(bytes);
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
console.error(e);
|
|
229
|
+
throw new Error(
|
|
230
|
+
//@ts-ignore
|
|
231
|
+
`error decompressing block ${e.code} at 0 (length ${maxFetch}) ${e}`);
|
|
232
|
+
}
|
|
233
|
+
// trim off lines after the last non-meta line
|
|
234
|
+
if (metaChar) {
|
|
235
|
+
// trim backward from the end
|
|
236
|
+
let lastNewline = -1;
|
|
237
|
+
const newlineByte = '\n'.charCodeAt(0);
|
|
238
|
+
const metaByte = metaChar.charCodeAt(0);
|
|
239
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
240
|
+
if (i === lastNewline + 1 && bytes[i] !== metaByte) {
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
if (bytes[i] === newlineByte) {
|
|
244
|
+
lastNewline = i;
|
|
245
|
+
}
|
|
311
246
|
}
|
|
312
|
-
|
|
247
|
+
bytes = bytes.slice(0, lastNewline + 1);
|
|
248
|
+
}
|
|
249
|
+
return bytes;
|
|
313
250
|
});
|
|
314
|
-
}
|
|
251
|
+
}
|
|
315
252
|
/**
|
|
316
253
|
* get a string containing the "header" region of the
|
|
317
254
|
* file, is the portion up to the first non-meta line
|
|
318
255
|
*
|
|
319
256
|
* @returns {Promise} for a string
|
|
320
257
|
*/
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
return
|
|
326
|
-
switch (_a.label) {
|
|
327
|
-
case 0: return [4 /*yield*/, this.getHeaderBuffer(opts)];
|
|
328
|
-
case 1:
|
|
329
|
-
bytes = _a.sent();
|
|
330
|
-
(0, util_1.checkAbortSignal)(opts.signal);
|
|
331
|
-
return [2 /*return*/, bytes.toString('utf8')];
|
|
332
|
-
}
|
|
333
|
-
});
|
|
258
|
+
getHeader(opts = {}) {
|
|
259
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
260
|
+
const bytes = yield this.getHeaderBuffer(opts);
|
|
261
|
+
(0, util_1.checkAbortSignal)(opts.signal);
|
|
262
|
+
return bytes.toString('utf8');
|
|
334
263
|
});
|
|
335
|
-
}
|
|
264
|
+
}
|
|
336
265
|
/**
|
|
337
266
|
* get an array of reference sequence names, in the order in which
|
|
338
267
|
* they occur in the file.
|
|
@@ -341,20 +270,12 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
341
270
|
*
|
|
342
271
|
* @returns {Promise} for an array of string sequence names
|
|
343
272
|
*/
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return __generator(this, function (_a) {
|
|
349
|
-
switch (_a.label) {
|
|
350
|
-
case 0: return [4 /*yield*/, this.getMetadata(opts)];
|
|
351
|
-
case 1:
|
|
352
|
-
metadata = _a.sent();
|
|
353
|
-
return [2 /*return*/, metadata.refIdToName];
|
|
354
|
-
}
|
|
355
|
-
});
|
|
273
|
+
getReferenceSequenceNames(opts = {}) {
|
|
274
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
275
|
+
const metadata = yield this.getMetadata(opts);
|
|
276
|
+
return metadata.refIdToName;
|
|
356
277
|
});
|
|
357
|
-
}
|
|
278
|
+
}
|
|
358
279
|
/**
|
|
359
280
|
* @param {object} metadata metadata object from the parsed index,
|
|
360
281
|
* containing columnNumbers, metaChar, and format
|
|
@@ -365,14 +286,13 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
365
286
|
* @returns {object} like `{startCoordinate, overlaps}`. overlaps is boolean,
|
|
366
287
|
* true if line is a data line that overlaps the given region
|
|
367
288
|
*/
|
|
368
|
-
|
|
369
|
-
var columnNumbers = _a.columnNumbers, metaChar = _a.metaChar, coordinateType = _a.coordinateType, format = _a.format;
|
|
289
|
+
checkLine({ columnNumbers, metaChar, coordinateType, format, }, regionRefName, regionStart, regionEnd, line) {
|
|
370
290
|
// skip meta lines
|
|
371
291
|
if (line.charAt(0) === metaChar) {
|
|
372
292
|
return { overlaps: false };
|
|
373
293
|
}
|
|
374
294
|
// check ref/start/end using column metadata from index
|
|
375
|
-
|
|
295
|
+
let { ref, start, end } = columnNumbers;
|
|
376
296
|
if (!ref) {
|
|
377
297
|
ref = 0;
|
|
378
298
|
}
|
|
@@ -385,15 +305,15 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
385
305
|
if (format === 'VCF') {
|
|
386
306
|
end = 8;
|
|
387
307
|
}
|
|
388
|
-
|
|
308
|
+
const maxColumn = Math.max(ref, start, end);
|
|
389
309
|
// this code is kind of complex, but it is fairly fast.
|
|
390
310
|
// basically, we want to avoid doing a split, because if the lines are really long
|
|
391
311
|
// that could lead to us allocating a bunch of extra memory, which is slow
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
for (
|
|
312
|
+
let currentColumnNumber = 1; // cols are numbered starting at 1 in the index metadata
|
|
313
|
+
let currentColumnStart = 0;
|
|
314
|
+
let refSeq = '';
|
|
315
|
+
let startCoordinate = -Infinity;
|
|
316
|
+
for (let i = 0; i < line.length + 1; i += 1) {
|
|
397
317
|
if (line[i] === '\t' || i === line.length) {
|
|
398
318
|
if (currentColumnNumber === ref) {
|
|
399
319
|
if (this.renameRefSeq(line.slice(currentColumnStart, i)) !==
|
|
@@ -408,12 +328,12 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
408
328
|
startCoordinate -= 1;
|
|
409
329
|
}
|
|
410
330
|
if (startCoordinate >= regionEnd) {
|
|
411
|
-
return { startCoordinate
|
|
331
|
+
return { startCoordinate, overlaps: false };
|
|
412
332
|
}
|
|
413
333
|
if (end === 0 || end === start) {
|
|
414
334
|
// if we have no end, we assume the feature is 1 bp long
|
|
415
335
|
if (startCoordinate + 1 <= regionStart) {
|
|
416
|
-
return { startCoordinate
|
|
336
|
+
return { startCoordinate, overlaps: false };
|
|
417
337
|
}
|
|
418
338
|
}
|
|
419
339
|
}
|
|
@@ -421,9 +341,7 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
421
341
|
refSeq = line.slice(currentColumnStart, i);
|
|
422
342
|
}
|
|
423
343
|
else if (currentColumnNumber === end) {
|
|
424
|
-
|
|
425
|
-
// this will never match if there is no end column
|
|
426
|
-
= void 0;
|
|
344
|
+
let endCoordinate;
|
|
427
345
|
// this will never match if there is no end column
|
|
428
346
|
if (format === 'VCF') {
|
|
429
347
|
endCoordinate = this._getVcfEnd(startCoordinate, refSeq, line.slice(currentColumnStart, i));
|
|
@@ -442,21 +360,21 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
442
360
|
}
|
|
443
361
|
}
|
|
444
362
|
}
|
|
445
|
-
return { startCoordinate
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
|
|
363
|
+
return { startCoordinate, overlaps: true };
|
|
364
|
+
}
|
|
365
|
+
_getVcfEnd(startCoordinate, refSeq, info) {
|
|
366
|
+
let endCoordinate = startCoordinate + refSeq.length;
|
|
449
367
|
// ignore TRA features as they specify CHR2 and END
|
|
450
368
|
// as being on a different chromosome
|
|
451
369
|
// if CHR2 is on the same chromosome, still ignore it
|
|
452
370
|
// because there should be another pairwise feature
|
|
453
371
|
// at the end of this one
|
|
454
|
-
|
|
372
|
+
const isTRA = info.indexOf('SVTYPE=TRA') !== -1;
|
|
455
373
|
if (info[0] !== '.' && !isTRA) {
|
|
456
|
-
|
|
457
|
-
for (
|
|
374
|
+
let prevChar = ';';
|
|
375
|
+
for (let j = 0; j < info.length; j += 1) {
|
|
458
376
|
if (prevChar === ';' && info.slice(j, j + 4) === 'END=') {
|
|
459
|
-
|
|
377
|
+
let valueEnd = info.indexOf(';', j);
|
|
460
378
|
if (valueEnd === -1) {
|
|
461
379
|
valueEnd = info.length;
|
|
462
380
|
}
|
|
@@ -470,61 +388,42 @@ var TabixIndexedFile = /** @class */ (function () {
|
|
|
470
388
|
return startCoordinate + 1;
|
|
471
389
|
}
|
|
472
390
|
return endCoordinate;
|
|
473
|
-
}
|
|
391
|
+
}
|
|
474
392
|
/**
|
|
475
393
|
* return the approximate number of data lines in the given reference sequence
|
|
476
394
|
* @param {string} refSeq reference sequence name
|
|
477
395
|
* @returns {Promise} for number of data lines present on that reference sequence
|
|
478
396
|
*/
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
return __generator(this, function (_a) {
|
|
483
|
-
return [2 /*return*/, this.index.lineCount(refName, opts)];
|
|
484
|
-
});
|
|
397
|
+
lineCount(refName, opts = {}) {
|
|
398
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
399
|
+
return this.index.lineCount(refName, opts);
|
|
485
400
|
});
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
return __generator(this, function (_b) {
|
|
492
|
-
switch (_b.label) {
|
|
493
|
-
case 0: return [4 /*yield*/, this.filehandle.read(Buffer.alloc(compressedSize), 0, compressedSize, position, opts)];
|
|
494
|
-
case 1:
|
|
495
|
-
_a = _b.sent(), bytesRead = _a.bytesRead, buffer = _a.buffer;
|
|
496
|
-
return [2 /*return*/, bytesRead < compressedSize ? buffer.slice(0, bytesRead) : buffer];
|
|
497
|
-
}
|
|
498
|
-
});
|
|
401
|
+
}
|
|
402
|
+
_readRegion(position, compressedSize, opts = {}) {
|
|
403
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
404
|
+
const { bytesRead, buffer } = yield this.filehandle.read(Buffer.alloc(compressedSize), 0, compressedSize, position, opts);
|
|
405
|
+
return bytesRead < compressedSize ? buffer.slice(0, bytesRead) : buffer;
|
|
499
406
|
});
|
|
500
|
-
}
|
|
407
|
+
}
|
|
501
408
|
/**
|
|
502
409
|
* read and uncompress the data in a chunk (composed of one or more
|
|
503
410
|
* contiguous bgzip blocks) of the file
|
|
504
411
|
* @param {Chunk} chunk
|
|
505
412
|
* @returns {Promise} for a string chunk of the file
|
|
506
413
|
*/
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
}
|
|
519
|
-
catch (e) {
|
|
520
|
-
throw new Error("error decompressing chunk ".concat(chunk.toString(), " ").concat(e));
|
|
521
|
-
}
|
|
522
|
-
return [2 /*return*/];
|
|
523
|
-
}
|
|
524
|
-
});
|
|
414
|
+
readChunk(chunk, opts = {}) {
|
|
415
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
416
|
+
// fetch the uncompressed data, uncompress carefully a block at a time,
|
|
417
|
+
// and stop when done
|
|
418
|
+
const compressedData = yield this._readRegion(chunk.minv.blockPosition, chunk.fetchedSize(), opts);
|
|
419
|
+
try {
|
|
420
|
+
return (0, bgzf_filehandle_1.unzipChunkSlice)(compressedData, chunk);
|
|
421
|
+
}
|
|
422
|
+
catch (e) {
|
|
423
|
+
throw new Error(`error decompressing chunk ${chunk.toString()} ${e}`);
|
|
424
|
+
}
|
|
525
425
|
});
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
}());
|
|
426
|
+
}
|
|
427
|
+
}
|
|
529
428
|
exports.default = TabixIndexedFile;
|
|
530
429
|
//# sourceMappingURL=tabixIndexedFile.js.map
|