aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/io/PipedInputStream.java
diff options
context:
space:
mode:
authorTom Tromey <tromey@cygnus.com>2000-04-21 01:18:16 +0000
committerTom Tromey <tromey@gcc.gnu.org>2000-04-21 01:18:16 +0000
commitd3474943bf309f085d515d7b20cb80c1d50e14cd (patch)
treedec5c6d7c3d3c314853e857fd39856ceb50ec5db /libjava/java/io/PipedInputStream.java
parent21caf59006e2c7dd06bf9840a7fc3cea321282f7 (diff)
downloadgcc-d3474943bf309f085d515d7b20cb80c1d50e14cd.zip
gcc-d3474943bf309f085d515d7b20cb80c1d50e14cd.tar.gz
gcc-d3474943bf309f085d515d7b20cb80c1d50e14cd.tar.bz2
Fix for PR java.io/204:
* java/io/PipedInputStream.java, java/io/PipedReader.java, java/io/PipedOutputStream.java, java/io/PipedWriter.java: Imported from Classpath. From-SVN: r33300
Diffstat (limited to 'libjava/java/io/PipedInputStream.java')
-rw-r--r--libjava/java/io/PipedInputStream.java732
1 files changed, 520 insertions, 212 deletions
diff --git a/libjava/java/io/PipedInputStream.java b/libjava/java/io/PipedInputStream.java
index d2dbecb..d1081a2 100644
--- a/libjava/java/io/PipedInputStream.java
+++ b/libjava/java/io/PipedInputStream.java
@@ -1,242 +1,550 @@
-/* Copyright (C) 1998, 1999 Free Software Foundation
+/* PipedInputStream.java -- Input stream that reads from an output stream
+ Copyright (C) 1998, 1999 Free Software Foundation, Inc.
- This file is part of libgcj.
+This file is part of GNU Classpath.
-This software is copyrighted work licensed under the terms of the
-Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
-details. */
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+As a special exception, if you link this library with other files to
+produce an executable, this library does not by itself cause the
+resulting executable to be covered by the GNU General Public License.
+This exception does not however invalidate any other reasons why the
+executable file might be covered by the GNU General Public License. */
+
+
package java.io;
/**
- * @author Warren Levy <warrenl@cygnus.com>
- * @date October 29, 1998.
+ * This class is an input stream that reads its bytes from an output stream
+ * to which it is connected.
+ * <p>
+ * Data is read and written to an internal buffer. It is highly recommended
+ * that the <code>PipedInputStream</code> and connected <code>PipedOutputStream</code>
+ * be part of different threads. If they are not, there is a possibility
+ * that the read and write operations could deadlock their thread.
+ *
+ * @version 0.0
+ *
+ * @author Aaron M. Renn (arenn@urbanophile.com)
+ */
+public class PipedInputStream extends InputStream
+{
+
+/*************************************************************************/
+
+/*
+ * Class Variables
*/
-/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3
- * "The Java Language Specification", ISBN 0-201-63451-1
- * plus online API docs for JDK 1.2 beta from http://www.javasoft.com.
- * Status: Believed complete and correct.
+
+/**
+ * The size of the internal buffer used for input/output. Note that this
+ * can be overriden by setting the system property
+ * <code>gnu.java.io.PipedInputStream.pipe_size</code> to the desired size shown
+ * in bytes. This is not a standard part of the class library. Note that
+ * since this variable is <code>final</code>, it cannot be changed to refect
+ * the size specified in the property.
+ * <p>
+ * The value for this variable is 2048.
+ */
+protected static final int PIPE_SIZE = 2048;
+
+/**
+ * This is the real pipe size. It defaults to PIPE_SIZE, unless overridden
+ * by use of the system property <code>gnu.java.io.PipedInputStream.pipe_size</code>.
+ */
+private static int pipe_size;
+
+/**
+ * This variable indicates whether or not the <code>read()</code> method will attempt
+ * return a short count if this will possibly keep the stream from blocking.
+ * The default for this is <code>false</code> because that is what what the JDK seems
+ * to imply in its javadocs. We set this to <code>false</code> if the system
+ * property <code>gnu.java.io.try_not_to_block</code> is set.
+ */
+private static boolean try_not_to_block = false;
+
+static
+{
+ pipe_size = Integer.getInteger("gnu.java.io.PipedInputStream.pipe_size",
+ PIPE_SIZE).intValue();
+
+ String block_prop = System.getProperty("gnu.java.io.try_not_to_block");
+ if (block_prop != null)
+ try_not_to_block = true;
+}
+
+/*************************************************************************/
+
+/*
+ * Instance Variables
*/
-
-public class PipedInputStream extends InputStream
+
+/**
+ * This is the internal circular buffer used for storing bytes written
+ * to the pipe and from which bytes are read by this stream
+ */
+protected byte[] buffer = new byte[pipe_size];
+
+/**
+ * The index into buffer where the bytes written byte the connected
+ * <code>PipedOutputStream</code> will be written. If this variables is less
+ * than 0, then the buffer is empty. If this variable is equal to
+ * <code>out</code>, then the buffer is full
+ */
+protected int in = -1;
+
+/**
+ * This index into the buffer where bytes will be read from.
+ */
+protected int out = 0;
+
+/**
+ * This variable is <code>true</code> if this object has ever been connected
+ * to a <code>PipedOutputStream</code>, and <code>false</code> otherwise. It is used
+ * to detect an attempt to connect an already connected stream or to
+ * otherwise use the stream before it is connected.
+ */
+private boolean ever_connected = false;
+
+/**
+ * This variable is set to <code>true</code> if the <code>close()</code> method is
+ * called. This value is checked prevents a caller from re-opening the
+ * stream.
+ */
+private boolean closed = false;
+
+/**
+ * This variable is the PipedOutputStream to which this stream is connected.
+ */
+PipedOutputStream src;
+
+/**
+ * Used by <code>read()</code> to call an overloaded method
+ */
+private byte[] read_buf = new byte[1];
+
+/*************************************************************************/
+
+/*
+ * Constructors
+ */
+
+/**
+ * This constructor creates a new <code>PipedInputStream</code> that is not
+ * connected to a <code>PipedOutputStream</code>. It must be connected before
+ * bytes can be read from this stream.
+ */
+public
+PipedInputStream()
{
- /* The size of the pipe's circular input buffer. */
- protected static final int PIPE_SIZE = 1024;
+ return;
+}
- /* The circular buffer into which incoming data is placed. */
- protected byte[] buffer;
+/*************************************************************************/
- /* The index in the buffer at which the next byte of data will be stored. */
- protected int in = -1;
+/**
+ * This constructor creates a new <code>PipedInputStream</code> and connects
+ * it to the passed in <code>PipedOutputStream</code>. The stream is then read
+ * for reading.
+ *
+ * @param src The <code>PipedOutputStream</code> to connect this stream to
+ *
+ * @exception IOException If an error occurs
+ */
+public
+PipedInputStream(PipedOutputStream src) throws IOException
+{
+ connect(src);
+}
- /* The index in the buffer at which the next byte of data will be read. */
- protected int out = 0;
+/*************************************************************************/
- /* The output stream this is connected to; used to check for errors. */
- private PipedOutputStream po = null;
+/*
+ * Instance Variables
+ */
- /* Flag to indicate that the output stream was closed. */
- private boolean outClosed = false;
+/**
+ * This method connects this stream to the passed in <code>PipedOutputStream</code>.
+ * This stream is then ready for reading. If this stream is already
+ * connected or has been previously closed, then an exception is thrown
+ *
+ * @param src The <code>PipedOutputStream</code> to connect this stream to
+ *
+ * @exception IOException If an error occurs
+ */
+public synchronized void
+connect(PipedOutputStream src) throws IOException
+{
+ if (src == this.src)
+ return;
- public PipedInputStream(PipedOutputStream src) throws IOException
- {
- buffer = new byte[PIPE_SIZE];
- connect(src);
- }
+ if (ever_connected)
+ throw new IOException("Already connected");
- public PipedInputStream()
- {
- buffer = new byte[PIPE_SIZE];
- }
+ if (closed)
+ throw new IOException("Stream is closed and cannot be reopened");
- public synchronized int available() throws IOException
- {
- if (in < 0)
- return 0;
+ src.connect(this);
- if (in > out)
- return in - out;
+ ever_connected = true;
+}
- // Buffer has wrapped around.
- return buffer.length - out + in;
- }
+/*************************************************************************/
- public void close() throws IOException
- {
- buffer = null;
- po = null;
+/**
+ * This methods closes the stream so that no more data can be read
+ * from it.
+ *
+ * @exception IOException If an error occurs
+ */
+public synchronized void
+close() throws IOException
+{
+ closed = true;
+ notifyAll();
+}
- // Mark as empty for available method.
- in = -1;
- }
+/*************************************************************************/
- public void connect(PipedOutputStream src) throws IOException
- {
- if (buffer == null)
- throw new IOException("pipe closed");
+/**
+ * This method returns the number of bytes that can be read from this stream
+ * before blocking could occur. This is the number of bytes that are
+ * currently unread in the internal circular buffer. Note that once this
+ * many additional bytes are read, the stream may block on a subsequent
+ * read, but it not guaranteed to block.
+ *
+ * @return The number of bytes that can be read before blocking might occur
+ *
+ * @exception IOException If an error occurs
+ */
+public synchronized int
+available() throws IOException
+{
+ if (in == -1)
+ return(0);
+ else if (in > out)
+ return(in - out);
+ else
+ return(in + (pipe_size - out));
+}
- if (po != null)
- if (po == src)
- return;
- else
- throw new IOException("pipe already connected");
+/*************************************************************************/
+
+/**
+ * Reads the next byte from the stream. The byte read is returned as
+ * and int in the range of 0-255. If a byte cannot be read because of an
+ * end of stream condition, -1 is returned. If the stream is already
+ * closed, an IOException will be thrown.
+ * <code>
+ * This method will block if no bytes are available to be read.
+ *
+ * @return The byte read or -1 if end of stream.
+ *
+ * @exception IOException If an error occurs
+ */
+public synchronized int
+read() throws IOException
+{
+ // Method operates by calling the multibyte overloaded read method
+ // Note that read_buf is an internal instance variable. I allocate it
+ // there to avoid constant reallocation overhead for applications that
+ // call this method in a loop at the cost of some unneeded overhead
+ // if this method is never called.
+
+ int bytes_read = read(read_buf, 0, read_buf.length);
+
+ if (bytes_read == -1)
+ return(-1);
+ else
+ return((read_buf[0] & 0xFF));
+}
+
+/*************************************************************************/
- po = src;
- try
+/**
+ * This method reads bytes from the stream into a caller supplied buffer.
+ * It starts storing bytes at position <code>offset</code> into the buffer and
+ * reads a maximum of <cod>>len</code> bytes. Note that this method can actually
+ * read fewer than <code>len</code> bytes. The actual number of bytes read is
+ * returned. A -1 is returned to indicated that no bytes can be read
+ * because the end of the stream was reached. If the stream is already
+ * closed, a -1 will again be returned to indicate the end of the stream.
+ * <p>
+ * This method will block if no bytes are available to be read.
+ *
+ * @param buf The buffer into which bytes will be stored
+ * @param offset The index into the buffer at which to start writing.
+ * @param len The maximum number of bytes to read.
+ */
+public synchronized int
+read(byte[] buf, int offset, int len) throws IOException
+{
+ if (!ever_connected)
+ throw new IOException("Not connected");
+
+ int bytes_read = 0;
+ for (;;)
{
- src.connect(this);
- }
- catch (IOException ex)
+ // If there are bytes, take them
+ if (in != -1)
+ {
+ int desired_bytes = len - bytes_read;
+
+ // We are in a "wrap" condition
+ if (out > in)
+ {
+ if (desired_bytes > (pipe_size - out))
+ {
+ if (in == 0)
+ desired_bytes = (pipe_size - out) - 1;
+ else
+ desired_bytes = pipe_size - out;
+
+ System.arraycopy(buffer, out, buf, offset + bytes_read,
+ desired_bytes);
+
+ bytes_read += desired_bytes;
+ out += desired_bytes;
+ desired_bytes = len - bytes_read;
+
+ if (out == pipe_size)
+ out = 0;
+
+ notifyAll();
+ }
+ else
+ {
+ if ((out + desired_bytes) == in)
+ --desired_bytes;
+
+ if (((out + desired_bytes) == pipe_size) && (in == 0))
+ desired_bytes = (pipe_size - out) - 1;
+
+ System.arraycopy(buffer, out, buf, offset + bytes_read,
+ desired_bytes);
+
+ bytes_read += desired_bytes;
+ out += desired_bytes;
+ desired_bytes = len - bytes_read;
+
+ if (out == pipe_size)
+ out = 0;
+
+ notifyAll();
+ }
+ }
+
+ // We are in a "no wrap" or condition (can also be fall through
+ // from above
+ if (in > out)
+ {
+ if (desired_bytes >= ((in - out) - 1))
+ desired_bytes = (in - out) - 1;
+
+ System.arraycopy(buffer, out, buf, offset + bytes_read,
+ desired_bytes);
+
+ bytes_read += desired_bytes;
+ out += desired_bytes;
+ desired_bytes = len - bytes_read;
+
+ if (out == pipe_size)
+ out = 0;
+
+ notifyAll();
+ }
+ }
+
+ // If we are done, return
+ if (bytes_read == len)
+ return(bytes_read);
+
+ // Return a short count if necessary
+ if (bytes_read < len)
+ if (try_not_to_block)
+ return(bytes_read);
+
+ // Handle the case where the end of stream was encountered.
+ if (closed)
+ {
+ // We never let in == out so there might be one last byte
+ // available that we have not copied yet.
+ if (in != -1)
+ {
+ buf[offset + bytes_read] = buffer[out];
+ in = -1;
+ ++out;
+ ++bytes_read;
+ }
+
+ if (bytes_read != 0)
+ return(bytes_read);
+ else
+ return(-1);
+ }
+
+ // Wait for a byte to be read
+ try
+ {
+ wait();
+ }
+ catch(InterruptedException e) { ; }
+ }
+}
+
+/*************************************************************************/
+
+/**
+ * This method receives a byte of input from the source PipedOutputStream.
+ * If there is no data ready to be written, or if the internal circular
+ * buffer is full, this method blocks.
+ *
+ * *****What is this method really supposed to do *********
+ */
+protected synchronized void
+receive(int byte_received) throws IOException
+{
+ int orig_in = in;
+
+ for (;;)
{
- po = null;
- throw ex;
+ // Wait for something to happen
+ try
+ {
+ wait();
+ }
+ catch(InterruptedException e) { ; }
+
+ // See if we woke up because the stream was closed on us
+ if (closed)
+ throw new IOException("Stream closed before receiving byte");
+
+ // See if a byte of data was received
+ if (in != orig_in)
+ return;
}
- }
-
- public synchronized int read() throws IOException
- {
- // TBD: Spec says to throw IOException if thread writing to output stream
- // died. What does this really mean? Theoretically, multiple threads
- // could be writing to this object. Do you track the first, last, or
- // all of them?
- if (po == null)
- if (buffer == null)
- throw new IOException("pipe closed");
- else
- throw new IOException("pipe unconnected");
+}
- // Block until there's something to read or output stream was closed.
- while (in < 0)
- try
- {
- if (outClosed)
- return -1;
- wait();
- }
- catch (InterruptedException ex)
- {
- throw new InterruptedIOException();
- }
-
- // Let other threads know there's room to write now.
- notifyAll();
-
- int retval = buffer[out++] & 0xFF;
-
- // Wrap back around if at end of the array.
- if (out >= buffer.length)
- out = 0;
-
- // When the last byte available is read, mark the buffer as empty.
- if (out == in)
- {
- in = -1;
- out = 0;
- }
-
- return retval;
- }
-
- public synchronized int read(byte[] b, int off, int len) throws IOException
- {
- if (off < 0 || len < 0 || off + len > b.length)
- throw new ArrayIndexOutOfBoundsException();
-
- // TBD: Spec says to throw IOException if thread writing to output stream
- // died. What does this really mean? Theoretically, multiple threads
- // could be writing to this object. Do you track the first, last, or
- // all of them?
- if (po == null)
- if (buffer == null)
- throw new IOException("pipe closed");
- else
- throw new IOException("pipe unconnected");
+/*************************************************************************/
- // Block until there's something to read or output stream was closed.
- while (in < 0)
- try
- {
- if (outClosed)
- return -1;
- wait();
- }
- catch (InterruptedException ex)
- {
- throw new InterruptedIOException();
- }
-
- // Let other threads know there's room to write now.
- notifyAll();
-
- int numRead;
- len = Math.min(len, available());
- if (in <= out && len >= (numRead = buffer.length - out))
- {
- // Buffer has wrapped around; need to copy in 2 steps.
- // Copy to the end of the buffer first; second copy may be of zero
- // bytes but that is ok. Doing it that way saves having to check
- // later if 'out' has grown to buffer.length.
- System.arraycopy(buffer, out, b, off, numRead);
- len -= numRead;
- off += numRead;
- out = 0;
- }
- else
- numRead = 0;
-
- System.arraycopy(buffer, out, b, off, len);
- numRead += len;
- out += len;
-
- // When the last byte available is read, mark the buffer as empty.
- if (out == in)
- {
- in = -1;
- out = 0;
- }
-
- return numRead;
- }
-
- protected synchronized void receive(int b) throws IOException
- {
- if (buffer == null)
- throw new IOException("pipe closed");
-
- // TBD: Spec says to throw IOException if thread reading from input stream
- // died. What does this really mean? Theoretically, multiple threads
- // could be reading to this object (why else would 'read' be synchronized?).
- // Do you track the first, last, or all of them?
-
- if (b < 0)
- {
- outClosed = true;
- notifyAll(); // In case someone was blocked in a read.
- return;
- }
-
- // Block until there's room in the pipe.
- while (in == out)
- try
- {
- wait();
- }
- catch (InterruptedException ex)
- {
- throw new InterruptedIOException();
- }
-
- // Check if buffer is empty.
- if (in < 0)
- in = 0;
-
- buffer[in++] = (byte) b;
-
- // Wrap back around if at end of the array.
- if (in >= buffer.length)
- in = 0;
-
- // Let other threads know there's something to read when this returns.
- notifyAll();
- }
+/**
+ * This method is used by the connected <code>PipedOutputStream</code> to
+ * write bytes into the buffer. It uses this method instead of directly
+ * writing the bytes in order to obtain ownership of the object's monitor
+ * for the purposes of calling <code>notify</code>.
+ *
+ * @param buf The array containing bytes to write to this stream
+ * @param offset The offset into the array to start writing from
+ * @param len The number of bytes to write.
+ *
+ * @exception IOException If an error occurs
+ */
+synchronized void
+write(byte[] buf, int offset, int len) throws IOException
+{
+ if (len <= 0)
+ return;
+
+ int total_written = 0;
+ while (total_written < len)
+ {
+ // If we are not at the end of the buffer with out = 0
+ if (!((in == (buffer.length - 1)) && (out == 0)))
+ {
+ // This is the "no wrap" situation
+ if ((in - 1) >= out)
+ {
+ int bytes_written = 0;
+ if ((buffer.length - in) > (len - total_written))
+ bytes_written = (len - total_written);
+ else if (out == 0)
+ bytes_written = (buffer.length - in) - 1;
+ else
+ bytes_written = (buffer.length - in);
+
+ if (bytes_written > 0)
+ System.arraycopy(buf, offset + total_written, buffer, in,
+ bytes_written);
+ total_written += bytes_written;
+ in += bytes_written;
+
+ if (in == buffer.length)
+ in = 0;
+
+ notifyAll();
+ }
+ // This is the "wrap" situtation
+ if ((out > in) && (total_written != len))
+ {
+ int bytes_written = 0;
+
+ // Do special processing if we are at the beginning
+ if (in == -1)
+ {
+ in = 0;
+
+ if (buffer.length > len)
+ bytes_written = len;
+ else
+ bytes_written = buffer.length - 1;
+ }
+ else if (((out - in) - 1) < (len - total_written))
+ {
+ bytes_written = (out - in) - 1;
+ }
+ else
+ {
+ bytes_written = len - total_written;
+ }
+
+ // If the buffer is full, wait for it to empty out
+ if ((out - 1) == in)
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e)
+ {
+ continue;
+ }
+ }
+
+ System.arraycopy(buf, offset + total_written, buffer, in,
+ bytes_written);
+ total_written += bytes_written;
+ in += bytes_written;
+
+ if (in == buffer.length)
+ in = 0;
+
+ notifyAll();
+ }
+ }
+ // Wait for some reads to occur before we write anything.
+ else
+ {
+ try
+ {
+ wait();
+ }
+ catch (InterruptedException e) { ; }
+ }
+ }
}
+
+} // class PipedInputStream
+