UNPKG

@gmod/cram

Version:

read CRAM files with pure Javascript

218 lines 7.14 kB
/* eslint-disable no-var */ // @ts-nocheck /* * Copyright (c) 2019-2020 Genome Research Ltd. * Author(s): James Bonfield * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * 3. Neither the names Genome Research Ltd and Wellcome Trust Sanger * Institute nor the names of its contributors may be used to endorse * or promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY GENOME RESEARCH LTD AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENOME RESEARCH * LTD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import IOStream from "./iostream.js"; // ---------------------------------------------------------------------- // rANS primitives itself // // RansGet* is decoder side function RansGetCumulativeFreq(R) { return R & 0xfff; } function RansGetSymbolFromFreq(C, f) { // NOTE: Inefficient. // In practice we would implement this via a precomputed // lookup table C2S[f]; see RansBuildC2S below. let s = 0; while (f >= C[s + 1]) { s++; } return s; } function RansBuildC2S(C) { const C2S = new Array(0x1000); let s = 0; for (let f = 0; f < 0x1000; f++) { while (f >= C[s + 1]) { s++; } C2S[f] = s; } return C2S; } function RansAdvanceStep(R, c, f) { return f * (R >> 12) + (R & 0xfff) - c; } function RansRenorm(src, R) { while (R < 1 << 23) { R = (R << 8) + src.ReadByte(); } return R; } // ---------------------------------------------------------------------- // Main rANS entry function: decodes a compressed src and // returns the uncompressed buffer. export function decode(src) { const stream = new IOStream(src); const order = stream.ReadByte(); const n_in = stream.ReadUint32(); const n_out = stream.ReadUint32(); return order == 0 ? RansDecode0(stream, n_out) : RansDecode1(stream, n_out); } // ---------------------------------------------------------------------- // Order-0 decoder // Decode a single table of order-0 frequences, // filling out the F and C arrays. function ReadFrequencies0(src, F, C) { // Initialise; not in the specification - implicit? for (var i = 0; i < 256; i++) { F[i] = 0; } let sym = src.ReadByte(); let last_sym = sym; let rle = 0; // Read F[] do { const f = src.ReadITF8(); F[sym] = f; if (rle > 0) { rle--; sym++; } else { sym = src.ReadByte(); if (sym == last_sym + 1) { rle = src.ReadByte(); } } last_sym = sym; } while (sym != 0); // Compute C[] from F[] C[0] = 0; for (var i = 0; i <= 255; i++) { C[i + 1] = C[i] + F[i]; } } function RansDecode0(src, nbytes) { // Decode frequencies const F = new Array(256); const C = new Array(256); ReadFrequencies0(src, F, C); // Fast lookup to avoid slow RansGetSymbolFromFreq const C2S = RansBuildC2S(C); // Initialise rANS state const R = new Array(4); for (var i = 0; i < 4; i++) { R[i] = src.ReadUint32(); } // Main decode loop const output = new Uint8Array(nbytes); for (var i = 0; i < nbytes; i++) { const i4 = i % 4; const f = RansGetCumulativeFreq(R[i4]); const s = C2S[f]; // Equiv to RansGetSymbolFromFreq(C, f); output[i] = s; R[i4] = RansAdvanceStep(R[i4], C[s], F[s]); R[i4] = RansRenorm(src, R[i4]); } return output; } // ---------------------------------------------------------------------- // Order-1 decoder // Decode a table of order-1 frequences, // filling out the F and C arrays. function ReadFrequencies1(src, F, C) { // Initialise; not in the specification - implicit? for (let i = 0; i < 256; i++) { F[i] = new Array(256); C[i] = new Array(256); for (let j = 0; j < 256; j++) { F[i][j] = 0; } } let sym = src.ReadByte(); let last_sym = sym; let rle = 0; // Read F[] do { ReadFrequencies0(src, F[sym], C[sym]); if (rle > 0) { rle--; sym++; } else { sym = src.ReadByte(); if (sym == last_sym + 1) { rle = src.ReadByte(); } } last_sym = sym; } while (sym != 0); } function RansDecode1(src, nbytes) { // Decode frequencies const F = new Array(256); const C = new Array(256); ReadFrequencies1(src, F, C); // Fast lookup to avoid slow RansGetSymbolFromFreq const C2S = new Array(256); for (var i = 0; i < 256; i++) { C2S[i] = RansBuildC2S(C[i]); } // Initialise rANS state const R = new Array(4); const L = new Array(4); for (var j = 0; j < 4; j++) { R[j] = src.ReadUint32(); L[j] = 0; } // Main decode loop const output = new Uint8Array(nbytes); const nbytes4 = Math.floor(nbytes / 4); for (var i = 0; i < nbytes4; i++) { for (var j = 0; j < 4; j++) { var f = RansGetCumulativeFreq(R[j]); // var s = RansGetSymbolFromFreq(C[L[j]], f); var s = C2S[L[j]][f]; // Precomputed version of above output[i + j * nbytes4] = s; R[j] = RansAdvanceStep(R[j], C[L[j]][s], F[L[j]][s]); R[j] = RansRenorm(src, R[j]); L[j] = s; } } // Now deal with the remainder if buffer size is not a multiple of 4, // using rANS state 3 exclusively. (It'd have been nice to have // designed this to just act as if we kept going with a bail out.) i = 4 * i; while (i < nbytes) { var f = RansGetCumulativeFreq(R[3]); var s = RansGetSymbolFromFreq(C[L[3]], f); output[i++] = s; R[3] = RansAdvanceStep(R[3], C[L[3]][s], F[L[3]][s]); R[3] = RansRenorm(src, R[3]); L[3] = s; } return output; } //# sourceMappingURL=rans.js.map