Path: blob/master/src/java.base/share/classes/java/io/BufferedInputStream.java
41152 views
/*1* Copyright (c) 1994, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.io;2627import jdk.internal.misc.Unsafe;28import jdk.internal.util.ArraysSupport;2930/**31* A {@code BufferedInputStream} adds32* functionality to another input stream-namely,33* the ability to buffer the input and to34* support the {@code mark} and {@code reset}35* methods. When the {@code BufferedInputStream}36* is created, an internal buffer array is37* created. As bytes from the stream are read38* or skipped, the internal buffer is refilled39* as necessary from the contained input stream,40* many bytes at a time. The {@code mark}41* operation remembers a point in the input42* stream and the {@code reset} operation43* causes all the bytes read since the most44* recent {@code mark} operation to be45* reread before new bytes are taken from46* the contained input stream.47*48* @author Arthur van Hoff49* @since 1.050*/51public class BufferedInputStream extends FilterInputStream {5253private static int DEFAULT_BUFFER_SIZE = 8192;5455/**56* As this class is used early during bootstrap, it's motivated to use57* Unsafe.compareAndSetObject instead of AtomicReferenceFieldUpdater58* (or VarHandles) to reduce dependencies and improve startup time.59*/60private static final Unsafe U = Unsafe.getUnsafe();6162private static final long BUF_OFFSET63= U.objectFieldOffset(BufferedInputStream.class, "buf");6465/**66* The internal buffer array where the data is stored. When necessary,67* it may be replaced by another array of68* a different size.69*/70/*71* We null this out with a CAS on close(), which is necessary since72* closes can be asynchronous. We use nullness of buf[] as primary73* indicator that this stream is closed. (The "in" field is also74* nulled out on close.)75*/76protected volatile byte[] buf;7778/**79* The index one greater than the index of the last valid byte in80* the buffer.81* This value is always82* in the range {@code 0} through {@code buf.length};83* elements {@code buf[0]} through {@code buf[count-1]}84* contain buffered input data obtained85* from the underlying input stream.86*/87protected int count;8889/**90* The current position in the buffer. This is the index of the next91* character to be read from the {@code buf} array.92* <p>93* This value is always in the range {@code 0}94* through {@code count}. If it is less95* than {@code count}, then {@code buf[pos]}96* is the next byte to be supplied as input;97* if it is equal to {@code count}, then98* the next {@code read} or {@code skip}99* operation will require more bytes to be100* read from the contained input stream.101*102* @see java.io.BufferedInputStream#buf103*/104protected int pos;105106/**107* The value of the {@code pos} field at the time the last108* {@code mark} method was called.109* <p>110* This value is always111* in the range {@code -1} through {@code pos}.112* If there is no marked position in the input113* stream, this field is {@code -1}. If114* there is a marked position in the input115* stream, then {@code buf[markpos]}116* is the first byte to be supplied as input117* after a {@code reset} operation. If118* {@code markpos} is not {@code -1},119* then all bytes from positions {@code buf[markpos]}120* through {@code buf[pos-1]} must remain121* in the buffer array (though they may be122* moved to another place in the buffer array,123* with suitable adjustments to the values124* of {@code count}, {@code pos},125* and {@code markpos}); they may not126* be discarded unless and until the difference127* between {@code pos} and {@code markpos}128* exceeds {@code marklimit}.129*130* @see java.io.BufferedInputStream#mark(int)131* @see java.io.BufferedInputStream#pos132*/133protected int markpos = -1;134135/**136* The maximum read ahead allowed after a call to the137* {@code mark} method before subsequent calls to the138* {@code reset} method fail.139* Whenever the difference between {@code pos}140* and {@code markpos} exceeds {@code marklimit},141* then the mark may be dropped by setting142* {@code markpos} to {@code -1}.143*144* @see java.io.BufferedInputStream#mark(int)145* @see java.io.BufferedInputStream#reset()146*/147protected int marklimit;148149/**150* Check to make sure that underlying input stream has not been151* nulled out due to close; if not return it;152*/153private InputStream getInIfOpen() throws IOException {154InputStream input = in;155if (input == null)156throw new IOException("Stream closed");157return input;158}159160/**161* Check to make sure that buffer has not been nulled out due to162* close; if not return it;163*/164private byte[] getBufIfOpen() throws IOException {165byte[] buffer = buf;166if (buffer == null)167throw new IOException("Stream closed");168return buffer;169}170171/**172* Creates a {@code BufferedInputStream}173* and saves its argument, the input stream174* {@code in}, for later use. An internal175* buffer array is created and stored in {@code buf}.176*177* @param in the underlying input stream.178*/179public BufferedInputStream(InputStream in) {180this(in, DEFAULT_BUFFER_SIZE);181}182183/**184* Creates a {@code BufferedInputStream}185* with the specified buffer size,186* and saves its argument, the input stream187* {@code in}, for later use. An internal188* buffer array of length {@code size}189* is created and stored in {@code buf}.190*191* @param in the underlying input stream.192* @param size the buffer size.193* @throws IllegalArgumentException if {@code size <= 0}.194*/195public BufferedInputStream(InputStream in, int size) {196super(in);197if (size <= 0) {198throw new IllegalArgumentException("Buffer size <= 0");199}200buf = new byte[size];201}202203/**204* Fills the buffer with more data, taking into account205* shuffling and other tricks for dealing with marks.206* Assumes that it is being called by a synchronized method.207* This method also assumes that all data has already been read in,208* hence pos > count.209*/210private void fill() throws IOException {211byte[] buffer = getBufIfOpen();212if (markpos < 0)213pos = 0; /* no mark: throw away the buffer */214else if (pos >= buffer.length) { /* no room left in buffer */215if (markpos > 0) { /* can throw away early part of the buffer */216int sz = pos - markpos;217System.arraycopy(buffer, markpos, buffer, 0, sz);218pos = sz;219markpos = 0;220} else if (buffer.length >= marklimit) {221markpos = -1; /* buffer got too big, invalidate mark */222pos = 0; /* drop buffer contents */223} else { /* grow buffer */224int nsz = ArraysSupport.newLength(pos,2251, /* minimum growth */226pos /* preferred growth */);227if (nsz > marklimit)228nsz = marklimit;229byte[] nbuf = new byte[nsz];230System.arraycopy(buffer, 0, nbuf, 0, pos);231if (!U.compareAndSetReference(this, BUF_OFFSET, buffer, nbuf)) {232// Can't replace buf if there was an async close.233// Note: This would need to be changed if fill()234// is ever made accessible to multiple threads.235// But for now, the only way CAS can fail is via close.236// assert buf == null;237throw new IOException("Stream closed");238}239buffer = nbuf;240}241}242count = pos;243int n = getInIfOpen().read(buffer, pos, buffer.length - pos);244if (n > 0)245count = n + pos;246}247248/**249* See250* the general contract of the {@code read}251* method of {@code InputStream}.252*253* @return the next byte of data, or {@code -1} if the end of the254* stream is reached.255* @throws IOException if this input stream has been closed by256* invoking its {@link #close()} method,257* or an I/O error occurs.258* @see java.io.FilterInputStream#in259*/260public synchronized int read() throws IOException {261if (pos >= count) {262fill();263if (pos >= count)264return -1;265}266return getBufIfOpen()[pos++] & 0xff;267}268269/**270* Read characters into a portion of an array, reading from the underlying271* stream at most once if necessary.272*/273private int read1(byte[] b, int off, int len) throws IOException {274int avail = count - pos;275if (avail <= 0) {276/* If the requested length is at least as large as the buffer, and277if there is no mark/reset activity, do not bother to copy the278bytes into the local buffer. In this way buffered streams will279cascade harmlessly. */280if (len >= getBufIfOpen().length && markpos < 0) {281return getInIfOpen().read(b, off, len);282}283fill();284avail = count - pos;285if (avail <= 0) return -1;286}287int cnt = (avail < len) ? avail : len;288System.arraycopy(getBufIfOpen(), pos, b, off, cnt);289pos += cnt;290return cnt;291}292293/**294* Reads bytes from this byte-input stream into the specified byte array,295* starting at the given offset.296*297* <p> This method implements the general contract of the corresponding298* {@link InputStream#read(byte[], int, int) read} method of299* the {@link InputStream} class. As an additional300* convenience, it attempts to read as many bytes as possible by repeatedly301* invoking the {@code read} method of the underlying stream. This302* iterated {@code read} continues until one of the following303* conditions becomes true: <ul>304*305* <li> The specified number of bytes have been read,306*307* <li> The {@code read} method of the underlying stream returns308* {@code -1}, indicating end-of-file, or309*310* <li> The {@code available} method of the underlying stream311* returns zero, indicating that further input requests would block.312*313* </ul> If the first {@code read} on the underlying stream returns314* {@code -1} to indicate end-of-file then this method returns315* {@code -1}. Otherwise this method returns the number of bytes316* actually read.317*318* <p> Subclasses of this class are encouraged, but not required, to319* attempt to read as many bytes as possible in the same fashion.320*321* @param b destination buffer.322* @param off offset at which to start storing bytes.323* @param len maximum number of bytes to read.324* @return the number of bytes read, or {@code -1} if the end of325* the stream has been reached.326* @throws IOException if this input stream has been closed by327* invoking its {@link #close()} method,328* or an I/O error occurs.329*/330public synchronized int read(byte b[], int off, int len)331throws IOException332{333getBufIfOpen(); // Check for closed stream334if ((off | len | (off + len) | (b.length - (off + len))) < 0) {335throw new IndexOutOfBoundsException();336} else if (len == 0) {337return 0;338}339340int n = 0;341for (;;) {342int nread = read1(b, off + n, len - n);343if (nread <= 0)344return (n == 0) ? nread : n;345n += nread;346if (n >= len)347return n;348// if not closed but no bytes available, return349InputStream input = in;350if (input != null && input.available() <= 0)351return n;352}353}354355/**356* See the general contract of the {@code skip}357* method of {@code InputStream}.358*359* @throws IOException if this input stream has been closed by360* invoking its {@link #close()} method,361* {@code in.skip(n)} throws an IOException,362* or an I/O error occurs.363*/364public synchronized long skip(long n) throws IOException {365getBufIfOpen(); // Check for closed stream366if (n <= 0) {367return 0;368}369long avail = count - pos;370371if (avail <= 0) {372// If no mark position set then don't keep in buffer373if (markpos <0)374return getInIfOpen().skip(n);375376// Fill in buffer to save bytes for reset377fill();378avail = count - pos;379if (avail <= 0)380return 0;381}382383long skipped = (avail < n) ? avail : n;384pos += skipped;385return skipped;386}387388/**389* Returns an estimate of the number of bytes that can be read (or390* skipped over) from this input stream without blocking by the next391* invocation of a method for this input stream. The next invocation might be392* the same thread or another thread. A single read or skip of this393* many bytes will not block, but may read or skip fewer bytes.394* <p>395* This method returns the sum of the number of bytes remaining to be read in396* the buffer ({@code count - pos}) and the result of calling the397* {@link java.io.FilterInputStream#in in}{@code .available()}.398*399* @return an estimate of the number of bytes that can be read (or skipped400* over) from this input stream without blocking.401* @throws IOException if this input stream has been closed by402* invoking its {@link #close()} method,403* or an I/O error occurs.404*/405public synchronized int available() throws IOException {406int n = count - pos;407int avail = getInIfOpen().available();408return n > (Integer.MAX_VALUE - avail)409? Integer.MAX_VALUE410: n + avail;411}412413/**414* See the general contract of the {@code mark}415* method of {@code InputStream}.416*417* @param readlimit the maximum limit of bytes that can be read before418* the mark position becomes invalid.419* @see java.io.BufferedInputStream#reset()420*/421public synchronized void mark(int readlimit) {422marklimit = readlimit;423markpos = pos;424}425426/**427* See the general contract of the {@code reset}428* method of {@code InputStream}.429* <p>430* If {@code markpos} is {@code -1}431* (no mark has been set or the mark has been432* invalidated), an {@code IOException}433* is thrown. Otherwise, {@code pos} is434* set equal to {@code markpos}.435*436* @throws IOException if this stream has not been marked or,437* if the mark has been invalidated, or the stream438* has been closed by invoking its {@link #close()}439* method, or an I/O error occurs.440* @see java.io.BufferedInputStream#mark(int)441*/442public synchronized void reset() throws IOException {443getBufIfOpen(); // Cause exception if closed444if (markpos < 0)445throw new IOException("Resetting to invalid mark");446pos = markpos;447}448449/**450* Tests if this input stream supports the {@code mark}451* and {@code reset} methods. The {@code markSupported}452* method of {@code BufferedInputStream} returns453* {@code true}.454*455* @return a {@code boolean} indicating if this stream type supports456* the {@code mark} and {@code reset} methods.457* @see java.io.InputStream#mark(int)458* @see java.io.InputStream#reset()459*/460public boolean markSupported() {461return true;462}463464/**465* Closes this input stream and releases any system resources466* associated with the stream.467* Once the stream has been closed, further read(), available(), reset(),468* or skip() invocations will throw an IOException.469* Closing a previously closed stream has no effect.470*471* @throws IOException if an I/O error occurs.472*/473public void close() throws IOException {474byte[] buffer;475while ( (buffer = buf) != null) {476if (U.compareAndSetReference(this, BUF_OFFSET, buffer, null)) {477InputStream input = in;478in = null;479if (input != null)480input.close();481return;482}483// Else retry in case a new buf was CASed in fill()484}485}486}487488489