001: /*
002: * @(#)BufferedInputStream.java 1.51 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package java.io;
029:
030: /**
031: * A <code>BufferedInputStream</code> adds
032: * functionality to another input stream-namely,
033: * the ability to buffer the input and to
034: * support the <code>mark</code> and <code>reset</code>
035: * methods. When the <code>BufferedInputStream</code>
036: * is created, an internal buffer array is
037: * created. As bytes from the stream are read
038: * or skipped, the internal buffer is refilled
039: * as necessary from the contained input stream,
040: * many bytes at a time. The <code>mark</code>
041: * operation remembers a point in the input
042: * stream and the <code>reset</code> operation
043: * causes all the bytes read since the most
044: * recent <code>mark</code> operation to be
045: * reread before new bytes are taken from
046: * the contained input stream.
047: *
048: * @version 1.44 11/09/00
049: * @since JDK1.0
050: */
051: public class BufferedInputStream extends FilterInputStream {
052:
053: private static int defaultBufferSize = 2048;
054:
055: /**
056: * The internal buffer array where the data is stored. When necessary,
057: * it may be replaced by another array of
058: * a different size.
059: */
060: protected byte buf[];
061:
062: /**
063: * The index one greater than the index of the last valid byte in
064: * the buffer.
065: * This value is always
066: * in the range <code>0</code> through <code>buf.length</code>;
067: * elements <code>buf[0]</code> through <code>buf[count-1]
068: * </code>contain buffered input data obtained
069: * from the underlying input stream.
070: */
071: protected int count;
072:
073: /**
074: * The current position in the buffer. This is the index of the next
075: * character to be read from the <code>buf</code> array.
076: * <p>
077: * This value is always in the range <code>0</code>
078: * through <code>count</code>. If it is less
079: * than <code>count</code>, then <code>buf[pos]</code>
080: * is the next byte to be supplied as input;
081: * if it is equal to <code>count</code>, then
082: * the next <code>read</code> or <code>skip</code>
083: * operation will require more bytes to be
084: * read from the contained input stream.
085: *
086: * @see java.io.BufferedInputStream#buf
087: */
088: protected int pos;
089:
090: /**
091: * The value of the <code>pos</code> field at the time the last
092: * <code>mark</code> method was called.
093: * <p>
094: * This value is always
095: * in the range <code>-1</code> through <code>pos</code>.
096: * If there is no marked position in the input
097: * stream, this field is <code>-1</code>. If
098: * there is a marked position in the input
099: * stream, then <code>buf[markpos]</code>
100: * is the first byte to be supplied as input
101: * after a <code>reset</code> operation. If
102: * <code>markpos</code> is not <code>-1</code>,
103: * then all bytes from positions <code>buf[markpos]</code>
104: * through <code>buf[pos-1]</code> must remain
105: * in the buffer array (though they may be
106: * moved to another place in the buffer array,
107: * with suitable adjustments to the values
108: * of <code>count</code>, <code>pos</code>,
109: * and <code>markpos</code>); they may not
110: * be discarded unless and until the difference
111: * between <code>pos</code> and <code>markpos</code>
112: * exceeds <code>marklimit</code>.
113: *
114: * @see java.io.BufferedInputStream#mark(int)
115: * @see java.io.BufferedInputStream#pos
116: */
117: protected int markpos = -1;
118:
119: /**
120: * The maximum read ahead allowed after a call to the
121: * <code>mark</code> method before subsequent calls to the
122: * <code>reset</code> method fail.
123: * Whenever the difference between <code>pos</code>
124: * and <code>markpos</code> exceeds <code>marklimit</code>,
125: * then the mark may be dropped by setting
126: * <code>markpos</code> to <code>-1</code>.
127: *
128: * @see java.io.BufferedInputStream#mark(int)
129: * @see java.io.BufferedInputStream#reset()
130: */
131: protected int marklimit;
132:
133: /**
134: * Check to make sure that this stream has not been closed
135: */
136: private void ensureOpen() throws IOException {
137: if (in == null)
138: throw new IOException("Stream closed");
139: }
140:
141: /**
142: * Creates a <code>BufferedInputStream</code>
143: * and saves its argument, the input stream
144: * <code>in</code>, for later use. An internal
145: * buffer array is created and stored in <code>buf</code>.
146: *
147: * @param in the underlying input stream.
148: */
149: public BufferedInputStream(InputStream in) {
150: this (in, defaultBufferSize);
151: }
152:
153: /**
154: * Creates a <code>BufferedInputStream</code>
155: * with the specified buffer size,
156: * and saves its argument, the input stream
157: * <code>in</code>, for later use. An internal
158: * buffer array of length <code>size</code>
159: * is created and stored in <code>buf</code>.
160: *
161: * @param in the underlying input stream.
162: * @param size the buffer size.
163: * @exception IllegalArgumentException if size <= 0.
164: */
165: public BufferedInputStream(InputStream in, int size) {
166: super (in);
167: if (size <= 0) {
168: throw new IllegalArgumentException("Buffer size <= 0");
169: }
170: buf = new byte[size];
171: }
172:
173: /**
174: * Fills the buffer with more data, taking into account
175: * shuffling and dealing with marks.
176: * Assumes that it is being called by a synchronized method.
177: * This method also assumes that all data has already been read in,
178: * hence pos > count.
179: */
180: private void fill() throws IOException {
181: if (markpos < 0)
182: pos = 0; /* no mark: throw away the buffer */
183: else if (pos >= buf.length) /* no room left in buffer */
184: if (markpos > 0) { /* can throw away early part of the buffer */
185: int sz = pos - markpos;
186: System.arraycopy(buf, markpos, buf, 0, sz);
187: pos = sz;
188: markpos = 0;
189: } else if (buf.length >= marklimit) {
190: markpos = -1; /* buffer got too big, invalidate mark */
191: pos = 0; /* drop buffer contents */
192: } else { /* grow buffer */
193: int nsz = pos * 2;
194: if (nsz > marklimit)
195: nsz = marklimit;
196: byte nbuf[] = new byte[nsz];
197: System.arraycopy(buf, 0, nbuf, 0, pos);
198: buf = nbuf;
199: }
200: count = pos;
201: int n = in.read(buf, pos, buf.length - pos);
202: if (n > 0)
203: count = n + pos;
204: }
205:
206: /**
207: * See
208: * the general contract of the <code>read</code>
209: * method of <code>InputStream</code>.
210: *
211: * @return the next byte of data, or <code>-1</code> if the end of the
212: * stream is reached.
213: * @exception IOException if an I/O error occurs.
214: * @see java.io.FilterInputStream#in
215: */
216: public synchronized int read() throws IOException {
217: if (pos >= count) {
218: ensureOpen();
219: fill();
220: if (pos >= count)
221: return -1;
222: }
223: return buf[pos++] & 0xff;
224: }
225:
226: /**
227: * Read characters into a portion of an array, reading from the underlying
228: * stream at most once if necessary.
229: */
230: private int read1(byte[] b, int off, int len) throws IOException {
231: int avail = count - pos;
232: if (avail <= 0) {
233: /* If the requested length is at least as large as the buffer, and
234: if there is no mark/reset activity, do not bother to copy the
235: bytes into the local buffer. In this way buffered streams will
236: cascade harmlessly. */
237: if (len >= buf.length && markpos < 0) {
238: return in.read(b, off, len);
239: }
240: fill();
241: avail = count - pos;
242: if (avail <= 0)
243: return -1;
244: }
245: int cnt = (avail < len) ? avail : len;
246: System.arraycopy(buf, pos, b, off, cnt);
247: pos += cnt;
248: return cnt;
249: }
250:
251: /**
252: * Reads bytes from this byte-input stream into the specified byte array,
253: * starting at the given offset.
254: *
255: * <p> This method implements the general contract of the corresponding
256: * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
257: * the <code>{@link InputStream}</code> class. As an additional
258: * convenience, it attempts to read as many bytes as possible by repeatedly
259: * invoking the <code>read</code> method of the underlying stream. This
260: * iterated <code>read</code> continues until one of the following
261: * conditions becomes true: <ul>
262: *
263: * <li> The specified number of bytes have been read,
264: *
265: * <li> The <code>read</code> method of the underlying stream returns
266: * <code>-1</code>, indicating end-of-file, or
267: *
268: * <li> The <code>available</code> method of the underlying stream
269: * returns zero, indicating that further input requests would block.
270: *
271: * </ul> If the first <code>read</code> on the underlying stream returns
272: * <code>-1</code> to indicate end-of-file then this method returns
273: * <code>-1</code>. Otherwise this method returns the number of bytes
274: * actually read.
275: *
276: * <p> Subclasses of this class are encouraged, but not required, to
277: * attempt to read as many bytes as possible in the same fashion.
278: *
279: * @param b destination buffer.
280: * @param off offset at which to start storing bytes.
281: * @param len maximum number of bytes to read.
282: * @return the number of bytes read, or <code>-1</code> if the end of
283: * the stream has been reached.
284: * @exception IOException if an I/O error occurs.
285: */
286: public synchronized int read(byte b[], int off, int len)
287: throws IOException {
288: ensureOpen();
289: if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
290: throw new IndexOutOfBoundsException();
291: } else if (len == 0) {
292: return 0;
293: }
294:
295: int n = read1(b, off, len);
296: if (n <= 0)
297: return n;
298: while ((n < len) && (in.available() > 0)) {
299: int n1 = read1(b, off + n, len - n);
300: if (n1 <= 0)
301: break;
302: n += n1;
303: }
304: return n;
305: }
306:
307: /**
308: * See the general contract of the <code>skip</code>
309: * method of <code>InputStream</code>.
310: *
311: * @param n the number of bytes to be skipped.
312: * @return the actual number of bytes skipped.
313: * @exception IOException if an I/O error occurs.
314: */
315: public synchronized long skip(long n) throws IOException {
316: ensureOpen();
317: if (n <= 0) {
318: return 0;
319: }
320: long avail = count - pos;
321:
322: if (avail <= 0) {
323: // If no mark position set then don't keep in buffer
324: if (markpos < 0)
325: return in.skip(n);
326:
327: // Fill in buffer to save bytes for reset
328: fill();
329: avail = count - pos;
330: if (avail <= 0)
331: return 0;
332: }
333:
334: long skipped = (avail < n) ? avail : n;
335: pos += skipped;
336: return skipped;
337: }
338:
339: /**
340: * Returns the number of bytes that can be read from this input
341: * stream without blocking.
342: * <p>
343: * The <code>available</code> method of
344: * <code>BufferedInputStream</code> returns the sum of the the number
345: * of bytes remaining to be read in the buffer
346: * (<code>count - pos</code>)
347: * and the result of calling the <code>available</code> method of the
348: * underlying input stream.
349: *
350: * @return the number of bytes that can be read from this input
351: * stream without blocking.
352: * @exception IOException if an I/O error occurs.
353: * @see java.io.FilterInputStream#in
354: */
355: public synchronized int available() throws IOException {
356: ensureOpen();
357: return (count - pos) + in.available();
358: }
359:
360: /**
361: * See the general contract of the <code>mark</code>
362: * method of <code>InputStream</code>.
363: *
364: * @param readlimit the maximum limit of bytes that can be read before
365: * the mark position becomes invalid.
366: * @see java.io.BufferedInputStream#reset()
367: */
368: public synchronized void mark(int readlimit) {
369: marklimit = readlimit;
370: markpos = pos;
371: }
372:
373: /**
374: * See the general contract of the <code>reset</code>
375: * method of <code>InputStream</code>.
376: * <p>
377: * If <code>markpos</code> is <code>-1</code>
378: * (no mark has been set or the mark has been
379: * invalidated), an <code>IOException</code>
380: * is thrown. Otherwise, <code>pos</code> is
381: * set equal to <code>markpos</code>.
382: *
383: * @exception IOException if this stream has not been marked or
384: * if the mark has been invalidated.
385: * @see java.io.BufferedInputStream#mark(int)
386: */
387: public synchronized void reset() throws IOException {
388: ensureOpen();
389: if (markpos < 0)
390: throw new IOException("Resetting to invalid mark");
391: pos = markpos;
392: }
393:
394: /**
395: * Tests if this input stream supports the <code>mark</code>
396: * and <code>reset</code> methods. The <code>markSupported</code>
397: * method of <code>BufferedInputStream</code> returns
398: * <code>true</code>.
399: *
400: * @return a <code>boolean</code> indicating if this stream type supports
401: * the <code>mark</code> and <code>reset</code> methods.
402: * @see java.io.InputStream#mark(int)
403: * @see java.io.InputStream#reset()
404: */
405: public boolean markSupported() {
406: return true;
407: }
408:
409: /**
410: * Closes this input stream and releases any system resources
411: * associated with the stream.
412: *
413: * @exception IOException if an I/O error occurs.
414: */
415: public void close() throws IOException {
416: if (in == null)
417: return;
418: in.close();
419: in = null;
420: buf = null;
421: pos = count = 0;
422: }
423: }
|