@gmod/cram
Version:
read CRAM files with pure Javascript
206 lines • 7.32 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TWO_PWR_56_DBL = exports.TWO_PWR_24_DBL = exports.TWO_PWR_64_DBL = exports.TWO_PWR_32_DBL = exports.TWO_PWR_16_DBL = void 0;
exports.readNullTerminatedStringFromBuffer = readNullTerminatedStringFromBuffer;
exports.decodeUtf8 = decodeUtf8;
exports.itf8Size = itf8Size;
exports.parseItf8 = parseItf8;
exports.parseItf8Sized = parseItf8Sized;
exports.parseLtf8 = parseLtf8;
exports.parseItem = parseItem;
exports.sequenceMD5 = sequenceMD5;
const md5_1 = __importDefault(require("md5"));
// Default TextDecoder (utf-8) is faster than 'latin1' in V8 and works
// identically on the ASCII content CRAM stores here (read names, sequence
// bases, BAM Z tag values). Lazy so that consumers (e.g. older Node) have a
// chance to set up the global before first decode.
let textDecoder;
function getTextDecoder() {
if (!textDecoder) {
textDecoder = new TextDecoder();
}
return textDecoder;
}
function readNullTerminatedStringFromBuffer(buffer) {
let end = 0;
while (end < buffer.length && buffer[end] !== 0) {
end++;
}
return getTextDecoder().decode(buffer.subarray(0, end));
}
function decodeUtf8(buffer) {
return getTextDecoder().decode(buffer);
}
exports.TWO_PWR_16_DBL = 1 << 16;
exports.TWO_PWR_32_DBL = exports.TWO_PWR_16_DBL * exports.TWO_PWR_16_DBL;
exports.TWO_PWR_64_DBL = exports.TWO_PWR_32_DBL * exports.TWO_PWR_32_DBL;
exports.TWO_PWR_24_DBL = 1 << 24;
exports.TWO_PWR_56_DBL = exports.TWO_PWR_24_DBL * exports.TWO_PWR_32_DBL;
function itf8Size(v) {
if (!(v & ~0x7f)) {
return 1;
}
if (!(v & ~0x3fff)) {
return 2;
}
if (!(v & ~0x1fffff)) {
return 3;
}
if (!(v & ~0xfffffff)) {
return 4;
}
return 5;
}
// Canonical ITF8 parser — cursor-mutating, no per-call allocation. Used by
// the hot path. The tuple-returning parseItf8Sized below is a thin wrapper
// for section parsers that work in terms of `let offset` arithmetic.
// See CRAMv3 §2.3 (Integer types): https://samtools.github.io/hts-specs/CRAMv3.pdf
function parseItf8(buffer, cursor) {
const offset = cursor.bytePosition;
const countFlags = buffer[offset];
if (countFlags < 0x80) {
cursor.bytePosition = offset + 1;
return countFlags;
}
if (countFlags < 0xc0) {
cursor.bytePosition = offset + 2;
return ((countFlags & 0x3f) << 8) | buffer[offset + 1];
}
if (countFlags < 0xe0) {
cursor.bytePosition = offset + 3;
return (((countFlags & 0x1f) << 16) |
(buffer[offset + 1] << 8) |
buffer[offset + 2]);
}
if (countFlags < 0xf0) {
cursor.bytePosition = offset + 4;
return (((countFlags & 0x0f) << 24) |
(buffer[offset + 1] << 16) |
(buffer[offset + 2] << 8) |
buffer[offset + 3]);
}
cursor.bytePosition = offset + 5;
return (((countFlags & 0x0f) << 28) |
(buffer[offset + 1] << 20) |
(buffer[offset + 2] << 12) |
(buffer[offset + 3] << 4) |
(buffer[offset + 4] & 0x0f));
}
// Tuple-returning wrapper for callers that prefer offset arithmetic
// (sectionParsers.ts). Allocates one cursor + one tuple per call — fine for
// section parsing (called O(slices) times) but not for the byte-decode loop.
function parseItf8Sized(buffer, offset) {
const cursor = { bytePosition: offset };
const value = parseItf8(buffer, cursor);
return [value, cursor.bytePosition - offset];
}
function parseLtf8(buffer, initialOffset) {
let offset = initialOffset;
const countFlags = buffer[offset];
let value;
// Single byte value < 0x80
if (countFlags < 0x80) {
value = countFlags;
offset += 1;
}
// Two byte value < 0xC0
else if (countFlags < 0xc0) {
value = ((countFlags << 8) | buffer[offset + 1]) & 0x3fff;
offset += 2;
}
// Three byte value < 0xE0
else if (countFlags < 0xe0) {
value =
((countFlags & 0x3f) << 16) |
(buffer[offset + 1] << 8) |
buffer[offset + 2];
offset += 3;
}
// Four byte value < 0xF0
else if (countFlags < 0xf0) {
value =
((countFlags & 0x1f) << 24) |
(buffer[offset + 1] << 16) |
(buffer[offset + 2] << 8) |
buffer[offset + 3];
offset += 4;
}
// Five byte value < 0xF8
else if (countFlags < 0xf8) {
value =
(buffer[offset] & 0x0f) * exports.TWO_PWR_32_DBL +
((buffer[offset + 1] << 24) |
(buffer[offset + 2] << 16) |
(buffer[offset + 3] << 8) |
buffer[offset + 4]);
offset += 5;
}
// Six byte value < 0xFC
else if (countFlags < 0xfc) {
value =
(((buffer[offset] & 0x07) << 8) | buffer[offset + 1]) * exports.TWO_PWR_32_DBL +
((buffer[offset + 2] << 24) |
(buffer[offset + 3] << 16) |
(buffer[offset + 4] << 8) |
buffer[offset + 5]);
offset += 6;
}
// Seven byte value < 0xFE
else if (countFlags < 0xfe) {
value =
(((buffer[offset] & 0x03) << 16) |
(buffer[offset + 1] << 8) |
buffer[offset + 2]) *
exports.TWO_PWR_32_DBL +
((buffer[offset + 3] << 24) |
(buffer[offset + 4] << 16) |
(buffer[offset + 5] << 8) |
buffer[offset + 6]);
offset += 7;
}
// Eight byte value < 0xFF
else if (countFlags < 0xff) {
value =
((buffer[offset + 1] << 24) |
(buffer[offset + 2] << 16) |
(buffer[offset + 3] << 8) |
buffer[offset + 4]) *
exports.TWO_PWR_32_DBL +
((buffer[offset + 5] << 24) |
(buffer[offset + 6] << 16) |
(buffer[offset + 7] << 8) |
buffer[offset + 8]);
offset += 8;
}
// Nine byte value
else {
value =
buffer[offset + 1] * exports.TWO_PWR_56_DBL +
((buffer[offset + 2] << 24) |
(buffer[offset + 3] << 16) |
(buffer[offset + 4] << 8) |
buffer[offset + 5]) *
exports.TWO_PWR_32_DBL +
((buffer[offset + 6] << 24) |
(buffer[offset + 7] << 16) |
(buffer[offset + 8] << 8) |
buffer[offset + 9]);
offset += 9;
}
return [value, offset - initialOffset];
}
function parseItem(buffer, parser, startBufferPosition = 0, startFilePosition = 0) {
const { offset, value } = parser(buffer, startBufferPosition);
return {
...value,
_endPosition: offset + startFilePosition,
_size: offset - startBufferPosition,
};
}
function sequenceMD5(seq) {
return (0, md5_1.default)(seq.toUpperCase().replaceAll(/[^\u0021-\u007e]/g, ''));
}
//# sourceMappingURL=util.js.map