blob: 771b1fb29ffaeb95115345ae5e5b197fbe6de3e9 [file] [log] [blame]
/*
* SimpleOutputStream
*
* Author: Lasse Collin <lasse.collin@tukaani.org>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
*/
package org.tukaani.xz;
import java.io.IOException;
import org.tukaani.xz.simple.SimpleFilter;
class SimpleOutputStream extends FinishableOutputStream {
private static final int FILTER_BUF_SIZE = 4096;
private FinishableOutputStream out;
private final SimpleFilter simpleFilter;
private final byte[] filterBuf = new byte[FILTER_BUF_SIZE];
private int pos = 0;
private int unfiltered = 0;
private IOException exception = null;
private boolean finished = false;
private final byte[] tempBuf = new byte[1];
static int getMemoryUsage() {
return 1 + FILTER_BUF_SIZE / 1024;
}
SimpleOutputStream(FinishableOutputStream out,
SimpleFilter simpleFilter) {
if (out == null)
throw new NullPointerException();
this.out = out;
this.simpleFilter = simpleFilter;
}
public void write(int b) throws IOException {
tempBuf[0] = (byte)b;
write(tempBuf, 0, 1);
}
public void write(byte[] buf, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len < 0 || off + len > buf.length)
throw new IndexOutOfBoundsException();
if (exception != null)
throw exception;
if (finished)
throw new XZIOException("Stream finished or closed");
while (len > 0) {
// Copy more unfiltered data into filterBuf.
int copySize = Math.min(len, FILTER_BUF_SIZE - (pos + unfiltered));
System.arraycopy(buf, off, filterBuf, pos + unfiltered, copySize);
off += copySize;
len -= copySize;
unfiltered += copySize;
// Filter the data in filterBuf.
int filtered = simpleFilter.code(filterBuf, pos, unfiltered);
assert filtered <= unfiltered;
unfiltered -= filtered;
// Write out the filtered data.
try {
out.write(filterBuf, pos, filtered);
} catch (IOException e) {
exception = e;
throw e;
}
pos += filtered;
// If end of filterBuf was reached, move the pending unfiltered
// data to the beginning of the buffer so that more data can
// be copied into filterBuf on the next loop iteration.
if (pos + unfiltered == FILTER_BUF_SIZE) {
System.arraycopy(filterBuf, pos, filterBuf, 0, unfiltered);
pos = 0;
}
}
}
private void writePending() throws IOException {
assert !finished;
if (exception != null)
throw exception;
try {
out.write(filterBuf, pos, unfiltered);
} catch (IOException e) {
exception = e;
throw e;
}
finished = true;
}
public void flush() throws IOException {
throw new UnsupportedOptionsException("Flushing is not supported");
}
public void finish() throws IOException {
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 {
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;
}
}