1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.commons.compress.archivers.zip;
21
22 import java.io.Closeable;
23 import java.io.DataOutput;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.OutputStream;
27 import java.nio.ByteBuffer;
28 import java.nio.channels.SeekableByteChannel;
29 import java.util.zip.CRC32;
30 import java.util.zip.Deflater;
31 import java.util.zip.ZipEntry;
32
33 import org.apache.commons.compress.parallel.ScatterGatherBackingStore;
34
35
36
37
38
39
40
41 public abstract class StreamCompressor implements Closeable {
42
43 private static final class DataOutputCompressor extends StreamCompressor {
44
45 private final DataOutput raf;
46
47 DataOutputCompressor(final Deflater deflater, final DataOutput raf) {
48 super(deflater);
49 this.raf = raf;
50 }
51
52 @Override
53 protected void writeOut(final byte[] data, final int offset, final int length) throws IOException {
54 raf.write(data, offset, length);
55 }
56 }
57
58 private static final class OutputStreamCompressor extends StreamCompressor {
59
60 private final OutputStream os;
61
62 OutputStreamCompressor(final Deflater deflater, final OutputStream os) {
63 super(deflater);
64 this.os = os;
65 }
66
67 @Override
68 protected void writeOut(final byte[] data, final int offset, final int length) throws IOException {
69 os.write(data, offset, length);
70 }
71 }
72
73 private static final class ScatterGatherBackingStoreCompressor extends StreamCompressor {
74
75 private final ScatterGatherBackingStore bs;
76
77 ScatterGatherBackingStoreCompressor(final Deflater deflater, final ScatterGatherBackingStore bs) {
78 super(deflater);
79 this.bs = bs;
80 }
81
82 @Override
83 protected void writeOut(final byte[] data, final int offset, final int length) throws IOException {
84 bs.writeOut(data, offset, length);
85 }
86 }
87
88 private static final class SeekableByteChannelCompressor extends StreamCompressor {
89
90 private final SeekableByteChannel channel;
91
92 SeekableByteChannelCompressor(final Deflater deflater, final SeekableByteChannel channel) {
93 super(deflater);
94 this.channel = channel;
95 }
96
97 @Override
98 protected void writeOut(final byte[] data, final int offset, final int length) throws IOException {
99 channel.write(ByteBuffer.wrap(data, offset, length));
100 }
101 }
102
103
104
105
106
107
108
109 private static final int DEFLATER_BLOCK_SIZE = 8192;
110 private static final int BUFFER_SIZE = 4096;
111
112
113
114
115
116
117
118
119 static StreamCompressor create(final DataOutput os, final Deflater deflater) {
120 return new DataOutputCompressor(deflater, os);
121 }
122
123
124
125
126
127
128
129
130 public static StreamCompressor create(final int compressionLevel, final ScatterGatherBackingStore bs) {
131 final Deflater deflater = new Deflater(compressionLevel, true);
132 return new ScatterGatherBackingStoreCompressor(deflater, bs);
133 }
134
135
136
137
138
139
140
141 static StreamCompressor create(final OutputStream os) {
142 return create(os, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
143 }
144
145
146
147
148
149
150
151
152 static StreamCompressor create(final OutputStream os, final Deflater deflater) {
153 return new OutputStreamCompressor(deflater, os);
154 }
155
156
157
158
159
160
161
162 public static StreamCompressor create(final ScatterGatherBackingStore bs) {
163 return create(Deflater.DEFAULT_COMPRESSION, bs);
164 }
165
166
167
168
169
170
171
172
173
174 static StreamCompressor create(final SeekableByteChannel os, final Deflater deflater) {
175 return new SeekableByteChannelCompressor(deflater, os);
176 }
177
178 private final Deflater deflater;
179 private final CRC32 crc = new CRC32();
180 private long writtenToOutputStreamForLastEntry;
181 private long sourcePayloadLength;
182 private long totalWrittenToOutputStream;
183 private final byte[] outputBuffer = new byte[BUFFER_SIZE];
184 private final byte[] readerBuf = new byte[BUFFER_SIZE];
185
186 StreamCompressor(final Deflater deflater) {
187 this.deflater = deflater;
188 }
189
190 @Override
191 public void close() throws IOException {
192 deflater.end();
193 }
194
195 void deflate() throws IOException {
196 final int len = deflater.deflate(outputBuffer, 0, outputBuffer.length);
197 if (len > 0) {
198 writeCounted(outputBuffer, 0, len);
199 }
200 }
201
202
203
204
205
206
207
208
209 public void deflate(final InputStream source, final int method) throws IOException {
210 reset();
211 int length;
212 while ((length = source.read(readerBuf, 0, readerBuf.length)) >= 0) {
213 write(readerBuf, 0, length, method);
214 }
215 if (method == ZipEntry.DEFLATED) {
216 flushDeflater();
217 }
218 }
219
220 private void deflateUntilInputIsNeeded() throws IOException {
221 while (!deflater.needsInput()) {
222 deflate();
223 }
224 }
225
226 void flushDeflater() throws IOException {
227 deflater.finish();
228 while (!deflater.finished()) {
229 deflate();
230 }
231 }
232
233
234
235
236
237
238 public long getBytesRead() {
239 return sourcePayloadLength;
240 }
241
242
243
244
245
246
247 public long getBytesWrittenForLastEntry() {
248 return writtenToOutputStreamForLastEntry;
249 }
250
251
252
253
254
255
256 public long getCrc32() {
257 return crc.getValue();
258 }
259
260
261
262
263
264
265 public long getTotalBytesWritten() {
266 return totalWrittenToOutputStream;
267 }
268
269 void reset() {
270 crc.reset();
271 deflater.reset();
272 sourcePayloadLength = 0;
273 writtenToOutputStreamForLastEntry = 0;
274 }
275
276
277
278
279
280
281
282
283
284
285
286 long write(final byte[] b, final int offset, final int length, final int method) throws IOException {
287 final long current = writtenToOutputStreamForLastEntry;
288 crc.update(b, offset, length);
289 if (method == ZipEntry.DEFLATED) {
290 writeDeflated(b, offset, length);
291 } else {
292 writeCounted(b, offset, length);
293 }
294 sourcePayloadLength += length;
295 return writtenToOutputStreamForLastEntry - current;
296 }
297
298
299
300
301
302
303
304 public void writeCounted(final byte[] data) throws IOException {
305 writeCounted(data, 0, data.length);
306 }
307
308
309
310
311
312
313
314
315
316 public void writeCounted(final byte[] data, final int offset, final int length) throws IOException {
317 writeOut(data, offset, length);
318 writtenToOutputStreamForLastEntry += length;
319 totalWrittenToOutputStream += length;
320 }
321
322 private void writeDeflated(final byte[] b, final int offset, final int length) throws IOException {
323 if (length > 0 && !deflater.finished()) {
324 if (length <= DEFLATER_BLOCK_SIZE) {
325 deflater.setInput(b, offset, length);
326 deflateUntilInputIsNeeded();
327 } else {
328 final int fullblocks = length / DEFLATER_BLOCK_SIZE;
329 for (int i = 0; i < fullblocks; i++) {
330 deflater.setInput(b, offset + i * DEFLATER_BLOCK_SIZE, DEFLATER_BLOCK_SIZE);
331 deflateUntilInputIsNeeded();
332 }
333 final int done = fullblocks * DEFLATER_BLOCK_SIZE;
334 if (done < length) {
335 deflater.setInput(b, offset + done, length - done);
336 deflateUntilInputIsNeeded();
337 }
338 }
339 }
340 }
341
342
343
344
345
346
347
348
349
350 protected abstract void writeOut(byte[] data, int offset, int length) throws IOException;
351 }