Author: krosenvold
Date: Mon Dec 22 15:24:02 2014
New Revision: 1647329
URL: http://svn.apache.org/r1647329
Log:
COMPRESS-296 Parallel compression. Added StreamCompressor and
ScatterZipOutputStream.
StreamCompressor is an extract of the deflation algorithm from
ZipArchiveOutputStream, which unfortunately
was too conflated with writing a file in a particular structure. Using the
actual zip file format as an
intermediate format for scatter-streams turned out to be fairly inefficient.
ScatterZipOuputStream
is 2-3x faster than using a zip file as intermediate format.
It would be possibly to refactor ZipArchiveOutputStream to use
StreamCompressor, but there would
be a slight break in backward compatibility regarding the protected writeOut
method, which
is moved to the streamCompressor class.
Added:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStream.java
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/StreamCompressor.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStreamTest.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/StreamCompressorTest.java
Modified:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
Added:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStream.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStream.java?rev=1647329&view=auto
==============================================================================
---
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStream.java
(added)
+++
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStream.java
Mon Dec 22 15:24:02 2014
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.commons.compress.archivers.zip;
+
+
+import org.apache.commons.compress.utils.BoundedInputStream;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.zip.Deflater;
+
+/**
+ * A zip output stream that is optimized for multi-threaded scatter/gather
construction of zip files.
+ * <p/>
+ * The internal data format of the entries used by this class are entirely
private to this class
+ * and are not part of any public api whatsoever.
+ * <p/>
+ * It is possible to extend this class to support different kinds of backing
storage, the default
+ * implementation only supports file-based backing.
+ * <p/>
+ * Thread safety: This class supports multiple threads. But the "writeTo"
method must be called
+ * by the thread that originally created the ZipArchiveEntry.
+ *
+ * @since 1.10
+ */
+public abstract class ScatterZipOutputStream {
+ private final Queue<CompressedEntry> items = new
ConcurrentLinkedQueue<CompressedEntry>();
+
+ private static class CompressedEntry {
+ final ZipArchiveEntry entry;
+ final long crc;
+ final long compressedSize;
+ final int method;
+ final long size;
+
+ public CompressedEntry(ZipArchiveEntry entry, long crc, long
compressedSize, int method, long size) {
+ this.entry = entry;
+ this.crc = crc;
+ this.compressedSize = compressedSize;
+ this.method = method;
+ this.size = size;
+ }
+
+ public ZipArchiveEntry transferToArchiveEntry(){
+ entry.setCompressedSize(compressedSize);
+ entry.setSize(size);
+ entry.setCrc(crc);
+ entry.setMethod(method);
+ return entry;
+ }
+ }
+
+ /**
+ * Add an archive entry to this scatter stream.
+ *
+ * @param zipArchiveEntry The entry to write
+ * @param payload The content to write for the entry
+ * @param method The compression method
+ * @throws IOException If writing fails
+ */
+ public void addArchiveEntry(ZipArchiveEntry zipArchiveEntry, InputStream
payload, int method) throws IOException {
+ StreamCompressor sc = getStreamCompressor();
+ sc.deflate(payload, method);
+ payload.close();
+ items.add(new CompressedEntry(zipArchiveEntry, sc.getCrc32(),
sc.getBytesWritten(), method, sc.getBytesRead()));
+ }
+
+ /**
+ * Write the contents of this scatter stream to a target archive.
+ *
+ * @param target The archive to receive the contents of this
#ScatterZipOutputStream
+ * @throws IOException If writing fails
+ */
+ public void writeTo(ZipArchiveOutputStream target) throws IOException {
+ closeBackingStorage();
+ InputStream data = getInputStream();
+ for (CompressedEntry compressedEntry : items) {
+ final BoundedInputStream rawStream = new BoundedInputStream(data,
compressedEntry.compressedSize);
+
target.addRawArchiveEntry(compressedEntry.transferToArchiveEntry(), rawStream);
+ rawStream.close();
+ }
+ data.close();
+ }
+
+ /**
+ * Returns a stream compressor that can be used to compress the data.
+ * <p/>
+ * This method is expected to return the same instance every time.
+ *
+ * @return The stream compressor
+ * @throws FileNotFoundException
+ */
+ protected abstract StreamCompressor getStreamCompressor() throws
FileNotFoundException;
+
+ /**
+ * An input stream that contains the scattered payload
+ *
+ * @return An InputStream, should be closed by the caller of this method.
+ * @throws IOException when something fails
+ */
+ protected abstract InputStream getInputStream() throws IOException;
+
+
+ /**
+ * Closes whatever storage is backing this scatter stream
+ */
+ protected abstract void closeBackingStorage() throws IOException;
+
+ /**
+ * Create a ScatterZipOutputStream with default compression level that is
backed by a file
+ *
+ * @param file The file to offload compressed data into.
+ * @return A ScatterZipOutputStream that is ready for use.
+ * @throws FileNotFoundException
+ */
+ public static ScatterZipOutputStream fileBased(File file) throws
FileNotFoundException {
+ return fileBased(file, Deflater.DEFAULT_COMPRESSION);
+ }
+
+ /**
+ * Create a ScatterZipOutputStream that is backed by a file
+ *
+ * @param file The file to offload compressed data into.
+ * @param compressionLevel The compression level to use, @see #Deflater
+ * @return A ScatterZipOutputStream that is ready for use.
+ * @throws FileNotFoundException
+ */
+ public static ScatterZipOutputStream fileBased(File file, int
compressionLevel) throws FileNotFoundException {
+ return new FileScatterOutputStream(file, compressionLevel);
+ }
+
+ private static class FileScatterOutputStream extends
ScatterZipOutputStream {
+ final File target;
+ private StreamCompressor streamDeflater;
+ final FileOutputStream os;
+
+ FileScatterOutputStream(File target, int compressionLevel) throws
FileNotFoundException {
+ this.target = target;
+ os = new FileOutputStream(target);
+ streamDeflater = StreamCompressor.create(compressionLevel, os);
+ }
+
+ @Override
+ protected StreamCompressor getStreamCompressor() throws
FileNotFoundException {
+ return streamDeflater;
+ }
+
+ @Override
+ protected InputStream getInputStream() throws IOException {
+ return new FileInputStream(target);
+ }
+
+ @SuppressWarnings("ResultOfMethodCallIgnored")
+ public void closeBackingStorage() throws IOException {
+ os.close();
+ }
+ }
+}
Added:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/StreamCompressor.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/StreamCompressor.java?rev=1647329&view=auto
==============================================================================
---
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/StreamCompressor.java
(added)
+++
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/StreamCompressor.java
Mon Dec 22 15:24:02 2014
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import java.io.DataOutput;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+
+/**
+ * Encapsulates a Deflater and crc calculator, handling multiple types of
output streams.
+ * Currently #ZipEntry.DEFLATED and #ZipEntry.STORED are the only supported
compression methods.
+ *
+ * @since 1.10
+ */
+public abstract class StreamCompressor {
+
+ /*
+ * Apparently Deflater.setInput gets slowed down a lot on Sun JVMs
+ * when it gets handed a really big buffer. See
+ * https://issues.apache.org/bugzilla/show_bug.cgi?id=45396
+ *
+ * Using a buffer size of 8 kB proved to be a good compromise
+ */
+ private static final int DEFLATER_BLOCK_SIZE = 8192;
+
+ private final Deflater def;
+
+ private final CRC32 crc = new CRC32();
+
+ int writtenToOutputStream = 0;
+ int sourcePayloadLength = 0;
+ long actualCrc;
+
+ private final int bufferSize = 4096;
+ private final byte[] outputBuffer = new byte[bufferSize];
+ private final byte[] readerBuf = new byte[bufferSize];
+
+ protected StreamCompressor(Deflater deflater) {
+ this.def = deflater;
+ }
+
+ /**
+ * Create a stream compressor with the given compression level.
+ *
+ * @param compressionLevel The #Deflater compression level
+ * @param os The #OutputStream stream to receive output
+ * @return A stream compressor
+ */
+ public static StreamCompressor create(int compressionLevel, OutputStream
os) {
+ final Deflater deflater = new Deflater(compressionLevel, true);
+ return new OutputStreamCompressor(deflater, os);
+ }
+
+ /**
+ * Create a stream compressor with the default compression level.
+ *
+ * @param os The #OutputStream stream to receive output
+ * @return A stream compressor
+ */
+ public static StreamCompressor create( OutputStream os) {
+ return create(Deflater.DEFAULT_COMPRESSION, os);
+ }
+
+ /**
+ * Create a stream compressor with the given compression level.
+ *
+ * @param compressionLevel The #Deflater compression level
+ * @param os The #DataOutput to receive output
+ * @return A stream compressor
+ */
+ public static StreamCompressor create(int compressionLevel, DataOutput os)
{
+ final Deflater deflater = new Deflater(compressionLevel, true);
+ return new DataOutputCompressor(deflater, os);
+ }
+
+ /**
+ * The crc32 of the last deflated file
+ * @return the crc32
+ */
+
+ public long getCrc32() {
+ return actualCrc;
+ }
+
+ /**
+ * Return the number of bytes read from the source stream
+ * @return The number of bytes read, never negative
+ */
+ public int getBytesRead() {
+ return sourcePayloadLength;
+ }
+
+ /**
+ * The number of bytes written to the output
+ * @return The number of bytes, never negative
+ */
+ public int getBytesWritten() {
+ return writtenToOutputStream;
+ }
+
+ /**
+ * Deflate the given source using the supplied compression method
+ * @param source The source to compress
+ * @param method The #ZipArchiveEntry compression method
+ * @throws IOException When failures happen
+ */
+
+ public void deflate(InputStream source, int method) throws IOException {
+ reset();
+ int length;
+
+ while(( length = source.read(readerBuf, 0, readerBuf.length)) >= 0){
+ crc.update(readerBuf, 0, length);
+ if (method == ZipArchiveEntry.DEFLATED) {
+ writeDeflated(readerBuf, 0, length);
+ } else {
+ writeOut(readerBuf, 0, length);
+ writtenToOutputStream += length;
+ }
+ sourcePayloadLength += length;
+ }
+ if (method == ZipArchiveEntry.DEFLATED) {
+ flushDeflater();
+ }
+ actualCrc = crc.getValue();
+
+
+ }
+
+ private void reset(){
+ crc.reset();
+ def.reset();
+ sourcePayloadLength = 0;
+ writtenToOutputStream = 0;
+ }
+
+ private void flushDeflater() throws IOException {
+ def.finish();
+ while (!def.finished()) {
+ deflate();
+ }
+ }
+
+ private void writeDeflated(byte[]b, int offset, int length)
+ throws IOException {
+ if (length > 0 && !def.finished()) {
+ if (length <= DEFLATER_BLOCK_SIZE) {
+ def.setInput(b, offset, length);
+ deflateUntilInputIsNeeded();
+ } else {
+ final int fullblocks = length / DEFLATER_BLOCK_SIZE;
+ for (int i = 0; i < fullblocks; i++) {
+ def.setInput(b, offset + i * DEFLATER_BLOCK_SIZE,
+ DEFLATER_BLOCK_SIZE);
+ deflateUntilInputIsNeeded();
+ }
+ final int done = fullblocks * DEFLATER_BLOCK_SIZE;
+ if (done < length) {
+ def.setInput(b, offset + done, length - done);
+ deflateUntilInputIsNeeded();
+ }
+ }
+ }
+ }
+
+ private void deflateUntilInputIsNeeded() throws IOException {
+ while (!def.needsInput()) {
+ deflate();
+ }
+ }
+
+ private void deflate() throws IOException {
+ int len = def.deflate(outputBuffer, 0, outputBuffer.length);
+ if (len > 0) {
+ writeOut(outputBuffer, 0, len);
+ writtenToOutputStream += len;
+ }
+ }
+
+ protected abstract void writeOut(byte[] data, int offset, int length)
throws IOException ;
+
+ private static final class OutputStreamCompressor extends StreamCompressor
{
+ private final OutputStream os;
+
+ public OutputStreamCompressor(Deflater deflater, OutputStream os) {
+ super(deflater);
+ this.os = os;
+ }
+
+ protected final void writeOut(byte[] data, int offset, int length)
+ throws IOException {
+ os.write(data, offset, length);
+ }
+ }
+
+ private static final class DataOutputCompressor extends StreamCompressor {
+ private final DataOutput raf;
+ public DataOutputCompressor(Deflater deflater, DataOutput raf) {
+ super(deflater);
+ this.raf = raf;
+ }
+
+ protected final void writeOut(byte[] data, int offset, int length)
+ throws IOException {
+ raf.write(data, offset, length);
+ }
+ }
+}
Modified:
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java?rev=1647329&r1=1647328&r2=1647329&view=diff
==============================================================================
---
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
(original)
+++
commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
Mon Dec 22 15:24:02 2014
@@ -505,24 +505,6 @@ public class ZipArchiveOutputStream exte
}
/**
- * Make a copy of this stream with all its settings, but point to a new
file.
- * Used for scatter/gather operations to make several streams from a
user-supplied master.
- *
- * @param newFile The file to use for the copy of this stream
- * @return A copy of this stream
- */
- public ZipArchiveOutputStream cloneWith(File newFile) throws IOException {
- ZipArchiveOutputStream zos = new ZipArchiveOutputStream(newFile);
- zos.setCreateUnicodeExtraFields(createUnicodeExtraFields);
- zos.setMethod(method);
- zos.setEncoding(encoding);
- zos.setFallbackToUTF8(fallbackToUTF8);
- zos.setUseLanguageEncodingFlag(useUTF8Flag);
- zos.setUseZip64(zip64Mode);
- return zos;
- }
-
- /**
* Ensures all bytes sent to the deflater are written to the stream.
*/
private void flushDeflater() throws IOException {
Modified:
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java?rev=1647329&r1=1647328&r2=1647329&view=diff
==============================================================================
---
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
(original)
+++
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
Mon Dec 22 15:24:02 2014
@@ -293,19 +293,6 @@ public final class ZipTestCase extends A
}
};
-
- public void testCloneZipOutputStream( ) throws IOException {
- File tempDir = createTempDir();
- File fred = new File(tempDir, "fred");
- ZipArchiveOutputStream zipArchiveOutputStream = new
ZipArchiveOutputStream(fred);
- File frank = new File(tempDir, "frank");
- ZipArchiveOutputStream actual =
zipArchiveOutputStream.cloneWith(frank);
- zipArchiveOutputStream.close();
- actual.close();
- assertTrue( fred.exists());
- assertTrue( frank.exists());
- }
-
public void testCopyRawEntriesFromFile
()
throws IOException {
Added:
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStreamTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStreamTest.java?rev=1647329&view=auto
==============================================================================
---
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStreamTest.java
(added)
+++
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/ScatterZipOutputStreamTest.java
Mon Dec 22 15:24:02 2014
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import org.apache.commons.compress.utils.IOUtils;
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class ScatterZipOutputStreamTest {
+
+ @Test
+ public void putArchiveEntry() throws Exception {
+ File scatteFile = File.createTempFile("scattertest", ".notzip");
+ ScatterZipOutputStream scatterZipOutputStream =
ScatterZipOutputStream.fileBased(scatteFile);
+ final byte[] B_PAYLOAD = "RBBBBBBS".getBytes();
+ final byte[] A_PAYLOAD = "XAAY".getBytes();
+
+ ZipArchiveEntry zab = new ZipArchiveEntry("b.txt");
+ scatterZipOutputStream.addArchiveEntry(zab, new
ByteArrayInputStream(B_PAYLOAD), ZipArchiveEntry.DEFLATED);
+
+ ZipArchiveEntry zae = new ZipArchiveEntry("a.txt");
+ scatterZipOutputStream.addArchiveEntry(zae, new
ByteArrayInputStream(A_PAYLOAD), ZipArchiveEntry.DEFLATED);
+
+ File target = File.createTempFile("scattertest", ".zip");
+ ZipArchiveOutputStream outputStream = new
ZipArchiveOutputStream(target);
+ scatterZipOutputStream.writeTo( outputStream);
+ outputStream.close();
+
+ ZipFile zf = new ZipFile(target);
+ final ZipArchiveEntry b_entry =
zf.getEntries("b.txt").iterator().next();
+ assertEquals(8, b_entry.getSize());
+ assertArrayEquals(B_PAYLOAD,
IOUtils.toByteArray(zf.getInputStream(b_entry)));
+
+ final ZipArchiveEntry a_entry =
zf.getEntries("a.txt").iterator().next();
+ assertEquals(4, a_entry.getSize());
+ assertArrayEquals(A_PAYLOAD,
IOUtils.toByteArray(zf.getInputStream(a_entry)));
+ }
+}
\ No newline at end of file
Added:
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/StreamCompressorTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/StreamCompressorTest.java?rev=1647329&view=auto
==============================================================================
---
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/StreamCompressorTest.java
(added)
+++
commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/StreamCompressorTest.java
Mon Dec 22 15:24:02 2014
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.commons.compress.archivers.zip;
+
+import org.junit.Test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.zip.ZipEntry;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+
+public class StreamCompressorTest {
+
+ @Test
+ public void storedEntries() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ StreamCompressor sc = StreamCompressor.create( baos);
+ sc.deflate(new ByteArrayInputStream("A".getBytes()), ZipEntry.STORED);
+ sc.deflate(new ByteArrayInputStream("BAD".getBytes()),
ZipEntry.STORED);
+ assertEquals(3, sc.getBytesRead());
+ assertEquals(3, sc.getBytesWritten());
+ assertEquals(344750961, sc.getCrc32());
+ sc.deflate(new ByteArrayInputStream("CAFE".getBytes()),
ZipEntry.STORED);
+ assertEquals("ABADCAFE", baos.toString());
+ }
+
+ @Test
+ public void deflatedEntries() throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ StreamCompressor sc = StreamCompressor.create( baos);
+ sc.deflate(new ByteArrayInputStream("AAAAAABBBBBB".getBytes()),
ZipEntry.DEFLATED);
+ assertEquals(12, sc.getBytesRead());
+ assertEquals(8, sc.getBytesWritten());
+ assertEquals(3299542, sc.getCrc32());
+
+ final byte[] actuals = baos.toByteArray();
+ byte[] expected = new byte[]{115,116,4,1,39,48,0,0};
+ // Note that this test really asserts stuff about the java Deflater,
which might be a little bit brittle
+ assertArrayEquals(expected, actuals);
+ }
+}
\ No newline at end of file