< prev index next >

src/java.base/share/classes/java/io/BufferedReader.java

Print this page
*** 23,18 ***
   * questions.
   */
  
  package java.io;
  
- 
  import java.util.Iterator;
  import java.util.NoSuchElementException;
  import java.util.Objects;
  import java.util.Spliterator;
  import java.util.Spliterators;
  import java.util.stream.Stream;
  import java.util.stream.StreamSupport;
  
  /**
   * Reads text from a character-input stream, buffering characters so as to
   * provide for the efficient reading of characters, arrays, and lines.
   *
--- 23,18 ---
   * questions.
   */
  
  package java.io;
  
  import java.util.Iterator;
  import java.util.NoSuchElementException;
  import java.util.Objects;
  import java.util.Spliterator;
  import java.util.Spliterators;
  import java.util.stream.Stream;
  import java.util.stream.StreamSupport;
+ import jdk.internal.misc.InternalLock;
  
  /**
   * Reads text from a character-input stream, buffering characters so as to
   * provide for the efficient reading of characters, arrays, and lines.
   *

*** 67,11 ***
   * @author      Mark Reinhold
   * @since       1.1
   */
  
  public class BufferedReader extends Reader {
- 
      private Reader in;
  
      private char[] cb;
      private int nChars, nextChar;
  
--- 67,10 ---

*** 86,22 ***
      /** The skipLF flag when the mark was set */
      private boolean markedSkipLF = false;
  
      private static int defaultCharBufferSize = 8192;
      private static int defaultExpectedLineLength = 80;
! 
      /**
       * Creates a buffering character-input stream that uses an input buffer of
       * the specified size.
       *
       * @param  in   A Reader
       * @param  sz   Input-buffer size
       *
       * @throws IllegalArgumentException  If {@code sz <= 0}
       */
      public BufferedReader(Reader in, int sz) {
!         super(in);
          if (sz <= 0)
              throw new IllegalArgumentException("Buffer size <= 0");
          this.in = in;
          cb = new char[sz];
          nextChar = nChars = 0;
--- 85,22 ---
      /** The skipLF flag when the mark was set */
      private boolean markedSkipLF = false;
  
      private static int defaultCharBufferSize = 8192;
      private static int defaultExpectedLineLength = 80;
!     
      /**
       * Creates a buffering character-input stream that uses an input buffer of
       * the specified size.
       *
       * @param  in   A Reader
       * @param  sz   Input-buffer size
       *
       * @throws IllegalArgumentException  If {@code sz <= 0}
       */
      public BufferedReader(Reader in, int sz) {
!         Objects.requireNonNull(in);
          if (sz <= 0)
              throw new IllegalArgumentException("Buffer size <= 0");
          this.in = in;
          cb = new char[sz];
          nextChar = nChars = 0;

*** 174,27 ***
       *         0 to 65535 ({@code 0x00-0xffff}), or -1 if the
       *         end of the stream has been reached
       * @throws     IOException  If an I/O error occurs
       */
      public int read() throws IOException {
!         synchronized (lock) {
!             ensureOpen();
!             for (;;) {
!                 if (nextChar >= nChars) {
!                     fill();
!                     if (nextChar >= nChars)
!                         return -1;
!                 }
!                 if (skipLF) {
!                     skipLF = false;
!                     if (cb[nextChar] == '\n') {
!                         nextChar++;
!                         continue;
!                     }
                  }
-                 return cb[nextChar++];
              }
          }
      }
  
      /**
       * Reads characters into a portion of an array, reading from the underlying
--- 173,41 ---
       *         0 to 65535 ({@code 0x00-0xffff}), or -1 if the
       *         end of the stream has been reached
       * @throws     IOException  If an I/O error occurs
       */
      public int read() throws IOException {
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
!             locker.lock();
!             try {
!                 return lockedRead();
!             } finally {
!                 locker.unlock();
!             }
!         } else {
!             synchronized (lock) {
!                 return lockedRead();
!             }
!         }
!     }
+ 
+     private int lockedRead() throws IOException {
+         ensureOpen();
+         for (;;) {
+             if (nextChar >= nChars) {
+                 fill();
+                 if (nextChar >= nChars)
+                     return -1;
+             }
+             if (skipLF) {
+                 skipLF = false;
+                 if (cb[nextChar] == '\n') {
+                     nextChar++;
+                     continue;
                  }
              }
+             return cb[nextChar++];
          }
      }
  
      /**
       * Reads characters into a portion of an array, reading from the underlying

*** 275,28 ***
       *
       * @throws     IndexOutOfBoundsException {@inheritDoc}
       * @throws     IOException  {@inheritDoc}
       */
      public int read(char[] cbuf, int off, int len) throws IOException {
!         synchronized (lock) {
!             ensureOpen();
!             Objects.checkFromIndexSize(off, len, cbuf.length);
!             if (len == 0) {
!                 return 0;
              }
! 
!             int n = read1(cbuf, off, len);
!             if (n <= 0) return n;
-             while ((n < len) && in.ready()) {
-                 int n1 = read1(cbuf, off + n, len - n);
-                 if (n1 <= 0) break;
-                 n += n1;
              }
-             return n;
          }
      }
  
      /**
       * Reads a line of text.  A line is considered to be terminated by any one
       * of a line feed ('\n'), a carriage return ('\r'), a carriage return
       * followed immediately by a line feed, or by reaching the end-of-file
       * (EOF).
--- 288,42 ---
       *
       * @throws     IndexOutOfBoundsException {@inheritDoc}
       * @throws     IOException  {@inheritDoc}
       */
      public int read(char[] cbuf, int off, int len) throws IOException {
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
!             locker.lock();
!             try {
!                 return lockedRead(cbuf, off, len);
+             } finally {
+                 locker.unlock();
              }
!         } else {
!             synchronized (lock) {
!                 return lockedRead(cbuf, off, len);
              }
          }
      }
  
+     private int lockedRead(char[] cbuf, int off, int len) throws IOException {
+         ensureOpen();
+         Objects.checkFromIndexSize(off, len, cbuf.length);
+         if (len == 0) {
+             return 0;
+         }
+ 
+         int n = read1(cbuf, off, len);
+         if (n <= 0) return n;
+         while ((n < len) && in.ready()) {
+             int n1 = read1(cbuf, off + n, len - n);
+             if (n1 <= 0) break;
+             n += n1;
+         }
+         return n;
+     }
+ 
      /**
       * Reads a line of text.  A line is considered to be terminated by any one
       * of a line feed ('\n'), a carriage return ('\r'), a carriage return
       * followed immediately by a line feed, or by reaching the end-of-file
       * (EOF).

*** 312,71 ***
       * @see        java.io.LineNumberReader#readLine()
       *
       * @throws     IOException  If an I/O error occurs
       */
      String readLine(boolean ignoreLF, boolean[] term) throws IOException {
          StringBuilder s = null;
          int startChar;
  
!         synchronized (lock) {
!             ensureOpen();
!             boolean omitLF = ignoreLF || skipLF;
-             if (term != null) term[0] = false;
  
!         bufferLoop:
!             for (;;) {
  
!                 if (nextChar >= nChars)
!                     fill();
!                 if (nextChar >= nChars) { /* EOF */
!                     if (s != null && s.length() > 0)
!                         return s.toString();
!                     else
!                         return null;
!                 }
!                 boolean eol = false;
!                 char c = 0;
!                 int i;
  
!                 /* Skip a leftover '\n', if necessary */
!                 if (omitLF && (cb[nextChar] == '\n'))
!                     nextChar++;
!                 skipLF = false;
!                 omitLF = false;
! 
!             charLoop:
!                 for (i = nextChar; i < nChars; i++) {
!                     c = cb[i];
!                     if ((c == '\n') || (c == '\r')) {
!                         if (term != null) term[0] = true;
!                         eol = true;
!                         break charLoop;
-                     }
                  }
  
!                 startChar = nextChar;
!                 nextChar = i;
- 
-                 if (eol) {
-                     String str;
-                     if (s == null) {
-                         str = new String(cb, startChar, i - startChar);
-                     } else {
-                         s.append(cb, startChar, i - startChar);
-                         str = s.toString();
-                     }
-                     nextChar++;
-                     if (c == '\r') {
-                         skipLF = true;
-                     }
-                     return str;
-                 }
  
!                 if (s == null)
!                     s = new StringBuilder(defaultExpectedLineLength);
!                 s.append(cb, startChar, i - startChar);
              }
          }
      }
  
      /**
       * Reads a line of text.  A line is considered to be terminated by any one
--- 339,85 ---
       * @see        java.io.LineNumberReader#readLine()
       *
       * @throws     IOException  If an I/O error occurs
       */
      String readLine(boolean ignoreLF, boolean[] term) throws IOException {
+         Object lock = this.lock;
+         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedReadLine(ignoreLF, term);
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 return lockedReadLine(ignoreLF, term);
+             }
+         }
+     }
+ 
+     private String lockedReadLine(boolean ignoreLF, boolean[] term) throws IOException {
          StringBuilder s = null;
          int startChar;
  
!         ensureOpen();
!         boolean omitLF = ignoreLF || skipLF;
!         if (term != null) term[0] = false;
  
!       bufferLoop:
!         for (;;) {
  
!             if (nextChar >= nChars)
!                 fill();
!             if (nextChar >= nChars) { /* EOF */
!                 if (s != null && s.length() > 0)
!                     return s.toString();
!                 else
!                     return null;
!             }
!             boolean eol = false;
!             char c = 0;
!             int i;
  
!             /* Skip a leftover '\n', if necessary */
!             if (omitLF && (cb[nextChar] == '\n'))
!                 nextChar++;
!             skipLF = false;
!             omitLF = false;
! 
!           charLoop:
!             for (i = nextChar; i < nChars; i++) {
!                 c = cb[i];
!                 if ((c == '\n') || (c == '\r')) {
!                     if (term != null) term[0] = true;
!                     eol = true;
!                     break charLoop;
                  }
+             }
  
!             startChar = nextChar;
!             nextChar = i;
  
!             if (eol) {
!                 String str;
!                 if (s == null) {
+                     str = new String(cb, startChar, i - startChar);
+                 } else {
+                     s.append(cb, startChar, i - startChar);
+                     str = s.toString();
+                 }
+                 nextChar++;
+                 if (c == '\r') {
+                     skipLF = true;
+                 }
+                 return str;
              }
+ 
+             if (s == null)
+                 s = new StringBuilder(defaultExpectedLineLength);
+             s.append(cb, startChar, i - startChar);
          }
      }
  
      /**
       * Reads a line of text.  A line is considered to be terminated by any one

*** 401,69 ***
       */
      public long skip(long n) throws IOException {
          if (n < 0L) {
              throw new IllegalArgumentException("skip value is negative");
          }
!         synchronized (lock) {
!             ensureOpen();
!             long r = n;
!             while (r > 0) {
!                 if (nextChar >= nChars)
!                     fill();
!                 if (nextChar >= nChars) /* EOF */
!                     break;
!                 if (skipLF) {
!                     skipLF = false;
!                     if (cb[nextChar] == '\n') {
!                         nextChar++;
!                     }
!                 }
!                 long d = nChars - nextChar;
!                 if (r <= d) {
!                     nextChar += r;
!                     r = 0;
!                     break;
!                 }
!                 else {
!                     r -= d;
!                     nextChar = nChars;
                  }
              }
!             return n - r;
          }
      }
  
      /**
       * Tells whether this stream is ready to be read.  A buffered character
       * stream is ready if the buffer is not empty, or if the underlying
       * character stream is ready.
       *
       * @throws     IOException  If an I/O error occurs
       */
      public boolean ready() throws IOException {
!         synchronized (lock) {
!             ensureOpen();
  
!             /*
!              * If newline needs to be skipped and the next char to be read
!              * is a newline character, then just skip it right away.
               */
!             if (skipLF) {
!                 /* Note that in.ready() will return true if and only if the next
!                  * read on the stream will not block.
!                  */
!                 if (nextChar >= nChars && in.ready()) {
!                     fill();
!                 }
-                 if (nextChar < nChars) {
-                     if (cb[nextChar] == '\n')
-                         nextChar++;
-                     skipLF = false;
-                 }
              }
-             return (nextChar < nChars) || in.ready();
          }
      }
  
      /**
       * Tells whether this stream supports the mark() operation, which it does.
       */
--- 442,97 ---
       */
      public long skip(long n) throws IOException {
          if (n < 0L) {
              throw new IllegalArgumentException("skip value is negative");
          }
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
!             locker.lock();
!             try {
!                 return lockedSkip(n);
!             } finally {
!                 locker.unlock();
!             }
!         } else {
!             synchronized (lock) {
!                 return lockedSkip(n);
!             }
!         }
!     }
! 
!     private long lockedSkip(long n) throws IOException {
!         ensureOpen();
!         long r = n;
!         while (r > 0) {
!             if (nextChar >= nChars)
!                 fill();
!             if (nextChar >= nChars) /* EOF */
!                 break;
+             if (skipLF) {
+                 skipLF = false;
+                 if (cb[nextChar] == '\n') {
+                     nextChar++;
                  }
              }
!             long d = nChars - nextChar;
+             if (r <= d) {
+                 nextChar += r;
+                 r = 0;
+                 break;
+             }
+             else {
+                 r -= d;
+                 nextChar = nChars;
+             }
          }
+         return n - r;
      }
  
      /**
       * Tells whether this stream is ready to be read.  A buffered character
       * stream is ready if the buffer is not empty, or if the underlying
       * character stream is ready.
       *
       * @throws     IOException  If an I/O error occurs
       */
      public boolean ready() throws IOException {
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
+             locker.lock();
+             try {
+                 return lockedReady();
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 return lockedReady();
+             }
+         }
+     }
  
!     private boolean lockedReady() throws IOException {
!         ensureOpen();
! 
+         /*
+          * If newline needs to be skipped and the next char to be read
+          * is a newline character, then just skip it right away.
+          */
+         if (skipLF) {
+             /* Note that in.ready() will return true if and only if the next
+              * read on the stream will not block.
               */
!             if (nextChar >= nChars && in.ready()) {
!                 fill();
!             }
!             if (nextChar < nChars) {
!                 if (cb[nextChar] == '\n')
!                     nextChar++;
!                 skipLF = false;
              }
          }
+         return (nextChar < nChars) || in.ready();
      }
  
      /**
       * Tells whether this stream supports the mark() operation, which it does.
       */

*** 489,46 ***
       */
      public void mark(int readAheadLimit) throws IOException {
          if (readAheadLimit < 0) {
              throw new IllegalArgumentException("Read-ahead limit < 0");
          }
!         synchronized (lock) {
!             ensureOpen();
!             this.readAheadLimit = readAheadLimit;
!             markedChar = nextChar;
!             markedSkipLF = skipLF;
          }
      }
  
      /**
       * Resets the stream to the most recent mark.
       *
       * @throws     IOException  If the stream has never been marked,
       *                          or if the mark has been invalidated
       */
      public void reset() throws IOException {
!         synchronized (lock) {
!             ensureOpen();
!             if (markedChar < 0)
!                 throw new IOException((markedChar == INVALIDATED)
!                                       ? "Mark invalid"
!                                       : "Stream not marked");
!             nextChar = markedChar;
!             skipLF = markedSkipLF;
          }
      }
  
      public void close() throws IOException {
!         synchronized (lock) {
!             if (in == null)
!                 return;
              try {
!                 in.close();
              } finally {
!                 in = null;
-                 cb = null;
              }
          }
      }
  
      /**
       * Returns a {@code Stream}, the elements of which are lines read from
--- 558,88 ---
       */
      public void mark(int readAheadLimit) throws IOException {
          if (readAheadLimit < 0) {
              throw new IllegalArgumentException("Read-ahead limit < 0");
          }
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
!             locker.lock();
!             try {
!                 lockedMark(readAheadLimit);
+             } finally {
+                 locker.unlock();
+             }
+         } else {
+             synchronized (lock) {
+                 lockedMark(readAheadLimit);
+             }
          }
      }
  
+     private void lockedMark(int readAheadLimit) throws IOException {
+         ensureOpen();
+         this.readAheadLimit = readAheadLimit;
+         markedChar = nextChar;
+         markedSkipLF = skipLF;
+     }
+ 
      /**
       * Resets the stream to the most recent mark.
       *
       * @throws     IOException  If the stream has never been marked,
       *                          or if the mark has been invalidated
       */
      public void reset() throws IOException {
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
!             locker.lock();
!             try {
!                 lockedReset();
!             } finally {
!                 locker.unlock();
!             }
+         } else {
+             synchronized (lock) {
+                 lockedReset();
+             }
          }
      }
  
+     private void lockedReset() throws IOException {
+         ensureOpen();
+         if (markedChar < 0)
+             throw new IOException((markedChar == INVALIDATED)
+                                   ? "Mark invalid"
+                                   : "Stream not marked");
+         nextChar = markedChar;
+         skipLF = markedSkipLF;
+     }
+ 
      public void close() throws IOException {
!         Object lock = this.lock;
!         if (lock instanceof InternalLock locker) {
!             locker.lock();
              try {
!                 lockedClose();
              } finally {
!                 locker.unlock();
              }
+         } else {
+             synchronized (lock) {
+                 lockedClose();
+             }
+         }
+     }
+ 
+     private void lockedClose() throws IOException {
+         if (in == null)
+             return;
+         try {
+             in.close();
+         } finally {
+             in = null;
+             cb = null;
          }
      }
  
      /**
       * Returns a {@code Stream}, the elements of which are lines read from
< prev index next >