001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.io;
019:
020: import org.apache.harmony.luni.util.Msg;
021:
022: /**
023: * <code>BufferedInputStream</code> is a class which takes an input stream and
024: * <em>buffers</em> the input. In this way, costly interaction with the
025: * original input stream can be minimized by reading buffered amounts of data
026: * infrequently. The drawback is that extra space is required to hold the buffer
027: * and that copying takes place when reading that buffer.
028: *
029: * @see BufferedOutputStream
030: */
031: public class BufferedInputStream extends FilterInputStream {
032: /**
033: * The buffer containing the current bytes read from the target InputStream.
034: */
035: protected byte[] buf;
036:
037: /**
038: * The total number of bytes inside the byte array <code>buf</code>.
039: */
040: protected int count;
041:
042: /**
043: * The current limit, which when passed, invalidates the current mark.
044: */
045: protected int marklimit;
046:
047: /**
048: * The currently marked position. -1 indicates no mark has been set or the
049: * mark has been invalidated.
050: */
051: protected int markpos = -1;
052:
053: /**
054: * The current position within the byte array <code>buf</code>.
055: */
056: protected int pos;
057:
058: private boolean closed = false;
059:
060: /**
061: * Constructs a new <code>BufferedInputStream</code> on the InputStream
062: * <code>in</code>. The default buffer size (8Kb) is allocated and all
063: * reads can now be filtered through this stream.
064: *
065: * @param in
066: * the InputStream to buffer reads on.
067: */
068: public BufferedInputStream(InputStream in) {
069: super (in);
070: buf = new byte[8192];
071: }
072:
073: /**
074: * Constructs a new BufferedInputStream on the InputStream <code>in</code>.
075: * The buffer size is specified by the parameter <code>size</code> and all
076: * reads can now be filtered through this BufferedInputStream.
077: *
078: * @param in
079: * the InputStream to buffer reads on.
080: * @param size
081: * the size of buffer to allocate.
082: */
083: public BufferedInputStream(InputStream in, int size) {
084: super (in);
085: if (size <= 0) {
086: // K0058=size must be > 0
087: throw new IllegalArgumentException(Msg.getString("K0058")); //$NON-NLS-1$
088: }
089: buf = new byte[size];
090: }
091:
092: /**
093: * Answers an int representing the number of bytes that are available before
094: * this BufferedInputStream will block. This method returns the number of
095: * bytes available in the buffer plus those available in the target stream.
096: *
097: * @return the number of bytes available before blocking.
098: *
099: * @throws IOException
100: * If an error occurs in this stream.
101: */
102: @Override
103: public synchronized int available() throws IOException {
104: if (buf == null) {
105: // K0059=Stream is closed
106: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
107: }
108: return count - pos + in.available();
109: }
110:
111: /**
112: * Close this BufferedInputStream. This implementation closes the target
113: * stream and releases any resources associated with it.
114: *
115: * @throws IOException
116: * If an error occurs attempting to close this stream.
117: */
118: @Override
119: public synchronized void close() throws IOException {
120: if (null != in) {
121: super .close();
122: in = null;
123: }
124: buf = null;
125: closed = true;
126: }
127:
128: private int fillbuf() throws IOException {
129: if (markpos == -1 || (pos - markpos >= marklimit)) {
130: /* Mark position not set or exceeded readlimit */
131: int result = in.read(buf);
132: if (result > 0) {
133: markpos = -1;
134: pos = 0;
135: count = result == -1 ? 0 : result;
136: }
137: return result;
138: }
139: if (markpos == 0 && marklimit > buf.length) {
140: /* Increase buffer size to accomodate the readlimit */
141: int newLength = buf.length * 2;
142: if (newLength > marklimit) {
143: newLength = marklimit;
144: }
145: byte[] newbuf = new byte[newLength];
146: System.arraycopy(buf, 0, newbuf, 0, buf.length);
147: buf = newbuf;
148: } else if (markpos > 0) {
149: System
150: .arraycopy(buf, markpos, buf, 0, buf.length
151: - markpos);
152: }
153: /* Set the new position and mark position */
154: pos -= markpos;
155: count = markpos = 0;
156: int bytesread = in.read(buf, pos, buf.length - pos);
157: count = bytesread <= 0 ? pos : pos + bytesread;
158: return bytesread;
159: }
160:
161: /**
162: * Set a Mark position in this BufferedInputStream. The parameter
163: * <code>readLimit</code> indicates how many bytes can be read before a
164: * mark is invalidated. Sending reset() will reposition the Stream back to
165: * the marked position provided <code>readLimit</code> has not been
166: * surpassed. The underlying buffer may be increased in size to allow
167: * <code>readlimit</code> number of bytes to be supported.
168: *
169: * @param readlimit
170: * the number of bytes to be able to read before invalidating the
171: * mark.
172: */
173: @Override
174: public synchronized void mark(int readlimit) {
175: marklimit = readlimit;
176: markpos = pos;
177: }
178:
179: /**
180: * Answers a boolean indicating whether or not this BufferedInputStream
181: * supports mark() and reset(). This implementation answers
182: * <code>true</code>.
183: *
184: * @return <code>true</code> for BufferedInputStreams.
185: */
186: @Override
187: public boolean markSupported() {
188: return true;
189: }
190:
191: /**
192: * Reads a single byte from this BufferedInputStream and returns the result
193: * as an int. The low-order byte is returned or -1 of the end of stream was
194: * encountered. If the underlying buffer does not contain any available
195: * bytes then it is filled and the first byte is returned.
196: *
197: * @return the byte read or -1 if end of stream.
198: *
199: * @throws IOException
200: * If the stream is already closed or another IOException
201: * occurs.
202: */
203: @Override
204: public synchronized int read() throws IOException {
205: if (in == null) {
206: // K0059=Stream is closed
207: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
208: }
209:
210: /* Are there buffered bytes available? */
211: if (pos >= count && fillbuf() == -1) {
212: return -1; /* no, fill buffer */
213: }
214:
215: /* Did filling the buffer fail with -1 (EOF)? */
216: if (count - pos > 0) {
217: return buf[pos++] & 0xFF;
218: }
219: return -1;
220: }
221:
222: /**
223: * Reads at most <code>length</code> bytes from this BufferedInputStream
224: * and stores them in byte array <code>buffer</code> starting at offset
225: * <code>offset</code>. Answer the number of bytes actually read or -1 if
226: * no bytes were read and end of stream was encountered. If all the buffered
227: * bytes have been used, a mark has not been set, and the requested number
228: * of bytes is larger than the receiver's buffer size, this implementation
229: * bypasses the buffer and simply places the results directly into
230: * <code>buffer</code>.
231: *
232: * @param buffer
233: * the byte array in which to store the read bytes.
234: * @param offset
235: * the offset in <code>buffer</code> to store the read bytes.
236: * @param length
237: * the maximum number of bytes to store in <code>buffer</code>.
238: * @return the number of bytes actually read or -1 if end of stream.
239: *
240: * @throws IOException
241: * If the stream is already closed or another IOException
242: * occurs.
243: */
244: @Override
245: public synchronized int read(byte[] buffer, int offset, int length)
246: throws IOException {
247: if (closed) {
248: // K0059=Stream is closed
249: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
250: }
251: // avoid int overflow
252: if (offset > buffer.length - length || offset < 0 || length < 0) {
253: throw new IndexOutOfBoundsException();
254: }
255: if (length == 0) {
256: return 0;
257: }
258: if (null == buf) {
259: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
260: }
261:
262: int required;
263: if (pos < count) {
264: /* There are bytes available in the buffer. */
265: int copylength = count - pos >= length ? length : count
266: - pos;
267: System.arraycopy(buf, pos, buffer, offset, copylength);
268: pos += copylength;
269: if (copylength == length || in.available() == 0) {
270: return copylength;
271: }
272: offset += copylength;
273: required = length - copylength;
274: } else {
275: required = length;
276: }
277:
278: while (true) {
279: int read;
280: /*
281: * If we're not marked and the required size is greater than the
282: * buffer, simply read the bytes directly bypassing the buffer.
283: */
284: if (markpos == -1 && required >= buf.length) {
285: read = in.read(buffer, offset, required);
286: if (read == -1) {
287: return required == length ? -1 : length - required;
288: }
289: } else {
290: if (fillbuf() == -1) {
291: return required == length ? -1 : length - required;
292: }
293: read = count - pos >= required ? required : count - pos;
294: System.arraycopy(buf, pos, buffer, offset, read);
295: pos += read;
296: }
297: required -= read;
298: if (required == 0) {
299: return length;
300: }
301: if (in.available() == 0) {
302: return length - required;
303: }
304: offset += read;
305: }
306: }
307:
308: /**
309: * Reset this BufferedInputStream to the last marked location. If the
310: * <code>readlimit</code> has been passed or no <code>mark</code> has
311: * been set, throw IOException. This implementation resets the target
312: * stream.
313: *
314: * @throws IOException
315: * If the stream is already closed or another IOException
316: * occurs.
317: */
318:
319: @Override
320: public synchronized void reset() throws IOException {
321: if (closed) {
322: // K0059=Stream is closed
323: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
324: }
325: if (-1 == markpos) {
326: // K005a=Mark has been invalidated.
327: throw new IOException(Msg.getString("K005a")); //$NON-NLS-1$
328: }
329: pos = markpos;
330: }
331:
332: /**
333: * Skips <code>amount</code> number of bytes in this BufferedInputStream.
334: * Subsequent <code>read()</code>'s will not return these bytes unless
335: * <code>reset()</code> is used.
336: *
337: * @param amount
338: * the number of bytes to skip.
339: * @return the number of bytes actually skipped.
340: *
341: * @throws IOException
342: * If the stream is already closed or another IOException
343: * occurs.
344: */
345: @Override
346: public synchronized long skip(long amount) throws IOException {
347: if (null == in) {
348: // K0059=Stream is closed
349: throw new IOException(Msg.getString("K0059")); //$NON-NLS-1$
350: }
351: if (amount < 1) {
352: return 0;
353: }
354:
355: if (count - pos >= amount) {
356: pos += amount;
357: return amount;
358: }
359: long read = count - pos;
360: pos = count;
361:
362: if (markpos != -1) {
363: if (amount <= marklimit) {
364: if (fillbuf() == -1) {
365: return read;
366: }
367: if (count - pos >= amount - read) {
368: pos += amount - read;
369: return amount;
370: }
371: // Couldn't get all the bytes, skip what we read
372: read += (count - pos);
373: pos = count;
374: return read;
375: }
376: markpos = -1;
377: }
378: return read + in.skip(amount - read);
379: }
380: }
|