Index: java/io/BufferedReader.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/io/BufferedReader.java,v retrieving revision 1.14 diff -u -r1.14 BufferedReader.java --- java/io/BufferedReader.java 14 Jun 2003 05:44:38 -0000 1.14 +++ java/io/BufferedReader.java 9 Jan 2004 21:45:03 -0000 @@ -63,26 +63,16 @@ Reader in; char[] buffer; /* Index of current read position. Must be >= 0 and <= limit. */ - /* There is a special case where pos may be equal to limit+1; this - * is used as an indicator that a readLine was done with a '\r' was - * the very last char in the buffer. Since we don't want to read-ahead - * and potentially block, we set pos this way to indicate the situation - * and deal with it later. Doing it this way rather than having a - * separate boolean field to indicate the condition has the advantage - * that it is self-clearing on things like mark/reset. - */ int pos; /* Limit of valid data in buffer. Must be >= pos and <= buffer.length. */ - /* This can be < pos in the one special case described above. */ int limit; /* The value -1 means there is no mark, or the mark has been invalidated. Otherwise, markPos is the index in the buffer of the marked position. - Must be >= 0 and <= pos. - Note we do not explicitly store the read-limit. - The implicit read-limit is (buffer.length - markPos), which is - guaranteed to be >= the read-limit requested in the call to mark. */ + Must be >= 0 and <= pos. */ int markPos = -1; + /* Maximum number of characters that can be read before markPos becomes invalid. */ + int markLimit = -1; // The JCL book specifies the default buffer size as 8K characters. // This is package-private because it is used by LineNumberReader. @@ -170,44 +160,8 @@ synchronized (lock) { checkStatus(); - // In this method we need to be aware of the special case where - // pos + 1 == limit. This indicates that a '\r' was the last char - // in the buffer during a readLine. We'll want to maintain that - // condition after we shift things around and if a larger buffer is - // needed to track readLimit, we'll have to make it one element - // larger to ensure we don't invalidate the mark too early, if the - // char following the '\r' is NOT a '\n'. This is ok because, per - // the spec, we are not required to invalidate when passing readLimit. - // - // Note that if 'pos > limit', then doing 'limit -= pos' will cause - // limit to be negative. This is the only way limit will be < 0. - - if (pos + readLimit > limit) - { - char[] old_buffer = buffer; - int extraBuffSpace = 0; - if (pos > limit) - extraBuffSpace = 1; - if (readLimit + extraBuffSpace > limit) - buffer = new char[readLimit + extraBuffSpace]; - limit -= pos; - if (limit >= 0) - { - System.arraycopy(old_buffer, pos, buffer, 0, limit); - pos = 0; - } - } - - if (limit < 0) - { - // Maintain the relationship of 'pos > limit'. - pos = 1; - limit = markPos = 0; - } - else - markPos = pos; - // Now pos + readLimit <= buffer.length. thus if we need to read - // beyond buffer.length, then we are allowed to invalidate markPos. + markPos = pos; + markLimit = readLimit; } } @@ -229,17 +183,7 @@ checkStatus(); if (markPos < 0) throw new IOException("mark never set or invalidated"); - - // Need to handle the extremely unlikely case where a readLine was - // done with a '\r' as the last char in the buffer; which was then - // immediately followed by a mark and a reset with NO intervening - // read of any sort. In that case, setting pos to markPos would - // lose that info and a subsequent read would thus not skip a '\n' - // (if one exists). The value of limit in this rare case is zero. - // We can assume that if limit is zero for other reasons, then - // pos is already set to zero and doesn't need to be readjusted. - if (limit > 0) - pos = markPos; + pos = markPos; } } @@ -286,122 +230,87 @@ synchronized (lock) { checkStatus(); - // Once again, we need to 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'. - boolean retAtEndOfBuffer = false; - - int avail = limit - pos; - if (count > avail) + + if (pos == limit && !refill()) + return -1; + + int charsTotal = 0; + while (true) { - if (avail > 0) - count = avail; - else // pos >= limit - { - if (limit == buffer.length) - markPos = -1; // read too far - invalidate the mark. - if (pos > limit) - { - // Set a boolean and make pos == limit to simplify things. - retAtEndOfBuffer = true; - --pos; - } - if (markPos < 0) - { - // Optimization: can read directly into buf. - if (count >= buffer.length && !retAtEndOfBuffer) - return in.read(buf, offset, count); - pos = limit = 0; - } - avail = in.read(buffer, limit, buffer.length - limit); - if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n') - { - --avail; - limit++; - } - if (avail < count) - { - if (avail <= 0) - return avail; - count = avail; - } - limit += avail; - } + int charsInBuffer = limit - pos; + int charsToRead = Math.min(charsInBuffer, count - charsTotal); + System.arraycopy(buffer, pos, buf, offset, charsToRead); + offset += charsToRead; + pos += charsToRead; + charsTotal += charsToRead; + if (charsTotal == count || !refill()) + return charsTotal; } - System.arraycopy(buffer, pos, buf, offset, count); - pos += count; - return count; } } - /* Read more data into the buffer. Update pos and limit appropriately. - Assumes pos==limit initially. May invalidate the mark if read too much. - 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'. - boolean retAtEndOfBuffer = false; - if (pos > limit) - { - retAtEndOfBuffer = true; - --pos; - } - - if (markPos >= 0 && limit == buffer.length) + /** Read more data into the buffer, updating pos and limit appropriately, + * and taking mark and markLimit into account. Assumes pos==limit initially. + * Return true if chars were added to the buffer, or false on EOF. */ + private boolean refill() throws IOException + { + // Check for invalidated mark. + if (markPos >= 0 && pos - markPos > markLimit) markPos = -1; + if (markPos < 0) - pos = limit = 0; - int count = in.read(buffer, limit, buffer.length - limit); - if (count > 0) - limit += count; - - if (retAtEndOfBuffer && buffer[pos] == '\n') + limit = pos = 0; + else if (markPos > 0) { - --count; - // If the mark was set to the location of the \n, then we - // must change it to fully pretend that the \n does not - // exist. - if (markPos == pos) - ++markPos; - ++pos; + // Shift marked bytes (if any) to the beginning of the array. + System.arraycopy(buffer, markPos, buffer, 0, limit - markPos); + limit -= markPos; + pos -= markPos; + markPos = 0; + } + else if (markLimit >= buffer.length) // markPos == 0 + { + // Need to grow the buffer now to have room for markLimit bytes. + // Note that the new buffer is one greater than markLimit. + // This is so that there will be one byte past marklimit to be read + // before having to call refill again, thus allowing marklimit to be + // invalidated. That way refill doesn't have to check marklimit. + char[] new_buffer = new char[markLimit + 1]; + System.arraycopy(buffer, 0, new_buffer, 0, limit); + buffer = new_buffer; + } + + int limitOrig = limit; + int count; + do + { + count = in.read(buffer, limit, buffer.length - limit); + if (count == -1) + break; + limit += count; } - - return count; + while (limit < buffer.length && in.ready()); + + if (count == -1 && limitOrig == limit) + return false; + return true; } - + + public int read() throws IOException { synchronized (lock) { checkStatus(); - if (pos >= limit && fill () <= 0) + if (pos == limit && !refill()) return -1; + return buffer[pos++]; } } - /* Return the end of the line starting at this.pos and ending at limit. - * The index returns is *before* any line terminators, or limit - * if no line terminators were found. - */ - private int lineEnd(int limit) - { - int i = pos; - for (; i < limit; i++) - { - char ch = buffer[i]; - if (ch == '\n' || ch == '\r') - break; - } - return i; - } - /** - * This method reads a single line of text from the input stream, returning + * Read a single line of text from the input stream, returning * it as a String. 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 @@ -413,65 +322,71 @@ */ 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. - // This special case is indicated by 'pos > limit'. - if (pos > limit) - { - int ch = read(); - if (ch < 0) - return null; - if (ch != '\n') - --pos; - } - int i = lineEnd(limit); - if (i < limit) - { - String str = new String(buffer, pos, i - pos); - pos = i + 1; - // If the last char in the buffer is a '\r', we must remember - // to check if the next char to be read after the buffer is refilled - // is a '\n'. If so, skip it. To indicate this condition, we set pos - // to be limit + 1, which normally is never possible. - if (buffer[i] == '\r') - if (pos == limit || buffer[pos] == '\n') - pos++; - return str; - } - StringBuffer sbuf = new StringBuffer(200); - sbuf.append(buffer, pos, i - pos); - pos = i; - // We only want to return null when no characters were read before - // EOF. So we must keep track of this separately. Otherwise we - // would treat an empty `sbuf' as an EOF condition, which is wrong - // when there is just a newline. - boolean eof = false; - for (;;) - { - int ch = read(); - if (ch < 0) - { - eof = true; - break; - } - if (ch == '\n' || ch == '\r') - { - // Check here if a '\r' was the last char in the buffer; if so, - // mark it as in the comment above to indicate future reads - // should skip a newline that is the next char read after - // refilling the buffer. - if (ch == '\r') - if (pos == limit || buffer[pos] == '\n') - pos++; - break; - } - i = lineEnd(limit); - sbuf.append(buffer, pos - 1, i - (pos - 1)); - pos = i; - } - return (sbuf.length() == 0 && eof) ? null : sbuf.toString(); + synchronized (lock) + { + checkStatus(); + + if (pos == limit && !refill()) + return null; + + StringBuffer sbuf = null; + String result = null; + + while (result == null) + { + int len = -1; + int i = pos; + char ch = '\0'; + for (; i < limit; i++) + { + ch = buffer[i]; + if (ch == '\n' || ch == '\r') + { + len = i - pos; + break; + } + } + + if (len >= 0) + { + // Found end of line. + if (sbuf == null) + result = new String(buffer, pos, len); + else + { + sbuf.append(buffer, pos, len); + result = sbuf.toString(); + } + + // Consume the line termination character(s). + pos = i + 1; + if (ch == '\r') + { + if (pos == limit) + { + // Special case: refill the buffer to check for a + // '\r\n' sequence that crosses the buffer boundary. + if (!refill()) + break; + } + if (buffer[pos] == '\n') + pos++; + } + } + else + { + // Reached limit of buffer. Store chars in StringBuffer and + // refill. + if (sbuf == null) + sbuf = new StringBuffer(len + 48); + sbuf.append(buffer, pos, limit - pos); + pos = limit; + if (!refill()) + result = sbuf.toString(); + } + } + return result; + } } /** @@ -494,53 +409,23 @@ synchronized (lock) { checkStatus(); - if (count <= 0) - return 0; - // Yet again, we need to handle the special case of a readLine - // that has a '\r' at the end of the buffer. In this case, we need - // to ignore a '\n' if it is the next char to be read. - // This special case is indicated by 'pos > limit' (i.e. avail < 0). - // To simplify things, if we're dealing with the special case for - // readLine, just read the next char (since the fill method will - // skip the '\n' for us). By doing this, we'll have to back up pos. - // That's easier than trying to keep track of whether we've skipped - // one element or not. - int ch; - if (pos > limit) - if ((ch = read()) < 0) - return 0; - else - --pos; - - int avail = limit - pos; + + final long origCount = count; - if (count < avail) + while (count > 0) { - pos += count; - return count; + if (pos == limit && !refill()) + if (count < origCount) + break; + else + return -1; // No chars were read before EOF. + + int numread = (int) Math.min((long) (limit - pos), count); + pos += numread; + count -= numread; } - pos = limit; - long todo = count - avail; - if (todo > buffer.length) - { - markPos = -1; - todo -= in.skip(todo); - } - else - { - while (todo > 0) - { - avail = fill(); - if (avail <= 0) - break; - if (avail > todo) - avail = (int) todo; - pos += avail; - todo -= avail; - } - } - return count - todo; + return origCount - count; } } @@ -548,5 +433,5 @@ { if (in == null) throw new IOException("Stream closed"); - } + } } Index: java/io/InputStreamReader.java =================================================================== RCS file: /cvs/gcc/gcc/libjava/java/io/InputStreamReader.java,v retrieving revision 1.15 diff -u -r1.15 InputStreamReader.java --- java/io/InputStreamReader.java 9 May 2003 07:10:58 -0000 1.15 +++ java/io/InputStreamReader.java 9 Jan 2004 21:45:03 -0000 @@ -87,9 +87,18 @@ */ public class InputStreamReader extends Reader { - BufferedInputStream in; + InputStream in; - // Buffer of chars read from in and converted but not consumed. + // Input buffer. + byte[] buffer; + // Index of current read position. Must be >= 0 and <= limit. + int pos; + // Limit of valid data in buffer. Must be >= pos and <= buffer.length. + int limit; + + private static final int DEFAULT_BUFFER_SIZE = 8192; + + // Buffer of chars converted but not consumed, used by single-char read(). char[] work; // Next available character (in work buffer) to read. int wpos; @@ -133,18 +142,11 @@ private InputStreamReader(InputStream in, BytesToUnicode decoder) { - // FIXME: someone could pass in a BufferedInputStream whose buffer - // is smaller than the longest encoded character for this - // encoding. We will probably go into an infinite loop in this - // case. We probably ought to just have our own byte buffering - // here. - this.in = in instanceof BufferedInputStream - ? (BufferedInputStream) in - : new BufferedInputStream(in); + this.in = in; /* Don't need to call super(in) here as long as the lock gets set. */ this.lock = in; converter = decoder; - converter.setInput(this.in.buf, 0, 0); + buffer = new byte[DEFAULT_BUFFER_SIZE]; } /** @@ -162,6 +164,8 @@ in = null; work = null; wpos = wcount = 0; + buffer = null; + converter = null; } } @@ -195,7 +199,7 @@ if (in == null) throw new IOException("Stream closed"); - if (wpos < wcount) + if (pos < limit) return true; // According to the spec, an InputStreamReader is ready if its @@ -212,36 +216,44 @@ * * @param buf The character array to recieve the data read * @param offset The offset into the array to start storing characters - * @param length The requested number of characters to read. + * @param count The requested number of characters to read. * * @return The actual number of characters read, or -1 if end of stream. * * @exception IOException If an error occurs */ - public int read (char[] buf, int offset, int length) throws IOException + public int read (char[] buf, int offset, int count) throws IOException { synchronized (lock) { if (in == null) throw new IOException("Stream closed"); - - if (length == 0) - return 0; - - int wavail = wcount - wpos; - if (wavail <= 0) + + int charsRead = 0; + int wchars = wcount - wpos; + if (wchars > 0) { - // Nothing waiting, so refill our buffer. - if (! refill ()) - return -1; - wavail = wcount - wpos; + // Flush chars from "work" buffer. + System.arraycopy(work, wpos, buf, offset, wchars); + offset += wchars; + charsRead += wchars; } - - if (length > wavail) - length = wavail; - System.arraycopy(work, wpos, buf, offset, length); - wpos += length; - return length; + + do + { + if (pos == limit && !refill()) + break; + + converter.setInput(buffer, pos, limit); + charsRead += converter.read(buf, offset + charsRead, count - charsRead); + pos = converter.pos; + } + while (charsRead < count && in.available() > 0); + + if (charsRead == 0) + return -1; + + return charsRead; } } @@ -259,48 +271,40 @@ if (in == null) throw new IOException("Stream closed"); - int wavail = wcount - wpos; - if (wavail <= 0) + int wchars = wcount - wpos; + if (wchars <= 0) { - // Nothing waiting, so refill our buffer. - if (! refill ()) - return -1; + // Nothing waiting, so refill the work buffer. + if (work == null) + work = new char[100]; + wpos = 0; + wcount = read(work, 0, work.length); } return work[wpos++]; } } - // Read more bytes and convert them into the WORK buffer. - // Return false on EOF. + /** + * Read more bytes into the INPUT buffer. Assumes pos == limit initially. + * @return false on EOF. + */ private boolean refill () throws IOException { - wcount = wpos = 0; - - if (work == null) - work = new char[100]; - - for (;;) + limit = pos = 0; + int count = 0; + do { - // We have knowledge of the internals of BufferedInputStream - // here. Eww. - in.mark (0); - // BufferedInputStream.refill() can only be called when - // `pos>=count'. - boolean r = in.pos < in.count || in.refill (); - in.reset (); - if (! r) - return false; - converter.setInput(in.buf, in.pos, in.count); - int count = converter.read (work, wpos, work.length - wpos); - in.skip(converter.inpos - in.pos); - if (count > 0) - { - wcount += count; - return true; - } + count = in.read(buffer, limit, buffer.length - limit); + if (count == -1) + break; + limit += count; } - } + while (limit < buffer.length && in.available() > 0); + if (count == -1 && limit == 0) + return false; + return true; + } } // class InputStreamReader