@@ -77,14 +77,17 @@ public class UnsynchronizedBufferedReader extends UnsynchronizedReader {
7777 */
7878 private char [] buf ;
7979
80- private int pos ;
80+ private int bufPos ;
8181
8282 private int end ;
8383
8484 private int mark = -1 ;
8585
8686 private int markLimit = -1 ;
8787
88+ private long pos ;
89+ private long posMark ;
90+
8891 /**
8992 * Constructs a new BufferedReader on the Reader {@code in}. The buffer gets the default size (8 KB).
9093 *
@@ -113,8 +116,8 @@ public UnsynchronizedBufferedReader(final Reader in, final int size) {
113116 * Peeks at the next input character, refilling the buffer if necessary. If this character is a newline character ("\n"), it is discarded.
114117 */
115118 final void chompNewline () throws IOException {
116- if ((pos != end || fillBuf () != EOF ) && buf [pos ] == LF ) {
117- pos ++;
119+ if ((bufPos != end || fillBuf () != EOF ) && buf [bufPos ] == LF ) {
120+ bufPos ++;
118121 }
119122 }
120123
@@ -141,12 +144,12 @@ public void close() throws IOException {
141144 private int fillBuf () throws IOException {
142145 // assert(pos == end);
143146
144- if (mark == EOF || pos - mark >= markLimit ) {
147+ if (mark == EOF || bufPos - mark >= markLimit ) {
145148 /* mark isn't set or has exceeded its limit. use the whole buffer */
146149 final int result = in .read (buf , 0 , buf .length );
147150 if (result > 0 ) {
148151 mark = -1 ;
149- pos = 0 ;
152+ bufPos = 0 ;
150153 end = result ;
151154 }
152155 return result ;
@@ -164,19 +167,49 @@ private int fillBuf() throws IOException {
164167 } else if (mark > 0 ) {
165168 /* make room by shifting the buffered data to left mark positions */
166169 System .arraycopy (buf , mark , buf , 0 , buf .length - mark );
167- pos -= mark ;
170+ bufPos -= mark ;
168171 end -= mark ;
169172 mark = 0 ;
170173 }
171174
172175 /* Set the new position and mark position */
173- final int count = in .read (buf , pos , buf .length - pos );
176+ final int count = in .read (buf , bufPos , buf .length - bufPos );
174177 if (count != EOF ) {
175178 end += count ;
176179 }
177180 return count ;
178181 }
179182
183+ /**
184+ * Gets the character position in the reader.
185+ * <p>
186+ * Returns {@link Long#MAX_VALUE} instead of overflowing.
187+ * </p>
188+ * <p>
189+ * Since this is a Reader, this is the character position, not the byte position (use an InputStream to count bytes).
190+ * </p>
191+ *
192+ * @return the character position in the reader.
193+ * @since 2.23.0
194+ */
195+ public long getPosition () {
196+ return pos ;
197+ }
198+
199+ /**
200+ * Increments the position by a given value, overflow sets the value to {@link Long#MAX_VALUE};
201+ *
202+ * @param v a value.
203+ * @return the new position.
204+ * @since 2.23.0
205+ */
206+ protected long incPos (final long v ) {
207+ // Like Math.addExact(long, long) but clamp to Long.MAX_VALUE.
208+ final long r = pos + v ;
209+ // Overflow iff both arguments have the opposite sign of the result.
210+ return pos = ((pos ^ r ) & (v ^ r )) < 0 ? Long .MAX_VALUE : r ;
211+ }
212+
180213 /**
181214 * Sets a mark position in this reader. The parameter {@code markLimit} indicates how many characters can be read before the mark is invalidated. Calling
182215 * {@link #reset()} will reposition the reader back to the marked position if {@code markLimit} has not been surpassed.
@@ -194,7 +227,8 @@ public void mark(final int markLimit) throws IOException {
194227 }
195228 checkOpen ();
196229 this .markLimit = markLimit ;
197- mark = pos ;
230+ mark = bufPos ;
231+ posMark = pos ;
198232 }
199233
200234 /**
@@ -250,8 +284,9 @@ public int peek(final char[] buf) throws IOException {
250284 public int read () throws IOException {
251285 checkOpen ();
252286 /* Are there buffered characters available? */
253- if (pos < end || fillBuf () != EOF ) {
254- return buf [pos ++];
287+ if (bufPos < end || fillBuf () != EOF ) {
288+ incPos (1 );
289+ return buf [bufPos ++];
255290 }
256291 return EOF ;
257292 }
@@ -281,52 +316,45 @@ public int read(final char[] buffer, int offset, final int length) throws IOExce
281316 if (length == 0 ) {
282317 return 0 ;
283318 }
284-
285319 int outstanding = length ;
286320 while (outstanding > 0 ) {
287-
288321 /*
289322 * If there are bytes in the buffer, grab those first.
290323 */
291- final int available = end - pos ;
324+ final int available = end - bufPos ;
292325 if (available > 0 ) {
293326 final int count = available >= outstanding ? outstanding : available ;
294- System .arraycopy (buf , pos , buffer , offset , count );
295- pos += count ;
327+ System .arraycopy (buf , bufPos , buffer , offset , count );
328+ bufPos += count ;
296329 offset += count ;
297330 outstanding -= count ;
298331 }
299-
300332 /*
301333 * Before attempting to read from the underlying stream, make sure we really, really want to. We won't bother if we're done, or if we've already got
302334 * some bytes and reading from the underlying stream would block.
303335 */
304336 if (outstanding == 0 || outstanding < length && !in .ready ()) {
305337 break ;
306338 }
307-
308339 // assert(pos == end);
309-
310340 /*
311341 * If we're unmarked and the requested size is greater than our buffer, read the bytes directly into the caller's buffer. We don't read into smaller
312342 * buffers because that could result in a many reads.
313343 */
314- if ((mark == -1 || pos - mark >= markLimit ) && outstanding >= buf .length ) {
344+ if ((mark == -1 || bufPos - mark >= markLimit ) && outstanding >= buf .length ) {
315345 final int count = in .read (buffer , offset , outstanding );
316346 if (count > 0 ) {
317347 outstanding -= count ;
318348 mark = -1 ;
319349 }
320-
321350 break ; // assume the source stream gave us all that it could
322351 }
323-
324352 if (fillBuf () == EOF ) {
325353 break ; // source is exhausted
326354 }
327355 }
328-
329356 final int count = length - outstanding ;
357+ incPos (count );
330358 return count > 0 || count == length ? count : EOF ;
331359 }
332360
@@ -340,24 +368,24 @@ public int read(final char[] buffer, int offset, final int length) throws IOExce
340368 public String readLine () throws IOException {
341369 checkOpen ();
342370 /* has the underlying stream been exhausted? */
343- if (pos == end && fillBuf () == EOF ) {
371+ if (bufPos == end && fillBuf () == EOF ) {
344372 return null ;
345373 }
346- for (int charPos = pos ; charPos < end ; charPos ++) {
374+ for (int charPos = bufPos ; charPos < end ; charPos ++) {
347375 final char ch = buf [charPos ];
348376 if (ch > CR ) {
349377 continue ;
350378 }
351379 if (ch == LF ) {
352- final String res = new String (buf , pos , charPos - pos );
353- pos = charPos + 1 ;
380+ final String res = new String (buf , bufPos , charPos - bufPos );
381+ bufPos = charPos + 1 ;
354382 return res ;
355383 }
356384 if (ch == CR ) {
357- final String res = new String (buf , pos , charPos - pos );
358- pos = charPos + 1 ;
359- if ((pos < end || fillBuf () != EOF ) && buf [pos ] == LF ) {
360- pos ++;
385+ final String res = new String (buf , bufPos , charPos - bufPos );
386+ bufPos = charPos + 1 ;
387+ if ((bufPos < end || fillBuf () != EOF ) && buf [bufPos ] == LF ) {
388+ bufPos ++;
361389 }
362390 return res ;
363391 }
@@ -367,9 +395,9 @@ public String readLine() throws IOException {
367395 final StringBuilder result = new StringBuilder (80 );
368396 /* Typical Line Length */
369397
370- result .append (buf , pos , end - pos );
398+ result .append (buf , bufPos , end - bufPos );
371399 while (true ) {
372- pos = end ;
400+ bufPos = end ;
373401
374402 /* Are there buffered characters available? */
375403 if (eol == LF ) {
@@ -380,19 +408,19 @@ public String readLine() throws IOException {
380408 // characters or null.
381409 return result .length () > 0 || eol != NUL ? result .toString () : null ;
382410 }
383- for (int charPos = pos ; charPos < end ; charPos ++) {
411+ for (int charPos = bufPos ; charPos < end ; charPos ++) {
384412 final char c = buf [charPos ];
385413 if (eol != NUL ) {
386414 if (eol == CR && c == LF ) {
387- if (charPos > pos ) {
388- result .append (buf , pos , charPos - pos - 1 );
415+ if (charPos > bufPos ) {
416+ result .append (buf , bufPos , charPos - bufPos - 1 );
389417 }
390- pos = charPos + 1 ;
418+ bufPos = charPos + 1 ;
391419 } else {
392- if (charPos > pos ) {
393- result .append (buf , pos , charPos - pos - 1 );
420+ if (charPos > bufPos ) {
421+ result .append (buf , bufPos , charPos - bufPos - 1 );
394422 }
395- pos = charPos ;
423+ bufPos = charPos ;
396424 }
397425 return result .toString ();
398426 }
@@ -401,9 +429,9 @@ public String readLine() throws IOException {
401429 }
402430 }
403431 if (eol == NUL ) {
404- result .append (buf , pos , end - pos );
432+ result .append (buf , bufPos , end - bufPos );
405433 } else {
406- result .append (buf , pos , end - pos - 1 );
434+ result .append (buf , bufPos , end - bufPos - 1 );
407435 }
408436 }
409437 }
@@ -420,7 +448,7 @@ public String readLine() throws IOException {
420448 @ Override
421449 public boolean ready () throws IOException {
422450 checkOpen ();
423- return end - pos > 0 || in .ready ();
451+ return end - bufPos > 0 || in .ready ();
424452 }
425453
426454 /**
@@ -436,7 +464,8 @@ public void reset() throws IOException {
436464 if (mark == -1 ) {
437465 throw new IOException ("mark == -1" );
438466 }
439- pos = mark ;
467+ bufPos = mark ;
468+ pos = posMark ;
440469 }
441470
442471 /**
@@ -460,28 +489,28 @@ public long skip(final long amount) throws IOException {
460489 if (amount < 1 ) {
461490 return 0 ;
462491 }
463- if (end - pos >= amount ) {
464- pos += Math .toIntExact (amount );
465- return amount ;
492+ if (end - bufPos >= amount ) {
493+ bufPos += Math .toIntExact (amount );
494+ return incPos ( amount ) ;
466495 }
467-
468- long read = end - pos ;
469- pos = end ;
496+ long read = end - bufPos ;
497+ bufPos = end ;
470498 while (read < amount ) {
471499 if (fillBuf () == EOF ) {
472- return read ;
500+ return incPos ( read ) ;
473501 }
474- if (end - pos >= amount - read ) {
475- pos += Math .toIntExact (amount - read );
476- return amount ;
502+ if (end - bufPos >= amount - read ) {
503+ bufPos += Math .toIntExact (amount - read );
504+ return incPos ( amount ) ;
477505 }
478506 // Couldn't get all the characters, skip what we read
479- read += end - pos ;
480- pos = end ;
507+ read += end - bufPos ;
508+ bufPos = end ;
481509 }
482- return amount ;
510+ return incPos ( amount ) ;
483511 }
484512
513+
485514 /**
486515 * Unwraps this instance by returning the underlying {@link Reader}.
487516 * <p>
0 commit comments