comparison src/goodjava/io/BufferedInputStream.java @ 1479:bd13aaeaf6d4

minor
author Franklin Schmidt <fschmidt@gmail.com>
date Fri, 24 Apr 2020 10:52:54 -0600
parents 37e582f2e266
children 1f41e5921090
comparison
equal deleted inserted replaced
1478:37e582f2e266 1479:bd13aaeaf6d4
49 * the contained input stream. 49 * the contained input stream.
50 * 50 *
51 * @author Arthur van Hoff 51 * @author Arthur van Hoff
52 * @since JDK1.0 52 * @since JDK1.0
53 */ 53 */
54 public 54 public class BufferedInputStream extends FilterInputStream {
55 class BufferedInputStream extends FilterInputStream { 55
56 56 private static int DEFAULT_BUFFER_SIZE = 8192;
57 private static int DEFAULT_BUFFER_SIZE = 8192; 57
58 58 /**
59 /** 59 * The maximum size of array to allocate.
60 * The maximum size of array to allocate. 60 * Some VMs reserve some header words in an array.
61 * Some VMs reserve some header words in an array. 61 * Attempts to allocate larger arrays may result in
62 * Attempts to allocate larger arrays may result in 62 * OutOfMemoryError: Requested array size exceeds VM limit
63 * OutOfMemoryError: Requested array size exceeds VM limit 63 */
64 */ 64 private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
65 private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; 65
66 66 /**
67 /** 67 * The internal buffer array where the data is stored. When necessary,
68 * The internal buffer array where the data is stored. When necessary, 68 * it may be replaced by another array of
69 * it may be replaced by another array of 69 * a different size.
70 * a different size. 70 */
71 */ 71 protected volatile byte buf[];
72 protected volatile byte buf[]; 72
73 73 /**
74 /** 74 * Atomic updater to provide compareAndSet for buf. This is
75 * Atomic updater to provide compareAndSet for buf. This is 75 * necessary because closes can be asynchronous. We use nullness
76 * necessary because closes can be asynchronous. We use nullness 76 * of buf[] as primary indicator that this stream is closed. (The
77 * of buf[] as primary indicator that this stream is closed. (The 77 * "in" field is also nulled out on close.)
78 * "in" field is also nulled out on close.) 78 */
79 */ 79 private static final
80 private static final 80 AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
81 AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = 81 AtomicReferenceFieldUpdater.newUpdater
82 AtomicReferenceFieldUpdater.newUpdater 82 (BufferedInputStream.class, byte[].class, "buf");
83 (BufferedInputStream.class, byte[].class, "buf"); 83
84 84 /**
85 /** 85 * The index one greater than the index of the last valid byte in
86 * The index one greater than the index of the last valid byte in 86 * the buffer.
87 * the buffer. 87 * This value is always
88 * This value is always 88 * in the range <code>0</code> through <code>buf.length</code>;
89 * in the range <code>0</code> through <code>buf.length</code>; 89 * elements <code>buf[0]</code> through <code>buf[count-1]
90 * elements <code>buf[0]</code> through <code>buf[count-1] 90 * </code>contain buffered input data obtained
91 * </code>contain buffered input data obtained 91 * from the underlying input stream.
92 * from the underlying input stream. 92 */
93 */ 93 protected int count;
94 protected int count; 94
95 95 /**
96 /** 96 * The current position in the buffer. This is the index of the next
97 * The current position in the buffer. This is the index of the next 97 * character to be read from the <code>buf</code> array.
98 * character to be read from the <code>buf</code> array. 98 * <p>
99 * <p> 99 * This value is always in the range <code>0</code>
100 * This value is always in the range <code>0</code> 100 * through <code>count</code>. If it is less
101 * through <code>count</code>. If it is less 101 * than <code>count</code>, then <code>buf[pos]</code>
102 * than <code>count</code>, then <code>buf[pos]</code> 102 * is the next byte to be supplied as input;
103 * is the next byte to be supplied as input; 103 * if it is equal to <code>count</code>, then
104 * if it is equal to <code>count</code>, then 104 * the next <code>read</code> or <code>skip</code>
105 * the next <code>read</code> or <code>skip</code> 105 * operation will require more bytes to be
106 * operation will require more bytes to be 106 * read from the contained input stream.
107 * read from the contained input stream. 107 *
108 * 108 * @see java.io.BufferedInputStream#buf
109 * @see java.io.BufferedInputStream#buf 109 */
110 */ 110 protected int pos;
111 protected int pos; 111
112 112 /**
113 /** 113 * The value of the <code>pos</code> field at the time the last
114 * The value of the <code>pos</code> field at the time the last 114 * <code>mark</code> method was called.
115 * <code>mark</code> method was called. 115 * <p>
116 * <p> 116 * This value is always
117 * This value is always 117 * in the range <code>-1</code> through <code>pos</code>.
118 * in the range <code>-1</code> through <code>pos</code>. 118 * If there is no marked position in the input
119 * If there is no marked position in the input 119 * stream, this field is <code>-1</code>. If
120 * stream, this field is <code>-1</code>. If 120 * there is a marked position in the input
121 * there is a marked position in the input 121 * stream, then <code>buf[markpos]</code>
122 * stream, then <code>buf[markpos]</code> 122 * is the first byte to be supplied as input
123 * is the first byte to be supplied as input 123 * after a <code>reset</code> operation. If
124 * after a <code>reset</code> operation. If 124 * <code>markpos</code> is not <code>-1</code>,
125 * <code>markpos</code> is not <code>-1</code>, 125 * then all bytes from positions <code>buf[markpos]</code>
126 * then all bytes from positions <code>buf[markpos]</code> 126 * through <code>buf[pos-1]</code> must remain
127 * through <code>buf[pos-1]</code> must remain 127 * in the buffer array (though they may be
128 * in the buffer array (though they may be 128 * moved to another place in the buffer array,
129 * moved to another place in the buffer array, 129 * with suitable adjustments to the values
130 * with suitable adjustments to the values 130 * of <code>count</code>, <code>pos</code>,
131 * of <code>count</code>, <code>pos</code>, 131 * and <code>markpos</code>); they may not
132 * and <code>markpos</code>); they may not 132 * be discarded unless and until the difference
133 * be discarded unless and until the difference 133 * between <code>pos</code> and <code>markpos</code>
134 * between <code>pos</code> and <code>markpos</code> 134 * exceeds <code>marklimit</code>.
135 * exceeds <code>marklimit</code>. 135 *
136 * 136 * @see java.io.BufferedInputStream#mark(int)
137 * @see java.io.BufferedInputStream#mark(int) 137 * @see java.io.BufferedInputStream#pos
138 * @see java.io.BufferedInputStream#pos 138 */
139 */ 139 protected int markpos = -1;
140 protected int markpos = -1; 140
141 141 /**
142 /** 142 * The maximum read ahead allowed after a call to the
143 * The maximum read ahead allowed after a call to the 143 * <code>mark</code> method before subsequent calls to the
144 * <code>mark</code> method before subsequent calls to the 144 * <code>reset</code> method fail.
145 * <code>reset</code> method fail. 145 * Whenever the difference between <code>pos</code>
146 * Whenever the difference between <code>pos</code> 146 * and <code>markpos</code> exceeds <code>marklimit</code>,
147 * and <code>markpos</code> exceeds <code>marklimit</code>, 147 * then the mark may be dropped by setting
148 * then the mark may be dropped by setting 148 * <code>markpos</code> to <code>-1</code>.
149 * <code>markpos</code> to <code>-1</code>. 149 *
150 * 150 * @see java.io.BufferedInputStream#mark(int)
151 * @see java.io.BufferedInputStream#mark(int) 151 * @see java.io.BufferedInputStream#reset()
152 * @see java.io.BufferedInputStream#reset() 152 */
153 */ 153 protected int marklimit;
154 protected int marklimit; 154
155 155 /**
156 /** 156 * Check to make sure that underlying input stream has not been
157 * Check to make sure that underlying input stream has not been 157 * nulled out due to close; if not return it;
158 * nulled out due to close; if not return it; 158 */
159 */ 159 private InputStream getInIfOpen() throws IOException {
160 private InputStream getInIfOpen() throws IOException { 160 InputStream input = in;
161 InputStream input = in; 161 if (input == null)
162 if (input == null) 162 throw new IOException("Stream closed");
163 throw new IOException("Stream closed"); 163 return input;
164 return input; 164 }
165 } 165
166 166 /**
167 /** 167 * Check to make sure that buffer has not been nulled out due to
168 * Check to make sure that buffer has not been nulled out due to 168 * close; if not return it;
169 * close; if not return it; 169 */
170 */ 170 private byte[] getBufIfOpen() throws IOException {
171 private byte[] getBufIfOpen() throws IOException { 171 byte[] buffer = buf;
172 byte[] buffer = buf; 172 if (buffer == null)
173 if (buffer == null) 173 throw new IOException("Stream closed");
174 throw new IOException("Stream closed"); 174 return buffer;
175 return buffer; 175 }
176 } 176
177 177 /**
178 /** 178 * Creates a <code>BufferedInputStream</code>
179 * Creates a <code>BufferedInputStream</code> 179 * and saves its argument, the input stream
180 * and saves its argument, the input stream 180 * <code>in</code>, for later use. An internal
181 * <code>in</code>, for later use. An internal 181 * buffer array is created and stored in <code>buf</code>.
182 * buffer array is created and stored in <code>buf</code>. 182 *
183 * 183 * @param in the underlying input stream.
184 * @param in the underlying input stream. 184 */
185 */ 185 public BufferedInputStream(InputStream in) {
186 public BufferedInputStream(InputStream in) { 186 this(in, DEFAULT_BUFFER_SIZE);
187 this(in, DEFAULT_BUFFER_SIZE); 187 }
188 } 188
189 189 /**
190 /** 190 * Creates a <code>BufferedInputStream</code>
191 * Creates a <code>BufferedInputStream</code> 191 * with the specified buffer size,
192 * with the specified buffer size, 192 * and saves its argument, the input stream
193 * and saves its argument, the input stream 193 * <code>in</code>, for later use. An internal
194 * <code>in</code>, for later use. An internal 194 * buffer array of length <code>size</code>
195 * buffer array of length <code>size</code> 195 * is created and stored in <code>buf</code>.
196 * is created and stored in <code>buf</code>. 196 *
197 * 197 * @param in the underlying input stream.
198 * @param in the underlying input stream. 198 * @param size the buffer size.
199 * @param size the buffer size. 199 * @exception IllegalArgumentException if {@code size <= 0}.
200 * @exception IllegalArgumentException if {@code size <= 0}. 200 */
201 */ 201 public BufferedInputStream(InputStream in, int size) {
202 public BufferedInputStream(InputStream in, int size) { 202 super(in);
203 super(in); 203 if (size <= 0) {
204 if (size <= 0) { 204 throw new IllegalArgumentException("Buffer size <= 0");
205 throw new IllegalArgumentException("Buffer size <= 0"); 205 }
206 } 206 buf = new byte[size];
207 buf = new byte[size]; 207 }
208 } 208
209 209 /**
210 /** 210 * Fills the buffer with more data, taking into account
211 * Fills the buffer with more data, taking into account 211 * shuffling and other tricks for dealing with marks.
212 * shuffling and other tricks for dealing with marks. 212 * Assumes that it is being called by a synchronized method.
213 * Assumes that it is being called by a synchronized method. 213 * This method also assumes that all data has already been read in,
214 * This method also assumes that all data has already been read in, 214 * hence pos > count.
215 * hence pos > count. 215 */
216 */ 216 private void fill() throws IOException {
217 private void fill() throws IOException { 217 byte[] buffer = getBufIfOpen();
218 byte[] buffer = getBufIfOpen(); 218 if (markpos < 0)
219 if (markpos < 0) 219 pos = 0; /* no mark: throw away the buffer */
220 pos = 0; /* no mark: throw away the buffer */ 220 else if (pos >= buffer.length) /* no room left in buffer */
221 else if (pos >= buffer.length) /* no room left in buffer */ 221 if (markpos > 0) { /* can throw away early part of the buffer */
222 if (markpos > 0) { /* can throw away early part of the buffer */ 222 int sz = pos - markpos;
223 int sz = pos - markpos; 223 System.arraycopy(buffer, markpos, buffer, 0, sz);
224 System.arraycopy(buffer, markpos, buffer, 0, sz); 224 pos = sz;
225 pos = sz; 225 markpos = 0;
226 markpos = 0; 226 } else if (buffer.length >= marklimit) {
227 } else if (buffer.length >= marklimit) { 227 markpos = -1; /* buffer got too big, invalidate mark */
228 markpos = -1; /* buffer got too big, invalidate mark */ 228 pos = 0; /* drop buffer contents */
229 pos = 0; /* drop buffer contents */ 229 } else if (buffer.length >= MAX_BUFFER_SIZE) {
230 } else if (buffer.length >= MAX_BUFFER_SIZE) { 230 throw new OutOfMemoryError("Required array size too large");
231 throw new OutOfMemoryError("Required array size too large"); 231 } else { /* grow buffer */
232 } else { /* grow buffer */ 232 int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
233 int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? 233 pos * 2 : MAX_BUFFER_SIZE;
234 pos * 2 : MAX_BUFFER_SIZE; 234 if (nsz > marklimit)
235 if (nsz > marklimit) 235 nsz = marklimit;
236 nsz = marklimit; 236 byte nbuf[] = new byte[nsz];
237 byte nbuf[] = new byte[nsz]; 237 System.arraycopy(buffer, 0, nbuf, 0, pos);
238 System.arraycopy(buffer, 0, nbuf, 0, pos); 238 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
239 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 239 // Can't replace buf if there was an async close.
240 // Can't replace buf if there was an async close. 240 // Note: This would need to be changed if fill()
241 // Note: This would need to be changed if fill() 241 // is ever made accessible to multiple threads.
242 // is ever made accessible to multiple threads. 242 // But for now, the only way CAS can fail is via close.
243 // But for now, the only way CAS can fail is via close. 243 // assert buf == null;
244 // assert buf == null; 244 throw new IOException("Stream closed");
245 throw new IOException("Stream closed"); 245 }
246 } 246 buffer = nbuf;
247 buffer = nbuf; 247 }
248 } 248 count = pos;
249 count = pos; 249 int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
250 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 250 if (n > 0)
251 if (n > 0) 251 count = n + pos;
252 count = n + pos; 252 }
253 } 253
254 254 /**
255 /** 255 * See
256 * See 256 * the general contract of the <code>read</code>
257 * the general contract of the <code>read</code> 257 * method of <code>InputStream</code>.
258 * method of <code>InputStream</code>. 258 *
259 * 259 * @return the next byte of data, or <code>-1</code> if the end of the
260 * @return the next byte of data, or <code>-1</code> if the end of the 260 * stream is reached.
261 * stream is reached. 261 * @exception IOException if this input stream has been closed by
262 * @exception IOException if this input stream has been closed by 262 * invoking its {@link #close()} method,
263 * invoking its {@link #close()} method, 263 * or an I/O error occurs.
264 * or an I/O error occurs. 264 * @see java.io.FilterInputStream#in
265 * @see java.io.FilterInputStream#in 265 */
266 */ 266 public synchronized int read() throws IOException {
267 public synchronized int read() throws IOException { 267 if (pos >= count) {
268 if (pos >= count) { 268 fill();
269 fill(); 269 if (pos >= count)
270 if (pos >= count) 270 return -1;
271 return -1; 271 }
272 } 272 return getBufIfOpen()[pos++] & 0xff;
273 return getBufIfOpen()[pos++] & 0xff; 273 }
274 } 274
275 275 /**
276 /** 276 * Read characters into a portion of an array, reading from the underlying
277 * Read characters into a portion of an array, reading from the underlying 277 * stream at most once if necessary.
278 * stream at most once if necessary. 278 */
279 */ 279 private int read1(byte[] b, int off, int len) throws IOException {
280 private int read1(byte[] b, int off, int len) throws IOException { 280 int avail = count - pos;
281 int avail = count - pos; 281 if (avail <= 0) {
282 if (avail <= 0) { 282 /* If the requested length is at least as large as the buffer, and
283 /* If the requested length is at least as large as the buffer, and 283 if there is no mark/reset activity, do not bother to copy the
284 if there is no mark/reset activity, do not bother to copy the 284 bytes into the local buffer. In this way buffered streams will
285 bytes into the local buffer. In this way buffered streams will 285 cascade harmlessly. */
286 cascade harmlessly. */ 286 if (len >= getBufIfOpen().length && markpos < 0) {
287 if (len >= getBufIfOpen().length && markpos < 0) { 287 return getInIfOpen().read(b, off, len);
288 return getInIfOpen().read(b, off, len); 288 }
289 } 289 fill();
290 fill(); 290 avail = count - pos;
291 avail = count - pos; 291 if (avail <= 0) return -1;
292 if (avail <= 0) return -1; 292 }
293 } 293 int cnt = (avail < len) ? avail : len;
294 int cnt = (avail < len) ? avail : len; 294 System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
295 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); 295 pos += cnt;
296 pos += cnt; 296 return cnt;
297 return cnt; 297 }
298 } 298
299 299 /**
300 /** 300 * Reads bytes from this byte-input stream into the specified byte array,
301 * Reads bytes from this byte-input stream into the specified byte array, 301 * starting at the given offset.
302 * starting at the given offset. 302 *
303 * 303 * <p> This method implements the general contract of the corresponding
304 * <p> This method implements the general contract of the corresponding 304 * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
305 * <code>{@link InputStream#read(byte[], int, int) read}</code> method of 305 * the <code>{@link InputStream}</code> class. As an additional
306 * the <code>{@link InputStream}</code> class. As an additional 306 * convenience, it attempts to read as many bytes as possible by repeatedly
307 * convenience, it attempts to read as many bytes as possible by repeatedly 307 * invoking the <code>read</code> method of the underlying stream. This
308 * invoking the <code>read</code> method of the underlying stream. This 308 * iterated <code>read</code> continues until one of the following
309 * iterated <code>read</code> continues until one of the following 309 * conditions becomes true: <ul>
310 * conditions becomes true: <ul> 310 *
311 * 311 * <li> The specified number of bytes have been read,
312 * <li> The specified number of bytes have been read, 312 *
313 * 313 * <li> The <code>read</code> method of the underlying stream returns
314 * <li> The <code>read</code> method of the underlying stream returns 314 * <code>-1</code>, indicating end-of-file, or
315 * <code>-1</code>, indicating end-of-file, or 315 *
316 * 316 * <li> The <code>available</code> method of the underlying stream
317 * <li> The <code>available</code> method of the underlying stream 317 * returns zero, indicating that further input requests would block.
318 * returns zero, indicating that further input requests would block. 318 *
319 * 319 * </ul> If the first <code>read</code> on the underlying stream returns
320 * </ul> If the first <code>read</code> on the underlying stream returns 320 * <code>-1</code> to indicate end-of-file then this method returns
321 * <code>-1</code> to indicate end-of-file then this method returns 321 * <code>-1</code>. Otherwise this method returns the number of bytes
322 * <code>-1</code>. Otherwise this method returns the number of bytes 322 * actually read.
323 * actually read. 323 *
324 * 324 * <p> Subclasses of this class are encouraged, but not required, to
325 * <p> Subclasses of this class are encouraged, but not required, to 325 * attempt to read as many bytes as possible in the same fashion.
326 * attempt to read as many bytes as possible in the same fashion. 326 *
327 * 327 * @param b destination buffer.
328 * @param b destination buffer. 328 * @param off offset at which to start storing bytes.
329 * @param off offset at which to start storing bytes. 329 * @param len maximum number of bytes to read.
330 * @param len maximum number of bytes to read. 330 * @return the number of bytes read, or <code>-1</code> if the end of
331 * @return the number of bytes read, or <code>-1</code> if the end of 331 * the stream has been reached.
332 * the stream has been reached. 332 * @exception IOException if this input stream has been closed by
333 * @exception IOException if this input stream has been closed by 333 * invoking its {@link #close()} method,
334 * invoking its {@link #close()} method, 334 * or an I/O error occurs.
335 * or an I/O error occurs. 335 */
336 */ 336 public synchronized int read(byte b[], int off, int len)
337 public synchronized int read(byte b[], int off, int len) 337 throws IOException
338 throws IOException 338 {
339 { 339 getBufIfOpen(); // Check for closed stream
340 getBufIfOpen(); // Check for closed stream 340 if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
341 if ((off | len | (off + len) | (b.length - (off + len))) < 0) { 341 throw new IndexOutOfBoundsException();
342 throw new IndexOutOfBoundsException(); 342 } else if (len == 0) {
343 } else if (len == 0) { 343 return 0;
344 return 0; 344 }
345 } 345
346 346 int n = 0;
347 int n = 0; 347 for (;;) {
348 for (;;) { 348 int nread = read1(b, off + n, len - n);
349 int nread = read1(b, off + n, len - n); 349 if (nread <= 0)
350 if (nread <= 0) 350 return (n == 0) ? nread : n;
351 return (n == 0) ? nread : n; 351 n += nread;
352 n += nread; 352 if (n >= len)
353 if (n >= len) 353 return n;
354 return n; 354 // if not closed but no bytes available, return
355 // if not closed but no bytes available, return 355 InputStream input = in;
356 InputStream input = in; 356 if (input != null && input.available() <= 0)
357 if (input != null && input.available() <= 0) 357 return n;
358 return n; 358 }
359 } 359 }
360 } 360
361 361 /**
362 /** 362 * See the general contract of the <code>skip</code>
363 * See the general contract of the <code>skip</code> 363 * method of <code>InputStream</code>.
364 * method of <code>InputStream</code>. 364 *
365 * 365 * @exception IOException if the stream does not support seek,
366 * @exception IOException if the stream does not support seek, 366 * or if this input stream has been closed by
367 * or if this input stream has been closed by 367 * invoking its {@link #close()} method, or an
368 * invoking its {@link #close()} method, or an 368 * I/O error occurs.
369 * I/O error occurs. 369 */
370 */ 370 public synchronized long skip(long n) throws IOException {
371 public synchronized long skip(long n) throws IOException { 371 getBufIfOpen(); // Check for closed stream
372 getBufIfOpen(); // Check for closed stream 372 if (n <= 0) {
373 if (n <= 0) { 373 return 0;
374 return 0; 374 }
375 } 375 long avail = count - pos;
376 long avail = count - pos; 376
377 377 if (avail <= 0) {
378 if (avail <= 0) { 378 // If no mark position set then don't keep in buffer
379 // If no mark position set then don't keep in buffer 379 if (markpos <0)
380 if (markpos <0) 380 return getInIfOpen().skip(n);
381 return getInIfOpen().skip(n); 381
382 382 // Fill in buffer to save bytes for reset
383 // Fill in buffer to save bytes for reset 383 fill();
384 fill(); 384 avail = count - pos;
385 avail = count - pos; 385 if (avail <= 0)
386 if (avail <= 0) 386 return 0;
387 return 0; 387 }
388 } 388
389 389 long skipped = (avail < n) ? avail : n;
390 long skipped = (avail < n) ? avail : n; 390 pos += skipped;
391 pos += skipped; 391 return skipped;
392 return skipped; 392 }
393 } 393
394 394 /**
395 /** 395 * Returns an estimate of the number of bytes that can be read (or
396 * Returns an estimate of the number of bytes that can be read (or 396 * skipped over) from this input stream without blocking by the next
397 * skipped over) from this input stream without blocking by the next 397 * invocation of a method for this input stream. The next invocation might be
398 * invocation of a method for this input stream. The next invocation might be 398 * the same thread or another thread. A single read or skip of this
399 * the same thread or another thread. A single read or skip of this 399 * many bytes will not block, but may read or skip fewer bytes.
400 * many bytes will not block, but may read or skip fewer bytes. 400 * <p>
401 * <p> 401 * This method returns the sum of the number of bytes remaining to be read in
402 * This method returns the sum of the number of bytes remaining to be read in 402 * the buffer (<code>count&nbsp;- pos</code>) and the result of calling the
403 * the buffer (<code>count&nbsp;- pos</code>) and the result of calling the 403 * {@link java.io.FilterInputStream#in in}.available().
404 * {@link java.io.FilterInputStream#in in}.available(). 404 *
405 * 405 * @return an estimate of the number of bytes that can be read (or skipped
406 * @return an estimate of the number of bytes that can be read (or skipped 406 * over) from this input stream without blocking.
407 * over) from this input stream without blocking. 407 * @exception IOException if this input stream has been closed by
408 * @exception IOException if this input stream has been closed by 408 * invoking its {@link #close()} method,
409 * invoking its {@link #close()} method, 409 * or an I/O error occurs.
410 * or an I/O error occurs. 410 */
411 */ 411 public synchronized int available() throws IOException {
412 public synchronized int available() throws IOException { 412 int n = count - pos;
413 int n = count - pos; 413 int avail = getInIfOpen().available();
414 int avail = getInIfOpen().available(); 414 return n > (Integer.MAX_VALUE - avail)
415 return n > (Integer.MAX_VALUE - avail) 415 ? Integer.MAX_VALUE
416 ? Integer.MAX_VALUE 416 : n + avail;
417 : n + avail; 417 }
418 } 418
419 419 /**
420 /** 420 * See the general contract of the <code>mark</code>
421 * See the general contract of the <code>mark</code> 421 * method of <code>InputStream</code>.
422 * method of <code>InputStream</code>. 422 *
423 * 423 * @param readlimit the maximum limit of bytes that can be read before
424 * @param readlimit the maximum limit of bytes that can be read before 424 * the mark position becomes invalid.
425 * the mark position becomes invalid. 425 * @see java.io.BufferedInputStream#reset()
426 * @see java.io.BufferedInputStream#reset() 426 */
427 */ 427 public synchronized void mark(int readlimit) {
428 public synchronized void mark(int readlimit) { 428 marklimit = readlimit;
429 marklimit = readlimit; 429 markpos = pos;
430 markpos = pos; 430 }
431 } 431
432 432 /**
433 /** 433 * See the general contract of the <code>reset</code>
434 * See the general contract of the <code>reset</code> 434 * method of <code>InputStream</code>.
435 * method of <code>InputStream</code>. 435 * <p>
436 * <p> 436 * If <code>markpos</code> is <code>-1</code>
437 * If <code>markpos</code> is <code>-1</code> 437 * (no mark has been set or the mark has been
438 * (no mark has been set or the mark has been 438 * invalidated), an <code>IOException</code>
439 * invalidated), an <code>IOException</code> 439 * is thrown. Otherwise, <code>pos</code> is
440 * is thrown. Otherwise, <code>pos</code> is 440 * set equal to <code>markpos</code>.
441 * set equal to <code>markpos</code>. 441 *
442 * 442 * @exception IOException if this stream has not been marked or,
443 * @exception IOException if this stream has not been marked or, 443 * if the mark has been invalidated, or the stream
444 * if the mark has been invalidated, or the stream 444 * has been closed by invoking its {@link #close()}
445 * has been closed by invoking its {@link #close()} 445 * method, or an I/O error occurs.
446 * method, or an I/O error occurs. 446 * @see java.io.BufferedInputStream#mark(int)
447 * @see java.io.BufferedInputStream#mark(int) 447 */
448 */ 448 public synchronized void reset() throws IOException {
449 public synchronized void reset() throws IOException { 449 getBufIfOpen(); // Cause exception if closed
450 getBufIfOpen(); // Cause exception if closed 450 if (markpos < 0)
451 if (markpos < 0) 451 throw new IOException("Resetting to invalid mark");
452 throw new IOException("Resetting to invalid mark"); 452 pos = markpos;
453 pos = markpos; 453 }
454 } 454
455 455 /**
456 /** 456 * Tests if this input stream supports the <code>mark</code>
457 * Tests if this input stream supports the <code>mark</code> 457 * and <code>reset</code> methods. The <code>markSupported</code>
458 * and <code>reset</code> methods. The <code>markSupported</code> 458 * method of <code>BufferedInputStream</code> returns
459 * method of <code>BufferedInputStream</code> returns 459 * <code>true</code>.
460 * <code>true</code>. 460 *
461 * 461 * @return a <code>boolean</code> indicating if this stream type supports
462 * @return a <code>boolean</code> indicating if this stream type supports 462 * the <code>mark</code> and <code>reset</code> methods.
463 * the <code>mark</code> and <code>reset</code> methods. 463 * @see java.io.InputStream#mark(int)
464 * @see java.io.InputStream#mark(int) 464 * @see java.io.InputStream#reset()
465 * @see java.io.InputStream#reset() 465 */
466 */ 466 public boolean markSupported() {
467 public boolean markSupported() { 467 return true;
468 return true; 468 }
469 } 469
470 470 /**
471 /** 471 * Closes this input stream and releases any system resources
472 * Closes this input stream and releases any system resources 472 * associated with the stream.
473 * associated with the stream. 473 * Once the stream has been closed, further read(), available(), reset(),
474 * Once the stream has been closed, further read(), available(), reset(), 474 * or skip() invocations will throw an IOException.
475 * or skip() invocations will throw an IOException. 475 * Closing a previously closed stream has no effect.
476 * Closing a previously closed stream has no effect. 476 *
477 * 477 * @exception IOException if an I/O error occurs.
478 * @exception IOException if an I/O error occurs. 478 */
479 */ 479 public void close() throws IOException {
480 public void close() throws IOException { 480 byte[] buffer;
481 byte[] buffer; 481 while ( (buffer = buf) != null) {
482 while ( (buffer = buf) != null) { 482 if (bufUpdater.compareAndSet(this, buffer, null)) {
483 if (bufUpdater.compareAndSet(this, buffer, null)) { 483 InputStream input = in;
484 InputStream input = in; 484 in = null;
485 in = null; 485 if (input != null)
486 if (input != null) 486 input.close();
487 input.close(); 487 return;
488 return; 488 }
489 } 489 // Else retry in case a new buf was CASed in fill()
490 // Else retry in case a new buf was CASed in fill() 490 }
491 } 491 }
492 }
493 } 492 }