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
18 package java.io;
19
20 import org.apache.harmony.luni.internal.nls.Messages;
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 */
38 public class BufferedInputStream extends FilterInputStream {
39 /**
40 * The buffer containing the current bytes read from the target InputStream.
41 */
42 protected volatile byte[] buf;
43
44 /**
45 * The total number of bytes inside the byte array {@code buf}.
46 */
47 protected int count;
48
49 /**
50 * The current limit, which when passed, invalidates the current mark.
51 */
52 protected int marklimit;
53
54 /**
55 * The currently marked position. -1 indicates no mark has been set or the
56 * mark has been invalidated.
57 */
58 protected int markpos = -1;
59
60 /**
61 * The current position within the byte array {@code buf}.
62 */
63 protected int pos;
64
65 /**
66 * Constructs a new {@code BufferedInputStream} on the {@link InputStream}
67 * {@code in}. The default buffer size (8 KB) is allocated and all reads
68 * can now be filtered through this stream.
69 *
70 * @param in
71 * the InputStream the buffer reads from.
72 */
73 public BufferedInputStream(InputStream in) {
74 super(in);
75 buf = new byte[8192];
76 }
77
78 /**
79 * Constructs a new {@code BufferedInputStream} on the {@link InputStream}
80 * {@code in}. The buffer size is specified by the parameter {@code size}
81 * and all reads are now filtered through this stream.
82 *
83 * @param in
84 * the input stream the buffer reads from.
85 * @param size
86 * the size of buffer to allocate.
87 * @throws IllegalArgumentException
88 * if {@code size < 0}.
89 */
90 public BufferedInputStream(InputStream in, int size) {
91 super(in);
92 if (size <= 0) {
93 // luni.A3=size must be > 0
94 throw new IllegalArgumentException(Messages.getString("luni.A3")); //$NON-NLS-1$
95 }
96 buf = new byte[size];
97 }
98
99 /**
100 * Returns the number of bytes that are available before this stream will
101 * block. This method returns the number of bytes available in the buffer
102 * plus those available in the source stream.
103 *
104 * @return the number of bytes available before blocking.
105 * @throws IOException
106 * if this stream is closed.
107 */
108 @Override
109 public synchronized int available() throws IOException {
110 InputStream localIn = in; // 'in' could be invalidated by close()
111 if (buf == null || localIn == null) {
112 // luni.24=Stream is closed
113 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
114 }
115 return count - pos + localIn.available();
116 }
117
118 /**
119 * Closes this stream. The source stream is closed and any resources
120 * associated with it are released.
121 *
122 * @throws IOException
123 * if an error occurs while closing this stream.
124 */
125 @Override
126 public void close() throws IOException {
127 buf = null;
128 InputStream localIn = in;
129 in = null;
130 if (localIn != null) {
131 localIn.close();
132 }
133 }
134
135 private int fillbuf(InputStream localIn, byte[] localBuf)
136 throws IOException {
137 if (markpos == -1 || (pos - markpos >= marklimit)) {
138 /* Mark position not set or exceeded readlimit */
139 int result = localIn.read(localBuf);
140 if (result > 0) {
141 markpos = -1;
142 pos = 0;
143 count = result == -1 ? 0 : result;
144 }
145 return result;
146 }
147 if (markpos == 0 && marklimit > localBuf.length) {
148 /* Increase buffer size to accommodate the readlimit */
149 int newLength = localBuf.length * 2;
150 if (newLength > marklimit) {
151 newLength = marklimit;
152 }
153 byte[] newbuf = new byte[newLength];
154 System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length);
155 // Reassign buf, which will invalidate any local references
156 // FIXME: what if buf was null?
157 localBuf = buf = newbuf;
158 } else if (markpos > 0) {
159 System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length
160 - markpos);
161 }
162 /* Set the new position and mark position */
163 pos -= markpos;
164 count = markpos = 0;
165 int bytesread = localIn.read(localBuf, pos, localBuf.length - pos);
166 count = bytesread <= 0 ? pos : pos + bytesread;
167 return bytesread;
168 }
169
170 /**
171 * Sets a mark position in this stream. The parameter {@code readlimit}
172 * indicates how many bytes can be read before a mark is invalidated.
173 * Calling {@code reset()} will reposition the stream back to the marked
174 * position if {@code readlimit} has not been surpassed. The underlying
175 * buffer may be increased in size to allow {@code readlimit} number of
176 * bytes to be supported.
177 *
178 * @param readlimit
179 * the number of bytes that can be read before the mark is
180 * invalidated.
181 * @see #reset()
182 */
183 @Override
184 public synchronized void mark(int readlimit) {
185 marklimit = readlimit;
186 markpos = pos;
187 }
188
189 /**
190 * Indicates whether {@code BufferedInputStream} supports the {@code mark()}
191 * and {@code reset()} methods.
192 *
193 * @return {@code true} for BufferedInputStreams.
194 * @see #mark(int)
195 * @see #reset()
196 */
197 @Override
198 public boolean markSupported() {
199 return true;
200 }
201
202 /**
203 * Reads a single byte from this stream and returns it as an integer in the
204 * range from 0 to 255. Returns -1 if the end of the source string has been
205 * reached. If the internal buffer does not contain any available bytes then
206 * it is filled from the source stream and the first byte is returned.
207 *
208 * @return the byte read or -1 if the end of the source stream has been
209 * reached.
210 * @throws IOException
211 * if this stream is closed or another IOException occurs.
212 */
213 @Override
214 public synchronized int read() throws IOException {
215 // Use local refs since buf and in may be invalidated by an
216 // unsynchronized close()
217 byte[] localBuf = buf;
218 InputStream localIn = in;
219 if (localBuf == null || localIn == null) {
220 // luni.24=Stream is closed
221 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
222 }
223
224 /* Are there buffered bytes available? */
225 if (pos >= count && fillbuf(localIn, localBuf) == -1) {
226 return -1; /* no, fill buffer */
227 }
228 // localBuf may have been invalidated by fillbuf
229 if (localBuf != buf) {
230 localBuf = buf;
231 if (localBuf == null) {
232 // luni.24=Stream is closed
233 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
234 }
235 }
236
237 /* Did filling the buffer fail with -1 (EOF)? */
238 if (count - pos > 0) {
239 return localBuf[pos++] & 0xFF;
240 }
241 return -1;
242 }
243
244 /**
245 * Reads at most {@code length} bytes from this stream and stores them in
246 * byte array {@code buffer} starting at offset {@code offset}. Returns the
247 * number of bytes actually read or -1 if no bytes were read and the end of
248 * the stream was encountered. If all the buffered bytes have been used, a
249 * mark has not been set and the requested number of bytes is larger than
250 * the receiver's buffer size, this implementation bypasses the buffer and
251 * simply places the results directly into {@code buffer}.
252 *
253 * @param buffer
254 * the byte array in which to store the bytes read.
255 * @param offset
256 * the initial position in {@code buffer} to store the bytes read
257 * from this stream.
258 * @param length
259 * the maximum number of bytes to store in {@code buffer}.
260 * @return the number of bytes actually read or -1 if end of stream.
261 * @throws IndexOutOfBoundsException
262 * if {@code offset < 0} or {@code length < 0}, or if
263 * {@code offset + length} is greater than the size of
264 * {@code buffer}.
265 * @throws IOException
266 * if the stream is already closed or another IOException
267 * occurs.
268 */
269 @Override
270 public synchronized int read(byte[] buffer, int offset, int length)
271 throws IOException {
272 // Use local ref since buf may be invalidated by an unsynchronized
273 // close()
274 byte[] localBuf = buf;
275 if (localBuf == null) {
276 // luni.24=Stream is closed
277 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
278 }
279 // avoid int overflow
280 if (offset > buffer.length - length || offset < 0 || length < 0) {
281 throw new IndexOutOfBoundsException();
282 }
283 if (length == 0) {
284 return 0;
285 }
286 InputStream localIn = in;
287 if (localIn == null) {
288 // luni.24=Stream is closed
289 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
290 }
291
292 int required;
293 if (pos < count) {
294 /* There are bytes available in the buffer. */
295 int copylength = count - pos >= length ? length : count - pos;
296 System.arraycopy(localBuf, pos, buffer, offset, copylength);
297 pos += copylength;
298 if (copylength == length || localIn.available() == 0) {
299 return copylength;
300 }
301 offset += copylength;
302 required = length - copylength;
303 } else {
304 required = length;
305 }
306
307 while (true) {
308 int read;
309 /*
310 * If we're not marked and the required size is greater than the
311 * buffer, simply read the bytes directly bypassing the buffer.
312 */
313 if (markpos == -1 && required >= localBuf.length) {
314 read = localIn.read(buffer, offset, required);
315 if (read == -1) {
316 return required == length ? -1 : length - required;
317 }
318 } else {
319 if (fillbuf(localIn, localBuf) == -1) {
320 return required == length ? -1 : length - required;
321 }
322 // localBuf may have been invalidated by fillbuf
323 if (localBuf != buf) {
324 localBuf = buf;
325 if (localBuf == null) {
326 // luni.24=Stream is closed
327 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
328 }
329 }
330
331 read = count - pos >= required ? required : count - pos;
332 System.arraycopy(localBuf, pos, buffer, offset, read);
333 pos += read;
334 }
335 required -= read;
336 if (required == 0) {
337 return length;
338 }
339 if (localIn.available() == 0) {
340 return length - required;
341 }
342 offset += read;
343 }
344 }
345
346 /**
347 * Resets this stream to the last marked location.
348 *
349 * @throws IOException
350 * if this stream is closed, no mark has been set or the mark is
351 * no longer valid because more than {@code readlimit} bytes
352 * have been read since setting the mark.
353 * @see #mark(int)
354 */
355 @Override
356 public synchronized void reset() throws IOException {
357 if (buf == null) {
358 // luni.24=Stream is closed
359 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
360 }
361 if (-1 == markpos) {
362 // luni.A4=Mark has been invalidated.
363 throw new IOException(Messages.getString("luni.A4")); //$NON-NLS-1$
364 }
365 pos = markpos;
366 }
367
368 /**
369 * Skips {@code amount} number of bytes in this stream. Subsequent
370 * {@code read()}'s will not return these bytes unless {@code reset()} is
371 * used.
372 *
373 * @param amount
374 * the number of bytes to skip. {@code skip} does nothing and
375 * returns 0 if {@code amount} is less than zero.
376 * @return the number of bytes actually skipped.
377 * @throws IOException
378 * if this stream is closed or another IOException occurs.
379 */
380 @Override
381 public synchronized long skip(long amount) throws IOException {
382 // Use local refs since buf and in may be invalidated by an
383 // unsynchronized close()
384 byte[] localBuf = buf;
385 InputStream localIn = in;
386 if (localBuf == null) {
387 // luni.24=Stream is closed
388 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
389 }
390 if (amount < 1) {
391 return 0;
392 }
393 if (localIn == null) {
394 // luni.24=Stream is closed
395 throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
396 }
397
398 if (count - pos >= amount) {
399 pos += amount;
400 return amount;
401 }
402 long read = count - pos;
403 pos = count;
404
405 if (markpos != -1) {
406 if (amount <= marklimit) {
407 if (fillbuf(localIn, localBuf) == -1) {
408 return read;
409 }
410 if (count - pos >= amount - read) {
411 pos += amount - read;
412 return amount;
413 }
414 // Couldn't get all the bytes, skip what we read
415 read += (count - pos);
416 pos = count;
417 return read;
418 }
419 }
420 return read + localIn.skip(amount - read);
421 }
422 }