1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.io; 19 20import java.util.Arrays; 21 22/** 23 * Wraps an existing {@link InputStream} and <em>buffers</em> the input. 24 * Expensive interaction with the underlying input stream is minimized, since 25 * most (smaller) requests can be satisfied by accessing the buffer alone. The 26 * drawback is that some extra space is required to hold the buffer and that 27 * copying takes place when filling that buffer, but this is usually outweighed 28 * by the performance benefits. 29 * 30 * <p/>A typical application pattern for the class looks like this:<p/> 31 * 32 * <pre> 33 * BufferedInputStream buf = new BufferedInputStream(new FileInputStream("file.java")); 34 * </pre> 35 * 36 * @see BufferedOutputStream 37 */ 38public class BufferedInputStream extends FilterInputStream { 39 /** 40 * The default buffer size if it is not specified. 41 * 42 * @hide 43 */ 44 public static final int DEFAULT_BUFFER_SIZE = 8192; 45 46 /** 47 * The buffer containing the current bytes read from the target InputStream. 48 */ 49 protected volatile byte[] buf; 50 51 /** 52 * The total number of bytes inside the byte array {@code buf}. 53 */ 54 protected int count; 55 56 /** 57 * The current limit, which when passed, invalidates the current mark. 58 */ 59 protected int marklimit; 60 61 /** 62 * The currently marked position. -1 indicates no mark has been set or the 63 * mark has been invalidated. 64 */ 65 protected int markpos = -1; 66 67 /** 68 * The current position within the byte array {@code buf}. 69 */ 70 protected int pos; 71 72 /** 73 * Constructs a new {@code BufferedInputStream}, providing {@code in} with a buffer 74 * of 8192 bytes. 75 * 76 * <p><strong>Warning:</strong> passing a null source creates a closed 77 * {@code BufferedInputStream}. All read operations on such a stream will 78 * fail with an IOException. 79 * 80 * @param in the {@code InputStream} the buffer reads from. 81 */ 82 public BufferedInputStream(InputStream in) { 83 this(in, DEFAULT_BUFFER_SIZE); 84 } 85 86 /** 87 * Constructs a new {@code BufferedInputStream}, providing {@code in} with {@code size} bytes 88 * of buffer. 89 * 90 * <p><strong>Warning:</strong> passing a null source creates a closed 91 * {@code BufferedInputStream}. All read operations on such a stream will 92 * fail with an IOException. 93 * 94 * @param in the {@code InputStream} the buffer reads from. 95 * @param size the size of buffer in bytes. 96 * @throws IllegalArgumentException if {@code size <= 0}. 97 */ 98 public BufferedInputStream(InputStream in, int size) { 99 super(in); 100 if (size <= 0) { 101 throw new IllegalArgumentException("size <= 0"); 102 } 103 buf = new byte[size]; 104 } 105 106 /** 107 * Returns an estimated number of bytes that can be read or skipped without blocking for more 108 * input. This method returns the number of bytes available in the buffer 109 * plus those available in the source stream, but see {@link InputStream#available} for 110 * important caveats. 111 * 112 * @return the estimated number of bytes available 113 * @throws IOException if this stream is closed or an error occurs 114 */ 115 @Override 116 public synchronized int available() throws IOException { 117 InputStream localIn = in; // 'in' could be invalidated by close() 118 if (buf == null || localIn == null) { 119 throw streamClosed(); 120 } 121 return count - pos + localIn.available(); 122 } 123 124 private IOException streamClosed() throws IOException { 125 throw new IOException("BufferedInputStream is closed"); 126 } 127 128 /** 129 * Closes this stream. The source stream is closed and any resources 130 * associated with it are released. 131 * 132 * @throws IOException 133 * if an error occurs while closing this stream. 134 */ 135 @Override 136 public void close() throws IOException { 137 buf = null; 138 InputStream localIn = in; 139 in = null; 140 if (localIn != null) { 141 localIn.close(); 142 } 143 } 144 145 private int fillbuf(InputStream localIn, byte[] localBuf) 146 throws IOException { 147 if (markpos == -1 || (pos - markpos >= marklimit)) { 148 /* Mark position not set or exceeded readlimit */ 149 int result = localIn.read(localBuf); 150 if (result > 0) { 151 markpos = -1; 152 pos = 0; 153 count = result == -1 ? 0 : result; 154 } 155 return result; 156 } 157 if (markpos == 0 && marklimit > localBuf.length) { 158 /* Increase buffer size to accommodate the readlimit */ 159 int newLength = localBuf.length * 2; 160 if (newLength > marklimit) { 161 newLength = marklimit; 162 } 163 byte[] newbuf = new byte[newLength]; 164 System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length); 165 // Reassign buf, which will invalidate any local references 166 // FIXME: what if buf was null? 167 localBuf = buf = newbuf; 168 } else if (markpos > 0) { 169 System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length 170 - markpos); 171 } 172 /* Set the new position and mark position */ 173 pos -= markpos; 174 count = markpos = 0; 175 int bytesread = localIn.read(localBuf, pos, localBuf.length - pos); 176 count = bytesread <= 0 ? pos : pos + bytesread; 177 return bytesread; 178 } 179 180 /** 181 * Sets a mark position in this stream. The parameter {@code readlimit} 182 * indicates how many bytes can be read before a mark is invalidated. 183 * Calling {@code reset()} will reposition the stream back to the marked 184 * position if {@code readlimit} has not been surpassed. The underlying 185 * buffer may be increased in size to allow {@code readlimit} number of 186 * bytes to be supported. 187 * 188 * @param readlimit 189 * the number of bytes that can be read before the mark is 190 * invalidated. 191 * @see #reset() 192 */ 193 @Override 194 public synchronized void mark(int readlimit) { 195 marklimit = readlimit; 196 markpos = pos; 197 } 198 199 /** 200 * Indicates whether {@code BufferedInputStream} supports the {@code mark()} 201 * and {@code reset()} methods. 202 * 203 * @return {@code true} for BufferedInputStreams. 204 * @see #mark(int) 205 * @see #reset() 206 */ 207 @Override 208 public boolean markSupported() { 209 return true; 210 } 211 212 /** 213 * Reads a single byte from this stream and returns it as an integer in the 214 * range from 0 to 255. Returns -1 if the end of the source string has been 215 * reached. If the internal buffer does not contain any available bytes then 216 * it is filled from the source stream and the first byte is returned. 217 * 218 * @return the byte read or -1 if the end of the source stream has been 219 * reached. 220 * @throws IOException 221 * if this stream is closed or another IOException occurs. 222 */ 223 @Override 224 public synchronized int read() throws IOException { 225 // Use local refs since buf and in may be invalidated by an 226 // unsynchronized close() 227 byte[] localBuf = buf; 228 InputStream localIn = in; 229 if (localBuf == null || localIn == null) { 230 throw streamClosed(); 231 } 232 233 /* Are there buffered bytes available? */ 234 if (pos >= count && fillbuf(localIn, localBuf) == -1) { 235 return -1; /* no, fill buffer */ 236 } 237 // localBuf may have been invalidated by fillbuf 238 if (localBuf != buf) { 239 localBuf = buf; 240 if (localBuf == null) { 241 throw streamClosed(); 242 } 243 } 244 245 /* Did filling the buffer fail with -1 (EOF)? */ 246 if (count - pos > 0) { 247 return localBuf[pos++] & 0xFF; 248 } 249 return -1; 250 } 251 252 @Override public synchronized int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { 253 // Use local ref since buf may be invalidated by an unsynchronized 254 // close() 255 byte[] localBuf = buf; 256 if (localBuf == null) { 257 throw streamClosed(); 258 } 259 Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount); 260 if (byteCount == 0) { 261 return 0; 262 } 263 InputStream localIn = in; 264 if (localIn == null) { 265 throw streamClosed(); 266 } 267 268 int required; 269 if (pos < count) { 270 /* There are bytes available in the buffer. */ 271 int copylength = count - pos >= byteCount ? byteCount : count - pos; 272 System.arraycopy(localBuf, pos, buffer, byteOffset, copylength); 273 pos += copylength; 274 if (copylength == byteCount || localIn.available() == 0) { 275 return copylength; 276 } 277 byteOffset += copylength; 278 required = byteCount - copylength; 279 } else { 280 required = byteCount; 281 } 282 283 while (true) { 284 int read; 285 /* 286 * If we're not marked and the required size is greater than the 287 * buffer, simply read the bytes directly bypassing the buffer. 288 */ 289 if (markpos == -1 && required >= localBuf.length) { 290 read = localIn.read(buffer, byteOffset, required); 291 if (read == -1) { 292 return required == byteCount ? -1 : byteCount - required; 293 } 294 } else { 295 if (fillbuf(localIn, localBuf) == -1) { 296 return required == byteCount ? -1 : byteCount - required; 297 } 298 // localBuf may have been invalidated by fillbuf 299 if (localBuf != buf) { 300 localBuf = buf; 301 if (localBuf == null) { 302 throw streamClosed(); 303 } 304 } 305 306 read = count - pos >= required ? required : count - pos; 307 System.arraycopy(localBuf, pos, buffer, byteOffset, read); 308 pos += read; 309 } 310 required -= read; 311 if (required == 0) { 312 return byteCount; 313 } 314 if (localIn.available() == 0) { 315 return byteCount - required; 316 } 317 byteOffset += read; 318 } 319 } 320 321 /** 322 * Resets this stream to the last marked location. 323 * 324 * @throws IOException 325 * if this stream is closed, no mark has been set or the mark is 326 * no longer valid because more than {@code readlimit} bytes 327 * have been read since setting the mark. 328 * @see #mark(int) 329 */ 330 @Override 331 public synchronized void reset() throws IOException { 332 if (buf == null) { 333 throw new IOException("Stream is closed"); 334 } 335 if (markpos == -1) { 336 throw new IOException("Mark has been invalidated."); 337 } 338 pos = markpos; 339 } 340 341 /** 342 * Skips {@code byteCount} bytes in this stream. Subsequent calls to 343 * {@code read} will not return these bytes unless {@code reset} is 344 * used. 345 * 346 * @param byteCount 347 * the number of bytes to skip. {@code skip} does nothing and 348 * returns 0 if {@code byteCount} is less than zero. 349 * @return the number of bytes actually skipped. 350 * @throws IOException 351 * if this stream is closed or another IOException occurs. 352 */ 353 @Override 354 public synchronized long skip(long byteCount) throws IOException { 355 // Use local refs since buf and in may be invalidated by an 356 // unsynchronized close() 357 byte[] localBuf = buf; 358 InputStream localIn = in; 359 if (localBuf == null) { 360 throw streamClosed(); 361 } 362 if (byteCount < 1) { 363 return 0; 364 } 365 if (localIn == null) { 366 throw streamClosed(); 367 } 368 369 if (count - pos >= byteCount) { 370 pos += byteCount; 371 return byteCount; 372 } 373 long read = count - pos; 374 pos = count; 375 376 if (markpos != -1) { 377 if (byteCount <= marklimit) { 378 if (fillbuf(localIn, localBuf) == -1) { 379 return read; 380 } 381 if (count - pos >= byteCount - read) { 382 pos += byteCount - read; 383 return byteCount; 384 } 385 // Couldn't get all the bytes, skip what we read 386 read += (count - pos); 387 pos = count; 388 return read; 389 } 390 } 391 return read + localIn.skip(byteCount - read); 392 } 393} 394