Buffered Input Stream for Java IO Learning (BufferedInputStream)

  • 2020-06-07 04:30:24
  • OfStack

Java IO BufferedInputStream

Summary:

BufferedInputStream is a buffered input stream, inherited from FilterInputStream, that adds functionality to another input stream, essentially via an internal buffered array. For example, after creating the corresponding BufferedInputStream of an input stream, when reading data through read(), BufferedInputStream will fill the data of the input stream into the buffer in batches. After reading the data of the buffer, the input stream will fill the data buffer again until reading the data.

List of main functions of BufferedInputStream:


BufferedInputStream(InputStream in) 
BufferedInputStream(InputStream in, int size) 
synchronized int available() 
void close() 
synchronized void mark(int readlimit) 
boolean markSupported() 
synchronized int read() 
synchronized int read(byte[] buffer, int offset, int byteCount) 
synchronized void reset() 
synchronized long skip(long byteCount) 

Sample code:


public class BufferedInputStreamTest { 
 
  private static final int LEN = 5; 
 
  public static void main(String[] args) { 
    testBufferedInputStream() ; 
  } 
  private static void testBufferedInputStream() { 
 
    //  create BufferedInputStream Byte stream, content is ArrayLetters An array of  
    try { 
      File file = new File("file.txt"); 
      InputStream in =new BufferedInputStream(new FileInputStream(file), 512); 
 
      //  Read from the byte stream 5 Bytes. " abcde ", a The corresponding 0x61 . b The corresponding 0x62 , and so on ... 
      for (int i=0; i<LEN; i++) { 
      //  If I can keep reading 1 Two bytes, then read the next 1 bytes  
        if (in.available() >= 0) { 
        //  Read "byte stream down. 1 Bytes"  
        int tmp = in.read(); 
        System.out.printf("%d : 0x%s\n", i, Integer.toHexString(tmp)); 
        } 
      } 
 
      //  If the byte stream does not support markup, simply exit  
      if (!in.markSupported()) { 
        System.out.println("make not supported!"); 
        return ; 
      } 
 
      //  Marks "Current index position", that is, marks the first 6 Elements of position -- " f "  
      // 1024 The corresponding marklimit 
      in.mark(1024); 
 
      //  skip 22 Bytes.  
      in.skip(22); 
 
      //  read 5 bytes  
      byte[] buf = new byte[LEN]; 
      in.read(buf, 0, LEN); 
      //  will buf convert String A string.  
      String str1 = new String(buf); 
      System.out.printf("str1=%s\n", str1); 
 
      //  Reset the Input Stream index to mark() Reset the marked position to" f "Place.  
      in.reset(); 
      //  Read from reset byte stream 5 Bytes to buf In the. Read" fghij "  
      in.read(buf, 0, LEN); 
      //  will buf convert String A string.  
      String str2 = new String(buf); 
      System.out.printf("str2=%s\n", str2); 
 
      in.close(); 
    } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
    } catch (SecurityException e) { 
      e.printStackTrace(); 
    } catch (IOException e) { 
      e.printStackTrace(); 
    } 
  } 
} 

Operation results:


0 : 0x61
1 : 0x62
2 : 0x63
3 : 0x64
4 : 0x65
str1=12345
str2=fghij

BufferInputStream code based on JDK8:


public class BufferedInputStream extends FilterInputStream { 
 
  private static int DEFAULT_BUFFER_SIZE = 8192;// The default buffer size is 8X1024 
  private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; 
  protected volatile byte buf[]; // The buffer array  
 
  /** 
   * Atomic updater to provide compareAndSet for buf. This is 
   * necessary because closes can be asynchronous. We use nullness 
   * of buf[] as primary indicator that this stream is closed. (The 
   * "in" field is also nulled out on close.) 
   */ 
  private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = 
      AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class, byte[].class, "buf"); 
  // Values in 0 to buf.len between  
  protected int count; 
  // in buffer The current position of, under 1 Two characters are read  
  protected int pos; 
  // A value of mark When the function was recently called, the value is -1 to pos 
  protected int markpos = -1; 
 
  /** 
   * The maximum read ahead allowed after a call to the 
   * <code>mark</code> method before subsequent calls to the 
   * <code>reset</code> method fail. 
   * Whenever the difference between <code>pos</code> 
   * and <code>markpos</code> exceeds <code>marklimit</code>, 
   * then the mark may be dropped by setting 
   * <code>markpos</code> to <code>-1</code>. 
   * 
   * @see   java.io.BufferedInputStream#mark(int) 
   * @see   java.io.BufferedInputStream#reset() 
   */ 
 
  protected int marklimit; 
 
  /** 
   * Check to make sure that underlying input stream has not been 
   * nulled out due to close; if not return it; 
   */ 
  // 
  private InputStream getInIfOpen() throws IOException { 
    InputStream input = in; 
    if (input == null) 
      throw new IOException("Stream closed"); 
    return input; 
  } 
 
  /** 
   * Check to make sure that buffer has not been nulled out due to 
   * close; if not return it; 
   */ 
  private byte[] getBufIfOpen() throws IOException { 
    byte[] buffer = buf; 
    if (buffer == null) 
      throw new IOException("Stream closed"); 
    return buffer; 
  } 
 
  /** 
   * Creates a <code>BufferedInputStream</code> 
   * and saves its argument, the input stream 
   * <code>in</code>, for later use. An internal 
   * buffer array is created and stored in <code>buf</code>. 
   * 
   * @param  in  the underlying input stream. 
   */ 
  // with InputStream Constructor of  
  public BufferedInputStream(InputStream in) { 
      this(in, DEFAULT_BUFFER_SIZE); 
  } 
 
  /** 
   * Creates a <code>BufferedInputStream</code> 
   * with the specified buffer size, 
   * and saves its argument, the input stream 
   * <code>in</code>, for later use. An internal 
   * buffer array of length <code>size</code> 
   * is created and stored in <code>buf</code>. 
   * 
   * @param  in   the underlying input stream. 
   * @param  size  the buffer size. 
   * @exception IllegalArgumentException if {@code size <= 0}. 
   */ 
  // with InputStream And the size of the constructor  
  public BufferedInputStream(InputStream in, int size) { 
    super(in); 
    if (size <= 0) { 
        throw new IllegalArgumentException("Buffer size <= 0"); 
    } 
    buf = new byte[size]; 
  } 
 
  /** 
   * Fills the buffer with more data, taking into account 
   * shuffling and other tricks for dealing with marks. 
   * Assumes that it is being called by a synchronized method. 
   * This method also assumes that all data has already been read in, 
   * hence pos > count. 
   */ 
  // 
  private void fill() throws IOException { 
    byte[] buffer = getBufIfOpen(); 
    if (markpos < 0) 
      pos = 0;      /* no mark: throw away the buffer */ 
    else if (pos >= buffer.length) /* no room left in buffer */ 
      if (markpos > 0) { /* can throw away early part of the buffer */ 
        int sz = pos - markpos; 
        System.arraycopy(buffer, markpos, buffer, 0, sz); 
        pos = sz; 
        markpos = 0; 
      } else if (buffer.length >= marklimit) { 
         markpos = -1;  /* buffer got too big, invalidate mark */ 
         pos = 0;    /* drop buffer contents */ 
      } else if (buffer.length >= MAX_BUFFER_SIZE) { 
        throw new OutOfMemoryError("Required array size too large"); 
      } else {      /* grow buffer */ 
        int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? 
        pos * 2 : MAX_BUFFER_SIZE; 
        if (nsz > marklimit) 
          nsz = marklimit; 
        byte nbuf[] = new byte[nsz]; 
        System.arraycopy(buffer, 0, nbuf, 0, pos); 
        if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 
          // Can't replace buf if there was an async close. 
          // Note: This would need to be changed if fill() 
          // is ever made accessible to multiple threads. 
          // But for now, the only way CAS can fail is via close. 
          // assert buf == null; 
          throw new IOException("Stream closed"); 
        } 
        buffer = nbuf; 
      } 
    count = pos; 
    int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 
    if (n > 0) 
      count = n + pos; 
    } 
 
  /** 
   * See 
   * the general contract of the <code>read</code> 
   * method of <code>InputStream</code>. 
   * 
   * @return   the next byte of data, or <code>-1</code> if the end of the 
   *       stream is reached. 
   * @exception IOException if this input stream has been closed by 
   *             invoking its {@link #close()} method, 
   *             or an I/O error occurs. 
   * @see    java.io.FilterInputStream#in 
   */ 
  // Read the 1 Three bytes, no data returned -1 
  public synchronized int read() throws IOException { 
    if (pos >= count) { 
      fill(); 
      if (pos >= count) 
        return -1; 
    } 
    return getBufIfOpen()[pos++] & 0xff; 
  } 
 
  /** 
   * Read characters into a portion of an array, reading from the underlying 
   * stream at most once if necessary. 
   */ 
  private int read1(byte[] b, int off, int len) throws IOException { 
    int avail = count - pos; 
    if (avail <= 0) { 
      /* If the requested length is at least as large as the buffer, and 
        if there is no mark/reset activity, do not bother to copy the 
        bytes into the local buffer. In this way buffered streams will 
        cascade harmlessly. */ 
      if (len >= getBufIfOpen().length && markpos < 0) { 
        return getInIfOpen().read(b, off, len); 
      } 
      fill(); 
      avail = count - pos; 
      if (avail <= 0) return -1; 
    } 
    int cnt = (avail < len) ? avail : len; 
    System.arraycopy(getBufIfOpen(), pos, b, off, cnt); 
    pos += cnt; 
    return cnt; 
  } 
 
  /** 
   * Reads bytes from this byte-input stream into the specified byte array, 
   * starting at the given offset. 
   * 
   * <p> This method implements the general contract of the corresponding 
   * <code>{@link InputStream#read(byte[], int, int) read}</code> method of 
   * the <code>{@link InputStream}</code> class. As an additional 
   * convenience, it attempts to read as many bytes as possible by repeatedly 
   * invoking the <code>read</code> method of the underlying stream. This 
   * iterated <code>read</code> continues until one of the following 
   * conditions becomes true: <ul> 
   * 
   *  <li> The specified number of bytes have been read, 
   * 
   *  <li> The <code>read</code> method of the underlying stream returns 
   *  <code>-1</code>, indicating end-of-file, or 
   * 
   *  <li> The <code>available</code> method of the underlying stream 
   *  returns zero, indicating that further input requests would block. 
   * 
   * </ul> If the first <code>read</code> on the underlying stream returns 
   * <code>-1</code> to indicate end-of-file then this method returns 
   * <code>-1</code>. Otherwise this method returns the number of bytes 
   * actually read. 
   * 
   * <p> Subclasses of this class are encouraged, but not required, to 
   * attempt to read as many bytes as possible in the same fashion. 
   * 
   * @param   b   destination buffer. 
   * @param   off  offset at which to start storing bytes. 
   * @param   len  maximum number of bytes to read. 
   * @return   the number of bytes read, or <code>-1</code> if the end of 
   *       the stream has been reached. 
   * @exception IOException if this input stream has been closed by 
   *             invoking its {@link #close()} method, 
   *             or an I/O error occurs. 
   */ 
  // 
  public synchronized int read(byte b[], int off, int len)throws IOException 
  { 
    getBufIfOpen(); // Check for closed stream 
    if ((off | len | (off + len) | (b.length - (off + len))) < 0) { 
      throw new IndexOutOfBoundsException(); 
    } else if (len == 0) { 
      return 0; 
    } 
 
    int n = 0; 
    for (;;) { 
      int nread = read1(b, off + n, len - n); 
      if (nread <= 0) 
        return (n == 0) ? nread : n; 
      n += nread; 
      if (n >= len) 
        return n; 
      // if not closed but no bytes available, return 
      InputStream input = in; 
      if (input != null && input.available() <= 0) 
        return n; 
    } 
  } 
 
  /** 
   * See the general contract of the <code>skip</code> 
   * method of <code>InputStream</code>. 
   * 
   * @exception IOException if the stream does not support seek, 
   *             or if this input stream has been closed by 
   *             invoking its {@link #close()} method, or an 
   *             I/O error occurs. 
   */ 
  // skip n Long data  
  public synchronized long skip(long n) throws IOException { 
    getBufIfOpen(); // Check for closed stream 
    if (n <= 0) { 
      return 0; 
    } 
    long avail = count - pos; 
 
    if (avail <= 0) { 
      // If no mark position set then don't keep in buffer 
      if (markpos <0) 
        return getInIfOpen().skip(n); 
 
      // Fill in buffer to save bytes for reset 
      fill(); 
      avail = count - pos; 
      if (avail <= 0) 
        return 0; 
    } 
 
    long skipped = (avail < n) ? avail : n; 
    pos += skipped; 
    return skipped; 
  } 
 
  /** 
   * Returns an estimate of the number of bytes that can be read (or 
   * skipped over) from this input stream without blocking by the next 
   * invocation of a method for this input stream. The next invocation might be 
   * the same thread or another thread. A single read or skip of this 
   * many bytes will not block, but may read or skip fewer bytes. 
   * <p> 
   * This method returns the sum of the number of bytes remaining to be read in 
   * the buffer (<code>count - pos</code>) and the result of calling the 
   * {@link java.io.FilterInputStream#in in}.available(). 
   * 
   * @return   an estimate of the number of bytes that can be read (or skipped 
   *       over) from this input stream without blocking. 
   * @exception IOException if this input stream has been closed by 
   *             invoking its {@link #close()} method, 
   *             or an I/O error occurs. 
   */ 
  // Returns how much data is left to read  
  public synchronized int available() throws IOException { 
    int n = count - pos; 
    int avail = getInIfOpen().available(); 
    return n > (Integer.MAX_VALUE - avail)? Integer.MAX_VALUE: n + avail; 
  } 
 
  /** 
   * See the general contract of the <code>mark</code> 
   * method of <code>InputStream</code>. 
   * 
   * @param  readlimit  the maximum limit of bytes that can be read before 
   *           the mark position becomes invalid. 
   * @see   java.io.BufferedInputStream#reset() 
   */ 
  public synchronized void mark(int readlimit) { 
    marklimit = readlimit; 
    markpos = pos; 
  } 
 
  /** 
   * See the general contract of the <code>reset</code> 
   * method of <code>InputStream</code>. 
   * <p> 
   * If <code>markpos</code> is <code>-1</code> 
   * (no mark has been set or the mark has been 
   * invalidated), an <code>IOException</code> 
   * is thrown. Otherwise, <code>pos</code> is 
   * set equal to <code>markpos</code>. 
   * 
   * @exception IOException if this stream has not been marked or, 
   *         if the mark has been invalidated, or the stream 
   *         has been closed by invoking its {@link #close()} 
   *         method, or an I/O error occurs. 
   * @see    java.io.BufferedInputStream#mark(int) 
   */ 
  public synchronized void reset() throws IOException { 
    getBufIfOpen(); // Cause exception if closed 
    if (markpos < 0) 
      throw new IOException("Resetting to invalid mark"); 
    pos = markpos; 
  } 
 
  /** 
   * Tests if this input stream supports the <code>mark</code> 
   * and <code>reset</code> methods. The <code>markSupported</code> 
   * method of <code>BufferedInputStream</code> returns 
   * <code>true</code>. 
   * 
   * @return a <code>boolean</code> indicating if this stream type supports 
   *     the <code>mark</code> and <code>reset</code> methods. 
   * @see   java.io.InputStream#mark(int) 
   * @see   java.io.InputStream#reset() 
   */ 
  // Whether markup is supported or not  
  public boolean markSupported() { 
    return true; 
  } 
 
  /** 
   * Closes this input stream and releases any system resources 
   * associated with the stream. 
   * Once the stream has been closed, further read(), available(), reset(), 
   * or skip() invocations will throw an IOException. 
   * Closing a previously closed stream has no effect. 
   * 
   * @exception IOException if an I/O error occurs. 
   */ 
  // Close the resource   
  public void close() throws IOException { 
    byte[] buffer; 
    while ( (buffer = buf) != null) { 
      if (bufUpdater.compareAndSet(this, buffer, null)) { 
        InputStream input = in; 
        in = null; 
        if (input != null) 
          input.close(); 
        return; 
      } 
      // Else retry in case a new buf was CASed in fill() 
    } 
  } 
} 

Thank you for reading, I hope to help you, thank you for your support to this site!


Related articles: