This is the mail archive of the
java-patches@sources.redhat.com
mailing list for the Java project.
PATCH: BufferedReader, DataInputStream merge
- To: java-patches at sources dot redhat dot com
- Subject: PATCH: BufferedReader, DataInputStream merge
- From: Bryce McKinlay <bryce at albatross dot co dot nz>
- Date: Tue, 28 Nov 2000 17:54:24 +1300
This patch merges BufferedReader with classpath. I've gone with the
libgcj implementations on the basis that the classpath one has known
bugs and libgcj's doesn't. I've also made several DataInputStream
methods more efficient and made skipBytes() work as it does in the JDK
1.3. Also, neither classpath nor libgcj correctly checked for a closed
stream in BufferedReader.
regards
[ bryce ]
2000-11-28 Bryce McKinlay <bryce@abatross.co.nz>
* java/io/DataInputStream.java: Merge classpath docs. Call in.read()
directly rather than read() in all cases. Make primitive read
implementations more efficient, as defined in JDK online docs.
(skipBytes): Behave like the JDK's implementation.
* java/io/BufferedReader.java: Merge classpath docs. Check for a
closed stream with checkStatus() whenever an IOException can be
thrown.
(checkStatus): New private method.
Index: BufferedReader.java
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/io/BufferedReader.java,v
retrieving revision 1.4
diff -u -r1.4 BufferedReader.java
--- BufferedReader.java 2000/03/07 19:55:26 1.4
+++ BufferedReader.java 2000/11/28 04:34:02
@@ -1,4 +1,4 @@
-/* Copyright (C) 1998, 1999 Free Software Foundation
+/* Copyright (C) 1998, 1999, 2000 Free Software Foundation
This file is part of libgcj.
@@ -8,15 +8,25 @@
package java.io;
-/**
- * @author Per Bothner <bothner@cygnus.com>
- * @date April 22, 1998.
- */
/* Written using "Java Class Libraries", 2nd edition, plus online
* API docs for JDK 1.2 beta from http://www.javasoft.com.
* Status: Believed complete and correct.
*/
+/**
+ * This subclass of <code>FilterReader</code> buffers input from an
+ * underlying implementation to provide a possibly more efficient read
+ * mechanism. It maintains the buffer and buffer state in instance
+ * variables that are available to subclasses. The default buffer size
+ * of 512 chars can be overridden by the creator of the stream.
+ * <p>
+ * This class also implements mark/reset functionality. It is capable
+ * of remembering any number of input chars, to the limits of
+ * system memory or the size of <code>Integer.MAX_VALUE</code>
+ *
+ * @author Per Bothner <bothner@cygnus.com>
+ * @author Aaron M. Renn <arenn@urbanophile.com>
+ */
public class BufferedReader extends Reader
{
Reader in;
@@ -43,11 +53,25 @@
guaranteed to be >= the read-limit requested in the call to mark. */
int markPos = -1;
+ /**
+ * Create a new <code>BufferedReader</code> that will read from the
+ * specified subordinate stream with a default buffer size of 4096 chars.
+ *
+ * @param in The subordinate stream to read from
+ */
public BufferedReader(Reader in)
{
this(in, 8192);
}
+ /**
+ * Create a new <code>BufferedReader</code> that will read from the
+ * specified subordinate stream with a buffer size that is specified by the
+ * caller.
+ *
+ * @param in The subordinate stream to read from
+ * @param bufsize The buffer size to use
+ */
public BufferedReader(Reader in, int size)
{
super(in.lock);
@@ -55,6 +79,11 @@
buffer = new char[size];
}
+ /**
+ * This method closes the stream
+ *
+ * @exception IOException If an error occurs
+ */
public void close() throws IOException
{
synchronized (lock)
@@ -66,13 +95,40 @@
}
}
+ /**
+ * Returns <code>true</code> to indicate that this class supports mark/reset
+ * functionality.
+ *
+ * @return <code>true</code>
+ */
public boolean markSupported()
{
return true;
}
+ /**
+ * Mark a position in the input to which the stream can be
+ * "reset" by calling the <code>reset()</code> method. The parameter
+ * <code>readlimit</code> is the number of chars that can be read from the
+ * stream after setting the mark before the mark becomes invalid. For
+ * example, if <code>mark()</code> is called with a read limit of 10, then
+ * when 11 chars of data are read from the stream before the
+ * <code>reset()</code> method is called, then the mark is invalid and the
+ * stream object instance is not required to remember the mark.
+ * <p>
+ * Note that the number of chars that can be remembered by this method
+ * can be greater than the size of the internal read buffer. It is also
+ * not dependent on the subordinate stream supporting mark/reset
+ * functionality.
+ *
+ * @param readlimit The number of chars that can be read before the mark
+ * becomes invalid
+ *
+ * @exception IOException If an error occurs
+ */
public void mark(int readLimit) throws IOException
{
+ checkStatus();
synchronized (lock)
{
// In this method we need to be aware of the special case where
@@ -116,8 +172,20 @@
}
}
+ /**
+ * Reset the stream to the point where the <code>mark()</code> method
+ * was called. Any chars that were read after the mark point was set will
+ * be re-read during subsequent reads.
+ * <p>
+ * This method will throw an IOException if the number of chars read from
+ * the stream since the call to <code>mark()</code> exceeds the mark limit
+ * passed when establishing the mark.
+ *
+ * @exception IOException If an error occurs;
+ */
public void reset() throws IOException
{
+ checkStatus();
synchronized (lock)
{
if (markPos < 0)
@@ -136,16 +204,45 @@
}
}
+ /**
+ * This method determines whether or not a stream is ready to be read. If
+ * This method returns <code>false</code> then this stream could (but is
+ * not guaranteed to) block on the next read attempt.
+ *
+ * @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
{
+ checkStatus();
synchronized (lock)
{
return pos < limit || in.ready();
}
}
+ /**
+ * This method read chars from a stream and stores them into a caller
+ * supplied buffer. It starts storing the data at index <code>offset</code> into
+ * the buffer and attempts to read <code>len</code> chars. This method can
+ * return before reading the number of chars requested. The actual number
+ * of chars read is returned as an int. A -1 is returned to indicate the
+ * end of the stream.
+ * <p>
+ * This method will block until some data can be read.
+ *
+ * @param buf The array into which the chars read should be stored
+ * @param offset The offset into the array to start storing chars
+ * @param count The requested number of chars to read
+ *
+ * @return The actual number of chars read, or -1 if end of stream.
+ *
+ * @exception IOException If an error occurs.
+ */
public int read(char[] buf, int offset, int count) throws IOException
{
+ checkStatus();
synchronized (lock)
{
// Once again, we need to handle the special case of a readLine
@@ -202,6 +299,7 @@
Return number of chars read (never 0), or -1 on eof. */
private int fill() throws IOException
{
+ checkStatus();
// Handle the special case of a readLine that has a '\r' at the end of
// the buffer. In this case, we'll need to skip a '\n' if it is the
// next char to be read. This special case is indicated by 'pos > limit'.
@@ -228,9 +326,10 @@
return count;
}
-
+
public int read() throws IOException
{
+ checkStatus();
synchronized (lock)
{
if (pos >= limit && fill () <= 0)
@@ -255,8 +354,20 @@
return i;
}
+ /**
+ * This method reads a single line of text from the input stream, returning
+ * it as a <code>String</code>. A line is terminated by "\n", a "\r", or
+ * an "\r\n" sequence. The system dependent line separator is not used.
+ * The line termination characters are not returned in the resulting
+ * <code>String</code>.
+ *
+ * @return The line of text read, or <code>null</code> if end of stream.
+ *
+ * @exception IOException If an error occurs
+ */
public String readLine() throws IOException
{
+ checkStatus();
// Handle the special case where a previous readLine (with no intervening
// reads/skips) had a '\r' at the end of the buffer.
// In this case, we'll need to skip a '\n' if it's the next char to be read.
@@ -317,8 +428,23 @@
return (sbuf.length() == 0 && eof) ? null : sbuf.toString();
}
+ /**
+ * This method skips the specified number of chars in the stream. It
+ * returns the actual number of chars skipped, which may be less than the
+ * requested amount.
+ * <p>
+ * This method first discards chars in the buffer, then calls the
+ * <code>skip</code> method on the underlying stream to skip the remaining chars.
+ *
+ * @param num_chars The requested number of chars to skip
+ *
+ * @return The actual number of chars skipped.
+ *
+ * @exception IOException If an error occurs
+ */
public long skip(long count) throws IOException
{
+ checkStatus();
if (count <= 0)
return 0;
synchronized (lock)
@@ -370,4 +496,10 @@
return count - todo;
}
}
+
+ private void checkStatus() throws IOException
+ {
+ if (in == null)
+ throw new IOException("Stream closed");
+ }
}
Index: DataInputStream.java
===================================================================
RCS file: /cvs/java/libgcj/libjava/java/io/DataInputStream.java,v
retrieving revision 1.6
diff -u -r1.6 DataInputStream.java
--- DataInputStream.java 2000/09/11 22:49:29 1.6
+++ DataInputStream.java 2000/11/28 04:34:02
@@ -27,7 +27,6 @@
* @author Aaron M. Renn (arenn@urbanophile.com)
* @date October 20, 1998.
*/
-
public class DataInputStream extends FilterInputStream implements DataInput
{
// readLine() hack to ensure that an '\r' not followed by an '\n' is
@@ -61,7 +60,7 @@
*/
public final int read(byte[] b) throws IOException
{
- return super.read(b, 0, b.length);
+ return in.read(b, 0, b.length);
}
/**
@@ -82,10 +81,7 @@
*/
public final int read(byte[] b, int off, int len) throws IOException
{
- if (off < 0 || len < 0 || off + len > b.length)
- throw new ArrayIndexOutOfBoundsException();
-
- return super.read(b, off, len);
+ return in.read(b, off, len);
}
/**
@@ -106,7 +102,10 @@
*/
public final boolean readBoolean() throws IOException
{
- return (readByte() != 0);
+ int b = in.read();
+ if (b < 0)
+ throw new EOFException();
+ return (b != 0);
}
/**
@@ -126,7 +125,7 @@
*/
public final byte readByte() throws IOException
{
- int i = read();
+ int i = in.read();
if (i < 0)
throw new EOFException();
@@ -160,7 +159,11 @@
*/
public final char readChar() throws IOException
{
- return (char) ((readByte() << 8) | readUnsignedByte());
+ int a = in.read();
+ int b = in.read();
+ if (b < 0)
+ throw new EOFException();
+ return (char) ((a << 8) | (b & 0xff));
}
/**
@@ -247,13 +250,10 @@
*/
public final void readFully(byte[] b, int off, int len) throws IOException
{
- if (off < 0 || len < 0 || off + len > b.length)
- throw new ArrayIndexOutOfBoundsException();
-
while (len > 0)
{
- // super.read will block until some data is available.
- int numread = super.read(b, off, len);
+ // in.read will block until some data is available.
+ int numread = in.read(b, off, len);
if (numread < 0)
throw new EOFException();
len -= numread;
@@ -290,11 +290,15 @@
*/
public final int readInt() throws IOException
{
- int retval = 0;
- for (int i = 0; i < 4; i++)
- retval |= readUnsignedByte() << (24 - i * 8);
-
- return retval;
+ int a = in.read();
+ int b = in.read();
+ int c = in.read();
+ int d = in.read();
+ if (d < 0)
+ throw new EOFException();
+
+ return (((a & 0xff) << 24) | ((b & 0xff) << 16) |
+ ((c & 0xff) << 8) | (d & 0xff));
}
/**
@@ -335,7 +339,7 @@
while (getnext)
{
getnext = false;
- c = read();
+ c = in.read();
if (c < 0) // got an EOF
return strb.length() > 0 ? strb.toString() : null;
ch = (char) c;
@@ -377,7 +381,7 @@
char next_ch = ' ';
if (in instanceof BufferedInputStream)
{
- next_c = read();
+ next_c = in.read();
next_ch = (char) (next_c & 0xFF);
if ((next_ch != '\n') && (next_c >= 0))
{
@@ -388,12 +392,12 @@
}
else if (markSupported())
{
- next_c = read();
+ next_c = in.read();
next_ch = (char) (next_c & 0xFF);
if ((next_ch != '\n') && (next_c >= 0))
{
mark(1);
- if ((read() & 0xFF) != '\n')
+ if ((in.read() & 0xFF) != '\n')
reset();
}
}
@@ -441,11 +445,25 @@
*/
public final long readLong() throws IOException
{
- long retval = 0L;
- for (int i = 0; i < 8; i++)
- retval |= (long) readUnsignedByte() << (56 - i * 8);
-
- return retval;
+ int a = in.read();
+ int b = in.read();
+ int c = in.read();
+ int d = in.read();
+ int e = in.read();
+ int f = in.read();
+ int g = in.read();
+ int h = in.read();
+ if (h < 0)
+ throw new EOFException();
+
+ return (((long)(a & 0xff) << 56) |
+ ((long)(b & 0xff) << 48) |
+ ((long)(c & 0xff) << 40) |
+ ((long)(d & 0xff) << 32) |
+ ((long)(e & 0xff) << 24) |
+ ((long)(f & 0xff) << 16) |
+ ((long)(g & 0xff) << 8) |
+ ((long)(h & 0xff)));
}
/**
@@ -477,7 +495,11 @@
*/
public final short readShort() throws IOException
{
- return (short) ((readByte() << 8) | readUnsignedByte());
+ int a = in.read();
+ int b = in.read();
+ if (b < 0)
+ throw new EOFException();
+ return (short) ((a << 8) | (b & 0xff));
}
/**
@@ -498,7 +520,7 @@
*/
public final int readUnsignedByte() throws IOException
{
- int i = read();
+ int i = in.read();
if (i < 0)
throw new EOFException();
@@ -532,7 +554,11 @@
*/
public final int readUnsignedShort() throws IOException
{
- return (readUnsignedByte() << 8) | readUnsignedByte();
+ int a = in.read();
+ int b = in.read();
+ if (b < 0)
+ throw new EOFException();
+ return (((a & 0xff) << 8) | (b & 0xff));
}
/**
@@ -664,34 +690,29 @@
/**
* This method attempts to skip and discard the specified number of bytes
* in the input stream. It may actually skip fewer bytes than requested.
- * The actual number of bytes skipped is returned. This method will not
- * skip any bytes if passed a negative number of bytes to skip.
+ * This method will not skip any bytes if passed a negative number of bytes
+ * to skip.
*
* @param n The requested number of bytes to skip.
- *
- * @return The number of bytes actually skipped.
- *
+ * @return The requested number of bytes to skip.
* @exception IOException If an error occurs.
+ * @specnote The JDK docs claim that this returns the number of bytes
+ * actually skipped. The JCL claims that this method can throw an
+ * EOFException. Neither of these appear to be true in the JDK 1.3's
+ * implementation. This tries to implement the actual JDK behaviour.
*/
public final int skipBytes(int n) throws IOException
{
- // The contract in the Java Lang. Spec. says that this never
- // throws an EOFException and infers that it doesn't block (since
- // it may skip less than the requested number of bytes).
- // BUT, the JCL book specifically says that this method blocks
- // and can throw an EOFException. Finally, the Java 1.2 online
- // doc simply refers to the general contract. As such, we will
- // stick to the contract and assume for now that the JCL book
- // is incorrect.
-
- // Since we're only skipping at most an int number of bytes, the cast
- // of return value to an int is fine.
- if (n > 0)
+ if (n <= 0)
+ return 0;
+ try
{
- n = Math.min(n, available());
- return (int) super.skip((long) n);
+ return (int) in.skip(n);
}
-
- return 0;
+ catch (EOFException x)
+ {
+ // do nothing.
+ }
+ return n;
}
}