/* | 
|
 * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved. | 
|
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | 
|
 * | 
|
 * This code is free software; you can redistribute it and/or modify it | 
|
 * under the terms of the GNU General Public License version 2 only, as | 
|
 * published by the Free Software Foundation.  Oracle designates this | 
|
 * particular file as subject to the "Classpath" exception as provided | 
|
 * by Oracle in the LICENSE file that accompanied this code. | 
|
 * | 
|
 * This code is distributed in the hope that it will be useful, but WITHOUT | 
|
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License | 
|
 * version 2 for more details (a copy is included in the LICENSE file that | 
|
 * accompanied this code). | 
|
 * | 
|
 * You should have received a copy of the GNU General Public License version | 
|
 * 2 along with this work; if not, write to the Free Software Foundation, | 
|
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | 
|
 * | 
|
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | 
|
 * or visit www.oracle.com if you need additional information or have any | 
|
 * questions. | 
|
*/  | 
|
package java.io;  | 
|
/** | 
|
 * A piped input stream should be connected | 
|
 * to a piped output stream; the piped  input | 
|
 * stream then provides whatever data bytes | 
|
 * are written to the piped output  stream. | 
|
 * Typically, data is read from a <code>PipedInputStream</code> | 
|
 * object by one thread  and data is written | 
|
 * to the corresponding <code>PipedOutputStream</code> | 
|
 * by some  other thread. Attempting to use | 
|
 * both objects from a single thread is not | 
|
 * recommended, as it may deadlock the thread. | 
|
 * The piped input stream contains a buffer, | 
|
 * decoupling read operations from write operations, | 
|
 * within limits. | 
|
 * A pipe is said to be <a name="BROKEN"> <i>broken</i> </a> if a | 
|
 * thread that was providing data bytes to the connected | 
|
 * piped output stream is no longer alive. | 
|
 * | 
|
 * @author  James Gosling | 
|
 * @see     java.io.PipedOutputStream | 
|
 * @since   JDK1.0 | 
|
*/  | 
|
public class PipedInputStream extends InputStream {  | 
|
boolean closedByWriter = false;  | 
|
volatile boolean closedByReader = false;  | 
|
boolean connected = false;  | 
|
        /* REMIND: identification of the read and write sides needs to be | 
|
           more sophisticated.  Either using thread groups (but what about | 
|
           pipes within a thread?) or using finalization (but it may be a | 
|
long time until the next GC). */  | 
|
Thread readSide;  | 
|
Thread writeSide;  | 
|
private static final int DEFAULT_PIPE_SIZE = 1024;  | 
|
/**  | 
|
* The default size of the pipe's circular input buffer.  | 
|
* @since JDK1.1  | 
|
*/  | 
|
// This used to be a constant before the pipe size was allowed  | 
|
// to change. This field will continue to be maintained  | 
|
    // for backward compatibility. | 
|
protected static final int PIPE_SIZE = DEFAULT_PIPE_SIZE;  | 
|
    /** | 
|
     * The circular buffer into which incoming data is placed. | 
|
     * @since   JDK1.1 | 
|
*/  | 
|
protected byte buffer[];  | 
|
    /** | 
|
     * The index of the position in the circular buffer at which the | 
|
     * next byte of data will be stored when received from the connected | 
|
     * piped output stream. <code>in<0</code> implies the buffer is empty, | 
|
     * <code>in==out</code> implies the buffer is full | 
|
     * @since   JDK1.1 | 
|
*/  | 
|
protected int in = -1;  | 
|
    /** | 
|
     * The index of the position in the circular buffer at which the next | 
|
     * byte of data will be read by this piped input stream. | 
|
     * @since   JDK1.1 | 
|
*/  | 
|
protected int out = 0;  | 
|
    /** | 
|
     * Creates a <code>PipedInputStream</code> so | 
|
     * that it is connected to the piped output | 
|
     * stream <code>src</code>. Data bytes written | 
|
     * to <code>src</code> will then be  available | 
|
     * as input from this stream. | 
|
     * | 
|
     * @param      src   the stream to connect to. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
*/  | 
|
public PipedInputStream(PipedOutputStream src) throws IOException {  | 
|
this(src, DEFAULT_PIPE_SIZE);  | 
|
}  | 
|
    /** | 
|
     * Creates a <code>PipedInputStream</code> so that it is | 
|
     * connected to the piped output stream | 
|
     * <code>src</code> and uses the specified pipe size for | 
|
     * the pipe's buffer. | 
|
     * Data bytes written to <code>src</code> will then | 
|
     * be available as input from this stream. | 
|
     * | 
|
     * @param      src   the stream to connect to. | 
|
     * @param      pipeSize the size of the pipe's buffer. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @exception  IllegalArgumentException if {@code pipeSize <= 0}. | 
|
     * @since      1.6 | 
|
*/  | 
|
public PipedInputStream(PipedOutputStream src, int pipeSize)  | 
|
throws IOException {  | 
|
initPipe(pipeSize);  | 
|
connect(src);  | 
|
}  | 
|
    /** | 
|
     * Creates a <code>PipedInputStream</code> so | 
|
     * that it is not yet {@linkplain #connect(java.io.PipedOutputStream) | 
|
     * connected}. | 
|
     * It must be {@linkplain java.io.PipedOutputStream#connect( | 
|
     * java.io.PipedInputStream) connected} to a | 
|
     * <code>PipedOutputStream</code> before being used. | 
|
*/  | 
|
    public PipedInputStream() { | 
|
initPipe(DEFAULT_PIPE_SIZE);  | 
|
}  | 
|
    /** | 
|
     * Creates a <code>PipedInputStream</code> so that it is not yet | 
|
     * {@linkplain #connect(java.io.PipedOutputStream) connected} and | 
|
     * uses the specified pipe size for the pipe's buffer. | 
|
     * It must be {@linkplain java.io.PipedOutputStream#connect( | 
|
     * java.io.PipedInputStream) | 
|
     * connected} to a <code>PipedOutputStream</code> before being used. | 
|
     * | 
|
     * @param      pipeSize the size of the pipe's buffer. | 
|
     * @exception  IllegalArgumentException if {@code pipeSize <= 0}. | 
|
     * @since      1.6 | 
|
*/  | 
|
    public PipedInputStream(int pipeSize) { | 
|
initPipe(pipeSize);  | 
|
}  | 
|
    private void initPipe(int pipeSize) { | 
|
if (pipeSize <= 0) {  | 
|
throw new IllegalArgumentException("Pipe Size <= 0");  | 
|
}  | 
|
buffer = new byte[pipeSize];  | 
|
}  | 
|
    /** | 
|
     * Causes this piped input stream to be connected | 
|
     * to the piped  output stream <code>src</code>. | 
|
     * If this object is already connected to some | 
|
     * other piped output  stream, an <code>IOException</code> | 
|
     * is thrown. | 
|
     * <p> | 
|
     * If <code>src</code> is an | 
|
     * unconnected piped output stream and <code>snk</code> | 
|
     * is an unconnected piped input stream, they | 
|
     * may be connected by either the call: | 
|
     * | 
|
     * <pre><code>snk.connect(src)</code> </pre> | 
|
     * <p> | 
|
     * or the call: | 
|
     * | 
|
     * <pre><code>src.connect(snk)</code> </pre> | 
|
     * <p> | 
|
     * The two calls have the same effect. | 
|
     * | 
|
     * @param      src   The piped output stream to connect to. | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
*/  | 
|
public void connect(PipedOutputStream src) throws IOException {  | 
|
src.connect(this);  | 
|
}  | 
|
    /** | 
|
     * Receives a byte of data.  This method will block if no input is | 
|
     * available. | 
|
     * @param b the byte being received | 
|
     * @exception IOException If the pipe is <a href="#BROKEN"> <code>broken</code></a>, | 
|
     *          {@link #connect(java.io.PipedOutputStream) unconnected}, | 
|
     *          closed, or if an I/O error occurs. | 
|
     * @since     JDK1.1 | 
|
*/  | 
|
protected synchronized void receive(int b) throws IOException {  | 
|
checkStateForReceive();  | 
|
writeSide = Thread.currentThread();  | 
|
if (in == out)  | 
|
awaitSpace();  | 
|
if (in < 0) {  | 
|
in = 0;  | 
|
out = 0;  | 
|
}  | 
|
buffer[in++] = (byte)(b & 0xFF);  | 
|
if (in >= buffer.length) {  | 
|
in = 0;  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Receives data into an array of bytes.  This method will | 
|
     * block until some input is available. | 
|
     * @param b the buffer into which the data is received | 
|
     * @param off the start offset of the data | 
|
     * @param len the maximum number of bytes received | 
|
     * @exception IOException If the pipe is <a href="#BROKEN"> broken</a>, | 
|
     *           {@link #connect(java.io.PipedOutputStream) unconnected}, | 
|
     *           closed,or if an I/O error occurs. | 
|
*/  | 
|
synchronized void receive(byte b[], int off, int len) throws IOException {  | 
|
checkStateForReceive();  | 
|
writeSide = Thread.currentThread();  | 
|
int bytesToTransfer = len;  | 
|
while (bytesToTransfer > 0) {  | 
|
if (in == out)  | 
|
awaitSpace();  | 
|
int nextTransferAmount = 0;  | 
|
if (out < in) {  | 
|
nextTransferAmount = buffer.length - in;  | 
|
} else if (in < out) {  | 
|
if (in == -1) {  | 
|
in = out = 0;  | 
|
nextTransferAmount = buffer.length - in;  | 
|
                } else { | 
|
nextTransferAmount = out - in;  | 
|
}  | 
|
}  | 
|
if (nextTransferAmount > bytesToTransfer)  | 
|
nextTransferAmount = bytesToTransfer;  | 
|
assert(nextTransferAmount > 0);  | 
|
System.arraycopy(b, off, buffer, in, nextTransferAmount);  | 
|
bytesToTransfer -= nextTransferAmount;  | 
|
off += nextTransferAmount;  | 
|
in += nextTransferAmount;  | 
|
if (in >= buffer.length) {  | 
|
in = 0;  | 
|
}  | 
|
}  | 
|
}  | 
|
private void checkStateForReceive() throws IOException {  | 
|
if (!connected) {  | 
|
throw new IOException("Pipe not connected");  | 
|
} else if (closedByWriter || closedByReader) {  | 
|
throw new IOException("Pipe closed");  | 
|
} else if (readSide != null && !readSide.isAlive()) {  | 
|
throw new IOException("Read end dead");  | 
|
}  | 
|
}  | 
|
private void awaitSpace() throws IOException {  | 
|
while (in == out) {  | 
|
checkStateForReceive();  | 
|
            /* full: kick any waiting readers */ | 
|
notifyAll();  | 
|
            try { | 
|
wait(1000);  | 
|
} catch (InterruptedException ex) {  | 
|
throw new java.io.InterruptedIOException();  | 
|
}  | 
|
}  | 
|
}  | 
|
    /** | 
|
     * Notifies all waiting threads that the last byte of data has been | 
|
     * received. | 
|
*/  | 
|
    synchronized void receivedLast() { | 
|
closedByWriter = true;  | 
|
notifyAll();  | 
|
}  | 
|
    /** | 
|
     * Reads the next byte of data from this piped input stream. The | 
|
     * value byte is returned as an <code>int</code> in the range | 
|
     * <code>0</code> to <code>255</code>. | 
|
     * This method blocks until input data is available, the end of the | 
|
     * stream is detected, or an exception is thrown. | 
|
     * | 
|
     * @return     the next byte of data, or <code>-1</code> if the end of the | 
|
     *             stream is reached. | 
|
     * @exception  IOException  if the pipe is | 
|
     *           {@link #connect(java.io.PipedOutputStream) unconnected}, | 
|
     *           <a href="#BROKEN"> <code>broken</code></a>, closed, | 
|
     *           or if an I/O error occurs. | 
|
*/  | 
|
public synchronized int read() throws IOException {  | 
|
if (!connected) {  | 
|
throw new IOException("Pipe not connected");  | 
|
} else if (closedByReader) {  | 
|
throw new IOException("Pipe closed");  | 
|
} else if (writeSide != null && !writeSide.isAlive()  | 
|
&& !closedByWriter && (in < 0)) {  | 
|
throw new IOException("Write end dead");  | 
|
}  | 
|
readSide = Thread.currentThread();  | 
|
int trials = 2;  | 
|
while (in < 0) {  | 
|
if (closedByWriter) {  | 
|
                /* closed by writer, return EOF */ | 
|
return -1;  | 
|
}  | 
|
if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {  | 
|
throw new IOException("Pipe broken");  | 
|
}  | 
|
            /* might be a writer waiting */ | 
|
notifyAll();  | 
|
            try { | 
|
wait(1000);  | 
|
} catch (InterruptedException ex) {  | 
|
throw new java.io.InterruptedIOException();  | 
|
}  | 
|
}  | 
|
int ret = buffer[out++] & 0xFF;  | 
|
if (out >= buffer.length) {  | 
|
out = 0;  | 
|
}  | 
|
if (in == out) {  | 
|
            /* now empty */ | 
|
in = -1;  | 
|
}  | 
|
return ret;  | 
|
}  | 
|
    /** | 
|
     * Reads up to <code>len</code> bytes of data from this piped input | 
|
     * stream into an array of bytes. Less than <code>len</code> bytes | 
|
     * will be read if the end of the data stream is reached or if | 
|
     * <code>len</code> exceeds the pipe's buffer size. | 
|
     * If <code>len </code> is zero, then no bytes are read and 0 is returned; | 
|
     * otherwise, the method blocks until at least 1 byte of input is | 
|
     * available, end of the stream has been detected, or an exception is | 
|
     * thrown. | 
|
     * | 
|
     * @param      b     the buffer into which the data is read. | 
|
     * @param      off   the start offset in the destination array <code>b</code> | 
|
     * @param      len   the maximum number of bytes read. | 
|
     * @return     the total number of bytes read into the buffer, or | 
|
     *             <code>-1</code> if there is no more data because the end of | 
|
     *             the stream has been reached. | 
|
     * @exception  NullPointerException If <code>b</code> is <code>null</code>. | 
|
     * @exception  IndexOutOfBoundsException If <code>off</code> is negative, | 
|
     * <code>len</code> is negative, or <code>len</code> is greater than | 
|
     * <code>b.length - off</code> | 
|
     * @exception  IOException if the pipe is <a href="#BROKEN"> <code>broken</code></a>, | 
|
     *           {@link #connect(java.io.PipedOutputStream) unconnected}, | 
|
     *           closed, or if an I/O error occurs. | 
|
*/  | 
|
public synchronized int read(byte b[], int off, int len) throws IOException {  | 
|
if (b == null) {  | 
|
throw new NullPointerException();  | 
|
} else if (off < 0 || len < 0 || len > b.length - off) {  | 
|
throw new IndexOutOfBoundsException();  | 
|
} else if (len == 0) {  | 
|
return 0;  | 
|
}  | 
|
        /* possibly wait on the first character */ | 
|
int c = read();  | 
|
if (c < 0) {  | 
|
return -1;  | 
|
}  | 
|
b[off] = (byte) c;  | 
|
int rlen = 1;  | 
|
while ((in >= 0) && (len > 1)) {  | 
|
int available;  | 
|
if (in > out) {  | 
|
available = Math.min((buffer.length - out), (in - out));  | 
|
            } else { | 
|
available = buffer.length - out;  | 
|
}  | 
|
            // A byte is read beforehand outside the loop | 
|
if (available > (len - 1)) {  | 
|
available = len - 1;  | 
|
}  | 
|
System.arraycopy(buffer, out, b, off + rlen, available);  | 
|
out += available;  | 
|
rlen += available;  | 
|
len -= available;  | 
|
if (out >= buffer.length) {  | 
|
out = 0;  | 
|
}  | 
|
if (in == out) {  | 
|
                /* now empty */ | 
|
in = -1;  | 
|
}  | 
|
}  | 
|
return rlen;  | 
|
}  | 
|
    /** | 
|
     * Returns the number of bytes that can be read from this input | 
|
     * stream without blocking. | 
|
     * | 
|
     * @return the number of bytes that can be read from this input stream | 
|
     *         without blocking, or {@code 0} if this input stream has been | 
|
     *         closed by invoking its {@link #close()} method, or if the pipe | 
|
     *         is {@link #connect(java.io.PipedOutputStream) unconnected}, or | 
|
     *          <a href="#BROKEN"> <code>broken</code></a>. | 
|
     * | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
     * @since   JDK1.0.2 | 
|
*/  | 
|
public synchronized int available() throws IOException {  | 
|
if(in < 0)  | 
|
return 0;  | 
|
else if(in == out)  | 
|
return buffer.length;  | 
|
else if (in > out)  | 
|
return in - out;  | 
|
else  | 
|
return in + buffer.length - out;  | 
|
}  | 
|
    /** | 
|
     * Closes this piped input stream and releases any system resources | 
|
     * associated with the stream. | 
|
     * | 
|
     * @exception  IOException  if an I/O error occurs. | 
|
*/  | 
|
public void close() throws IOException {  | 
|
closedByReader = true;  | 
|
        synchronized (this) { | 
|
in = -1;  | 
|
}  | 
|
}  | 
|
}  |