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 - 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 - 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");
+ }
+
}
signature.asc
Description: This is a digitally signed message part.
