diff options
author | Jesse Wilson <jessewilson@google.com> | 2010-03-05 10:48:21 -0800 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2010-03-05 10:48:21 -0800 |
commit | 4dc73b89159ebdb24f1cd6768d01415780f39f01 (patch) | |
tree | c3d67d320f858502320c2737c527f9e3ca96e34e /archive | |
parent | 0592da01c5b4537eda0331909c221dde3a043942 (diff) | |
parent | dc3939cc3523fd3daaee4efe753f6e82145e8cff (diff) | |
download | libcore-4dc73b89159ebdb24f1cd6768d01415780f39f01.zip libcore-4dc73b89159ebdb24f1cd6768d01415780f39f01.tar.gz libcore-4dc73b89159ebdb24f1cd6768d01415780f39f01.tar.bz2 |
am 101cbfe4: Merge "Implementing the Java 7 APIs for DeflaterOutputStreams."
Merge commit '101cbfe486ad1744f666e924f600dd1b8cdecea7' into dalvik-dev
* commit '101cbfe486ad1744f666e924f600dd1b8cdecea7':
Implementing the Java 7 APIs for DeflaterOutputStreams.
Diffstat (limited to 'archive')
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]); + } + } +} |