This is the mail archive of the java-patches@sources.redhat.com mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

java.io PipedInputStream PipedOutputStream PipedReader PipedWriter


The Piped I/O classes we inherited from classpath were somewhat broken
(especially PipedReader/PipedWriter). These new implementations are
much simpler and hopefully less buggy - they work for my code and pass
all the test cases I found (JCL, Kaffe, Classpath).

I'm checking these in.

regards

  [ bryce ]


2001-01-05  Bryce McKinlay  <bryce@albatross.co.nz>

	* java/io/PipedInputStream: Rewritten. Now simpler and more correct.
	* java/io/PipedOutputStream: Updated to match new PipedInputStream.
	* java/io/PipedReader: New implementation based on new PipedInputStream.
	* java/io/PipedWriter: Updated to match new PipedReader.

Index: PipedInputStream.java
===================================================================
RCS file: /cvs/gcc/egcs/libjava/java/io/PipedInputStream.java,v
retrieving revision 1.7
diff -u -b -r1.7 PipedInputStream.java
--- PipedInputStream.java	2000/08/07 19:59:48	1.7
+++ PipedInputStream.java	2001/01/05 07:57:13
@@ -1,5 +1,5 @@
-/* PipedInputStream.java -- Input stream that reads from an output stream
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+/* PipedInputStream.java -- Read portion of piped streams.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -24,467 +24,334 @@
 This exception does not however invalidate any other reasons why the
 executable file might be covered by the GNU General Public License. */
 
+// NOTE: This implementation is very similar to that of PipedReader.  If you 
+// fix a bug in here, chances are you should make a similar change to the 
+// PipedReader code.
 
 package java.io;
 
 /**
-  * This class is an input stream that reads its bytes from an output stream
+  * 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.
+  * be part of different threads.  If they are not, the read and write 
+  * operations could deadlock their thread.
   *
-  * @version 0.0
+  * @specnote The JDK implementation appears to have some undocumented 
+  *           functionality where it keeps track of what thread is writing
+  *           to pipe and throws an IOException if that thread susequently
+  *           dies. This behaviour seems dubious and unreliable - we don't
+  *           implement it.
   *
   * @author Aaron M. Renn (arenn@urbanophile.com)
   */
 public class PipedInputStream extends InputStream
 {
+  /** PipedOutputStream to which this is connected. Null only if this 
+    * InputStream hasn't been connected yet. */
+  PipedOutputStream source;
 
-/*************************************************************************/
+  /** Set to true if close() has been called on this InputStream. */
+  boolean closed;
 
-/*
- * Class Variables
+  /**
+    * The size of the internal buffer used for input/output.
  */
+  protected static final int PIPE_SIZE = 2048;
 
-/**
-  * 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;
-
-static
-{
-  pipe_size = Integer.getInteger("gnu.java.io.PipedInputStream.pipe_size",
-                                 PIPE_SIZE).intValue();
-}
-
-/*************************************************************************/
-
-/*
- * Instance Variables
- */
-
-/**
+  /**
   * 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];
+  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
+  /**
+    * The index into buffer where the next byte from the connected
+    * <code>PipedOutputStream</code> will be written. If this variable is 
+    * equal to <code>out</code>, then the buffer is full. If set to < 0,
+    * the buffer is empty.
   */
-protected int in = -1;
+  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;
+  protected int out = 0;
 
-/**
-  * This variable is the PipedOutputStream to which this stream is connected.
-  */
-PipedOutputStream src;
+  /** Buffer used to implement single-argument read/receive */
+  private byte[] read_buf = new byte[1];
 
-/**
-  * Used by <code>read()</code> to call an overloaded method
+  /**
+    * 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.
   */
-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()
-{
-  return;
-}
-
-/*************************************************************************/
+  public PipedInputStream()
+  {
+  }
 
-/**
+  /**
   * 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.
+    * it to the passed in <code>PipedOutputStream</code>. The stream is then 
+    * ready for reading.
   *
-  * @param src The <code>PipedOutputStream</code> to connect this stream to
+    * @param source The <code>PipedOutputStream</code> to connect this stream to
   *
-  * @exception IOException If an error occurs
-  */
-public
-PipedInputStream(PipedOutputStream src) throws IOException
-{
-  connect(src);
-}
-
-/*************************************************************************/
-
-/*
- * Instance Variables
+    * @exception IOException If <code>source</code> is already connected.
  */
+  public PipedInputStream(PipedOutputStream source) throws IOException
+  {
+    connect(source);
+  }
 
-/**
+  /**
   * 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
+    * @exception IOException If this PipedInputStream or <code>source</code> 
+    *                        has been connected already.
   */
-public synchronized void
-connect(PipedOutputStream 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");
+  public void connect(PipedOutputStream source) throws IOException
+  {
+    // The JDK (1.3) does not appear to check for a previously closed 
+    // connection here.
 
-  src.connect(this);
+    if (this.source != null || source.sink != null)
+      throw new IOException ("Already connected");
 
-  ever_connected = true;
-}
-
-/*************************************************************************/
+    source.sink = this;
+    this.source = source;
+  }
 
-/**
-  * This methods closes the stream so that no more data can be read
-  * from it.
+  /**
+  * This method receives a byte of input from the source PipedOutputStream.
+  * If the internal circular buffer is full, this method blocks.
   *
-  * @exception IOException If an error occurs
+  * @param byte_received The byte to write to this stream
+  *
+  * @exception IOException if error occurs
+  * @specnote Weird. This method must be some sort of accident.
   */
-public synchronized void
-close() throws IOException
-{
-  closed = true;
-  notifyAll();
-}
-
-/*************************************************************************/
+  protected synchronized void receive(int b) throws IOException
+  {
+    read_buf[0] = (byte) (b & 0xff);
+    receive (read_buf, 0, 1);
+  }
 
-/**
-  * 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.
+  /**
+    * This method is used by the connected <code>PipedOutputStream</code> to
+    * write bytes into the buffer.
   *
-  * @return The number of bytes that can be read before blocking might occur
+    * @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
+    * @specnote This code should be in PipedOutputStream.write, but we
+    *           put it here in order to support that bizarre recieve(int)
+    *           method.
   */
-public synchronized int
-available() throws IOException
-{
-  if (in == -1)
-    return(0);
-  else if (in > out)
-    return(in - out);
+  synchronized void receive(byte[] buf, int offset, int len)
+    throws IOException
+  {
+    if (closed)
+      throw new IOException ("Pipe closed");
+
+    int bufpos = offset;
+    int copylen;
+    
+    while (len > 0)
+      {
+        try
+	  {
+	    while (in == out)
+	      {
+		// The pipe is full. Wake up any readers and wait for them.
+		notifyAll();
+		wait();
+		// The pipe could have been closed while we were waiting.
+	        if (closed)
+		  throw new IOException ("Pipe closed");
+	      }
+	  }
+	catch (InterruptedException ix)
+	  {
+            throw new InterruptedIOException ();
+	  }
+
+	if (in < 0) // The pipe is empty.
+	  in = 0;
+	
+	// Figure out how many bytes from buf can be copied without 
+	// overrunning out or going past the length of the buffer.
+	if (in < out)
+	  copylen = Math.min (len, out - in);
   else
-    return(in + (pipe_size - out));
-}
+	  copylen = Math.min (len, buffer.length - in);
 
-/*************************************************************************/
+	// Copy bytes until the pipe is filled, wrapping if neccessary.
+	System.arraycopy(buf, bufpos, buffer, in, copylen);
+	len -= copylen;
+	bufpos += copylen;
+	in += copylen;
+	if (in == buffer.length)
+	  in = 0;
+      }
+    // Notify readers that new data is in the pipe.
+    notifyAll();
+  }
 
-/**
-  * 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 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.
   *
-  * @return The byte read or -1 if end of stream.
-  *
-  * @exception IOException If an error occurs
+    * @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() throws IOException
-{
+  public 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);
+    int r = read(read_buf, 0, 1);
 
-  if (bytes_read == -1)
-    return(-1);
+    if (r == -1)
+      return -1;
   else
-    return((read_buf[0] & 0xFF));
-}
-
-/*************************************************************************/
+      return read_buf[0];
+  }
 
-/**
+  /**
   * 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.
+    * because the end of the stream was reached - ie close() was called on the
+    * connected PipedOutputStream.
   * <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.
+    *
+    * @exception IOException If <code>close()/code> was called on this Piped
+    *                        InputStream.
   */
-public synchronized int
-read(byte[] buf, int offset, int len) throws IOException
-{
-  if (!ever_connected)
-    throw new IOException("Not connected"); 
+  public synchronized int read(byte[] buf, int offset, int len)
+    throws IOException
+  {
+    if (source == null)
+      throw new IOException ("Not connected");
+    if (closed)
+      throw new IOException ("Pipe closed");
 
-  int bytes_read = 0;
-  for (;;)
+    // If the buffer is empty, wait until there is something in the pipe 
+    // to read.
+    try
     {
-      // If there are bytes, take them.
-      if (in != -1)
+	while (in < 0)
         {
-          int desired_bytes = len - bytes_read;
-
-          // We are in a "wrap" condition.
-          if (out >= in)
+	    if (source.closed)
+	      return -1;
+	    wait();
+	  }
+      }
+    catch (InterruptedException ix)
             {
-	      desired_bytes = Math.min (desired_bytes, pipe_size - out);
-
-	      System.arraycopy (buffer, out, buf, offset + bytes_read,
-				desired_bytes);
+        throw new InterruptedIOException();
+      }
 
-	      bytes_read += desired_bytes;
-	      out += desired_bytes;
-	      desired_bytes = len - bytes_read;
+    int total = 0;
+    int copylen;
 
-	      if (out == pipe_size)
+    while (true)
 		{
-		  out = 0;
-		  // OUT has wrapped.  Make sure that we don't falsely
-		  // indicate that the buffer is full.
-		  if (in == 0)
-		    in = -1;
-		}
+	// Figure out how many bytes from the pipe can be copied without 
+	// overrunning in or going past the length of buf.
+	if (out < in)
+	  copylen = Math.min (len, in - out);
+	else
+	  copylen = Math.min (len, buffer.length - out);
 
-	      notifyAll();
-            }
+        System.arraycopy (buffer, out, buf, offset, copylen);
+	offset += copylen;
+	len -= copylen;
+	out += copylen;
+	total += copylen;
 
-          // We are in a "no wrap".  This can be triggered by
-          // fall-through from the above.
-          if (in > out)
-            {
-	      desired_bytes = Math.min (desired_bytes, in - 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 == buffer.length)
+	  out = 0;
 
 	      if (out == in)
 		{
-		  // Don't falsely indicate that the buffer is full.
-		  out = 0;
+	    // Pipe is now empty.
 		  in = -1;
-		}
-              else if (out == pipe_size)
                 out = 0;
-
-              notifyAll();
             }
-        }
-
-      // Return when we've read something.  A short return is ok.
-      // Also return in the case where LEN==0.
-      if (bytes_read > 0 || bytes_read == len)
-	return(bytes_read);
 
-      // Handle the case where the end of stream was encountered.
-      if (closed)
+        // If output buffer is filled or the pipe is empty, we're done.
+	if (len == 0 || in == -1)
         {
-	  if (in == -1)
-	    {
-	      // The stream is closed and empty.  We've already
-	      // returned if bytes were read.  So we know EOF is the
-	      // only answer.
-	      return -1;
-	    }
-
-	  // I think this shouldn't happen.  I don't think there is a
-	  // way to get here when nothing has been read but there are
-	  // bytes in the buffer.  Still...
-	  continue;
+	    // Notify any waiting outputstream that there is now space
+	    // to write.
+	    notifyAll();
+	    return total;
         }
-
-      // Wait for a byte to be received.
-      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.
-  *
-  * @param byte_received The byte to write to this stream
-  *
-  * @exception IOException if error occurs
-  *
-  */
-protected synchronized void
-receive(int byte_received) throws IOException
-{
-  // This is really slow, but it has the benefit of not duplicating
-  // the complicated machinery in receive(byte[],int,int).
-  byte[] buf = new byte[1];
-  buf[0] = (byte) (byte_received & 0xff);
-  receive (buf, 0, 1);
-}
-
-/*************************************************************************/
 
-/**
-  * 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>.
+  /**
+    * 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.
   *
-  * @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.
+    * @return The number of bytes that can be read before blocking might occur
   *
   * @exception IOException If an error occurs
   */
-synchronized void
-receive(byte[] buf, int offset, int len) throws IOException
-{
-  if (len <= 0)
-    return;
-
-  int total_written = 0;
- outer:
-  while (total_written < len)
-    {
-      // If the buffer is full, then wait.
-      // Also, if we are at the end of the buffer and OUT is 0, wait.
-      if (! (in == out
-	     || (in == pipe_size - 1 && out == 0)))
-        {
-          // This is the "no wrap" situation
-	  if (in > out)
-            {
-              int bytes_written = 0;
-              if ((pipe_size - in) > (len - total_written))
-                bytes_written = (len - total_written);
-              else if (out == 0)
-                bytes_written = (pipe_size - in) - 1;
-              else 
-                bytes_written = (pipe_size - in);
-
-              if (bytes_written > 0) 
-		{
-		  System.arraycopy(buf, offset + total_written, buffer, in, 
-				   bytes_written);
-		  total_written += bytes_written;
-		  in += bytes_written;
-
-		  if (in == pipe_size)
-		    in = 0;
-
-		  notifyAll();
-		}
-            }
-
-          // This is the "wrap" situtation
-          if ((out > in) && (total_written != len))
+  public synchronized int available() throws IOException
             {
-              int bytes_written = 0;
+    // The JDK 1.3 implementation does not appear to check for the closed or 
+    // unconnected stream conditions here.
 
-              // Do special processing if we are at the beginning
-              if (in == -1)
-                {
-                  in = 0;
-		  bytes_written = Math.min (len - total_written, pipe_size);
-                }
+    if (in < 0)
+      return 0;
+    else if (out < in)
+      return in - out;
               else
-		{
-		  bytes_written = Math.min (len - total_written,
-					    out - in);
+      return (buffer.length - out) + in;
 		}
-
-              System.arraycopy(buf, offset + total_written, buffer, in,
-                               bytes_written);
-              total_written += bytes_written;
-              in += bytes_written;
-
-              if (in == pipe_size)
-                in = 0;
 
-              notifyAll();
-            }
-        }
-      // Wait for some reads to occur before we write anything.
-      else
-        {
-          try
+  /**
+  * 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
             {
-              wait();
-            }
-          catch (InterruptedException e) { ; }
-        }
+    closed = true;
+    // Wake any thread which may be in receive() waiting to write data.
+    notifyAll();
     }
 }
-
-} // class PipedInputStream
Index: PipedOutputStream.java
===================================================================
RCS file: /cvs/gcc/egcs/libjava/java/io/PipedOutputStream.java,v
retrieving revision 1.6
diff -u -b -r1.6 PipedOutputStream.java
--- PipedOutputStream.java	2000/04/21 21:41:32	1.6
+++ PipedOutputStream.java	2001/01/05 07:57:13
@@ -1,5 +1,5 @@
 /* PipedOutputStream.java -- Write portion of piped streams.
-   Copyright (C) 1998, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -27,6 +27,10 @@
 
 package java.io;
 
+// NOTE: This implementation is very similar to that of PipedWriter.  If you 
+// fix a bug in here, chances are you should make a similar change to the 
+// PipedWriter code.
+
 /**
   * This class writes its bytes to a <code>PipedInputStream</code> to 
   * which it is connected.
@@ -36,171 +40,131 @@
   * 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
- */
-
-/**
-  * This is the <code>PipedInputStream</code> 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 <code>close</code> method has ever
-  * been called for this stream.
-  */
-private boolean closed;
-
-/*************************************************************************/
-
-/**
-  * This method initializes a new <code>PipedOutputStream</code> instance.
-  * This constructor creates an unconnected object.  It must be connected
-  * to a <code>PipedInputStream</code> object using the <code>connect</code>
+  /** Target PipedInputStream to which this is connected. Null only if this 
+    * OutputStream hasn't been connected yet. */
+  PipedInputStream sink;
+  
+  /** Set to true if close() has been called on this OutputStream. */
+  boolean closed;
+  
+  /** 
+    * Create an unconnected PipedOutputStream.  It must be connected
+    * to a <code>PipedInputStream</code> using the <code>connect</code>
   * method prior to writing any data or an exception will be thrown.
   */
-public
-PipedOutputStream()
-{
-  ; // Do Nothing
-}
+  public PipedOutputStream()
+  {
+  }
 
-/*************************************************************************/
-
-/**
-  * This method initializes a new <code>PipedOutputStream</code> instance
+  /**
+     * Create a new <code>PipedOutputStream</code> instance
   * to write to the specified <code>PipedInputStream</code>.  This stream
   * is then ready for writing.
-  *
-  * @param snk The <code>PipedInputStream</code> to connect this stream to.
   *
-  * @exception IOException If an error occurs
-  */
-public
-PipedOutputStream(PipedInputStream snk) throws IOException
-{
-  connect(snk);
-} 
-
-/*************************************************************************/
-
-/*
- * Instance Methods
- */
-
-/**
-  * This method connects this object to the specified 
-  * <code>PipedInputStream</code> 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 <code>PipedInputStream</code> to connect this stream to
-  *
-  * @exception IOException If an error occurs
-  */
-public synchronized void
-connect(PipedInputStream 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");
-
-  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
+    * @param sink The <code>PipedInputStream</code> to connect this stream to.
   *
-  * @exception IOException If an error occurs
+    * @exception IOException If <code>sink</code> has already been connected 
+    *                        to a different PipedOutputStream.
   */
-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 <code>PipedInputStream</code> to which
-  * this object is connected has a full buffer.
+  public PipedOutputStream(PipedInputStream sink) throws IOException
+  {
+    sink.connect(this);
+  }
+
+  /**
+    * Connects this object to the specified <code>PipedInputStream</code> 
+    * object.  This stream will then be ready for writing.
+    *
+    * @param sink The <code>PipedInputStream</code> to connect this stream to
+    *
+    * @exception IOException If the stream has not been connected or has
+    *                        been closed.
+    */
+  public void connect(PipedInputStream sink) throws IOException
+  {
+    if (sink != null)
+      throw new IOException ("Already connected");
+    sink.connect(this);
+  }
+
+  /**
+    * Write a single byte of date to the stream.  Note that this method will 
+    * block if the <code>PipedInputStream</code> to which this object is 
+    * connected has a full buffer.
   *
   * @param b The byte of data to be written, passed as an <code>int</code>.
   *
-  * @exception IOException If an error occurs
+    * @exception IOException If the stream has not been connected or has
+    *                        been closed.
   */
-public synchronized void
-write(int b) throws IOException
-{
-  snk.receive (b);
-}
+  public void write(int b) throws IOException
+  {
+    if (sink == null)
+      throw new IOException ("Not connected");
+    if (closed)
+      throw new IOException ("Pipe closed");
 
-/*************************************************************************/
+    sink.receive (b);
+  }
 
-/**
+  /**
   * This method writes <code>len</code> bytes of data from the byte array
   * <code>buf</code> starting at index <code>offset</code> in the array
   * to the stream.  Note that this method will block if the  
   * <code>PipedInputStream</code> 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 buf The array containing bytes to write to the 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
+    * @exception IOException If the stream has not been connected or has
+    *                        been closed.
   */
-public void
-write(byte[] buf, int offset, int len) throws IOException
-{
-  snk.receive (buf, 0, len);
-}
+  public void write(byte[] b, int off, int len) throws IOException
+  {
+    if (sink == null)
+      throw new IOException ("Not connected");
+    if (closed)
+      throw new IOException ("Pipe closed");
 
-/*************************************************************************/
+    sink.receive (b, off, len);
+  }
 
-/**
-  * This method flushes any unwritten bytes to the output and notifies
-  * any waiting readers that the pipe is ready to be read.
+  /**
+    * This method does nothing.
   *
-  * @exception IOException If an error occurs.
+    * @exception IOException If the stream is closed.
+    * @specnote You'd think that this method would block until the sink
+    *           had read all available data. Thats not the case - this method
+    *           appears to be a no-op?
   */
-public void
-flush() throws IOException
-{
-  return;
-}
+  public void flush()
+  {
+  }
 
-} // class PipedOutputStream
-
+  /**
+    * 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()
+  {
+    // A close call on an unconnected PipedOutputStream has no effect.
+    if (sink != null)
+      {
+	closed = true;
+	// Notify any waiting readers that the stream is now closed.
+	synchronized (sink)
+	{	  
+	  sink.notifyAll();
+	}
+      }
+  }
+}
Index: PipedReader.java
===================================================================
RCS file: /cvs/gcc/egcs/libjava/java/io/PipedReader.java,v
retrieving revision 1.5
diff -u -b -r1.5 PipedReader.java
--- PipedReader.java	2000/04/21 20:12:05	1.5
+++ PipedReader.java	2001/01/05 07:57:13
@@ -1,5 +1,5 @@
-/* PipedReader.java -- Input stream that reads from an output stream
-   Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+/* PipedReader.java -- Read portion of piped character streams.
+   Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -24,479 +24,312 @@
 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;
 
+// NOTE: This implementation is very similar to that of PipedInputStream. 
+// If you fix a bug in here, chances are you should make a similar change to 
+// the PipedInputStream code.
+
 /**
-  * This class is an input stream that reads its chars from an output stream
-  * to which it is connected. 
+  * An input stream that reads characters from a piped writer 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
+  * @specnote The JDK implementation appears to have some undocumented 
+  *           functionality where it keeps track of what thread is writing
+  *           to pipe and throws an IOException if that thread susequently
+  *           dies. This behaviour seems dubious and unreliable - we don't
+  *           implement it.
   *
   * @author Aaron M. Renn (arenn@urbanophile.com)
   */
 public class PipedReader extends Reader
 {
+  /** PipedWriter to which this is connected. Null only if this 
+    * Reader hasn't been connected yet. */
+  PipedWriter source;
 
-/*************************************************************************/
-
-/*
- * Class Variables
- */
-
-/**
-  * 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;
+  /** Set to true if close() has been called on this Reader. */
+  boolean closed;
 
-/**
-  * 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>.
+  /**
+    * The size of the internal buffer used for input/output.
   */
-private static int pipe_size;
+  static final int PIPE_SIZE = 2048;
 
-static
-{
-  pipe_size =  Integer.getInteger("gnu.java.io.PipedReader.pipe_size",
-                                  PIPE_SIZE).intValue();
-}
-
-/*************************************************************************/
-
-/*
- * Instance Variables
- */
-
-/**
+  /**
   * 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];
+  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
+  /**
+    * The index into buffer where the next char from the connected
+    * <code>PipedWriter</code> will be written. If this variable is 
+    * equal to <code>out</code>, then the buffer is full. If set to < 0,
+    * the buffer is empty.
   */
-private int in = -1;
+  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;
+  int out = 0;
 
-/*************************************************************************/
+  /** Buffer used to implement single-argument read/receive */
+  char[] read_buf = new char[1];
 
-/*
- * Constructors
+  /**
+    * 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.
  */
-
-/**
-  * 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;
-}
-
-/*************************************************************************/
+  public PipedReader()
+  {
+  }
 
-/**
+  /**
   * 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.
+    * it to the passed in <code>PipedWriter</code>. The stream is then 
+    * ready for reading.
   *
-  * @param src The <code>PipedWriter</code> to connect this stream to
+    * @param source The <code>PipedWriter</code> to connect this stream to
   *
-  * @exception IOException If an error occurs
+    * @exception IOException If <code>source</code> is already connected.
   */
-public
-PipedReader(PipedWriter src) throws IOException
-{
-  connect(src);
-}
-
-/*************************************************************************/
-
-/*
- * Instance Variables
- */
+  public PipedReader(PipedWriter source) throws IOException
+  {
+    connect(source);
+  }
 
-/**
+  /**
   * 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
+    * @exception IOException If this PipedReader or <code>source</code> 
+    *                        has been connected already.
   */
-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)
+  public void connect(PipedWriter source) throws IOException
     {
-      src.connect(this);
+    // The JDK (1.3) does not appear to check for a previously closed 
+    // connection here.
 
-      ever_connected = true;
-    } // synchronized
-}
+    if (this.source != null || source.sink != null)
+      throw new IOException ("Already connected");
 
-/*************************************************************************/
+    source.sink = this;
+    this.source = source;
+  }
 
-/**
-  * This methods closes the stream so that no more data can be read
-  * from it.
+  /**
+    * This method is used by the connected <code>PipedWriter</code> to
+    * write chars into the buffer.
+    *
+    * @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
+    * @specnote This code should be in PipedWriter.write, but we
+    *           put it here in order to support that bizarre recieve(int)
+    *           method.
   */
-public void
-close() throws IOException
-{
-  synchronized (lock)
+  synchronized void receive(char[] buf, int offset, int len)
+    throws IOException
     {
-      closed = true;
-      notifyAll();
-    } // synchronized
-}
+    if (closed)
+      throw new IOException ("Pipe closed");
 
-/*************************************************************************/
+    int bufpos = offset;
+    int copylen;
 
-/**
-  * 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
-{
-  if (in == -1)
-    return(false);
-
-  if (out == (in - 1))
-    return(false);
+    while (len > 0)
+      {
+        try
+	  {
+	    while (in == out)
+	      {
+		// The pipe is full. Wake up any readers and wait for them.
+		notifyAll();
+		wait();
+		// The pipe could have been closed while we were waiting.
+	        if (closed)
+		  throw new IOException ("Pipe closed");
+	      }
+	  }
+	catch (InterruptedException ix)
+	  {
+            throw new InterruptedIOException ();
+	  }
 
-  if ((out == pipe_size) && (in == 0))
-    return(false);
+	if (in < 0) // The pipe is empty.
+	  in = 0;
 
-  return(true);
-}
+	// Figure out how many chars from buf can be copied without 
+	// overrunning out or going past the length of the buffer.
+	if (in < out)
+	  copylen = Math.min (len, out - in);
+	else
+	  copylen = Math.min (len, buffer.length - in);
 
-/*************************************************************************/
+	// Copy chars until the pipe is filled, wrapping if neccessary.
+	System.arraycopy(buf, bufpos, buffer, in, copylen);
+	len -= copylen;
+	bufpos += copylen;
+	in += copylen;
+	if (in == buffer.length)
+	  in = 0;
+      }
+    // Notify readers that new data is in the pipe.
+    notifyAll();
+  }
 
-/**
-  * This method reads a single char from the pipe and returns it as an
-  * <code>int</code>.
+  /**
+    * 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.
-  *
-  * @return An char read from the pipe, or -1 if the end of stream is 
-  * reached.
   *
-  * @exception IOException If an error occurs.
+    * @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() throws IOException
-{
-  char[] buf = new char[1];
+  public int read() throws IOException
+  {
+    // Method operates by calling the multichar 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.
 
-  return(read(buf, 0, buf.length));
-}
+    int r = read(read_buf, 0, 1);
 
-/*************************************************************************/
+    if (r == -1)
+      return -1;
+    else
+      return read_buf[0];
+  }
 
-/**
-  * This method reads chars from the stream into a caller supplied buffer.
+  /**
+    * This method reads characters 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.
+    * because the end of the stream was reached - ie close() was called on the
+    * connected PipedWriter.
   * <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.
+    *
+    * @exception IOException If <code>close()/code> was called on this Piped
+    *                        Reader.
   */
-public int
-read(char[] buf, int offset, int len) throws IOException
-{
-  if (!ever_connected)
-    throw new IOException("Not connected"); 
+  public synchronized int read(char[] buf, int offset, int len)
+    throws IOException
+  {
+    if (source == null)
+      throw new IOException ("Not connected");
+    if (closed)
+      throw new IOException ("Pipe closed");
 
-  synchronized (lock)
+    // If the buffer is empty, wait until there is something in the pipe 
+    // to read.
+    try
     {
-      int chars_read = 0;
-      for (;;)
+	while (in < 0)
 	{
-	  // If there are chars, take them
-	  if (in != -1)
+	    if (source.closed)
+	      return -1;
+	    wait();
+	  }
+      }
+    catch (InterruptedException ix)
 	    {
-	      int desired_chars = len - chars_read;
+        throw new InterruptedIOException();
+      }
 
-	      // We are in a "wrap" condition
-	      if (out > in)
-		{
-		  if (desired_chars > (pipe_size - out))
+    int total = 0;
+    int copylen;
+    
+    while (true)
 		    {
-		      if (in == 0)
-			desired_chars = (pipe_size - out) - 1;
+	// Figure out how many chars from the pipe can be copied without 
+	// overrunning in or going past the length of buf.
+	if (out < in)
+	  copylen = Math.min (len, in - out);
 		      else
-			desired_chars = pipe_size - out;
-
-		      System.arraycopy(buffer, out, buf, offset + chars_read,
-				       desired_chars);
+	  copylen = Math.min (len, buffer.length - out);
 
-		      chars_read += desired_chars;
-		      out += desired_chars;
-		      desired_chars = len - chars_read;
+        System.arraycopy (buffer, out, buf, offset, copylen);
+	offset += copylen;
+	len -= copylen;
+	out += copylen;
+	total += copylen;
 
-		      if (out == pipe_size)
+	if (out == buffer.length)
 			out = 0;
 
-		      notifyAll();
-		    }
-		  else
+	if (out == in)
 		    {
-		      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)
+	    // Pipe is now empty.
+	    in = -1;
 			out = 0;
-
-		      notifyAll();
-		    }
 		}
  
-	      // We are in a "no wrap" or condition (can also be fall through
-	      // from above
-	      if (in > out)
+        // If output buffer is filled or the pipe is empty, we're done.
+	if (len == 0 || in == -1)
 		{
-		  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;
-
+	    // Notify any waiting Writer that there is now space
+	    // to write.
 		  notifyAll();
+	    return total;
 		}
 	    }
-
-	  // If we are done, return
-	  if (chars_read == len)
-	    return(chars_read);
-
-	  // Return a short count if necessary
-	  if (chars_read > 0 && chars_read < len)
-	    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)
+  public synchronized boolean ready() throws IOException
 		{
-		  buf[offset + chars_read] = buffer[out];
-		  in = -1;
-		  ++out;
-		  ++chars_read;
-		}
+    // The JDK 1.3 implementation does not appear to check for the closed or 
+    // unconnected stream conditions here.
+    
+    if (in < 0)
+      return false;
 
-	      if (chars_read != 0)
-		return(chars_read);
+    int count;
+    if (out < in)
+      count = in - out;
 	      else
-		return(-1);
-	    }
+      count = (buffer.length - out) - in;
 
-	  // Wait for a char to be read
-	  try
-	    {
-	      wait();
+    return (count > 0);
 	    }
-	  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.
+  /**
+  * This methods closes the stream so that no more data can be read
+  * from it.
   *
   * @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)
+  public synchronized void close() throws IOException
 		{
-		  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;
-
+    closed = true;
+    // Wake any thread which may be in receive() waiting to write data.
 		  notifyAll();
 		}
-	    }
-	  // Wait for some reads to occur before we write anything.
-	  else
-	    {
-	      try
-		{
-		  wait();
-		}
-	      catch (InterruptedException e) { ; }
-	    }
-	}
-    } // synchronized
 }
-
-} // class PipedReader
-
Index: PipedWriter.java
===================================================================
RCS file: /cvs/gcc/egcs/libjava/java/io/PipedWriter.java,v
retrieving revision 1.4
diff -u -b -r1.4 PipedWriter.java
--- PipedWriter.java	2000/04/21 01:18:16	1.4
+++ PipedWriter.java	2001/01/05 07:57:13
@@ -1,5 +1,5 @@
-/* PipedWriter.java -- Write portion of piped streams.
-   Copyright (C) 1998 Free Software Foundation, Inc.
+/* PipedWriter.java -- Write portion of piped character streams.
+   Copyright (C) 1998, 2000, 2001 Free Software Foundation, Inc.
 
 This file is part of GNU Classpath.
 
@@ -27,6 +27,10 @@
 
 package java.io;
 
+// NOTE: This implementation is very similar to that of PipedOutputStream. 
+// If you fix a bug in here, chances are you should make a similar change to 
+// the PipedOutputStream code.
+
 /**
   * This class writes its chars to a <code>PipedReader</code> to 
   * which it is connected.
@@ -36,182 +40,130 @@
   * 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
- */
-
-/**
-  * This is the <code>PipedReader</code> 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 <code>close</code> method has ever
-  * been called for this stream.
-  */
-private boolean closed;
-
-/*************************************************************************/
-
-/**
-  * This method initializes a new <code>PipedWriter</code> instance.
-  * This constructor creates an unconnected object.  It must be connected
-  * to a <code>PipedReader</code> object using the <code>connect</code>
+  /** Target PipedReader to which this is connected. Null only if this 
+    * Writer hasn't been connected yet. */
+  PipedReader sink;
+  
+  /** Set to true if close() has been called on this Writer. */
+  boolean closed;
+
+  /** Buffer used to implement single-argument write */
+  char[] read_buf = new char[1];
+  
+  /** 
+    * Create an unconnected PipedWriter.  It must be connected
+    * to a <code>PipedReader</code> using the <code>connect</code>
   * method prior to writing any data or an exception will be thrown.
   */
-public
-PipedWriter()
-{
-  ; // Do Nothing
-}
+  public PipedWriter()
+  {
+  }
 
-/*************************************************************************/
-
-/**
-  * This method initializes a new <code>PipedWriter</code> instance
+  /**
+     * Create a new <code>PipedWriter</code> instance
   * to write to the specified <code>PipedReader</code>.  This stream
   * is then ready for writing.
   *
-  * @param snk The <code>PipedReader</code> to connect this stream to.
+    * @param sink The <code>PipedReader</code> to connect this stream to.
   *
-  * @exception IOException If an error occurs
+    * @exception IOException If <code>sink</code> has already been connected 
+    *                        to a different PipedWriter.
   */
-public
-PipedWriter(PipedReader snk) throws IOException
-{
-  connect(snk);
-} 
+  public PipedWriter(PipedReader sink) throws IOException
+  {
+    sink.connect(this);
+  }
 
-/*************************************************************************/
-
-/*
- * Instance Methods
- */
-
-/**
-  * This method connects this object to the specified 
-  * <code>PipedReader</code> 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.
+  /**
+    * Connects this object to the specified <code>PipedReader</code> 
+    * object.  This stream will then be ready for writing.
   *
-  * @param snk The <code>PipedReader</code> 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
+    * @param sink The <code>PipedReader</code> to connect this stream to
   *
-  * @exception IOException If an error occurs
+    * @exception IOException If the stream has not been connected or has
+    *                        been closed.
   */
-public void
-close() throws IOException
-{
-  synchronized (lock) {
-
-  closed = true;
-  snk.close();
-  notifyAll();
-
-  } // synchronized
-}
+  public void connect(PipedReader sink) throws IOException
+  {
+    if (sink != null)
+      throw new IOException ("Already connected");
+    sink.connect(this);
+  }
 
-/*************************************************************************/
-
-/**
-  * This methods writes a single byte of data to the pipe.  This call may
-  * block if the pipe is full.
+  /**
+    * Write a single char of date to the stream.  Note that this method will 
+    * block if the <code>PipedReader</code> to which this object is 
+    * connected has a full buffer.
   *
-  * @param c The <code>char</code> to write, passed as an <code>int</code>.
+    * @param b The char of data to be written, passed as an <code>int</code>.
   *
-  * @exception IOException If an error occurs.
+    * @exception IOException If the stream has not been connected or has
+    *                        been closed.
   */
-public void
-write(int c) throws IOException
-{
-  char[] buf = new char[1];
-  buf[0] = (char)c;
+  public void write(char b) throws IOException
+  {
+    read_buf[0] = b;
+    sink.receive (read_buf, 0, 1);
+  }
 
-  write(buf, 0, buf.length);
-} 
-
-/*************************************************************************/
-
-/**
+  /**
   * This method writes <code>len</code> chars of data from the char array
   * <code>buf</code> starting at index <code>offset</code> in the array
   * to the stream.  Note that this method will block if the  
   * <code>PipedReader</code> 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 buf The array containing chars to write to the 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
+    * @exception IOException If the stream has not been connected or has
+    *                        been closed.
   */
-public void
-write(char[] buf, int offset, int len) throws IOException
-{
-  snk.write(buf, 0, len);
-}
+  public void write(char[] b, int off, int len) throws IOException
+  {
+    if (sink == null)
+      throw new IOException ("Not connected");
+    if (closed)
+      throw new IOException ("Pipe closed");
 
-/*************************************************************************/
+    sink.receive (b, off, 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.
+  /**
+    * This method does nothing.
+    *
+    * @exception IOException If the stream is closed.
+    * @specnote You'd think that this method would block until the sink
+    *           had read all available data. Thats not the case - this method
+    *           appears to be a no-op?
+    */
+  public void flush()
+  {
+  }
+  
+  /**
+    * 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
-flush() throws IOException
-{
-  ; // Do Nothing
+  public void close()
+  {
+    // A close call on an unconnected PipedWriter has no effect.
+    if (sink != null)
+      {
+	closed = true;
+	// Notify any waiting readers that the stream is now closed.
+	synchronized (sink)
+	{	  
+	  sink.notifyAll();
+	}
+      }
+  }
 }
-
-} // class PipedWriter
-

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]