diff options
author | Tom Tromey <tromey@cygnus.com> | 2000-04-21 01:18:16 +0000 |
---|---|---|
committer | Tom Tromey <tromey@gcc.gnu.org> | 2000-04-21 01:18:16 +0000 |
commit | d3474943bf309f085d515d7b20cb80c1d50e14cd (patch) | |
tree | dec5c6d7c3d3c314853e857fd39856ceb50ec5db /libjava/java/io/PipedInputStream.java | |
parent | 21caf59006e2c7dd06bf9840a7fc3cea321282f7 (diff) | |
download | gcc-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.java | 732 |
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 + |