public class BufferedInputStream extends FilterInputStream { private static int defaultBufferSize = 8192; // 内部用来存储数据的缓冲数组 protected volatile byte buf[]; private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater .newUpdater(BufferedInputStream.class, byte[].class, "buf"); protected int count; protected int pos; protected int markpos = -1; protected int marklimit; //构造器,传入一个输入流,为内部的字节数组开辟空间 public BufferedInputStream(InputStream in) { this(in, defaultBufferSize); } public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; } //获取输入流 private InputStream getInIfOpen() throws IOException { InputStream input = in;//in继承自FilterInputStream if (input == null) throw new IOException("Stream closed"); return input; } //获取缓冲数组首地址 private byte[] getBufIfOpen() throws IOException { byte[] buffer = buf; if (buffer == null) throw new IOException("Stream closed"); return buffer; } /** * 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 { /* 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; } /** * 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 */ 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. */ 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. */ public synchronized int available() throws IOException { return getInIfOpen().available() + (count - pos); } /** * 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() */ 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() } } }