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

来源:互联网 发布:java的排序函数 编辑:程序博客网 时间:2024/06/01 10:50

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()        }    }