summaryrefslogtreecommitdiffstats
path: root/archive
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2010-03-04 17:18:44 -0800
committerJesse Wilson <jessewilson@google.com>2010-03-05 10:16:01 -0800
commitcf900b4862df0e261b3cce2195ff0654124ad7db (patch)
treef70dd7f91a2aee547b37decf4d2b2de3ee083724 /archive
parent08862aa94f18c8ea220e72c4faf66b680dc5e3fa (diff)
downloadlibcore-cf900b4862df0e261b3cce2195ff0654124ad7db.zip
libcore-cf900b4862df0e261b3cce2195ff0654124ad7db.tar.gz
libcore-cf900b4862df0e261b3cce2195ff0654124ad7db.tar.bz2
Implementing the Java 7 APIs for DeflaterOutputStreams.
See bug 1729487.
Diffstat (limited to 'archive')
-rw-r--r--archive/src/main/java/java/util/zip/Deflater.java83
-rw-r--r--archive/src/main/java/java/util/zip/DeflaterOutputStream.java48
-rw-r--r--archive/src/test/java/java/util/zip/AllTests.java29
-rw-r--r--archive/src/test/java/java/util/zip/DeflaterOutputStreamTest.java79
-rw-r--r--archive/src/test/java/java/util/zip/DeflaterTest.java72
5 files changed, 295 insertions, 16 deletions
diff --git a/archive/src/main/java/java/util/zip/Deflater.java b/archive/src/main/java/java/util/zip/Deflater.java
index eb49f12..41a337c 100644
--- a/archive/src/main/java/java/util/zip/Deflater.java
+++ b/archive/src/main/java/java/util/zip/Deflater.java
@@ -75,9 +75,38 @@ public class Deflater {
*/
public static final int NO_COMPRESSION = 0;
- private static final int Z_NO_FLUSH = 0;
+ /**
+ * Use buffering for best compression.
+ *
+ * @hide
+ * @since 1.7
+ */
+ public static final int NO_FLUSH = 0;
- private static final int Z_FINISH = 4;
+ /**
+ * Flush buffers so recipients can immediately decode the data sent thus
+ * far. This mode may degrade compression.
+ *
+ * @hide
+ * @since 1.7
+ */
+ public static final int SYNC_FLUSH = 2;
+
+ /**
+ * Flush buffers so recipients can immediately decode the data sent thus
+ * far. The compression state is also reset to permit random access and
+ * recovery for clients who have discarded or damaged their own copy. This
+ * mode may degrade compression.
+ *
+ * @hide
+ * @since 1.7
+ */
+ public static final int FULL_FLUSH = 3;
+
+ /**
+ * Flush buffers and mark the end of the datastream.
+ */
+ private static final int FINISH = 4;
// Fill in the JNI id caches
private static native void oneTimeInitialization();
@@ -90,7 +119,7 @@ public class Deflater {
oneTimeInitialization();
}
- private int flushParm = Z_NO_FLUSH;
+ private int flushParm = NO_FLUSH;
private boolean finished;
@@ -175,19 +204,45 @@ public class Deflater {
* @return the number of bytes of compressed data written to {@code buf}.
*/
public synchronized int deflate(byte[] buf, int off, int nbytes) {
+ return deflateImpl(buf, off, nbytes, flushParm);
+ }
+
+ /**
+ * Deflates data (previously passed to {@code setInput}) into a specific
+ * region within the supplied buffer, optionally flushing the input buffer.
+ *
+ * @param buf the buffer to write compressed data to.
+ * @param off the offset within {@code buf} at which to start writing to.
+ * @param nbytes maximum number of bytes of compressed data to be written.
+ * @param flush one of {@link #NO_FLUSH}, {@link #SYNC_FLUSH} or
+ * {@link #FULL_FLUSH}.
+ * @return the number of compressed bytes written to {@code buf}. If this
+ * equals {@code nbytes}, the number of bytes of input to be flushed
+ * may have exceeded the output buffer's capacity. In this case,
+ * finishing a flush will require the output buffer to be drained
+ * and additional calls to {@link #deflate} to be made.
+ * @hide
+ * @since 1.7
+ */
+ public synchronized int deflate(byte[] buf, int off, int nbytes, int flush) {
+ if (flush != NO_FLUSH && flush != SYNC_FLUSH && flush != FULL_FLUSH) {
+ throw new IllegalArgumentException();
+ }
+ return deflateImpl(buf, off, nbytes, flush);
+ }
+
+ private synchronized int deflateImpl(
+ byte[] buf, int off, int nbytes, int flush) {
if (streamHandle == -1) {
throw new IllegalStateException();
}
- // avoid int overflow, check null buf
- if (off <= buf.length && nbytes >= 0 && off >= 0
- && buf.length - off >= nbytes) {
- // put a stub buffer, no effect.
- if (null == inputBuffer) {
- setInput(STUB_INPUT_BUFFER);
- }
- return deflateImpl(buf, off, nbytes, streamHandle, flushParm);
+ if (off > buf.length || nbytes < 0 || off < 0 || buf.length - off < nbytes) {
+ throw new ArrayIndexOutOfBoundsException();
+ }
+ if (inputBuffer == null) {
+ setInput(STUB_INPUT_BUFFER);
}
- throw new ArrayIndexOutOfBoundsException();
+ return deflateImpl(buf, off, nbytes, streamHandle, flush);
}
private synchronized native int deflateImpl(byte[] buf, int off,
@@ -229,7 +284,7 @@ public class Deflater {
* @see #finished
*/
public synchronized void finish() {
- flushParm = Z_FINISH;
+ flushParm = FINISH;
}
/**
@@ -325,7 +380,7 @@ public class Deflater {
throw new NullPointerException();
}
- flushParm = Z_NO_FLUSH;
+ flushParm = NO_FLUSH;
finished = false;
resetImpl(streamHandle);
inputBuffer = null;
diff --git a/archive/src/main/java/java/util/zip/DeflaterOutputStream.java b/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
index 197426f..7ea27fa 100644
--- a/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
+++ b/archive/src/main/java/java/util/zip/DeflaterOutputStream.java
@@ -45,6 +45,8 @@ public class DeflaterOutputStream extends FilterOutputStream {
boolean done = false;
+ private final boolean syncFlush;
+
/**
* This constructor lets you pass the {@code Deflater} specifying the
* compression algorithm.
@@ -57,7 +59,7 @@ public class DeflaterOutputStream extends FilterOutputStream {
* data.
*/
public DeflaterOutputStream(OutputStream os, Deflater def) {
- this(os, def, BUF_SIZE);
+ this(os, def, BUF_SIZE, false);
}
/**
@@ -71,7 +73,7 @@ public class DeflaterOutputStream extends FilterOutputStream {
* is the OutputStream where to write the compressed data to.
*/
public DeflaterOutputStream(OutputStream os) {
- this(os, new Deflater());
+ this(os, new Deflater(), BUF_SIZE, false);
}
/**
@@ -88,6 +90,30 @@ public class DeflaterOutputStream extends FilterOutputStream {
* is the size to be used for the internal buffer.
*/
public DeflaterOutputStream(OutputStream os, Deflater def, int bsize) {
+ this(os, def, bsize, false);
+ }
+
+ /**
+ * @hide
+ * @since 1.7
+ */
+ public DeflaterOutputStream(OutputStream os, boolean syncFlush) {
+ this(os, new Deflater(), BUF_SIZE, syncFlush);
+ }
+
+ /**
+ * @hide
+ * @since 1.7
+ */
+ public DeflaterOutputStream(OutputStream os, Deflater def, boolean syncFlush) {
+ this(os, def, BUF_SIZE, syncFlush);
+ }
+
+ /**
+ * @hide
+ * @since 1.7
+ */
+ public DeflaterOutputStream(OutputStream os, Deflater def, int bsize, boolean syncFlush) {
super(os);
if (os == null || def == null) {
throw new NullPointerException();
@@ -96,6 +122,7 @@ public class DeflaterOutputStream extends FilterOutputStream {
throw new IllegalArgumentException();
}
this.def = def;
+ this.syncFlush = syncFlush;
buf = new byte[bsize];
}
@@ -192,4 +219,21 @@ public class DeflaterOutputStream extends FilterOutputStream {
throw new ArrayIndexOutOfBoundsException();
}
}
+
+ /**
+ * Flushes the underlying stream. This flushes only the bytes that can be
+ * compressed at the highest level.
+ *
+ * <p>For deflater output streams constructed with Java 7's
+ * {@code syncFlush} parameter set to true (not yet available on Android),
+ * this first flushes all outstanding data so that it may be immediately
+ * read by its recipient. Doing so may degrade compression.
+ */
+ @Override public void flush() throws IOException {
+ if (syncFlush) {
+ int count = def.deflate(buf, 0, buf.length, Deflater.SYNC_FLUSH);
+ out.write(buf, 0, count);
+ }
+ out.flush();
+ }
}
diff --git a/archive/src/test/java/java/util/zip/AllTests.java b/archive/src/test/java/java/util/zip/AllTests.java
new file mode 100644
index 0000000..df1d8de
--- /dev/null
+++ b/archive/src/test/java/java/util/zip/AllTests.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 java.util.zip;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class AllTests {
+ public static Test suite() {
+ TestSuite suite = new TestSuite();
+ suite.addTestSuite(DeflaterOutputStreamTest.class);
+ suite.addTestSuite(DeflaterTest.class);
+ return suite;
+ }
+}
diff --git a/archive/src/test/java/java/util/zip/DeflaterOutputStreamTest.java b/archive/src/test/java/java/util/zip/DeflaterOutputStreamTest.java
new file mode 100644
index 0000000..5d155fe
--- /dev/null
+++ b/archive/src/test/java/java/util/zip/DeflaterOutputStreamTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 java.util.zip;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class DeflaterOutputStreamTest extends TestCase {
+
+ public void testSyncFlushEnabled() throws Exception {
+ InflaterInputStream in = createInflaterStream(true);
+ assertEquals(1, in.read());
+ assertEquals(2, in.read());
+ assertEquals(3, in.read());
+ }
+
+ public void testSyncFlushDisabled() throws Exception {
+ InflaterInputStream in = createInflaterStream(false);
+ try {
+ in.read();
+ fail();
+ } catch (IOException expected) {
+ }
+ }
+
+ /**
+ * Creates an optionally-flushing deflater stream, writes some bytes to it,
+ * and flushes it. Returns an inflater stream that reads this deflater's
+ * output.
+ *
+ * <p>These bytes are written on a separate thread so that when the inflater
+ * stream is read, that read will fail when no bytes are available. Failing
+ * takes 3 seconds, co-ordinated by PipedInputStream's 'broken pipe'
+ * timeout. The 3 second delay is unfortunate but seems to be the easiest
+ * way demonstrate that data is unavailable. Ie. other techniques will cause
+ * the dry read to block indefinitely.
+ */
+ private InflaterInputStream createInflaterStream(final boolean flushing)
+ throws Exception {
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ final PipedOutputStream pout = new PipedOutputStream();
+ PipedInputStream pin = new PipedInputStream(pout);
+
+ executor.submit(new Callable<Void>() {
+ public Void call() throws Exception {
+ OutputStream out = new DeflaterOutputStream(pout, flushing);
+ out.write(1);
+ out.write(2);
+ out.write(3);
+ out.flush();
+ return null;
+ }
+ }).get();
+ executor.shutdown();
+
+ return new InflaterInputStream(pin);
+ }
+}
diff --git a/archive/src/test/java/java/util/zip/DeflaterTest.java b/archive/src/test/java/java/util/zip/DeflaterTest.java
new file mode 100644
index 0000000..d8ce66e
--- /dev/null
+++ b/archive/src/test/java/java/util/zip/DeflaterTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed 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 java.util.zip;
+
+import junit.framework.TestCase;
+
+public class DeflaterTest extends TestCase {
+
+ private byte[] compressed = new byte[32];
+ private byte[] decompressed = new byte[20];
+ private Deflater deflater = new Deflater();
+ private Inflater inflater = new Inflater();
+ private int totalDeflated = 0;
+ private int totalInflated = 0;
+
+ public void testDeflate() throws DataFormatException {
+ deflater.setInput(new byte[] { 1, 2, 3 });
+ deflateInflate(Deflater.NO_FLUSH);
+ assertTrue(totalInflated < 3);
+ assertEquals(0, decompressed[2]); // the 3rd byte shouldn't have been flushed yet
+
+ deflater.setInput(new byte[] { 4, 5, 6 });
+ deflateInflate(Deflater.SYNC_FLUSH);
+ assertEquals(6, totalInflated);
+ assertDecompressed(1, 2, 3, 4, 5, 6);
+ assertEquals(0, inflater.inflate(decompressed));
+
+ deflater.setInput(new byte[] { 7, 8, 9 });
+ deflateInflate(Deflater.FULL_FLUSH);
+ assertEquals(9, totalInflated);
+ assertDecompressed(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ assertEquals(0, inflater.inflate(decompressed));
+ inflater = new Inflater(true); // safe because we did a FULL_FLUSH
+
+ deflater.setInput(new byte[] { 10, 11, 12 });
+ deflateInflate(Deflater.SYNC_FLUSH);
+ assertEquals(12, totalInflated);
+ assertDecompressed(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
+ assertEquals(0, inflater.inflate(decompressed));
+ }
+
+ private void deflateInflate(int flush) throws DataFormatException {
+ int lastDeflated = deflater.deflate(compressed, totalDeflated,
+ compressed.length - totalDeflated, flush);
+ assertTrue(inflater.needsInput());
+ inflater.setInput(compressed, totalDeflated, lastDeflated);
+ totalDeflated += lastDeflated;
+ totalInflated += inflater.inflate(decompressed, totalInflated,
+ decompressed.length - totalInflated);
+ }
+
+ private void assertDecompressed(int... expected) {
+ for (int i = 0; i < decompressed.length; i++) {
+ int expectedValue = i < expected.length ? expected[i] : 0;
+ assertEquals(expectedValue, decompressed[i]);
+ }
+ }
+}