ocean-subgraph/src/lzma/lzma-decoder.ts

312 lines
10 KiB
TypeScript

import { RangeDecoder } from './range-decoder';
import { OutWindow } from './out-window';
import { BitTreeDecoder } from './bit-tree-decoder';
import { LenDecoder } from './len-decoder';
import { LZMA } from './lzma';
/**
* LZMA Decoder
* @author Nidin Vinayakan
*/
export class LzmaDecoder {
//Public
public markerIsMandatory: boolean
public rangeDec: RangeDecoder //RangeDecoder
public outWindow: OutWindow //OutWindow
public lc: i32
public pb: i32
public lp: u8
public dictSize: u32
public dictSizeInProperties: u32
//Private
private litProbs: Uint16Array
private posSlotDecoder: Array<BitTreeDecoder>
private alignDecoder: BitTreeDecoder
private posDecoders: Uint16Array
private isMatch: Uint16Array
private isRep: Uint16Array
private isRepG0: Uint16Array
private isRepG1: Uint16Array
private isRepG2: Uint16Array
private isRep0Long: Uint16Array
private lenDecoder: LenDecoder
private repLenDecoder: LenDecoder
constructor() {
this.posSlotDecoder = BitTreeDecoder.constructArray(6, LZMA.kNumLenToPosStates) //6
this.alignDecoder = new BitTreeDecoder(LZMA.kNumAlignBits)
this.posDecoders = new Uint16Array(1 + LZMA.kNumFullDistances - LZMA.kEndPosModelIndex)
this.isMatch = new Uint16Array(LZMA.kNumStates << LZMA.kNumPosBitsMax)
this.isRep = new Uint16Array(LZMA.kNumStates)
this.isRepG0 = new Uint16Array(LZMA.kNumStates)
this.isRepG1 = new Uint16Array(LZMA.kNumStates)
this.isRepG2 = new Uint16Array(LZMA.kNumStates)
this.isRep0Long = new Uint16Array(LZMA.kNumStates << LZMA.kNumPosBitsMax)
this.lenDecoder = new LenDecoder()
this.repLenDecoder = new LenDecoder()
this.rangeDec = new RangeDecoder()
this.outWindow = new OutWindow()
}
public init(): void {
this.initLiterals()
this.initDist()
LZMA.INIT_PROBS(this.isMatch)
LZMA.INIT_PROBS(this.isRep)
LZMA.INIT_PROBS(this.isRepG0)
LZMA.INIT_PROBS(this.isRepG1)
LZMA.INIT_PROBS(this.isRepG2)
LZMA.INIT_PROBS(this.isRep0Long)
this.lenDecoder.init()
this.repLenDecoder.init()
}
public create(): void {
this.outWindow.create(this.dictSize)
this.createLiterals()
}
@inline
private createLiterals(): void {
this.litProbs = new Uint16Array(0x300 << (this.lc + this.lp))
}
@inline
private initLiterals(): void {
var num: i32 = 0x300 << (this.lc + this.lp)
for (var i: i32 = 0; i < num; i++) {
this.litProbs.__unchecked_set(i, LZMA.PROB_INIT_VAL)
}
}
@inline
private decodeLiteral(state:u16, rep0:u32): void {
var prevByte:u32 = 0
if (!this.outWindow.isEmpty()) prevByte = this.outWindow.getByte(1)
var symbol:u16 = 1
var litState:u32 =
((this.outWindow.totalPos & ((1 << this.lp) - 1)) << this.lc) +
(prevByte >>> (8 - this.lc))
var probsOffset: i32 = (0x300 * litState) | 0
if (state >= 7) {
let matchByte = this.outWindow.getByte(rep0 + 1)
do {
let matchBit = (matchByte >>> 7) & 1
matchByte <<= 1
let bit = this.rangeDec.decodeBit(
this.litProbs,
probsOffset + ((1 + matchBit) << 8) + symbol,
)
symbol = (symbol << 1) | bit
if (matchBit != bit) break
} while (symbol < 0x100)
}
while (symbol < 0x100) {
symbol =
(symbol << 1) |
this.rangeDec.decodeBit(this.litProbs, probsOffset + symbol)
}
this.outWindow.putByte(<u8>(symbol - 0x100))
}
private decodeDistance(len: i32): u32 {
var lenState: u32 = <u32>len
if (lenState > LZMA.kNumLenToPosStates - 1) lenState = LZMA.kNumLenToPosStates - 1
var posSlot:u32 = this.posSlotDecoder[lenState].decode(this.rangeDec)
if (posSlot < 4) return posSlot
var numDirectBits:u32 = (posSlot >>> 1) - 1
var dist:u32 = (2 | (posSlot & 1)) << numDirectBits
if (posSlot < LZMA.kEndPosModelIndex) {
dist += LZMA.BitTreeReverseDecode(
this.posDecoders,
numDirectBits,
this.rangeDec,
dist - posSlot,
)
} else {
dist +=
this.rangeDec.decodeDirectBits(numDirectBits - LZMA.kNumAlignBits) << LZMA.kNumAlignBits
dist += this.alignDecoder.reverseDecode(this.rangeDec)
}
return dist
}
@inline
private initDist(): void {
for (var i:u32 = 0; i < LZMA.kNumLenToPosStates; i++) {
this.posSlotDecoder[i].init()
}
this.alignDecoder.init()
LZMA.INIT_PROBS(this.posDecoders)
}
@inline
public decodeProperties(properties: Uint8Array): void {
var prop = new Uint8Array(4)
var p0 = properties.__unchecked_get(0)
prop.__unchecked_set(0, p0)
if (p0 >= 9 * 5 * 5) {
throw 1
// throw 'Incorrect LZMA properties'
}
var p1 = p0 % 9
prop.__unchecked_set(1, p1)
p0 /= 9
prop.__unchecked_set(0, p0)
var p2 = p0 / 5
var p3 = p0 % 5
prop.__unchecked_set(2, p2)
prop.__unchecked_set(3, p3)
this.lc = p1
this.pb = p2
this.lp = p3
this.dictSizeInProperties = 0
for (var i: u32 = 0; i < 4; i++) {
this.dictSizeInProperties |= <u32>properties.__unchecked_get(i + 1) << (8 * i)
}
this.dictSize = this.dictSizeInProperties
if (this.dictSize < LZMA.LZMA_DIC_MIN) {
this.dictSize = LZMA.LZMA_DIC_MIN
}
}
@inline
private updateState_Literal(state: u8): u8 {
if (state < 4) return 0
else if (state < 10) return state - 3
else return state - 6
}
@inline
private updateState_ShortRep(state: u8): u8 {
return state < 7 ? 9 : 11
}
@inline
private updateState_Rep(state: u8): u8 {
return state < 7 ? 8 : 11
}
@inline
private updateState_Match(state: u8): u8 {
return state < 7 ? 7 : 10
}
public decode(unpackSizeDefined: boolean, unpackSize: i32): i32 {
this.init()
this.rangeDec.init()
if (unpackSizeDefined) {
this.outWindow.outStream = new Uint8Array(unpackSize)
} else {
this.outWindow.outStream = new Uint8Array(4)
}
var rep0:u32 = 0,
rep1:u32 = 0,
rep2:u32 = 0,
rep3:u32 = 0
var state:u8 = 0
for (;;) {
if (unpackSizeDefined && unpackSize == 0 && !this.markerIsMandatory) {
if (this.rangeDec.isFinishedOK()) {
return LZMA.LZMA_RES_FINISHED_WITHOUT_MARKER
}
}
var posState = this.outWindow.totalPos & ((1 << this.pb) - 1)
if (this.rangeDec.decodeBit(this.isMatch, (state << LZMA.kNumPosBitsMax) + posState) == 0) {
if (unpackSizeDefined && unpackSize == 0) {
return LZMA.LZMA_RES_ERROR
}
this.decodeLiteral(state, rep0)
state = this.updateState_Literal(state)
unpackSize--
continue
}
var len: i32
if (this.rangeDec.decodeBit(this.isRep, state) != 0) {
if (unpackSizeDefined && unpackSize == 0) {
return LZMA.LZMA_RES_ERROR
}
if (this.outWindow.isEmpty()) {
return LZMA.LZMA_RES_ERROR
}
if (this.rangeDec.decodeBit(this.isRepG0, state) == 0) {
if (this.rangeDec.decodeBit(this.isRep0Long, (state << LZMA.kNumPosBitsMax) + posState) == 0) {
state = this.updateState_ShortRep(state)
this.outWindow.putByte(this.outWindow.getByte(rep0 + 1))
unpackSize--
continue
}
} else {
var dist: i32
if (this.rangeDec.decodeBit(this.isRepG1, state) == 0) {
dist = rep1
} else {
if (this.rangeDec.decodeBit(this.isRepG2, state) == 0) {
dist = rep2
} else {
dist = rep3
rep3 = rep2
}
rep2 = rep1
}
rep1 = rep0
rep0 = dist
}
len = this.repLenDecoder.decode(this.rangeDec, posState)
state = this.updateState_Rep(state)
} else {
rep3 = rep2
rep2 = rep1
rep1 = rep0
len = this.lenDecoder.decode(this.rangeDec, posState)
state = this.updateState_Match(state)
rep0 = this.decodeDistance(len)
if (rep0 == 0xffffffff) {
return this.rangeDec.isFinishedOK() ? LZMA.LZMA_RES_FINISHED_WITH_MARKER : LZMA.LZMA_RES_ERROR
}
if (unpackSizeDefined && unpackSize == 0) {
return LZMA.LZMA_RES_ERROR
}
if (rep0 >= this.dictSize || !this.outWindow.checkDistance(rep0)) {
return LZMA.LZMA_RES_ERROR
}
}
len += LZMA.kMatchMinLen
var isError: boolean = false
if (unpackSizeDefined && unpackSize < len) {
len = unpackSize
isError = true
}
this.outWindow.copyMatch(rep0 + 1, len)
unpackSize -= len
if (isError) {
return LZMA.LZMA_RES_ERROR
}
}
return LZMA.LZMA_RES_ERROR
}
}