@gmod/cram
Version:
read CRAM files with pure Javascript
198 lines (165 loc) • 5.43 kB
text/typescript
/* eslint-disable no-var */
// @ts-nocheck
/*
* Copyright (c) 2019 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.
*/
// Name tokeniser
//
// This is a reference implementation designed to match the
// written specification as closely as possible. It is *NOT*
// an efficient implementation, but see comments below.
import { concatUint8Array } from '../util.ts'
import arith_gen from './arith_gen.ts'
import IOStream from './iostream.ts'
import * as rans from './rans4x16.ts'
const arith = new arith_gen()
const TOK_TYPE = 0
const TOK_STRING = 1
const TOK_CHAR = 2
const TOK_DIGITS0 = 3
const TOK_DZLEN = 4
const TOK_DUP = 5
const TOK_DIFF = 6
const TOK_DIGITS = 7
const TOK_DELTA = 8
const TOK_DELTA0 = 9
const TOK_MATCH = 10
const TOK_NOP = 11
const TOK_END = 12
// ----------------------------------------------------------------------
// Token byte streams
function DecodeTokenByteStreams(src, in_size, use_arith, nnames) {
let t = -1
const B = new Array(256)
while (!src.EOF()) {
const ttype = src.ReadByte()
const tok_new = ttype & 128
const tok_dup = ttype & 64
const type = ttype & 63
if (tok_new) {
t++
B[t] = new Array(13)
}
if (type != TOK_TYPE && tok_new) {
const M = new Array(nnames - 1).fill(TOK_MATCH)
B[t][TOK_TYPE] = new IOStream(concatUint8Array([new Uint8Array(type), M]))
}
if (tok_dup) {
const dup_pos = src.ReadByte()
const dup_type = src.ReadByte()
B[t][type] = new IOStream(B[dup_pos][dup_type].buf)
} else {
const clen = src.ReadUint7()
const data = src.ReadData(clen)
B[t][type] = use_arith ? arith.decode(data) : rans.decode(data)
B[t][type] = new IOStream(B[t][type])
}
}
return B
}
// ----------------------------------------------------------------------
// Token decode
function LeftPadNumber(val, len) {
let str = val + ''
while (str.length < len) {
str = '0' + str
}
return str
}
function DecodeSingleName(B, N, T, n) {
let type = B[0][TOK_TYPE].ReadByte()
const dist = B[0][type].ReadUint32()
const m = n - dist
if (type == TOK_DUP) {
N[n] = N[m]
T[n] = T[m]
return N[n]
}
let t = 1
N[n] = ''
T[n] = new Array(256)
do {
type = B[t][TOK_TYPE].ReadByte()
switch (type) {
case TOK_CHAR:
T[n][t] = B[t][TOK_CHAR].ReadChar()
break
case TOK_STRING:
T[n][t] = B[t][TOK_STRING].ReadString()
break
case TOK_DIGITS:
T[n][t] = B[t][TOK_DIGITS].ReadUint32()
break
case TOK_DIGITS0:
var d = B[t][TOK_DIGITS0].ReadUint32()
var l = B[t][TOK_DZLEN].ReadByte()
T[n][t] = LeftPadNumber(d, l)
break
case TOK_DELTA:
T[n][t] = (T[m][t] >> 0) + B[t][TOK_DELTA].ReadByte()
break
case TOK_DELTA0:
var d = (T[m][t] >> 0) + B[t][TOK_DELTA0].ReadByte()
var l = T[m][t].length
T[n][t] = LeftPadNumber(d, l)
break
case TOK_MATCH:
T[n][t] = T[m][t]
break
default:
T[n][t] = ''
break
}
N[n] += T[n][t++]
} while (type != TOK_END)
return N[n]
}
// ----------------------------------------------------------------------
// Main tokeniser decode entry function: decodes a compressed src and
// returns the uncompressed buffer.
export function decode(src, len, separator) {
var src = new IOStream(src)
const ulen = src.ReadUint32()
const nnames = src.ReadUint32()
const use_arith = src.ReadByte()
const B = DecodeTokenByteStreams(src, len, use_arith, nnames)
const N = new Array(nnames)
const T = new Array(nnames)
let str = ''
if (separator === undefined) {
separator = '\n'
}
for (let i = 0; i < nnames; i++) {
str += DecodeSingleName(B, N, T, i) + separator
}
return str
}