From: Tom Tromey Date: Fri, 21 Apr 2000 01:18:16 +0000 (+0000) Subject: Fix for PR java.io/204: X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=d3474943bf309f085d515d7b20cb80c1d50e14cd;p=gcc.git 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 --- diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 212e9aa2aac..d5e16880ad6 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,5 +1,10 @@ 2000-04-20 Tom Tromey + 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. + Fix for PR libgcj/212: * gcj/javaprims.h (_Jv_word, _Jv_word2): Removed definitions. * include/jvm.h (_Jv_word, _Jv_word2): Define. diff --git a/libjava/java/io/PipedInputStream.java b/libjava/java/io/PipedInputStream.java index d2dbecbba11..d1081a2ce3d 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 - * @date October 29, 1998. + * This class is an input stream that reads its bytes from an output stream + * to which it is connected. + *

+ * Data is read and written to an internal buffer. It is highly recommended + * that the PipedInputStream and connected PipedOutputStream + * 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 + * gnu.java.io.PipedInputStream.pipe_size to the desired size shown + * in bytes. This is not a standard part of the class library. Note that + * since this variable is final, it cannot be changed to refect + * the size specified in the property. + *

+ * 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 gnu.java.io.PipedInputStream.pipe_size. + */ +private static int pipe_size; + +/** + * This variable indicates whether or not the read() method will attempt + * return a short count if this will possibly keep the stream from blocking. + * The default for this is false because that is what what the JDK seems + * to imply in its javadocs. We set this to false if the system + * property gnu.java.io.try_not_to_block 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 + * PipedOutputStream will be written. If this variables is less + * than 0, then the buffer is empty. If this variable is equal to + * out, 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 true if this object has ever been connected + * to a PipedOutputStream, and false 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 true if the close() 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 read() to call an overloaded method + */ +private byte[] read_buf = new byte[1]; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This constructor creates a new PipedInputStream that is not + * connected to a PipedOutputStream. 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 PipedInputStream and connects + * it to the passed in PipedOutputStream. The stream is then read + * for reading. + * + * @param src The PipedOutputStream 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 PipedOutputStream. + * 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 PipedOutputStream 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. + * + * 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 offset into the buffer and + * reads a maximum of >len bytes. Note that this method can actually + * read fewer than len 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. + *

+ * 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 PipedOutputStream 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 notify. + * + * @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 + diff --git a/libjava/java/io/PipedOutputStream.java b/libjava/java/io/PipedOutputStream.java index 901fb9e5484..6d1061230be 100644 --- a/libjava/java/io/PipedOutputStream.java +++ b/libjava/java/io/PipedOutputStream.java @@ -1,97 +1,209 @@ -// PipedOutputStream.java - Write bytes to a pipe. +/* PipedOutputStream.java -- Write portion of piped streams. + Copyright (C) 1998 Free Software Foundation, Inc. -/* Copyright (C) 1998, 1999, 2000 Free Software Foundation +This file is part of GNU Classpath. - This file is part of libgcj. +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. */ -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ package java.io; /** - * @author Tom Tromey - * @date September 24, 1998 + * This class writes its bytes to a PipedInputStream to + * which it is connected. + *

+ * It is highly recommended that a PipedOutputStream and its + * connected PipedInputStream be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedOutputStream extends OutputStream +{ + +/*************************************************************************/ + +/* + * Instance Variables */ -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 - * "The Java Language Specification", ISBN 0-201-63451-1 - * Status: Believed complete and correct. +/** + * This is the PipedInputStream to which this object + * is connected. + */ +private PipedInputStream snk; + +/** + * This flag indicates whether or not this stream has ever been + * connected to an input stream + */ +private boolean ever_connected; + +/** + * This flag indicates whether the close method has ever + * been called for this stream. + */ +private boolean closed; + +/*************************************************************************/ + +/** + * This method initializes a new PipedOutputStream instance. + * This constructor creates an unconnected object. It must be connected + * to a PipedInputStream object using the connect + * method prior to writing any data or an exception will be thrown. + */ +public +PipedOutputStream() +{ + ; // Do Nothing +} + +/*************************************************************************/ + +/** + * This method initializes a new PipedOutputStream instance + * to write to the specified PipedInputStream. This stream + * is then ready for writing. + * + * @param snk The PipedInputStream to connect this stream to. + * + * @exception IOException If an error occurs + */ +public +PipedOutputStream(PipedInputStream snk) throws IOException +{ + connect(snk); +} + +/*************************************************************************/ + +/* + * Instance Methods */ -public class PipedOutputStream extends OutputStream +/** + * This method connects this object to the specified + * PipedInputStream object. This stream will then be ready + * for writing. If this stream is already connected or has been + * previously closed, then an exception is thrown. + * + * @param snk The PipedInputStream to connect this stream to + * + * @exception IOException If an error occurs + */ +public synchronized void +connect(PipedInputStream snk) throws IOException { - public void close () throws IOException - { - closed = true; - - // Notify PipedInputStream that there is no more data to be had. - destination.receive(-1); - } - - public void connect (PipedInputStream dest) throws IOException - { - if (closed) - throw new IOException("pipe closed"); - - if (destination != null) - if (destination == dest) - return; - else - throw new IOException("pipe already connected"); - - destination = dest; - try - { - dest.connect(this); - } - catch (IOException ex) - { - destination = null; - throw ex; - } - } - - public synchronized void flush () throws IOException - { - // There doesn't seem to be anything to do here. - - // TBD: Should this maybe do a notifyAll as a way for the user - // to wake up the input stream to check for bytes to read? Shouldn't - // be necessary but if there aren't any bytes, other threads will just - // go blocak again anyway so it wouldn't hurt. - } - - public PipedOutputStream () - { - closed = false; - } - - public PipedOutputStream (PipedInputStream dest) throws IOException - { - closed = false; - connect (dest); - } - - public void write (int oneByte) throws IOException - { - if (closed) - throw new IOException (); - destination.receive(oneByte); - } - - public void write (byte[] buffer, int offset, int count) throws IOException - { - if (closed) - throw new IOException (); - if (offset < 0 || count < 0 || offset + count > buffer.length) - throw new ArrayIndexOutOfBoundsException (); - for (int i = 0; i < count; ++i) - destination.receive (buffer[offset + i]); - } - - // Instance variables. - private PipedInputStream destination; - private boolean closed; + if (snk == this.snk) + return; + + if (ever_connected) + throw new IOException("Already connected"); + + if (closed) + throw new IOException("Stream is closed and cannot be reopened"); + + this.snk = snk; + ever_connected = true; + snk.src = this; + + snk.connect(this); +} + +/*************************************************************************/ + +/** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ +public synchronized void +close() throws IOException +{ + closed = true; + snk.close(); + notifyAll(); +} + +/*************************************************************************/ + +/** + * This method writes a single byte of date to the stream. Note that + * this method will block if the PipedInputStream to which + * this object is connected has a full buffer. + * + * @param b The byte of data to be written, passed as an int. + * + * @exception IOException If an error occurs + */ +public synchronized void +write(int b) throws IOException +{ + byte[] buf = new byte[1]; + buf[0] = (byte)(b & 0xFF); + + snk.write(buf, 0, buf.length); } + +/*************************************************************************/ + +/** + * This method writes len bytes of data from the byte array + * buf starting at index offset in the array + * to the stream. Note that this method will block if the + * PipedInputStream to which this object is connected has + * a buffer that cannot hold all of the bytes to be written. + * + * @param buf The array containing bytes to write to thes stream. + * @param offset The index into the array to start writing bytes from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs + */ +public void +write(byte[] buf, int offset, int len) throws IOException +{ + snk.write(buf, 0, len); +} + +/*************************************************************************/ + +/** + * This method flushes any unwritten bytes to the output and notifies + * any waiting readers that the pipe is ready to be read. + * + * @exception IOException If an error occurs. + */ +public void +flush() throws IOException +{ + return; +} + +} // class PipedOutputStream + diff --git a/libjava/java/io/PipedReader.java b/libjava/java/io/PipedReader.java index faac9865cb8..72e516a2c10 100644 --- a/libjava/java/io/PipedReader.java +++ b/libjava/java/io/PipedReader.java @@ -1,210 +1,519 @@ -// PipedReader.java - Piped character stream. +/* PipedReader.java -- Input stream that reads from an output stream + Copyright (C) 1998, 1999 Free Software Foundation, Inc. -/* Copyright (C) 1998, 1999 Free Software Foundation +This file is part of GNU Classpath. - This file is part of libgcj. +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. */ -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ package java.io; /** - * @author Tom Tromey - * @date September 25, 1998 + * This class is an input stream that reads its chars from an output stream + * to which it is connected. + *

+ * Data is read and written to an internal buffer. It is highly recommended + * that the PipedReader and connected PipedWriter + * 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 PipedReader extends Reader +{ + +/*************************************************************************/ + +/* + * Class Variables */ -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 - * "The Java Language Specification", ISBN 0-201-63451-1 - * Status: Complete to 1.1. +/** + * The size of the internal buffer used for input/output. Note that this + * can be overriden by setting the system property + * gnu.java.io.PipedReader.pipe_size to the desired size shown + * in chars. This is not a standard part of the class library. Note that + * since this variable is final, it cannot be changed to refect + * the size specified in the property. + *

+ * The value for this variable is 2048. + */ +private 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 gnu.java.io.PipedReader.pipe_size. + */ +private static int pipe_size; + +/** + * This variable indicates whether or not the read() method will attempt + * return a short count if this will possibly keep the stream from blocking. + * The default for this is false because that is what what the JDK seems + * to imply in its javadocs. We set this to false if the system + * property gnu.java.io.try_not_to_block is set. + */ +private static boolean try_not_to_block = false; + +static +{ + pipe_size = Integer.getInteger("gnu.java.io.PipedReader.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 PipedReader extends Reader +/** + * This is the internal circular buffer used for storing chars written + * to the pipe and from which chars are read by this stream + */ +private char[] buffer = new char[pipe_size]; + +/** + * The index into buffer where the chars written char the connected + * PipedWriter will be written. If this variables is less + * than 0, then the buffer is empty. If this variable is equal to + * out, then the buffer is full + */ +private int in = -1; + +/** + * This index into the buffer where chars will be read from. + */ +private int out = 0; + +/** + * This variable is true if this object has ever been connected + * to a PipedWriter, and false 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 true if the close() method is + * called. This value is checked prevents a caller from re-opening the + * stream. + */ +private boolean closed = false; + +/** + * This variable is the PipedWriter to which this stream is connected. + */ +PipedWriter src; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This constructor creates a new PipedReader that is not + * connected to a PipedWriter. It must be connected before + * chars can be read from this stream. + */ +public +PipedReader() +{ + return; +} + +/*************************************************************************/ + +/** + * This constructor creates a new PipedReader and connects + * it to the passed in PipedWriter. The stream is then read + * for reading. + * + * @param src The PipedWriter to connect this stream to + * + * @exception IOException If an error occurs + */ +public +PipedReader(PipedWriter src) throws IOException +{ + connect(src); +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This method connects this stream to the passed in PipedWriter. + * 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 PipedWriter to connect this stream to + * + * @exception IOException If an error occurs + */ +public void +connect(PipedWriter src) throws IOException +{ + if (src == this.src) + return; + + if (ever_connected) + throw new IOException("Already connected"); + + if (closed) + throw new IOException("Stream is closed and cannot be reopened"); + + synchronized (lock) { + + src.connect(this); + + ever_connected = true; + + } // synchronized +} + +/*************************************************************************/ + +/** + * This methods closes the stream so that no more data can be read + * from it. + * + * @exception IOException If an error occurs + */ +public void +close() throws IOException +{ + synchronized (lock) { + + closed = true; + notifyAll(); + + } // synchronized +} + +/*************************************************************************/ + +/** + * This method determines whether or not this stream is ready to be read. + * If this metho returns false an attempt to read may (but is + * not guaranteed to) block. + * + * @return true if this stream is ready to be read, false otherwise + * + * @exception IOException If an error occurs + */ +public boolean +ready() throws IOException { - public void close () throws IOException - { - closed = true; - } - - public void connect (PipedWriter src) throws IOException - { - if (closed) - throw new IOException ("already closed"); - if (writer != null) - { - if (writer == src) - return; - throw new IOException ("already connected"); - } - try - { - writer = src; - writer.connect(this); - } - catch (IOException e) - { - writer = null; - throw e; - } - } - - public PipedReader () - { - super (); - writer = null; - closed = false; - in = -1; - out = 0; - pipeBuffer = new char[1024]; - } - - public PipedReader (PipedWriter src) throws IOException - { - super (); - closed = false; - in = -1; - out = 0; - pipeBuffer = new char[1024]; - connect (src); - } - - public int read (char buf[], int offset, int count) throws IOException - { - if (closed) - throw new IOException ("closed"); - if (count < 0) - throw new ArrayIndexOutOfBoundsException (); - int toCopy = count; - synchronized (lock) - { - while (toCopy > 0) - { - // Wait for data in the pipe. If the writer is closed and - // no data has been copied into the output buffer, return - // the magic EOF number. - while (in == -1) - { - if (writer.isClosed()) - { - if (toCopy < count) - return count - toCopy; - return -1; - } - - // Note that JCL doesn't say this is the right thing - // to do. Still, it feels right, and we must deal - // with an interrupt somehow. - try - { - lock.wait(); - } - catch (InterruptedException e) - { - InterruptedIOException io - = new InterruptedIOException (e.getMessage()); - io.bytesTransferred = count - toCopy; - throw io; - } - } - // Now copy some data from pipe into user buffer. - int len; - if (in < out) - len = pipeBuffer.length - out; - else - len = in - out; - len = len > toCopy ? toCopy : len; - System.arraycopy(pipeBuffer, out, buf, offset, len); - out += len; - if (out == pipeBuffer.length) - out = 0; - toCopy -= len; - offset += len; - // If we've read all the data, then reset so that we know - // there is nothing left to be read. - if (in == out) - in = -1; - // Tell anybody waiting for space in the buffer. - lock.notifyAll(); - } - } - return count; - } - - void receive (char buf[], int offset, int count) throws IOException - { - if (count < 0) - throw new ArrayIndexOutOfBoundsException (); - int original = count; - synchronized (lock) - { - while (count > 0) - { - // Wait until there is some space in the buffer. - while (in == out) - { - try - { - lock.wait(); - } - catch (InterruptedException e) - { - // Turn interrupts into IO interrupts. - InterruptedIOException io - = new InterruptedIOException (e.getMessage()); - io.bytesTransferred = original - count; - throw io; - } - } - - // Compute destination in the pipe. - int base, len; - if (in == -1) - { - base = 0; - len = pipeBuffer.length; - } - else if (in < out) - { - base = in; - len = out - in; - } - else - { - base = in; - len = pipeBuffer.length - in; - } - int copyLen = len > count ? count : len; - // Copy data and update local state. - System.arraycopy(buf, offset, pipeBuffer, base, copyLen); - in = base + copyLen; - if (in == pipeBuffer.length) - in = 0; - count -= copyLen; - offset += copyLen; - // Tell anybody waiting for data. - lock.notifyAll(); - } - } - } - - - boolean isClosed () - { - return closed; - } - - // The associated writer. - private PipedWriter writer; - // True if this reader has been closed. - boolean closed; - - // Index of next character to overwrite when receive() is called. - // If -1, then that means the buffer is empty. - private int in; - // Index of next character to return from read(). - private int out; - - // The pipe buffer itself. - private char[] pipeBuffer; + if (in == -1) + return(false); + + if (out == (in - 1)) + return(false); + + if ((out == pipe_size) && (in == 0)) + return(false); + + return(true); } + +/*************************************************************************/ + +/** + * This method reads a single char from the pipe and returns it as an + * int. + *

+ * This method will block if no chars are available to be read. + * + * @return An char read from the pipe, or -1 if the end of stream is + * reached. + * + * @exception IOException If an error occurs. + */ +public int +read() throws IOException +{ + char[] buf = new char[1]; + + return(read(buf, 0, buf.length)); +} + +/*************************************************************************/ + +/** + * This method reads chars from the stream into a caller supplied buffer. + * It starts storing chars at position offset into the buffer and + * reads a maximum of >len chars. Note that this method can actually + * read fewer than len chars. The actual number of chars read is + * returned. A -1 is returned to indicated that no chars 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. + *

+ * This method will block if no chars are available to be read. + * + * @param buf The buffer into which chars will be stored + * @param offset The index into the buffer at which to start writing. + * @param len The maximum number of chars to read. + */ +public int +read(char[] buf, int offset, int len) throws IOException +{ + if (!ever_connected) + throw new IOException("Not connected"); + + synchronized (lock) { + + int chars_read = 0; + for (;;) + { + // If there are chars, take them + if (in != -1) + { + int desired_chars = len - chars_read; + + // We are in a "wrap" condition + if (out > in) + { + if (desired_chars > (pipe_size - out)) + { + if (in == 0) + desired_chars = (pipe_size - out) - 1; + else + desired_chars = pipe_size - out; + + System.arraycopy(buffer, out, buf, offset + chars_read, + desired_chars); + + chars_read += desired_chars; + out += desired_chars; + desired_chars = len - chars_read; + + if (out == pipe_size) + out = 0; + + notifyAll(); + } + else + { + if ((out + desired_chars) == in) + --desired_chars; + + if (((out + desired_chars) == pipe_size) && (in == 0)) + desired_chars = (pipe_size - out) - 1; + + System.arraycopy(buffer, out, buf, offset + chars_read, + desired_chars); + + chars_read += desired_chars; + out += desired_chars; + desired_chars = len - chars_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_chars >= ((in - out) - 1)) + desired_chars = (in - out) - 1; + + System.arraycopy(buffer, out, buf, offset + chars_read, + desired_chars); + + chars_read += desired_chars; + out += desired_chars; + desired_chars = len - chars_read; + + if (out == pipe_size) + out = 0; + + notifyAll(); + } + } + + // If we are done, return + if (chars_read == len) + return(chars_read); + + // Return a short count if necessary + if (chars_read < len) + if (try_not_to_block) + return(chars_read); + + // Handle the case where the end of stream was encountered. + if (closed) + { + // We never let in == out so there might be one last char + // available that we have not copied yet. + if (in != -1) + { + buf[offset + chars_read] = buffer[out]; + in = -1; + ++out; + ++chars_read; + } + + if (chars_read != 0) + return(chars_read); + else + return(-1); + } + + // Wait for a char to be read + try + { + wait(); + } + catch(InterruptedException e) { ; } + } + + } // synchronized +} + +/*************************************************************************/ + +/** + * This method is used by the connected PipedWriter to + * write chars into the buffer. It uses this method instead of directly + * writing the chars in order to obtain ownership of the object's monitor + * for the purposes of calling notify. + * + * @param buf The array containing chars to write to this stream + * @param offset The offset into the array to start writing from + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ +void +write(char[] buf, int offset, int len) throws IOException +{ + if (len <= 0) + return; + + synchronized (lock) { + + 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 chars_written = 0; + if ((buffer.length - in) > (len - total_written)) + chars_written = (len - total_written); + else if (out == 0) + chars_written = (buffer.length - in) - 1; + else + chars_written = (buffer.length - in); + + if (chars_written > 0) + System.arraycopy(buf, offset + total_written, buffer, in, + chars_written); + total_written += chars_written; + in += chars_written; + + if (in == buffer.length) + in = 0; + + notifyAll(); + } + // This is the "wrap" situtation + if ((out > in) && (total_written != len)) + { + int chars_written = 0; + + // Do special processing if we are at the beginning + if (in == -1) + { + in = 0; + + if (buffer.length > len) + chars_written = len; + else + chars_written = buffer.length - 1; + } + else if (((out - in) - 1) < (len - total_written)) + { + chars_written = (out - in) - 1; + } + else + { + chars_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, + chars_written); + total_written += chars_written; + in += chars_written; + + if (in == buffer.length) + in = 0; + + notifyAll(); + } + } + // Wait for some reads to occur before we write anything. + else + { + try + { + wait(); + } + catch (InterruptedException e) { ; } + } + } + } // synchronized +} + +} // class PipedReader + diff --git a/libjava/java/io/PipedWriter.java b/libjava/java/io/PipedWriter.java index 7efed3976da..cc36acdf0d5 100644 --- a/libjava/java/io/PipedWriter.java +++ b/libjava/java/io/PipedWriter.java @@ -1,88 +1,217 @@ -// PipedWriter.java - Piped character stream. +/* PipedWriter.java -- Write portion of piped streams. + Copyright (C) 1998 Free Software Foundation, Inc. -/* Copyright (C) 1998, 1999 Free Software Foundation +This file is part of GNU Classpath. - This file is part of libgcj. +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. */ -This software is copyrighted work licensed under the terms of the -Libgcj License. Please consult the file "LIBGCJ_LICENSE" for -details. */ package java.io; /** - * @author Tom Tromey - * @date September 25, 1998 + * This class writes its chars to a PipedReader to + * which it is connected. + *

+ * It is highly recommended that a PipedWriter and its + * connected PipedReader be in different threads. If + * they are in the same thread, read and write operations could deadlock + * the thread. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class PipedWriter extends Writer +{ + +/*************************************************************************/ + +/* + * Instance Variables */ -/* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 - * "The Java Language Specification", ISBN 0-201-63451-1 - * Status: Complete to 1.1. +/** + * This is the PipedReader to which this object + * is connected. + */ +private PipedReader snk; + +/** + * This flag indicates whether or not this stream has ever been + * connected to an input stream + */ +private boolean ever_connected; + +/** + * This flag indicates whether the close method has ever + * been called for this stream. + */ +private boolean closed; + +/*************************************************************************/ + +/** + * This method initializes a new PipedWriter instance. + * This constructor creates an unconnected object. It must be connected + * to a PipedReader object using the connect + * method prior to writing any data or an exception will be thrown. + */ +public +PipedWriter() +{ + ; // Do Nothing +} + +/*************************************************************************/ + +/** + * This method initializes a new PipedWriter instance + * to write to the specified PipedReader. This stream + * is then ready for writing. + * + * @param snk The PipedReader to connect this stream to. + * + * @exception IOException If an error occurs + */ +public +PipedWriter(PipedReader snk) throws IOException +{ + connect(snk); +} + +/*************************************************************************/ + +/* + * Instance Methods */ -public class PipedWriter extends Writer +/** + * This method connects this object to the specified + * PipedReader object. This stream will then be ready + * for writing. If this stream is already connected or has been + * previously closed, then an exception is thrown. + * + * @param snk The PipedReader to connect this stream to + * + * @exception IOException If an error occurs + */ +public void +connect(PipedReader snk) throws IOException +{ + if (snk == this.snk) + return; + + if (ever_connected) + throw new IOException("Already connected"); + + if (closed) + throw new IOException("Stream is closed and cannot be reopened"); + + synchronized (lock) { + + this.snk = snk; + ever_connected = true; + snk.src = this; + + snk.connect(this); + + } // synchronized +} + +/*************************************************************************/ + +/** + * This method closes this stream so that no more data can be written + * to it. Any further attempts to write to this stream may throw an + * exception + * + * @exception IOException If an error occurs + */ +public void +close() throws IOException { - public void close () throws IOException - { - closed = true; - } - - public void connect (PipedReader sink) throws IOException - { - if (closed) - throw new IOException ("already closed"); - if (reader != null) - { - if (reader == sink) - return; - throw new IOException ("already connected"); - } - try - { - reader = sink; - reader.connect(this); - } - catch (IOException e) - { - reader = null; - throw e; - } - } - - public void flush () throws IOException - { - // We'll throw an exception if we're closed, but there's nothing - // else to do here. - if (closed) - throw new IOException ("closed"); - } - - public PipedWriter () - { - super (); - closed = false; - } - - public PipedWriter (PipedReader sink) throws IOException - { - super (); - closed = false; - connect (sink); - } - - public void write (char buffer[], int offset, int count) throws IOException - { - if (closed) - throw new IOException ("closed"); - reader.receive(buffer, offset, count); - } - - boolean isClosed () - { - return closed; - } - - // The associated reader. - private PipedReader reader; - private boolean closed; + synchronized (lock) { + + closed = true; + snk.close(); + notifyAll(); + + } // synchronized } + +/*************************************************************************/ + +/** + * This methods writes a single byte of data to the pipe. This call may + * block if the pipe is full. + * + * @param c The char to write, passed as an int. + * + * @exception IOException If an error occurs. + */ +public void +write(int c) throws IOException +{ + char[] buf = new char[1]; + buf[0] = (char)c; + + write(buf, 0, buf.length); +} + +/*************************************************************************/ + +/** + * This method writes len chars of data from the char array + * buf starting at index offset in the array + * to the stream. Note that this method will block if the + * PipedReader to which this object is connected has + * a buffer that cannot hold all of the chars to be written. + * + * @param buf The array containing chars to write to thes stream. + * @param offset The index into the array to start writing chars from. + * @param len The number of chars to write. + * + * @exception IOException If an error occurs + */ +public void +write(char[] buf, int offset, int len) throws IOException +{ + snk.write(buf, 0, len); +} + +/*************************************************************************/ + +/** + * This method flushes any unwritten chars to the underlying output + * sink. This method does nothing in this class because this class does + * not buffer any chars. + * + * @exception IOException If an error occurs + */ +public void +flush() throws IOException +{ + ; // Do Nothing +} + +} // class PipedWriter +