1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.zip;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.nio.ByteOrder;
24
25 import org.apache.commons.compress.compressors.lzw.LZWInputStream;
26
27
28
29
30
31
32
33 final class UnshrinkingInputStream extends LZWInputStream {
34 private static final int MAX_CODE_SIZE = 13;
35 private static final int MAX_TABLE_SIZE = 1 << MAX_CODE_SIZE;
36 private final boolean[] isUsed;
37
38
39
40
41
42
43 UnshrinkingInputStream(final InputStream inputStream) {
44 super(inputStream, ByteOrder.LITTLE_ENDIAN);
45 setClearCode(DEFAULT_CODE_SIZE);
46 initializeTables(MAX_CODE_SIZE);
47 isUsed = new boolean[getPrefixesLength()];
48 for (int i = 0; i < 1 << 8; i++) {
49 isUsed[i] = true;
50 }
51 setTableSize(getClearCode() + 1);
52 }
53
54 @Override
55 protected int addEntry(final int previousCode, final byte character) throws IOException {
56 int tableSize = getTableSize();
57 while (tableSize < MAX_TABLE_SIZE && isUsed[tableSize]) {
58 tableSize++;
59 }
60 setTableSize(tableSize);
61 final int idx = addEntry(previousCode, character, MAX_TABLE_SIZE);
62 if (idx >= 0) {
63 isUsed[idx] = true;
64 }
65 return idx;
66 }
67
68 @Override
69 protected int decompressNextSymbol() throws IOException {
70
71
72
73
74
75
76
77
78
79
80
81
82 final int code = readNextCode();
83 if (code < 0) {
84 return -1;
85 }
86 if (code != getClearCode()) {
87 boolean addedUnfinishedEntry = false;
88 int effectiveCode = code;
89 if (!isUsed[code]) {
90 effectiveCode = addRepeatOfPreviousCode();
91 addedUnfinishedEntry = true;
92 }
93 return expandCodeToOutputStack(effectiveCode, addedUnfinishedEntry);
94 }
95 final int subCode = readNextCode();
96 if (subCode < 0) {
97 throw new IOException("Unexpected EOF;");
98 }
99 if (subCode == 1) {
100 if (getCodeSize() >= MAX_CODE_SIZE) {
101 throw new IOException("Attempt to increase code size beyond maximum");
102 }
103 incrementCodeSize();
104 } else if (subCode == 2) {
105 partialClear();
106 setTableSize(getClearCode() + 1);
107 } else {
108 throw new IOException("Invalid clear code subcode " + subCode);
109 }
110 return 0;
111 }
112
113 private void partialClear() {
114 final boolean[] isParent = new boolean[MAX_TABLE_SIZE];
115 for (int i = 0; i < isUsed.length; i++) {
116 if (isUsed[i] && getPrefix(i) != UNUSED_PREFIX) {
117 isParent[getPrefix(i)] = true;
118 }
119 }
120 for (int i = getClearCode() + 1; i < isParent.length; i++) {
121 if (!isParent[i]) {
122 isUsed[i] = false;
123 setPrefix(i, UNUSED_PREFIX);
124 }
125 }
126 }
127 }