@gmod/cram
Version:
read CRAM files with pure Javascript
137 lines • 4.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.dataSeriesTypes = void 0;
const index_ts_1 = require("../codecs/index.js");
// the hardcoded data type to be decoded for each core
// data field
const dataSeriesTypes = {
BF: 'int',
CF: 'int',
RI: 'int',
RL: 'int',
AP: 'int',
RG: 'int',
MF: 'int',
NS: 'int',
NP: 'int',
TS: 'int',
NF: 'int',
TC: 'byte',
TN: 'int',
FN: 'int',
FC: 'byte',
FP: 'int',
BS: 'byte',
IN: 'byteArray',
SC: 'byteArray',
DL: 'int',
BA: 'byte',
BB: 'byteArray',
RS: 'int',
PD: 'int',
HC: 'int',
MQ: 'int',
RN: 'byteArray',
QS: 'byte',
QQ: 'byteArray',
TL: 'int',
// TM: 'ignore',
// TV: 'ignore',
};
exports.dataSeriesTypes = dataSeriesTypes;
// For each reference base index 0..4 (A,C,G,T,N), the three other bases plus N
// (or T for ref=N), in the order they're packed into the 2-bit substitution code
const SUBSTITUTIONS = [
['C', 'G', 'T', 'N'],
['A', 'G', 'T', 'N'],
['A', 'C', 'T', 'N'],
['A', 'C', 'G', 'N'],
['A', 'C', 'G', 'T'],
];
function parseSubstitutionMatrix(byteArray) {
const matrix = new Array(5);
for (let i = 0; i < 5; i++) {
const row = new Array(4);
const byte = byteArray[i];
const subs = SUBSTITUTIONS[i];
for (let j = 0; j < 4; j++) {
row[(byte >> (6 - 2 * j)) & 3] = subs[j];
}
matrix[i] = row;
}
return matrix;
}
class CramContainerCompressionScheme {
readNamesIncluded;
APdelta;
referenceRequired;
tagIdsDictionary;
substitutionMatrix;
dataSeriesCodecCache = {};
tagCodecCache = {};
tagEncoding = {};
dataSeriesEncoding;
constructor(content) {
// interpret some of the preservation map tags for convenient use
// preservation-map defaults when a key is absent, per the CRAM spec
// (matches htslib cram_decode.c): RN=false, AP=true, RR=true.
this.readNamesIncluded = content.preservation.RN ?? false;
this.APdelta = content.preservation.AP ?? true;
this.referenceRequired = content.preservation.RR ?? true;
this.tagIdsDictionary = content.preservation.TD;
this.substitutionMatrix = parseSubstitutionMatrix(content.preservation.SM);
this.dataSeriesEncoding = content.dataSeriesEncoding;
this.tagEncoding = content.tagEncoding;
}
/**
* @param {string} tagName three-character tag name
* @private
*/
getCodecForTag(tagName) {
if (!this.tagCodecCache[tagName]) {
const encodingData = this.tagEncoding[tagName];
if (!encodingData) {
throw new Error('Error, no tag encoding');
}
// all tags are byte array data
this.tagCodecCache[tagName] = (0, index_ts_1.instantiateCodec)(encodingData, 'byteArray');
}
return this.tagCodecCache[tagName];
}
/**
*
* @param {number} tagListId ID of the tag list to fetch from the tag dictionary
* @private
*/
getTagNames(tagListId) {
return this.tagIdsDictionary[tagListId];
}
getCodecForDataSeries(dataSeriesName) {
let r = this.dataSeriesCodecCache[dataSeriesName];
if (r === undefined) {
const encodingData = this.dataSeriesEncoding[dataSeriesName];
if (encodingData) {
r = (0, index_ts_1.instantiateCodec)(encodingData, dataSeriesTypes[dataSeriesName]);
// TS can't unify the per-key cache value type with the generic
// TDataSeries — store via an untyped slot.
this.dataSeriesCodecCache[dataSeriesName] =
r;
}
}
return r;
}
// Used implicitly by snapshot tests to keep the codec caches (which contain
// class instances and are noisy/non-stable) out of the serialized form.
toJSON() {
const data = {};
Object.keys(this).forEach(k => {
if (k.endsWith('Cache')) {
return;
}
data[k] = this[k];
});
return data;
}
}
exports.default = CramContainerCompressionScheme;
//# sourceMappingURL=compressionScheme.js.map