1 /* Copyright (C) 1998, 1999 Cygnus Solutions
3 This file is part of libgcj.
5 This software is copyrighted work licensed under the terms of the
6 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
12 * @author Per Bothner <bothner@cygnus.com>
13 * @date April 22, 1998.
15 /* Written using "Java Class Libraries", 2nd edition, plus online
16 * API docs for JDK 1.2 beta from http://www.javasoft.com.
17 * Status: Believed complete and correct.
20 public class BufferedReader extends Reader
24 /* Index of current read position. Must be >= 0 and <= limit. */
25 /* There is a special case where pos may be equal to limit+1; this
26 * is used as an indicator that a readLine was done with a '\r' was
27 * the very last char in the buffer. Since we don't want to read-ahead
28 * and potentially block, we set pos this way to indicate the situation
29 * and deal with it later. Doing it this way rather than having a
30 * separate boolean field to indicate the condition has the advantage
31 * that it is self-clearing on things like mark/reset.
34 /* Limit of valid data in buffer. Must be >= pos and <= buffer.length. */
35 /* This can be < pos in the one special case described above. */
38 /* The value -1 means there is no mark, or the mark has been invalidated.
39 Otherwise, markPos is the index in the buffer of the marked position.
40 Must be >= 0 and <= pos.
41 Note we do not explicitly store the read-limit.
42 The implicit read-limit is (buffer.length - markPos), which is
43 guaranteed to be >= the read-limit requested in the call to mark. */
46 public BufferedReader(Reader in)
51 public BufferedReader(Reader in, int size)
55 buffer = new char[size];
58 public void close() throws IOException
69 public boolean markSupported()
74 public void mark(int readLimit) throws IOException
78 // In this method we need to be aware of the special case where
79 // pos + 1 == limit. This indicates that a '\r' was the last char
80 // in the buffer during a readLine. We'll want to maintain that
81 // condition after we shift things around and if a larger buffer is
82 // needed to track readLimit, we'll have to make it one element
83 // larger to ensure we don't invalidate the mark too early, if the
84 // char following the '\r' is NOT a '\n'. This is ok because, per
85 // the spec, we are not required to invalidate when passing readLimit.
87 // Note that if 'pos > limit', then doing 'limit -= pos' will cause
88 // limit to be negative. This is the only way limit will be < 0.
90 if (pos + readLimit > limit)
92 char[] old_buffer = buffer;
93 int extraBuffSpace = 0;
96 if (readLimit + extraBuffSpace > limit)
97 buffer = new char[readLimit + extraBuffSpace];
101 System.arraycopy(old_buffer, pos, buffer, 0, limit);
108 // Maintain the relationship of 'pos > limit'.
114 // Now pos + readLimit <= buffer.length. thus if we need to read
115 // beyond buffer.length, then we are allowed to invalidate markPos.
119 public void reset() throws IOException
124 throw new IOException("mark never set or invalidated");
126 // Need to handle the extremely unlikely case where a readLine was
127 // done with a '\r' as the last char in the buffer; which was then
128 // immediately followed by a mark and a reset with NO intervening
129 // read of any sort. In that case, setting pos to markPos would
130 // lose that info and a subsequent read would thus not skip a '\n'
131 // (if one exists). The value of limit in this rare case is zero.
132 // We can assume that if limit is zero for other reasons, then
133 // pos is already set to zero and doesn't need to be readjusted.
139 public boolean ready() throws IOException
143 return pos < limit || in.ready();
147 public int read(char[] buf, int offset, int count) throws IOException
151 // Once again, we need to handle the special case of a readLine
152 // that has a '\r' at the end of the buffer. In this case, we'll
153 // need to skip a '\n' if it is the next char to be read.
154 // This special case is indicated by 'pos > limit'.
155 boolean retAtEndOfBuffer = false;
157 int avail = limit - pos;
164 if (limit == buffer.length)
165 markPos = -1; // read too far - invalidate the mark.
168 // Set a boolean and make pos == limit to simplify things.
169 retAtEndOfBuffer = true;
174 // Optimization: can read directly into buf.
175 if (count >= buffer.length && !retAtEndOfBuffer)
176 return in.read(buf, offset, count);
179 avail = in.read(buffer, limit, buffer.length - limit);
180 if (retAtEndOfBuffer && avail > 0 && buffer[limit] == '\n')
194 System.arraycopy(buffer, pos, buf, offset, count);
200 /* Read more data into the buffer. Update pos and limit appropriately.
201 Assumes pos==limit initially. May invalidate the mark if read too much.
202 Return number of chars read (never 0), or -1 on eof. */
203 private int fill() throws IOException
205 // Handle the special case of a readLine that has a '\r' at the end of
206 // the buffer. In this case, we'll need to skip a '\n' if it is the
207 // next char to be read. This special case is indicated by 'pos > limit'.
208 boolean retAtEndOfBuffer = false;
211 retAtEndOfBuffer = true;
215 if (markPos >= 0 && limit == buffer.length)
219 int count = in.read(buffer, limit, buffer.length - limit);
223 if (retAtEndOfBuffer && buffer[pos] == '\n')
232 public int read() throws IOException
236 if (pos >= limit && fill () <= 0)
238 return buffer[pos++];
242 /* Return the end of the line starting at this.pos and ending at limit.
243 * The index returns is *before* any line terminators, or limit
244 * if no line terminators were found.
246 private int lineEnd(int limit)
249 for (; i < limit; i++)
252 if (ch == '\n' || ch == '\r')
258 public String readLine() throws IOException
260 // Handle the special case where a previous readLine (with no intervening
261 // reads/skips) had a '\r' at the end of the buffer.
262 // In this case, we'll need to skip a '\n' if it's the next char to be read.
263 // This special case is indicated by 'pos > limit'.
272 int i = lineEnd(limit);
275 String str = new String(buffer, pos, i - pos);
277 // If the last char in the buffer is a '\r', we must remember
278 // to check if the next char to be read after the buffer is refilled
279 // is a '\n'. If so, skip it. To indicate this condition, we set pos
280 // to be limit + 1, which normally is never possible.
281 if (buffer[i] == '\r')
282 if (pos == limit || buffer[pos] == '\n')
286 StringBuffer sbuf = new StringBuffer(200);
287 sbuf.append(buffer, pos, i - pos);
289 // We only want to return null when no characters were read before
290 // EOF. So we must keep track of this separately. Otherwise we
291 // would treat an empty `sbuf' as an EOF condition, which is wrong
292 // when there is just a newline.
302 if (ch == '\n' || ch == '\r')
304 // Check here if a '\r' was the last char in the buffer; if so,
305 // mark it as in the comment above to indicate future reads
306 // should skip a newline that is the next char read after
307 // refilling the buffer.
309 if (pos == limit || buffer[pos] == '\n')
314 sbuf.append(buffer, pos - 1, i - (pos - 1));
317 return (sbuf.length() == 0 && eof) ? null : sbuf.toString();
320 public long skip(long count) throws IOException
326 // Yet again, we need to handle the special case of a readLine
327 // that has a '\r' at the end of the buffer. In this case, we need
328 // to ignore a '\n' if it is the next char to be read.
329 // This special case is indicated by 'pos > limit' (i.e. avail < 0).
330 // To simplify things, if we're dealing with the special case for
331 // readLine, just read the next char (since the fill method will
332 // skip the '\n' for us). By doing this, we'll have to back up pos.
333 // That's easier than trying to keep track of whether we've skipped
334 // one element or not.
337 if ((ch = read()) < 0)
342 int avail = limit - pos;
351 long todo = count - avail;
352 if (todo > buffer.length)
355 todo -= in.skip(todo);