The patch seems to solve both bugs while doing the right thing.

It hasn't been tested well though and the last thing I want is new bugs. 
Does anybody have or know a application which utilises the 
BufferedInputReader with marks?

If it works I'll finish it. The read(byte[]) method doesn't work correct 
anymore now that read(byte[],int,int) has been fixed.
Index: BufferedInputStream.java
===================================================================
--- BufferedInputStream.java	(revision 242)
+++ BufferedInputStream.java	(working copy)
@@ -143,28 +143,6 @@
     protected int marklimit;
 
     /**
-     * Check to make sure that underlying input stream has not been
-     * nulled out due to close; if not return it;
-     */
-    private InputStream getInIfOpen() throws IOException {
-        InputStream input = in;
-	if (input == null)
-	    throw new IOException("Stream closed");
-        return input;
-    }
-
-    /**
-     * Check to make sure that buffer has not been nulled out due to
-     * close; if not return it;
-     */
-    private byte[] getBufIfOpen() throws IOException {
-        byte[] buffer = buf;
-	if (buffer == null)
-	    throw new IOException("Stream closed");
-        return buffer;
-    }
-
-    /**
      * Creates a <code>BufferedInputStream</code>
      * and saves its  argument, the input stream
      * <code>in</code>, for later use. An internal
@@ -197,45 +175,38 @@
     }
 
     /**
-     * Fills the buffer with more data, taking into account
-     * shuffling and other tricks for dealing with marks.
-     * Assumes that it is being called by a synchronized method.
-     * This method also assumes that all data has already been read in,
-     * hence pos > count.
+     * Fills the empty buffer without blocking.
+     * Takes care of the marks too.
      */
     private void fill() throws IOException {
-        byte[] buffer = getBufIfOpen();
-	if (markpos < 0)
-	    pos = 0;		/* no mark: throw away the buffer */
-	else if (pos >= buffer.length)	/* no room left in buffer */
-	    if (markpos > 0) {	/* can throw away early part of the buffer */
-		int sz = pos - markpos;
-		System.arraycopy(buffer, markpos, buffer, 0, sz);
-		pos = sz;
-		markpos = 0;
-	    } else if (buffer.length >= marklimit) {
-		markpos = -1;	/* buffer got too big, invalidate mark */
-		pos = 0;	/* drop buffer contents */
-	    } else {		/* grow buffer */
-		int nsz = pos * 2;
-		if (nsz > marklimit)
-		    nsz = marklimit;
-		byte nbuf[] = new byte[nsz];
-		System.arraycopy(buffer, 0, nbuf, 0, pos);
-                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
-                    // Can't replace buf if there was an async close.
-                    // Note: This would need to be changed if fill()
-                    // is ever made accessible to multiple threads.
-                    // But for now, the only way CAS can fail is via close.
-                    // assert buf == null;
-                    throw new IOException("Stream closed");
-                }
-                buffer = nbuf;
-	    }
-        count = pos;
-	int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
-        if (n > 0)
-            count = n + pos;
+        assert pos >= count;
+        if (markpos < 0)	// no mark: discard buffer
+            pos = 0;
+        else if (markpos > 0) {	// move data from markpos back to buf[0]
+            int readAheadSize = pos - markpos;
+            System.arraycopy(buf, markpos, buf, 0, readAheadSize);
+            pos = readAheadSize;
+            markpos = 0;
+        } else {
+            assert markpos == 0;
+            if (buf.length < marklimit) {	// double the size of buf
+                int newSize = pos * 2;
+                if (newSize > marklimit)
+                    newSize = marklimit;
+                byte[] newBuf = new byte[newSize];
+                System.arraycopy(buf, 0, newBuf, 0, pos);
+                buf = newBuf;
+            }
+        }
+
+        int read = buf.length - pos;
+        int available = in.available();
+        if (available < read)
+            read = available;
+        read = in.read(buf, pos, read);
+	if (read < 0)
+            read = 0;
+        count = pos + read;
     }
 
     /**
@@ -251,39 +222,16 @@
      * @see        java.io.FilterInputStream#in
      */
     public synchronized int read() throws IOException {
-	if (pos >= count) {
-	    fill();
-	    if (pos >= count)
-		return -1;
-	}
-	return getBufIfOpen()[pos++] & 0xff;
+        verifyNotClose();
+        if (pos >= count) {
+            fill();
+            if (pos >= count)	// block & wait or return -1
+                return in.read();
+        }
+        return buf[pos++];
     }
 
     /**
-     * Read characters into a portion of an array, reading from the underlying
-     * stream at most once if necessary.
-     */
-    private int read1(byte[] b, int off, int len) throws IOException {
-	int avail = count - pos;
-	if (avail <= 0) {
-	    /* If the requested length is at least as large as the buffer, and
-	       if there is no mark/reset activity, do not bother to copy the
-	       bytes into the local buffer.  In this way buffered streams will
-	       cascade harmlessly. */
-	    if (len >= getBufIfOpen().length && markpos < 0) {
-		return getInIfOpen().read(b, off, len);
-	    }
-	    fill();
-	    avail = count - pos;
-	    if (avail <= 0) return -1;
-	}
-	int cnt = (avail < len) ? avail : len;
-	System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
-	pos += cnt;
-	return cnt;
-    }
-
-    /**
      * Reads bytes from this byte-input stream into the specified byte array,
      * starting at the given offset.
      *
@@ -323,80 +271,91 @@
     public synchronized int read(byte b[], int off, int len)
 	throws IOException
     {
-        getBufIfOpen(); // Check for closed stream
-        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
-	    throw new IndexOutOfBoundsException();
-	} else if (len == 0) {
+        // NullPointerException if b is null:
+        if (off < 0 || len < 0 || (b.length - off < len))
+            throw new IndexOutOfBoundsException();
+        verifyNotClose();
+        if (len == 0)
             return 0;
+        int avail = count - pos;
+        assert avail >= 0;	// see javadoc count & pos
+
+        if (avail >= len) {
+            System.arraycopy(buf, pos, b, off, len);
+            pos += len;
+            return len;
         }
 
-	int n = 0;
-        for (;;) {
-            int nread = read1(b, off + n, len - n);
-            if (nread <= 0) 
-                return (n == 0) ? nread : n;
-            n += nread;
-            if (n >= len)
-                return n;
-            // if not closed but no bytes available, return
-            InputStream input = in;
-            if (input != null && input.available() <= 0)
-                return n;
+        if (avail != 0) {
+            System.arraycopy(buf, pos, b, off, avail);
+            pos += len;
         }
+        int done = avail;
+
+        do {
+            fill();
+            avail = count - pos;
+            assert avail >= 0;
+            if (avail == 0)
+                break;
+            int read = len - done;
+            assert read > 0;
+            if (read > avail)
+                read = avail;
+            System.arraycopy(buf, pos, b, off + done, read);
+            pos += read;
+            done += read;
+        } while (done != len);
+        return done;
     }
 
     /**
-     * See the general contract of the <code>skip</code>
-     * method of <code>InputStream</code>.
-     *
-     * @exception  IOException  if the stream does not support seek,
-     *				or if this input stream has been closed by
-     *				invoking its [EMAIL PROTECTED] #close()} method, or an
-     *				I/O error occurs.
+     * Skips over and discards [EMAIL PROTECTED] n} bytes of data from this input stream.
+     * This implementation never skips more bytes than [EMAIL PROTECTED] #available()}.
+     * @see InputStream#skip(long)
      */
     public synchronized long skip(long n) throws IOException {
-        getBufIfOpen(); // Check for closed stream
-	if (n <= 0) {
-	    return 0;
-	}
-	long avail = count - pos;
-     
-        if (avail <= 0) {
-            // If no mark position set then don't keep in buffer
-            if (markpos <0) 
-                return getInIfOpen().skip(n);
-            
-            // Fill in buffer to save bytes for reset
-            fill();
-            avail = count - pos;
-            if (avail <= 0)
-                return 0;
+        verifyNotClose();
+        if (n <= 0)
+            return 0;
+        long avail = count - pos;
+        assert avail >= 0;	// see javadoc count & pos
+
+        if (avail >= n) {
+            pos += n;
+            return n;
         }
-        
-        long skipped = (avail < n) ? avail : n;
-        pos += skipped;
+
+        // IOException if in does not support seek:
+        long skipped = in.skip(n - avail);
+
+        skipped += avail;
+        assert pos + avail == count;
+        pos = count;
+
         return skipped;
     }
 
     /**
      * Returns an estimate of the number of bytes that can be read (or
      * skipped over) from this input stream without blocking by the next
-     * invocation of a method for this input stream. The next invocation might be
-     * the same thread or another thread.  A single read or skip of this
+     * invocation of a method for this input stream. The next invocation might
+     * be the same thread or another thread.  A single read or skip of this
      * many bytes will not block, but may read or skip fewer bytes.
      * <p>
-     * This method returns the sum of the number of bytes remaining to be read in
-     * the buffer (<code>count&nbsp;- pos</code>) and the result of calling the
-     * [EMAIL PROTECTED] java.io.FilterInputStream#in in}.available().
+     * This method returns the sum of the number of bytes remaining to be read
+     * in the buffer (<code>count&nbsp;- pos</code>) and the result of calling
+     * the [EMAIL PROTECTED] java.io.FilterInputStream#in in}.available().
      *
-     * @return     an estimate of the number of bytes that can be read (or skipped
-     *             over) from this input stream without blocking.
+     * @return     an estimate of the number of bytes that can be read (or
+     *             skipped over) from this input stream without blocking.
      * @exception  IOException  if this input stream has been closed by
      *                          invoking its [EMAIL PROTECTED] #close()} method,
      *                          or an I/O error occurs.
      */
     public synchronized int available() throws IOException {
-	return getInIfOpen().available() + (count - pos);
+        verifyNotClose();
+        return in.available() + (count - pos);
     }
 
     /** 
@@ -429,10 +388,10 @@
      * @see        java.io.BufferedInputStream#mark(int)
      */
     public synchronized void reset() throws IOException {
-        getBufIfOpen(); // Cause exception if closed
-	if (markpos < 0)
-	    throw new IOException("Resetting to invalid mark");
-	pos = markpos;
+        verifyNotClose();
+        if (markpos < 0)
+            throw new IOException("Resetting to invalid mark");
+        pos = markpos;
     }
 
     /**
@@ -472,4 +431,14 @@
             // Else retry in case a new buf was CASed in fill()
         }
     }
+
+    /**
+     * Verifies that the [EMAIL PROTECTED] #close()} method has not been called yet.
+     * @throws IOException if closed.
+     */
+    private void verifyNotClose() throws IOException {
+        if (buf == null || in == null)
+            throw new IOException("Stream closed");
+    }
+
 }

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to