BufferedInputStream.java revision dd828f42a5c83b4270d4fbf6fce2da1878f1e84a
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 org.apache.harmony.luni.util.Msg; 21 22// BEGIN android-added 23import java.util.logging.Logger; 24// END android-added 25 26/** 27 * Wraps an existing {@link InputStream} and <em>buffers</em> the input. 28 * Expensive interaction with the underlying input stream is minimized, since 29 * most (smaller) requests can be satisfied by accessing the buffer alone. The 30 * drawback is that some extra space is required to hold the buffer and that 31 * copying takes place when filling that buffer, but this is usually outweighed 32 * by the performance benefits. 33 * 34 * <p/>A typical application pattern for the class looks like this:<p/> 35 * 36 * <pre> 37 * BufferedInputStream buf = new BufferedInputStream(new FileInputStream("file.java")); 38 * </pre> 39 * 40 * @see BufferedOutputStream 41 * 42 * @since Android 1.0 43 */ 44public class BufferedInputStream extends FilterInputStream { 45 // BEGIN android-changed 46 // The address of the buffer should not be cached in a register. 47 // This was changed to be more close to the RI. 48 /** 49 * The buffer containing the current bytes read from the target InputStream. 50 * 51 * @since Android 1.0 52 */ 53 protected volatile byte[] buf; 54 // END android-changed 55 56 /** 57 * The total number of bytes inside the byte array {@code buf}. 58 * 59 * @since Android 1.0 60 */ 61 protected int count; 62 63 /** 64 * The current limit, which when passed, invalidates the current mark. 65 * 66 * @since Android 1.0 67 */ 68 protected int marklimit; 69 70 /** 71 * The currently marked position. -1 indicates no mark has been set or the 72 * mark has been invalidated. 73 * 74 * @since Android 1.0 75 */ 76 protected int markpos = -1; 77 78 /** 79 * The current position within the byte array {@code buf}. 80 * 81 * @since Android 1.0 82 */ 83 protected int pos; 84 85 private boolean closed = false; 86 87 /** 88 * Constructs a new {@code BufferedInputStream} on the {@link InputStream} 89 * {@code in}. The default buffer size (8 KB) is allocated and all reads 90 * can now be filtered through this stream. 91 * 92 * @param in 93 * the InputStream the buffer reads from. 94 * @since Android 1.0 95 */ 96 public BufferedInputStream(InputStream in) { 97 super(in); 98 buf = new byte[8192]; 99 100 // BEGIN android-added 101 /* 102 * For Android, we want to discourage the use of this constructor (with 103 * its arguably too-large default), so we note its use in the log. We 104 * don't disable it, nor do we alter the default, however, because we 105 * still aim to behave compatibly, and the default value, though not 106 * documented, is established by convention. 107 */ 108 Logger.global.info( 109 "Default buffer size used in BufferedInputStream " + 110 "constructor. It would be " + 111 "better to be explicit if a 8k buffer is required."); 112 // END android-added 113 } 114 115 /** 116 * Constructs a new {@code BufferedInputStream} on the {@link InputStream} 117 * {@code in}. The buffer size is specified by the parameter {@code size} 118 * and all reads are now filtered through this stream. 119 * 120 * @param in 121 * the input stream the buffer reads from. 122 * @param size 123 * the size of buffer to allocate. 124 * @throws IllegalArgumentException 125 * if {@code size < 0}. 126 * @since Android 1.0 127 */ 128 public BufferedInputStream(InputStream in, int size) { 129 super(in); 130 if (size <= 0) { 131 // K0058=size must be > 0 132 throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$ 133 } 134 buf = new byte[size]; 135 } 136 137 /** 138 * Returns the number of bytes that are available before this stream will 139 * block. This method returns the number of bytes available in the buffer 140 * plus those available in the source stream. 141 * 142 * @return the number of bytes available before blocking. 143 * @throws IOException 144 * if this stream is closed. 145 * @since Android 1.0 146 */ 147 @Override 148 public synchronized int available() throws IOException { 149 if (buf == null) { 150 // K0059=Stream is closed 151 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 152 } 153 return count - pos + in.available(); 154 } 155 156 /** 157 * Closes this stream. The source stream is closed and any resources 158 * associated with it are released. 159 * 160 * @throws IOException 161 * if an error occurs while closing this stream. 162 * @since Android 1.0 163 */ 164 @Override 165 public synchronized void close() throws IOException { 166 if (null != in) { 167 super.close(); 168 in = null; 169 } 170 buf = null; 171 closed = true; 172 } 173 174 private int fillbuf() throws IOException { 175 if (markpos == -1 || (pos - markpos >= marklimit)) { 176 /* Mark position not set or exceeded readlimit */ 177 int result = in.read(buf); 178 if (result > 0) { 179 markpos = -1; 180 pos = 0; 181 count = result == -1 ? 0 : result; 182 } 183 return result; 184 } 185 if (markpos == 0 && marklimit > buf.length) { 186 /* Increase buffer size to accomodate the readlimit */ 187 int newLength = buf.length * 2; 188 if (newLength > marklimit) { 189 newLength = marklimit; 190 } 191 byte[] newbuf = new byte[newLength]; 192 System.arraycopy(buf, 0, newbuf, 0, buf.length); 193 buf = newbuf; 194 } else if (markpos > 0) { 195 System.arraycopy(buf, markpos, buf, 0, buf.length - markpos); 196 } 197 /* Set the new position and mark position */ 198 pos -= markpos; 199 count = markpos = 0; 200 int bytesread = in.read(buf, pos, buf.length - pos); 201 count = bytesread <= 0 ? pos : pos + bytesread; 202 return bytesread; 203 } 204 205 /** 206 * Sets a mark position in this stream. The parameter {@code readlimit} 207 * indicates how many bytes can be read before a mark is invalidated. 208 * Calling {@code reset()} will reposition the stream back to the marked 209 * position if {@code readlimit} has not been surpassed. The underlying 210 * buffer may be increased in size to allow {@code readlimit} number of 211 * bytes to be supported. 212 * 213 * @param readlimit 214 * the number of bytes that can be read before the mark is 215 * invalidated. 216 * @see #reset() 217 * @since Android 1.0 218 */ 219 @Override 220 public synchronized void mark(int readlimit) { 221 marklimit = readlimit; 222 markpos = pos; 223 } 224 225 /** 226 * Indicates whether {@code BufferedInputStream} supports the {@code mark()} 227 * and {@code reset()} methods. 228 * 229 * @return {@code true} for BufferedInputStreams. 230 * @see #mark(int) 231 * @see #reset() 232 * @since Android 1.0 233 */ 234 @Override 235 public boolean markSupported() { 236 return true; 237 } 238 239 /** 240 * Reads a single byte from this stream and returns it as an integer in the 241 * range from 0 to 255. Returns -1 if the end of the source string has been 242 * reached. If the internal buffer does not contain any available bytes then 243 * it is filled from the source stream and the first byte is returned. 244 * 245 * @return the byte read or -1 if the end of the source stream has been 246 * reached. 247 * @throws IOException 248 * if this stream is closed or another IOException occurs. 249 * @since Android 1.0 250 */ 251 @Override 252 public synchronized int read() throws IOException { 253 if (in == null) { 254 // K0059=Stream is closed 255 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 256 } 257 258 /* Are there buffered bytes available? */ 259 if (pos >= count && fillbuf() == -1) { 260 return -1; /* no, fill buffer */ 261 } 262 263 /* Did filling the buffer fail with -1 (EOF)? */ 264 if (count - pos > 0) { 265 return buf[pos++] & 0xFF; 266 } 267 return -1; 268 } 269 270 /** 271 * Reads at most {@code length} bytes from this stream and stores them in 272 * byte array {@code buffer} starting at offset {@code offset}. Returns the 273 * number of bytes actually read or -1 if no bytes were read and the end of 274 * the stream was encountered. If all the buffered bytes have been used, a 275 * mark has not been set and the requested number of bytes is larger than 276 * the receiver's buffer size, this implementation bypasses the buffer and 277 * simply places the results directly into {@code buffer}. 278 * 279 * @param buffer 280 * the byte array in which to store the bytes read. 281 * @param offset 282 * the initial position in {@code buffer} to store the bytes read 283 * from this stream. 284 * @param length 285 * the maximum number of bytes to store in {@code buffer}. 286 * @return the number of bytes actually read or -1 if end of stream. 287 * @throws IndexOutOfBoundsException 288 * if {@code offset < 0} or {@code length < 0}, or if 289 * {@code offset + length} is greater than the size of 290 * {@code buffer}. 291 * @throws IOException 292 * if the stream is already closed or another IOException 293 * occurs. 294 * @since Android 1.0 295 */ 296 @Override 297 public synchronized int read(byte[] buffer, int offset, int length) 298 throws IOException { 299 if (closed) { 300 // K0059=Stream is closed 301 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 302 } 303 // avoid int overflow 304 // BEGIN android-changed 305 // Exception priorities (in case of multiple errors) differ from 306 // RI, but are spec-compliant. 307 // made implicit null check explicit, used (offset | length) < 0 308 // instead of (offset < 0) || (length < 0) to safe one operation 309 if (buffer == null) { 310 throw new NullPointerException(Msg.getString("K0047")); //$NON-NLS-1$ 311 } 312 if ((offset | length) < 0 || offset > buffer.length - length) { 313 throw new IndexOutOfBoundsException(Msg.getString("K002f")); //$NON-NLS-1$ 314 } 315 // END android-changed 316 if (length == 0) { 317 return 0; 318 } 319 if (null == buf) { 320 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 321 } 322 323 int required; 324 if (pos < count) { 325 /* There are bytes available in the buffer. */ 326 int copylength = count - pos >= length ? length : count - pos; 327 System.arraycopy(buf, pos, buffer, offset, copylength); 328 pos += copylength; 329 if (copylength == length || in.available() == 0) { 330 return copylength; 331 } 332 offset += copylength; 333 required = length - copylength; 334 } else { 335 required = length; 336 } 337 338 while (true) { 339 int read; 340 /* 341 * If we're not marked and the required size is greater than the 342 * buffer, simply read the bytes directly bypassing the buffer. 343 */ 344 if (markpos == -1 && required >= buf.length) { 345 read = in.read(buffer, offset, required); 346 if (read == -1) { 347 return required == length ? -1 : length - required; 348 } 349 } else { 350 if (fillbuf() == -1) { 351 return required == length ? -1 : length - required; 352 } 353 read = count - pos >= required ? required : count - pos; 354 System.arraycopy(buf, pos, buffer, offset, read); 355 pos += read; 356 } 357 required -= read; 358 if (required == 0) { 359 return length; 360 } 361 if (in.available() == 0) { 362 return length - required; 363 } 364 offset += read; 365 } 366 } 367 368 /** 369 * Resets this stream to the last marked location. 370 * 371 * @throws IOException 372 * if this stream is closed, no mark has been set or the mark is 373 * no longer valid because more than {@code readlimit} bytes 374 * have been read since setting the mark. 375 * @see #mark(int) 376 * @since Android 1.0 377 */ 378 @Override 379 public synchronized void reset() throws IOException { 380 // BEGIN android-changed 381 /* 382 * These exceptions get thrown in some "normalish" circumstances, 383 * so it is preferable to avoid loading up the whole big set of 384 * messages just for these cases. 385 */ 386 if (closed) { 387 throw new IOException("Stream is closed"); 388 } 389 if (-1 == markpos) { 390 throw new IOException("Mark has been invalidated."); 391 } 392 // END android-changed 393 pos = markpos; 394 } 395 396 /** 397 * Skips {@code amount} number of bytes in this stream. Subsequent 398 * {@code read()}'s will not return these bytes unless {@code reset()} is 399 * used. 400 * 401 * @param amount 402 * the number of bytes to skip. {@code skip} does nothing and 403 * returns 0 if {@code amount} is less than zero. 404 * @return the number of bytes actually skipped. 405 * @throws IOException 406 * if this stream is closed or another IOException occurs. 407 * @since Android 1.0 408 */ 409 @Override 410 public synchronized long skip(long amount) throws IOException { 411 if (null == in) { 412 // K0059=Stream is closed 413 throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$ 414 } 415 if (amount < 1) { 416 return 0; 417 } 418 419 if (count - pos >= amount) { 420 pos += amount; 421 return amount; 422 } 423 long read = count - pos; 424 pos = count; 425 426 if (markpos != -1) { 427 if (amount <= marklimit) { 428 if (fillbuf() == -1) { 429 return read; 430 } 431 if (count - pos >= amount - read) { 432 pos += amount - read; 433 return amount; 434 } 435 // Couldn't get all the bytes, skip what we read 436 read += (count - pos); 437 pos = count; 438 return read; 439 } 440 markpos = -1; 441 } 442 return read + in.skip(amount - read); 443 } 444} 445 446