BufferedInputSream默认是现实给出了一个8192byte大小的数组。
private static int DEFAULT_BUFFER_SIZE = 8192;
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
在其中有一些参数用来表示缓存数组的存取情况。
protected int pos;
protected int count;
pos用来表示在当前数组中,被消费到的位置,正常情况下,当read()方法被调用时,被取出的数据就是buf[pos]位置上的数据。count则表示在缓存数组中可以被读取的数据大小,因此pos大小应该是介于0与count之间的,而count的大小应该是介于buf这个数组的长度上面的。
protected int markpos = -1;
protected int marklimit;
markpos表示添加书签的位置而marklimit则表示在读取过书签失效的最大位置。
在每次要根据buf来进行相关的操作的时候都要通过getBufIfOpen()来获得。
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
每次都根据buf的引用来确认是否为空,保证在多线程情况下,能够及时确认到stream的关闭。
通过read()来获取数据。
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
根据前面对于pos和count的解释,当pos大于等于count的时候,证明buf数组中已经没有可以获取的数据,则需要fill()方法来扩充或者补充buf中的数据,如果经过fill()方法后,pos仍旧大于等于count,则代表stream已经被读完,则返回-1,代表已经被读完。如果经过fill()方法pos已经小于count或者本身pos已经小于count,那么直接返回buf[pos]上的数据,pos并自加准备取下一个位置上的数据。
但是取出来的数据需要和0xff相与,实则是为了保证表示读取完毕的Integer-1与正常读取的Byte-1进行区别。Integer类型的-1,则为32位1,而Byte形式的-1则是1111 1111,返回时如果直接转为Integer会直接变为-1,导致无法确认究竟结束还是真的读取数据为Byte为-1,所以讲Byte与0xff(也就是8位1)按位相与的结果再转化为Integer则是高24位为0,第八位为1,转化的结果则是255,避免和结束标志冲突。
fill()则是前面用来补充缓存数组中的数据的方法。
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;
}
当进入fill()方法后,如果不存在markpos,则表示当前的缓存数组完全可以被替换掉,因此pos被置为0,准备从0开始到数组结束全部更新。此时,将会从给出的数据流中读取pos位置到缓存数组总长度大小的数据加入到缓存数组当中。而count,也就是可读取数据的最后一位,则为之前放入缓存数组的数据长度加上pos。
如果存在markpos的情况下,也就说当前缓存数组中的数据需要得到一定的保留。
如果pos已经大于等于缓存数组的大小,先确认markpos是否存在,如果大于0,则表明在markpos之前的数据可以被丢弃,那么直接就把markpos到pos的数据复制到缓存数组中,并将pos赋值到原本数组长度减去markpos的长度,以确保接下来的数据可以被正确读取,并在接下来继续填充数据,markpos也自然被赋值为0。
如果markpos已经为0,则确认当前数组的长度是否已经大于marklimit,则表明已经没有必要继续保持markpos,则赋值为-1,并pos赋值为0,准备将缓存数组全部重新填充数据。
如果并没有大于marklimit,则需要扩充数组以确保书签的成功保留,但是如果当前的数组大小已经最大大小,则会抛出错误。
如果并没有超出最大大小,则当前缓存数组可以继续扩容。
扩容时,当前pos的大小应该等于当前数组的大小,则选择2倍pos和最大数组大小中的较小者作为扩容后的数组大小。之后将原本数组的数据复制到新生成的较大数组,并通过cas确保线程安全的将新数组作为新的缓存数组复制给当前BufferedInputStream。完成之后,重新给新数组如同之前一样补充数据。