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