source: josm/trunk/src/org/tukaani/xz/lzma/LZMADecoder.java@ 13472

Last change on this file since 13472 was 13350, checked in by stoecker, 6 years ago

see #15816 - add XZ support

File size: 5.9 KB
Line 
1/*
2 * LZMADecoder
3 *
4 * Authors: Lasse Collin <lasse.collin@tukaani.org>
5 * Igor Pavlov <http://7-zip.org/>
6 *
7 * This file has been put into the public domain.
8 * You can do whatever you want with this file.
9 */
10
11package org.tukaani.xz.lzma;
12
13import java.io.IOException;
14import org.tukaani.xz.lz.LZDecoder;
15import org.tukaani.xz.rangecoder.RangeDecoder;
16
17public final class LZMADecoder extends LZMACoder {
18 private final LZDecoder lz;
19 private final RangeDecoder rc;
20 private final LiteralDecoder literalDecoder;
21 private final LengthDecoder matchLenDecoder = new LengthDecoder();
22 private final LengthDecoder repLenDecoder = new LengthDecoder();
23
24 public LZMADecoder(LZDecoder lz, RangeDecoder rc, int lc, int lp, int pb) {
25 super(pb);
26 this.lz = lz;
27 this.rc = rc;
28 this.literalDecoder = new LiteralDecoder(lc, lp);
29 reset();
30 }
31
32 public void reset() {
33 super.reset();
34 literalDecoder.reset();
35 matchLenDecoder.reset();
36 repLenDecoder.reset();
37 }
38
39 /**
40 * Returns true if LZMA end marker was detected. It is encoded as
41 * the maximum match distance which with signed ints becomes -1. This
42 * function is needed only for LZMA1. LZMA2 doesn't use the end marker
43 * in the LZMA layer.
44 */
45 public boolean endMarkerDetected() {
46 return reps[0] == -1;
47 }
48
49 public void decode() throws IOException {
50 lz.repeatPending();
51
52 while (lz.hasSpace()) {
53 int posState = lz.getPos() & posMask;
54
55 if (rc.decodeBit(isMatch[state.get()], posState) == 0) {
56 literalDecoder.decode();
57 } else {
58 int len = rc.decodeBit(isRep, state.get()) == 0
59 ? decodeMatch(posState)
60 : decodeRepMatch(posState);
61
62 // NOTE: With LZMA1 streams that have the end marker,
63 // this will throw CorruptedInputException. LZMAInputStream
64 // handles it specially.
65 lz.repeat(reps[0], len);
66 }
67 }
68
69 rc.normalize();
70 }
71
72 private int decodeMatch(int posState) throws IOException {
73 state.updateMatch();
74
75 reps[3] = reps[2];
76 reps[2] = reps[1];
77 reps[1] = reps[0];
78
79 int len = matchLenDecoder.decode(posState);
80 int distSlot = rc.decodeBitTree(distSlots[getDistState(len)]);
81
82 if (distSlot < DIST_MODEL_START) {
83 reps[0] = distSlot;
84 } else {
85 int limit = (distSlot >> 1) - 1;
86 reps[0] = (2 | (distSlot & 1)) << limit;
87
88 if (distSlot < DIST_MODEL_END) {
89 reps[0] |= rc.decodeReverseBitTree(
90 distSpecial[distSlot - DIST_MODEL_START]);
91 } else {
92 reps[0] |= rc.decodeDirectBits(limit - ALIGN_BITS)
93 << ALIGN_BITS;
94 reps[0] |= rc.decodeReverseBitTree(distAlign);
95 }
96 }
97
98 return len;
99 }
100
101 private int decodeRepMatch(int posState) throws IOException {
102 if (rc.decodeBit(isRep0, state.get()) == 0) {
103 if (rc.decodeBit(isRep0Long[state.get()], posState) == 0) {
104 state.updateShortRep();
105 return 1;
106 }
107 } else {
108 int tmp;
109
110 if (rc.decodeBit(isRep1, state.get()) == 0) {
111 tmp = reps[1];
112 } else {
113 if (rc.decodeBit(isRep2, state.get()) == 0) {
114 tmp = reps[2];
115 } else {
116 tmp = reps[3];
117 reps[3] = reps[2];
118 }
119
120 reps[2] = reps[1];
121 }
122
123 reps[1] = reps[0];
124 reps[0] = tmp;
125 }
126
127 state.updateLongRep();
128
129 return repLenDecoder.decode(posState);
130 }
131
132
133 private class LiteralDecoder extends LiteralCoder {
134 private final LiteralSubdecoder[] subdecoders;
135
136 LiteralDecoder(int lc, int lp) {
137 super(lc, lp);
138
139 subdecoders = new LiteralSubdecoder[1 << (lc + lp)];
140 for (int i = 0; i < subdecoders.length; ++i)
141 subdecoders[i] = new LiteralSubdecoder();
142 }
143
144 void reset() {
145 for (int i = 0; i < subdecoders.length; ++i)
146 subdecoders[i].reset();
147 }
148
149 void decode() throws IOException {
150 int i = getSubcoderIndex(lz.getByte(0), lz.getPos());
151 subdecoders[i].decode();
152 }
153
154
155 private class LiteralSubdecoder extends LiteralSubcoder {
156 void decode() throws IOException {
157 int symbol = 1;
158
159 if (state.isLiteral()) {
160 do {
161 symbol = (symbol << 1) | rc.decodeBit(probs, symbol);
162 } while (symbol < 0x100);
163
164 } else {
165 int matchByte = lz.getByte(reps[0]);
166 int offset = 0x100;
167 int matchBit;
168 int bit;
169
170 do {
171 matchByte <<= 1;
172 matchBit = matchByte & offset;
173 bit = rc.decodeBit(probs, offset + matchBit + symbol);
174 symbol = (symbol << 1) | bit;
175 offset &= (0 - bit) ^ ~matchBit;
176 } while (symbol < 0x100);
177 }
178
179 lz.putByte((byte)symbol);
180 state.updateLiteral();
181 }
182 }
183 }
184
185
186 private class LengthDecoder extends LengthCoder {
187 int decode(int posState) throws IOException {
188 if (rc.decodeBit(choice, 0) == 0)
189 return rc.decodeBitTree(low[posState]) + MATCH_LEN_MIN;
190
191 if (rc.decodeBit(choice, 1) == 0)
192 return rc.decodeBitTree(mid[posState])
193 + MATCH_LEN_MIN + LOW_SYMBOLS;
194
195 return rc.decodeBitTree(high)
196 + MATCH_LEN_MIN + LOW_SYMBOLS + MID_SYMBOLS;
197 }
198 }
199}
Note: See TracBrowser for help on using the repository browser.