FilterInputStream与装饰模式(2)--BufferedInputStream源码

版权声明:本文为博主原创文章,转载请注明作者与出处,http://blog.csdn.net/lixingtao0520 https://blog.csdn.net/lixingtao0520/article/details/75453102

BufferedInputStream :存在缓冲器的输入流,并提供mark和reset方法,在 BufferedInputStream构造器初始化时,会创建一个缓冲数组。mark方法标记输入流的当前位置点,reset方法会从最近一次的mark点重新读取流数据。

       BufferedInputStream 源码:   

 public class BufferedInputStream extends FilterInputStream {

   //存储数据的缓冲数组;必要是可以被另外一个不同大小的数组替换;volatile表明是多线程即时可见
   protected volatile byte buf[];

  //缓冲数组的原子更新器,针对volatile修饰的buf数组
   private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
          AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class,  byte[].class, "buf");


 //缓冲数组中有效的字节数,buf[0]和buf[count-1]存储了缓冲的数据流
  protected int count;

  //缓冲数组的当前读取位置,值在0和count之间
  protected int pos;
  
  //最后一次mark方法调用后,存储的标记位置,值在-1和pos之间;当markpos!=-1,则从buf[markpos]至buf[pos-1]之间的值均在缓冲数组中保留,如果markpos=0,在pos读到缓冲数组末尾时,如果buffer的长度超过了marklimit则此markpos标记会被抛弃
  protected int markpos = -1;
 
  //保留标记位置的最大区间,buffer的长度大于marklimit,标记位置不再保留。
  protected int marklimit;

  //检验底层数据流是否为null,如果不为null,则返回此流
  private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }
  
  //校验缓冲数组是否为null,不为null,则返回此数组
   private byte[] getBufIfOpen() throws IOException {
        byte[] buffer = buf;
        if (buffer == null)
            throw new IOException("Stream closed");
        return buffer;
    }

   //构造函数,defaultBufferSize默认大小为8192
    public BufferedInputStream(InputStream in) {
        this(in, defaultBufferSize);
    }
   //构造函数,同时创建一个大小为size的缓冲数组
   public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }
    
   /*用数据来填充缓存数组,填充时规则:
     1 如果没有标记位置(mark<0),则可以将缓冲数组中的数据都舍弃(throw away),将pos=0,以备继续读取底层流数据,覆盖原数据(全部覆盖还是只覆盖数组前面一部分,取决于还有多少流数据要被读取);
     2 如果有标记位置(markpos>=0),且pos<buffer.length时,表明buffer中有空间,则可以继续读取流数据
     3 如果有标记位置(markpos>=0),且pos>=buffer.length时,表明已经读到缓冲数组的末尾。
       1) 如果markpos>0,也就是说此时标记位置markpos到pos之间的数据需要保存(以便reset),而缓冲数组中markpos之前的数据可以舍弃。将缓冲数组中需要保留的数据复制到此数组前面sz=pos-markpos的空间中,pos为sz,markpos=0为起始位置。然后可以从底层数据流中继续缓冲进buffer中大小为length-pos的数据。count为实际读取的字节大小+pos.
       2) 如果markpos=0,且buffer的长度大于marklimit,则标记位置不再保留,pos=0,此时将缓冲数组的数据舍弃,继续读取底层流数据(即走规则1),当调用reset方法时,此时会报异常(Resettin to invalid mark)
       3)如果markpos=0,且buffer.length<=marklimit,则对buffer扩容,buffer的大小扩展为pos*2(当pos*2<marklimit时;如果pos*2>marklimit,则buffer的扩容后的大小为marklimit),并将原buffer中保存的数据拷贝到新buffer中,继续读取底层流数据。
     注:通过修改defaultBufferSize的大小,本地可以重现以上各种规则下的fill操作        
    */
   private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer 没有标记位置时,将pos定位到缓冲数组的起始位置 */
        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 {            /* grow buffer */
                int nsz = pos * 2;
                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;
    } 

  //返回当前位置+1的byte值(int表示)。
  //byte[i] & 0xff:此操作是将byte数据转成int型;使用了这个操作,最终的整形数据只有低8位有数据,其他位数都为0。
  (0xff二进制一共32位,后8位是1,其他位为0)
  public synchronized int read() throws IOException {
        if (pos >= count) {//判断当前位置是否>=count值,如果是,则进行fill操作,填充缓冲数组
            fill();
            if (pos >= count)
                return -1;
        }
        //如果pos小于count值,则返回数组当前索引的值
        return getBufIfOpen()[pos++] & 0xff;
    }

}

 

补充其他方法如下:

   /*read1方法,此方法为私有方法:
    * 判断缓冲数组是否可读,
    *  1、如果可读,从缓冲数组中读取cnt长度,cnt是可读长度与欲读长度的较小者。
    *  2、如果avail<=0,即缓冲数组中没有可读字节,判断len是否大于缓冲数组长度且无mark标记,
    *    如果是,则直接从底层流读取数据,并返回
    *    如果不是,则fill缓冲数组,继续判断是否可读,如果可读走流程1,不可读则已到达流末尾,直接返回-1.
    */
    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;
    }

    /*此方法尽可能的读取数据,直到达到欲读长度len,或流的末尾,才停止。通过调用read1方法实现。
    * 根据条件判断是否达到欲读长度len或流的末尾来一次或多次调用read1方法
    */
    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;
        }
    }

      /* 略过多少字节
       * 返回实际略过的字节数
       */
      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;
    }

   //返回可读字节数量
   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;
    }

    // mark方法,标记当前位置
   public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }

   //reset方法:重置当前位置为markpos
    public synchronized void reset() throws IOException {
        getBufIfOpen(); // Cause exception if closed
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }

   //是否支持mark和reset方法
   public boolean markSupported() {
        return true;
    }

   //关闭流,并释放缓冲数组中的数据
   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()
        }
    }


猜你喜欢

转载自blog.csdn.net/lixingtao0520/article/details/75453102