aboutsummaryrefslogtreecommitdiff
path: root/src/org/tukaani/xz
diff options
context:
space:
mode:
authorLasse Collin <lasse.collin@tukaani.org>2011-08-16 17:45:48 +0300
committerLasse Collin <lasse.collin@tukaani.org>2011-08-16 17:45:48 +0300
commit91731d444f7e210e44aa1f80d3401337016fbe13 (patch)
treea70f9e0214885680f7951ffd1906336aa0a2a486 /src/org/tukaani/xz
parent7dee7976d6df71942556d74083d566ca328c4e10 (diff)
downloadxz-java-91731d444f7e210e44aa1f80d3401337016fbe13.tar.gz
Small fixes to output streams.
All output streams: - Improve error handling. XZOutputStream: - Rename flushBlock to endBlock, and document that endBlock doesn't flush. - Remove setBlockFlushing.
Diffstat (limited to 'src/org/tukaani/xz')
-rw-r--r--src/org/tukaani/xz/DeltaOutputStream.java73
-rw-r--r--src/org/tukaani/xz/LZMA2OutputStream.java66
-rw-r--r--src/org/tukaani/xz/SimpleOutputStream.java64
-rw-r--r--src/org/tukaani/xz/UncompressedLZMA2OutputStream.java107
-rw-r--r--src/org/tukaani/xz/XZOutputStream.java161
5 files changed, 337 insertions, 134 deletions
diff --git a/src/org/tukaani/xz/DeltaOutputStream.java b/src/org/tukaani/xz/DeltaOutputStream.java
index d95edd5..b58ec80 100644
--- a/src/org/tukaani/xz/DeltaOutputStream.java
+++ b/src/org/tukaani/xz/DeltaOutputStream.java
@@ -15,10 +15,13 @@ import org.tukaani.xz.delta.DeltaEncoder;
class DeltaOutputStream extends FinishableOutputStream {
private static final int TMPBUF_SIZE = 4096;
- private final FinishableOutputStream out;
+ private FinishableOutputStream out;
private final DeltaEncoder delta;
private final byte[] tmpbuf = new byte[TMPBUF_SIZE];
+ private boolean finished = false;
+ private IOException exception = null;
+
static int getMemoryUsage() {
return 1 + TMPBUF_SIZE / 1024;
}
@@ -38,26 +41,72 @@ class DeltaOutputStream extends FinishableOutputStream {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
- while (len > TMPBUF_SIZE) {
- delta.encode(buf, off, TMPBUF_SIZE, tmpbuf);
- out.write(tmpbuf);
- off += TMPBUF_SIZE;
- len -= TMPBUF_SIZE;
- }
+ if (exception != null)
+ throw exception;
+
+ if (finished)
+ throw new XZIOException("Stream finished");
+
+ try {
+ while (len > TMPBUF_SIZE) {
+ delta.encode(buf, off, TMPBUF_SIZE, tmpbuf);
+ out.write(tmpbuf);
+ off += TMPBUF_SIZE;
+ len -= TMPBUF_SIZE;
+ }
- delta.encode(buf, off, len, tmpbuf);
- out.write(tmpbuf, 0, len);
+ delta.encode(buf, off, len, tmpbuf);
+ out.write(tmpbuf, 0, len);
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
}
public void flush() throws IOException {
- out.flush();
+ if (exception != null)
+ throw exception;
+
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
+
+ try {
+ out.flush();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
}
public void finish() throws IOException {
- out.finish();
+ if (!finished) {
+ if (exception != null)
+ throw exception;
+
+ try {
+ out.finish();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
+
+ finished = true;
+ }
}
public void close() throws IOException {
- out.close();
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ if (exception == null)
+ exception = e;
+ }
+
+ out = null;
+ }
+
+ if (exception != null)
+ throw exception;
}
}
diff --git a/src/org/tukaani/xz/LZMA2OutputStream.java b/src/org/tukaani/xz/LZMA2OutputStream.java
index 6eb0052..c042456 100644
--- a/src/org/tukaani/xz/LZMA2OutputStream.java
+++ b/src/org/tukaani/xz/LZMA2OutputStream.java
@@ -19,7 +19,7 @@ import org.tukaani.xz.lzma.LZMAEncoder;
class LZMA2OutputStream extends FinishableOutputStream {
static final int COMPRESSED_SIZE_MAX = 64 << 10;
- private final FinishableOutputStream out;
+ private FinishableOutputStream out;
private final DataOutputStream outData;
private final LZEncoder lz;
@@ -50,6 +50,9 @@ class LZMA2OutputStream extends FinishableOutputStream {
}
LZMA2OutputStream(FinishableOutputStream out, LZMA2Options options) {
+ if (out == null)
+ throw new NullPointerException();
+
this.out = out;
outData = new DataOutputStream(out);
rc = new RangeEncoder(COMPRESSED_SIZE_MAX);
@@ -82,7 +85,7 @@ class LZMA2OutputStream extends FinishableOutputStream {
throw exception;
if (finished)
- throw new XZIOException("Cannot write to a finished stream");
+ throw new XZIOException("Stream finished or closed");
try {
while (len > 0) {
@@ -165,45 +168,84 @@ class LZMA2OutputStream extends FinishableOutputStream {
}
private void writeEndMarker() throws IOException {
+ assert !finished;
+
if (exception != null)
throw exception;
- if (!finished) {
- lz.setFinishing();
+ lz.setFinishing();
+ try {
while (pendingSize > 0) {
lzma.encodeForLZMA2();
writeChunk();
}
out.write(0x00);
- finished = true;
+ } catch (IOException e) {
+ exception = e;
+ throw e;
}
+
+ finished = true;
}
public void flush() throws IOException {
if (exception != null)
throw exception;
- if (!finished) {
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
+
+ try {
lz.setFlushing();
while (pendingSize > 0) {
lzma.encodeForLZMA2();
writeChunk();
}
- }
- out.flush();
+ out.flush();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
}
public void finish() throws IOException {
- writeEndMarker();
- out.finish();
+ if (!finished) {
+ writeEndMarker();
+
+ try {
+ out.finish();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
+
+ finished = true;
+ }
}
public void close() throws IOException {
- writeEndMarker();
- out.close();
+ if (out != null) {
+ if (!finished) {
+ try {
+ writeEndMarker();
+ } catch (IOException e) {}
+ }
+
+ try {
+ out.close();
+ } catch (IOException e) {
+ if (exception == null)
+ exception = e;
+ }
+
+ out = null;
+ }
+
+ if (exception != null)
+ throw exception;
}
}
diff --git a/src/org/tukaani/xz/SimpleOutputStream.java b/src/org/tukaani/xz/SimpleOutputStream.java
index 6938cb8..377a208 100644
--- a/src/org/tukaani/xz/SimpleOutputStream.java
+++ b/src/org/tukaani/xz/SimpleOutputStream.java
@@ -15,7 +15,7 @@ import org.tukaani.xz.simple.SimpleFilter;
class SimpleOutputStream extends FinishableOutputStream {
private static final int TMPBUF_SIZE = 4096;
- private final FinishableOutputStream out;
+ private FinishableOutputStream out;
private final SimpleFilter simpleFilter;
private final byte[] tmpbuf = new byte[TMPBUF_SIZE];
@@ -31,6 +31,9 @@ class SimpleOutputStream extends FinishableOutputStream {
SimpleOutputStream(FinishableOutputStream out,
SimpleFilter simpleFilter) {
+ if (out == null)
+ throw new NullPointerException();
+
this.out = out;
this.simpleFilter = simpleFilter;
}
@@ -49,7 +52,7 @@ class SimpleOutputStream extends FinishableOutputStream {
throw exception;
if (finished)
- throw new XZIOException("Cannot write to a finished stream");
+ throw new XZIOException("Stream finished or closed");
while (len > 0) {
// Copy more unfiltered data into tmpbuf.
@@ -65,7 +68,13 @@ class SimpleOutputStream extends FinishableOutputStream {
unfiltered -= filtered;
// Write out the filtered data.
- out.write(tmpbuf, pos, filtered);
+ try {
+ out.write(tmpbuf, pos, filtered);
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
+
pos += filtered;
// If end of tmpbuf was reached, move the pending unfiltered
@@ -79,13 +88,19 @@ class SimpleOutputStream extends FinishableOutputStream {
}
private void writePending() throws IOException {
+ assert !finished;
+
if (exception != null)
throw exception;
- if (!finished) {
+ try {
out.write(tmpbuf, pos, unfiltered);
- finished = true;
+ } catch (IOException e) {
+ exception = e;
+ throw e;
}
+
+ finished = true;
}
public void flush() throws IOException {
@@ -93,12 +108,43 @@ class SimpleOutputStream extends FinishableOutputStream {
}
public void finish() throws IOException {
- writePending();
- out.finish();
+ if (!finished) {
+ // If it fails, don't call out.finish().
+ writePending();
+
+ try {
+ out.finish();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
+ }
}
public void close() throws IOException {
- writePending();
- out.close();
+ if (out != null) {
+ if (!finished) {
+ // out.close() must be called even if writePending() fails.
+ // writePending() saves the possible exception so we can
+ // ignore exceptions here.
+ try {
+ writePending();
+ } catch (IOException e) {}
+ }
+
+ try {
+ out.close();
+ } catch (IOException e) {
+ // If there is an earlier exception, the exception
+ // from out.close() is lost.
+ if (exception == null)
+ exception = e;
+ }
+
+ out = null;
+ }
+
+ if (exception != null)
+ throw exception;
}
}
diff --git a/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java b/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java
index febac28..c62ced0 100644
--- a/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java
+++ b/src/org/tukaani/xz/UncompressedLZMA2OutputStream.java
@@ -13,7 +13,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
class UncompressedLZMA2OutputStream extends FinishableOutputStream {
- private final FinishableOutputStream out;
+ private FinishableOutputStream out;
private final DataOutputStream outData;
private final byte[] uncompBuf
@@ -21,12 +21,18 @@ class UncompressedLZMA2OutputStream extends FinishableOutputStream {
private int uncompPos = 0;
private boolean dictResetNeeded = true;
+ private boolean finished = false;
+ private IOException exception = null;
+
static int getMemoryUsage() {
// uncompBuf + a little extra
return 70;
}
UncompressedLZMA2OutputStream(FinishableOutputStream out) {
+ if (out == null)
+ throw new NullPointerException();
+
this.out = out;
outData = new DataOutputStream(out);
}
@@ -41,14 +47,25 @@ class UncompressedLZMA2OutputStream extends FinishableOutputStream {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
- while (len > 0) {
- int copySize = Math.min(uncompBuf.length - uncompPos, len);
- System.arraycopy(buf, off, uncompBuf, uncompPos, copySize);
- len -= copySize;
- uncompPos += copySize;
-
- if (uncompPos == uncompBuf.length)
- writeChunk();
+ if (exception != null)
+ throw exception;
+
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
+
+ try {
+ while (len > 0) {
+ int copySize = Math.min(uncompBuf.length - uncompPos, len);
+ System.arraycopy(buf, off, uncompBuf, uncompPos, copySize);
+ len -= copySize;
+ uncompPos += copySize;
+
+ if (uncompPos == uncompBuf.length)
+ writeChunk();
+ }
+ } catch (IOException e) {
+ exception = e;
+ throw e;
}
}
@@ -60,26 +77,76 @@ class UncompressedLZMA2OutputStream extends FinishableOutputStream {
dictResetNeeded = false;
}
+ private void writeEndMarker() throws IOException {
+ if (exception != null)
+ throw exception;
+
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
+
+ try {
+ if (uncompPos > 0)
+ writeChunk();
+
+ out.write(0x00);
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
+ }
+
public void flush() throws IOException {
- if (uncompPos > 0)
- writeChunk();
+ if (exception != null)
+ throw exception;
+
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
+
+ try {
+ if (uncompPos > 0)
+ writeChunk();
- out.flush();
+ out.flush();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
}
public void finish() throws IOException {
- if (uncompPos > 0)
- writeChunk();
+ if (!finished) {
+ writeEndMarker();
+
+ try {
+ out.finish();
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
- out.write(0x00);
- out.finish();
+ finished = true;
+ }
}
public void close() throws IOException {
- if (uncompPos > 0)
- writeChunk();
+ if (out != null) {
+ if (!finished) {
+ try {
+ writeEndMarker();
+ } catch (IOException e) {}
+ }
+
+ try {
+ out.close();
+ } catch (IOException e) {
+ if (exception == null)
+ exception = e;
+ }
+
+ out = null;
+ }
- out.write(0x00);
- out.close();
+ if (exception != null)
+ throw exception;
}
}
diff --git a/src/org/tukaani/xz/XZOutputStream.java b/src/org/tukaani/xz/XZOutputStream.java
index b14142e..77454fe 100644
--- a/src/org/tukaani/xz/XZOutputStream.java
+++ b/src/org/tukaani/xz/XZOutputStream.java
@@ -54,9 +54,9 @@ import org.tukaani.xz.index.IndexEncoder;
*/
public class XZOutputStream extends FinishableOutputStream {
private OutputStream out;
- private StreamFlags streamFlags = new StreamFlags();
- private Check check;
- private IndexEncoder index = new IndexEncoder();
+ private final StreamFlags streamFlags = new StreamFlags();
+ private final Check check;
+ private final IndexEncoder index = new IndexEncoder();
private BlockOutputStream blockEncoder = null;
private FilterEncoder[] filters;
@@ -68,12 +68,6 @@ public class XZOutputStream extends FinishableOutputStream {
*/
private boolean filtersSupportFlushing;
- /**
- * True if <code>flush()</code> should use <code>flushBlock()</code>
- * even if the filter chain does support flushing.
- */
- private boolean alwaysFlushBlock = false;
-
private IOException exception = null;
private boolean finished = false;
@@ -274,14 +268,11 @@ public class XZOutputStream extends FinishableOutputStream {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
- if (len == 0)
- return;
-
if (exception != null)
throw exception;
if (finished)
- throw new XZIOException("Cannot write to a finished stream");
+ throw new XZIOException("Stream finished or closed");
try {
if (blockEncoder == null)
@@ -295,13 +286,40 @@ public class XZOutputStream extends FinishableOutputStream {
}
/**
- * Finishes the current XZ Block. This is a helper for flushBlock()
- * and finish().
+ * Finishes the current XZ Block (but not the whole XZ Stream).
+ * This doesn't flush the stream so it's possible that not all data will
+ * be decompressible from the output stream when this function returns.
+ * Call also <code>flush()</code> if flushing is wanted in addition to
+ * finishing the current XZ Block.
+ * <p>
+ * If there is no unfinished Block open, this function will do nothing.
+ * (No empty XZ Block will be created.)
+ * <p>
+ * This function can be useful, for example, to create
+ * random-accessible .xz files.
+ * <p>
+ * Starting a new XZ Block means that the encoder state is reset.
+ * Doing this very often will increase the size of the compressed
+ * file a lot (more than plain <code>flush()</code> would do).
+ *
+ * @throws XZIOException
+ * XZ Stream has grown too big
+ *
+ * @throws XZIOException
+ * stream finished or closed
+ *
+ * @throws IOException may be thrown by the underlying output stream
*/
- private void endBlock() throws IOException {
+ public void endBlock() throws IOException {
if (exception != null)
throw exception;
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
+
+ // NOTE: Once there is threading with multiple Blocks, it's possible
+ // that this function will be more like a barrier that returns
+ // before the last Block has been finished.
if (blockEncoder != null) {
try {
blockEncoder.finish();
@@ -316,29 +334,6 @@ public class XZOutputStream extends FinishableOutputStream {
}
/**
- * Finishes the current XZ Block (but not the whole XZ Stream) and
- * calls <code>out.flush()</code>.
- * All buffered pending data will then be decompressible from
- * the output stream. If there is no unfinished Block open,
- * no empty Block will be created.
- * <p>
- * <code>flushBlock()</code> resets the encoder state so there will be
- * a bigger penalty in compressed file size than with <code>flush()</code>.
- * <p>
- * <code>flushBlock()</code> can be useful, for example, to create
- * random-accessible .xz files.
- *
- * @throws XZIOException
- * XZ Stream has grown too big
- *
- * @throws IOException may be thrown by the underlying output stream
- */
- public void flushBlock() throws IOException {
- endBlock();
- out.flush();
- }
-
- /**
* Flushes the encoder and calls <code>out.flush()</code>.
* All buffered pending data will then be decompressible from
* the output stream.
@@ -349,45 +344,41 @@ public class XZOutputStream extends FinishableOutputStream {
* smaller penalty with flushing than BT4.
* <p>
* Some filters don't support flushing. If the filter chain has
- * such a filter, <code>flush()</code> is equivalent to
- * <code>flushBlock()</code>.
- * <p>
- * If <code>setBlockFlushing(true)</code> has been used,
- * <code>flush()</code> is equivalent to <code>flushBlock()</code>
- * even if the filter chain does support flushing.
+ * such a filter, <code>flush()</code> will call <code>endBlock()</code>
+ * before flushing.
*
* @throws XZIOException
* XZ Stream has grown too big
*
+ * @throws XZIOException
+ * stream finished or closed
+ *
* @throws IOException may be thrown by the underlying output stream
*/
public void flush() throws IOException {
if (exception != null)
throw exception;
- if (!filtersSupportFlushing || alwaysFlushBlock)
- flushBlock();
- else if (blockEncoder != null)
- blockEncoder.flush(); // This also calls out.flush().
- else
- out.flush();
- }
+ if (finished)
+ throw new XZIOException("Stream finished or closed");
- /**
- * Sets the default flushing mode for <code>flush()</code>.
- * <p>
- * Calling <code>setBlockFlushing(true)</code> will make
- * <code>flush()</code> equivalent to <code>flushBlock()</code>
- * even with filter chains that support flushing. Calling
- * <code>setBlockFlushing(false)</code> will restore the default
- * behavior.
- * <p>
- * This function is rarely useful. Normally you should use
- * <code>flushBlock()</code> directly if you want to start
- * a new XZ Block.
- */
- public void setBlockFlushing(boolean flushFlushesBlock) {
- alwaysFlushBlock = flushFlushesBlock;
+ try {
+ if (blockEncoder != null) {
+ if (filtersSupportFlushing) {
+ // This will eventually call out.flush() so
+ // no need to do it here again.
+ blockEncoder.flush();
+ } else {
+ endBlock();
+ out.flush();
+ }
+ } else {
+ out.flush();
+ }
+ } catch (IOException e) {
+ exception = e;
+ throw e;
+ }
}
/**
@@ -417,11 +408,15 @@ public class XZOutputStream extends FinishableOutputStream {
try {
index.encode(out);
encodeStreamFooter();
- finished = true;
} catch (IOException e) {
exception = e;
throw e;
}
+
+ // Set it to true only if everything goes fine. Setting it earlier
+ // would cause repeated calls to finish() do nothing instead of
+ // throwing an exception to indicate an earlier error.
+ finished = true;
}
}
@@ -438,20 +433,24 @@ public class XZOutputStream extends FinishableOutputStream {
* @throws IOException may be thrown by the underlying output stream
*/
public void close() throws IOException {
- // If finish() throws an exception, it stores the exception to
- // the variable "exception". So we can ignore the possible
- // exception here.
- try {
- finish();
- } catch (IOException e) {}
+ if (out != null) {
+ // If finish() throws an exception, it stores the exception to
+ // the variable "exception". So we can ignore the possible
+ // exception here.
+ try {
+ finish();
+ } catch (IOException e) {}
- try {
- out.close();
- } catch (IOException e) {
- // Remember the exception but only if there is no previous
- // pending exception.
- if (exception == null)
- exception = e;
+ try {
+ out.close();
+ } catch (IOException e) {
+ // Remember the exception but only if there is no previous
+ // pending exception.
+ if (exception == null)
+ exception = e;
+ }
+
+ out = null;
}
if (exception != null)