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/PipedReader.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/PipedReader.java')
-rw-r--r-- | libjava/java/io/PipedReader.java | 703 |
1 files changed, 506 insertions, 197 deletions
diff --git a/libjava/java/io/PipedReader.java b/libjava/java/io/PipedReader.java index faac986..72e516a 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 <tromey@cygnus.com> - * @date September 25, 1998 + * This class is an input stream that reads its chars 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>PipedReader</code> and connected <code>PipedWriter</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 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 + * <code>gnu.java.io.PipedReader.pipe_size</code> to the desired size shown + * in chars. 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. + */ +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 <code>gnu.java.io.PipedReader.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.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 + * <code>PipedWriter</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 + */ +private int in = -1; + +/** + * This index into the buffer where chars will be read from. + */ +private int out = 0; + +/** + * This variable is <code>true</code> if this object has ever been connected + * to a <code>PipedWriter</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 PipedWriter to which this stream is connected. + */ +PipedWriter src; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This constructor creates a new <code>PipedReader</code> that is not + * connected to a <code>PipedWriter</code>. It must be connected before + * chars can be read from this stream. + */ +public +PipedReader() +{ + return; +} + +/*************************************************************************/ + +/** + * This constructor creates a new <code>PipedReader</code> and connects + * it to the passed in <code>PipedWriter</code>. The stream is then read + * for reading. + * + * @param src The <code>PipedWriter</code> 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 <code>PipedWriter</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>PipedWriter</code> 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 <code>false</code> an attempt to read may (but is + * not guaranteed to) block. + * + * @return <code>true</code> if this stream is ready to be read, <code>false</code> 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 + * <code>int</code>. + * <p> + * 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 <code>offset</code> into the buffer and + * reads a maximum of <cod>>len</code> chars. Note that this method can actually + * read fewer than <code>len</code> 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. + * <p> + * 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 <code>PipedWriter</code> 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 <code>notify</code>. + * + * @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 + |