uk.ac.starlink.util.FastPipedOutputStream Maven / Gradle / Ivy
Show all versions of stil Show documentation
package uk.ac.starlink.util;
import java.io.IOException;
import java.io.OutputStream;
/**
* This class is equivalent to {@link java.io.PipedOutputStream}. In the
* interface it only adds a constructor which allows for specifying the buffer
* size. Its implementation, however, is much simpler and a lot more efficient
* than its equivalent. It doesn't rely on polling. Instead it uses proper
* synchronization with its counterpart be.re.io.PipedInputStream
.
*
* Multiple writers can write in this stream concurrently. The block written
* by a writer is put in completely. Other writers can't come in between.
*
* This class was pinched directly from the improved suggestion for
* Sun's PipedOutputStream in Sun's bug report bug #4404700
*
* @author WD
* @see
* @see FastPipedInputStream
*/
class FastPipedOutputStream extends OutputStream
{
FastPipedInputStream sink;
/**
* Creates an unconnected FastPipedOutputStream.
*/
public
FastPipedOutputStream() throws IOException
{
this(null);
}
/**
* Creates a PipedOutputStream with a default buffer size and connects it to
* sink
.
* @exception IOException It was already connected.
*/
public
FastPipedOutputStream(FastPipedInputStream sink) throws IOException
{
this(sink, 0x10000);
}
/**
* Creates a PipedOutputStream with buffer size bufferSize
and
* connects it to sink
.
* @exception IOException It was already connected.
*/
public
FastPipedOutputStream(FastPipedInputStream sink, int bufferSize) throws IOException
{
if (sink != null)
{
connect(sink);
sink.buffer = new byte[bufferSize];
}
}
/**
* @exception IOException The pipe is not connected.
*/
public void
close() throws IOException
{
if (sink == null)
{
throw new IOException("Unconnected pipe");
}
synchronized (sink.buffer)
{
sink.closed = true;
flush();
}
}
/**
* @exception IOException The pipe is already connected.
*/
public void
connect(FastPipedInputStream sink) throws IOException
{
if (this.sink != null)
{
throw new IOException("Pipe already connected");
}
this.sink = sink;
sink.source = this;
}
protected void
finalize() throws Throwable
{
close();
}
public void
flush() throws IOException
{
synchronized (sink.buffer)
{
// Release all readers.
sink.buffer.notifyAll();
}
}
public void
write(int b) throws IOException
{
write(new byte[] {(byte) b});
}
public void
write(byte[] b) throws IOException
{
write(b, 0, b.length);
}
/**
* @exception IOException The pipe is not connected or a reader has closed
it.
*/
public void
write(byte[] b, int off, int len) throws IOException
{
if (sink == null)
{
throw new IOException("Unconnected pipe");
}
if (sink.closed)
{
throw new IOException("Broken pipe");
}
synchronized (sink.buffer)
{
if
(
sink.writePosition == sink.readPosition &&
sink.writeLaps > sink.readLaps
)
{
// The circular buffer is full, so wait for some reader to consume
// something.
try
{
sink.buffer.wait();
}
catch (InterruptedException e)
{
throw new IOException(e.getMessage());
}
// Try again.
write(b, off, len);
return;
}
// Don't write more than the capacity indicated by len or the space
// available in the circular buffer.
int amount =
Math.min
(
len,
(
sink.writePosition < sink.readPosition ?
sink.readPosition : sink.buffer.length
) - sink.writePosition
);
System.arraycopy(b, off, sink.buffer, sink.writePosition, amount);
sink.writePosition += amount;
if (sink.writePosition == sink.buffer.length)
{
sink.writePosition = 0;
++sink.writeLaps;
}
// The buffer is only released when the complete desired block was
// written.
if (amount < len)
{
write(b, off + amount, len - amount);
}
else
{
sink.buffer.notifyAll();
}
}
}
}