/**************************************************************************
* Copyright (c) 2001 by Punch Telematix. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix nor the names of *
* other contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission.*
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE *
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, *
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE *
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN *
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
/*
** $Id: PipedInputStream.java,v 1.1.1.1 2004/07/12 14:07:45 cvs Exp $
*/
package java.io;
public class PipedInputStream extends InputStream {
/** The (default) size of the pipe buffer.
*/
protected static final int PIPE_SIZE = 1024;
/** The buffer in which bytes are stored.
*/
protected byte[] buffer = new byte[PIPE_SIZE];
private byte[] onebuffer = new byte[1];
/** The index where the next byte will be stored.
*/
protected int in;
/** The index where the next byte will be retrieved.
*/
protected int out;
/** The PipedOutputStream to which we are connected.
*/
PipedOutputStream src;
private Thread producer;
private Thread consumer;
int readable;
int storable = PIPE_SIZE;
/** When 'consumerclosed' is true, the pipe is really closed.
*/
private boolean consumerclosed;
volatile boolean producerclosed;
public PipedInputStream(PipedOutputStream src) throws IOException {
connect(src);
}
public PipedInputStream() {}
public void connect(PipedOutputStream src) throws IOException {
if (this.src == null && src.dst == null) {
this.src = src;
src.dst = this;
}
else {
throw new IOException("allready connected");
}
}
public synchronized int available() throws IOException {
if (this.consumerclosed) {
return 0;
}
else {
return this.readable;
}
}
public void close() throws IOException {
this.consumerclosed = true;
}
public synchronized int read(byte[] buffer, int offset, int count)
throws IOException {
int result = 0;
int chunk;
if (this.consumerclosed) {
throw new IOException("closed");
}
if (src == null) {
throw new IOException("unconnected");
}
if (this.consumer == null) {
this.consumer = Thread.currentThread();
}
if (count < 0 || offset < 0) {
throw new ArrayIndexOutOfBoundsException();
}
if (count == 0) {
return 0;
}
try {
while (this.readable == 0) {
this.wait(500);
if (this.readable == 0) {
if (this.producerclosed) {
return -1;
}
if (this.producer != null && !this.producer.isAlive()) {
throw new IOException("no producer " + producer + " Alive ? "
+ producer.isAlive());
} else if (this.producerclosed) {
break;
}
} else {
break;
}
}
while (this.readable > 0 && count > 0) {
if (in <= out) {
chunk = PIPE_SIZE - out;
} else {
chunk = in - out;
}
if (chunk > count) {
chunk = count;
}
System.arraycopy(this.buffer, out, buffer, offset, chunk);
offset += chunk;
count -= chunk;
this.readable -= chunk;
this.storable += chunk;
result += chunk;
out += chunk;
if (out == PIPE_SIZE) {
out = 0;
}
if (out == in) {
out = 0;
in = -1;
}
}
} catch (InterruptedException ie) {
throw new InterruptedIOException();
}
this.notify();
return (result == 0) ? -1 : result;
}
public synchronized int read() throws IOException {
int result = this.read(this.onebuffer, 0, 1);
if (result == 1) {
return (0x000000ff & this.onebuffer[0]);
}
else {
return -1;
}
}
synchronized void receive(byte[] buffer, int offset, int count) throws IOException {
int chunk;
if (this.consumerclosed) {
throw new IOException("closed");
}
if (this.producer == null) {
this.producer = Thread.currentThread();
}
if (this.consumer != null && !this.consumer.isAlive()) {
this.consumerclosed = true;
throw new IOException("consumer dead");
}
while (count > 0) {
try {
/*
** We check wether there is space for writing bytes and if not, we notify the consumer
** that he should read a bit to clear up some space. After each wait pause, we see if either
** the pipe has not been closed or the consumer, if there is allready one, is still alive. If
** that is not the case, we close the pipe and throw IOException. Note that when 'storable'
** really is 0, we do an implied flush operation by setting 'readable' to 'storable'.
*/
while (this.storable == 0) {
this.readable = PIPE_SIZE;
this.notify();
this.wait(500);
if (this.consumerclosed) {
throw new IOException("closed");
}
if (this.consumer != null && ! this.consumer.isAlive()) {
this.consumerclosed = true;
throw new IOException("consumer dead");
}
}
/*
** See how many bytes we can write, taking wraparound in the stream buffer
** into account. First see if we have an empty pipe (in < 0) and than see
** if the 'in' index has wrapped around allready or not, adjusting the length
** of the chunk we can copy in one go.
**
** Note that we don't change the 'readable' field nor do we invoke a notify;
** when the while loop above has been taken, a notify and change of 'readable'
** has been issued allready, otherwise, we leave it up to the flush operation
** of the PipedOutputStream to issue a notify and change of 'readable'.
*/
if (this.in < 0) {
this.in = 0;
}
if (in < out) {
chunk = out - in;
}
else {
chunk = PIPE_SIZE - in;
}
if (count < chunk) {
chunk = count;
}
System.arraycopy(buffer, offset, this.buffer, in, chunk);
offset += chunk;
count -= chunk;
this.readable += chunk;
this.storable -= chunk;
this.in += chunk;
if (this.in == PIPE_SIZE) {
this.in = 0;
}
}
catch (InterruptedException ie) {
throw new InterruptedIOException();
}
}
}
protected synchronized void receive(int oneByte) throws IOException {
this.onebuffer[0] = (byte) (oneByte & 0xff);
this.receive(this.onebuffer, 0, 1);
}
}