/* * LZMADecoder * * Authors: Lasse Collin * Igor Pavlov * * This file has been put into the public domain. * You can do whatever you want with this file. */ package org.tukaani.xz.lzma; import java.io.IOException; import org.tukaani.xz.lz.LZDecoder; import org.tukaani.xz.rangecoder.RangeDecoder; public final class LZMADecoder extends LZMACoder { private final LZDecoder lz; private final RangeDecoder rc; private final LiteralDecoder literalDecoder; private final LengthDecoder matchLenDecoder = new LengthDecoder(); private final LengthDecoder repLenDecoder = new LengthDecoder(); public LZMADecoder(LZDecoder lz, RangeDecoder rc, int lc, int lp, int pb) { super(pb); this.lz = lz; this.rc = rc; this.literalDecoder = new LiteralDecoder(lc, lp); reset(); } public void reset() { super.reset(); literalDecoder.reset(); matchLenDecoder.reset(); repLenDecoder.reset(); } /** * Returns true if LZMA end marker was detected. It is encoded as * the maximum match distance which with signed ints becomes -1. This * function is needed only for LZMA1. LZMA2 doesn't use the end marker * in the LZMA layer. */ public boolean endMarkerDetected() { return reps[0] == -1; } public void decode() throws IOException { lz.repeatPending(); while (lz.hasSpace()) { int posState = lz.getPos() & posMask; if (rc.decodeBit(isMatch[state.get()], posState) == 0) { literalDecoder.decode(); } else { int len = rc.decodeBit(isRep, state.get()) == 0 ? decodeMatch(posState) : decodeRepMatch(posState); // NOTE: With LZMA1 streams that have the end marker, // this will throw CorruptedInputException. LZMAInputStream // handles it specially. lz.repeat(reps[0], len); } } rc.normalize(); } private int decodeMatch(int posState) throws IOException { state.updateMatch(); reps[3] = reps[2]; reps[2] = reps[1]; reps[1] = reps[0]; int len = matchLenDecoder.decode(posState); int distSlot = rc.decodeBitTree(distSlots[getDistState(len)]); if (distSlot < DIST_MODEL_START) { reps[0] = distSlot; } else { int limit = (distSlot >> 1) - 1; reps[0] = (2 | (distSlot & 1)) << limit; if (distSlot < DIST_MODEL_END) { reps[0] |= rc.decodeReverseBitTree( distSpecial[distSlot - DIST_MODEL_START]); } else { reps[0] |= rc.decodeDirectBits(limit - ALIGN_BITS) << ALIGN_BITS; reps[0] |= rc.decodeReverseBitTree(distAlign); } } return len; } private int decodeRepMatch(int posState) throws IOException { if (rc.decodeBit(isRep0, state.get()) == 0) { if (rc.decodeBit(isRep0Long[state.get()], posState) == 0) { state.updateShortRep(); return 1; } } else { int tmp; if (rc.decodeBit(isRep1, state.get()) == 0) { tmp = reps[1]; } else { if (rc.decodeBit(isRep2, state.get()) == 0) { tmp = reps[2]; } else { tmp = reps[3]; reps[3] = reps[2]; } reps[2] = reps[1]; } reps[1] = reps[0]; reps[0] = tmp; } state.updateLongRep(); return repLenDecoder.decode(posState); } private class LiteralDecoder extends LiteralCoder { private final LiteralSubdecoder[] subdecoders; LiteralDecoder(int lc, int lp) { super(lc, lp); subdecoders = new LiteralSubdecoder[1 << (lc + lp)]; for (int i = 0; i < subdecoders.length; ++i) subdecoders[i] = new LiteralSubdecoder(); } void reset() { for (int i = 0; i < subdecoders.length; ++i) subdecoders[i].reset(); } void decode() throws IOException { int i = getSubcoderIndex(lz.getByte(0), lz.getPos()); subdecoders[i].decode(); } private class LiteralSubdecoder extends LiteralSubcoder { void decode() throws IOException { int symbol = 1; if (state.isLiteral()) { do { symbol = (symbol << 1) | rc.decodeBit(probs, symbol); } while (symbol < 0x100); } else { int matchByte = lz.getByte(reps[0]); int offset = 0x100; int matchBit; int bit; do { matchByte <<= 1; matchBit = matchByte & offset; bit = rc.decodeBit(probs, offset + matchBit + symbol); symbol = (symbol << 1) | bit; offset &= (0 - bit) ^ ~matchBit; } while (symbol < 0x100); } lz.putByte((byte)symbol); state.updateLiteral(); } } } private class LengthDecoder extends LengthCoder { int decode(int posState) throws IOException { if (rc.decodeBit(choice, 0) == 0) return rc.decodeBitTree(low[posState]) + MATCH_LEN_MIN; if (rc.decodeBit(choice, 1) == 0) return rc.decodeBitTree(mid[posState]) + MATCH_LEN_MIN + LOW_SYMBOLS; return rc.decodeBitTree(high) + MATCH_LEN_MIN + LOW_SYMBOLS + MID_SYMBOLS; } } }