001: /*
002: * @(#)ExtBufferedInputStream.java 0.3-2 18/06/1999
003: *
004: * This file is part of the HTTPClient package
005: * Copyright (C) 1996-1999 Ronald Tschalär
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free
019: * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
020: * MA 02111-1307, USA
021: *
022: * For questions, suggestions, bug-reports, enhancement-requests etc.
023: * I may be contacted at:
024: *
025: * ronald@innovation.ch
026: *
027: */
028:
029: package HTTPClient;
030:
031: import java.io.IOException;
032: import java.io.InputStream;
033: import java.io.FilterInputStream;
034:
035: /**
036: * This class is a modified copy of java.io.BufferedInputStream which fixes
037: * the problem in fill when an InterrupedIOException occurs and which
038: * extends the class to allow searching for a string in the internal buffer
039: * (used for multipart content-types).
040: *
041: * @version 0.3-2 18/06/1999
042: * @author Ronald Tschalär
043: * @author Arthur van Hoff
044: */
045:
046: /*
047: * @(#)BufferedInputStream.java 1.26 97/03/03
048: *
049: * Copyright (c) 1995, 1996 Sun Microsystems, Inc. All Rights Reserved.
050: *
051: * This software is the confidential and proprietary information of Sun
052: * Microsystems, Inc. ("Confidential Information"). You shall not
053: * disclose such Confidential Information and shall use it only in
054: * accordance with the terms of the license agreement you entered into
055: * with Sun.
056: *
057: * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
058: * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
059: * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
060: * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
061: * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
062: * THIS SOFTWARE OR ITS DERIVATIVES.
063: *
064: * CopyrightVersion 1.1_beta
065: *
066: */
067:
068: final class ExtBufferedInputStream extends FilterInputStream {
069: /**
070: * The buffer where data is stored.
071: *
072: * @since JDK1.0
073: */
074: protected byte buf[];
075:
076: /**
077: * The index one greater than the index of the last valid byte in
078: * the buffer.
079: *
080: * @since JDK1.0
081: */
082: protected int count;
083:
084: /**
085: * The current position in the buffer. This is the index of the next
086: * character to be read from the <code>buf</code> array.
087: *
088: * @see java.io.BufferedInputStream#buf
089: * @since JDK1.0
090: */
091: protected int pos;
092:
093: /**
094: * The value of the <code>pos</code> field at the time the last
095: * <code>mark</code> method was called. The value of this field is
096: * <code>-1</code> if there is no current mark.
097: *
098: * @see java.io.BufferedInputStream#mark(int)
099: * @see java.io.BufferedInputStream#pos
100: * @since JDK1.0
101: */
102: protected int markpos = -1;
103:
104: /**
105: * The maximum read ahead allowed after a call to the
106: * <code>mark</code> method before subsequent calls to the
107: * <code>reset</code> method fail.
108: *
109: * @see java.io.BufferedInputStream#mark(int)
110: * @see java.io.BufferedInputStream#reset()
111: * @since JDK1.0
112: */
113: protected int marklimit;
114:
115: /**
116: * Creates a new buffered input stream to read data from the
117: * specified input stream with a default 512-byte buffer size.
118: *
119: * @param in the underlying input stream.
120: * @since JDK1.0
121: */
122: public ExtBufferedInputStream(InputStream in) {
123: this (in, 2048);
124: }
125:
126: /**
127: * Creates a new buffered input stream to read data from the
128: * specified input stream with the specified buffer size.
129: *
130: * @param in the underlying input stream.
131: * @param size the buffer size.
132: * @since JDK1.0
133: */
134: public ExtBufferedInputStream(InputStream in, int size) {
135: super (in);
136: buf = new byte[size];
137: }
138:
139: /**
140: * Fills the buffer with more data, taking into account
141: * shuffling and other tricks for dealing with marks.
142: * Assumes that it is being called by a synchronized method.
143: * This method also assumes that all data has already been read in,
144: * hence pos > count.
145: */
146: private void fill() throws IOException {
147: if (markpos < 0)
148: pos = 0; /* no mark: throw away the buffer */
149: else if (pos >= buf.length) /* no room left in buffer */
150: {
151: if (markpos > 0) /* can throw away early part of the buffer */
152: {
153: int sz = pos - markpos;
154: System.arraycopy(buf, markpos, buf, 0, sz);
155: pos = sz;
156: markpos = 0;
157: } else if (buf.length >= marklimit) {
158: markpos = -1; /* buffer got too big, invalidate mark */
159: pos = 0; /* drop buffer contents */
160: } else /* grow buffer */
161: {
162: int nsz = pos * 2;
163: if (nsz > marklimit)
164: nsz = marklimit;
165: byte nbuf[] = new byte[nsz];
166: System.arraycopy(buf, 0, nbuf, 0, pos);
167: buf = nbuf;
168: }
169: }
170: count = pos; // in case read() throws InterruptedIOException
171: int n = in.read(buf, pos, buf.length - pos);
172: count = n <= 0 ? pos : n + pos;
173: }
174:
175: /**
176: * Reads the next byte of data from this buffered input stream. The
177: * value byte is returned as an <code>int</code> in the range
178: * <code>0</code> to <code>255</code>. If no byte is available
179: * because the end of the stream has been reached, the value
180: * <code>-1</code> is returned. This method blocks until input data
181: * is available, the end of the stream is detected, or an exception
182: * is thrown.
183: * <p>
184: * The <code>read</code> method of <code>BufferedInputStream</code>
185: * returns the next byte of data from its buffer if the buffer is not
186: * empty. Otherwise, it refills the buffer from the underlying input
187: * stream and returns the next character, if the underlying stream
188: * has not returned an end-of-stream indicator.
189: *
190: * @return the next byte of data, or <code>-1</code> if the end of the
191: * stream is reached.
192: * @exception IOException if an I/O error occurs.
193: * @see java.io.FilterInputStream#in
194: * @since JDK1.0
195: */
196: public synchronized int read() throws IOException {
197: if (pos >= count) {
198: fill();
199: if (pos >= count)
200: return -1;
201: }
202: return buf[pos++] & 0xff;
203: }
204:
205: /**
206: * Reads bytes into a portion of an array. This method will block until
207: * some input is available, an I/O error occurs, or the end of the stream
208: * is reached.
209: *
210: * <p> If this stream's buffer is not empty, bytes are copied from it into
211: * the array argument. Otherwise, the buffer is refilled from the
212: * underlying input stream and, unless the stream returns an end-of-stream
213: * indication, the array argument is filled with characters from the
214: * newly-filled buffer.
215: *
216: * <p> As an optimization, if the buffer is empty, the mark is not valid,
217: * and <code>len</code> is at least as large as the buffer, then this
218: * method will read directly from the underlying stream into the given
219: * array. Thus redundant <code>BufferedInputStream</code>s will not copy
220: * data unnecessarily.
221: *
222: * @param b destination buffer.
223: * @param off offset at which to start storing bytes.
224: * @param len maximum number of bytes to read.
225: * @return the number of bytes read, or <code>-1</code> if the end of
226: * the stream has been reached.
227: * @exception IOException if an I/O error occurs.
228: */
229: public synchronized int read(byte b[], int off, int len)
230: throws IOException {
231: int avail = count - pos;
232: if (avail <= 0) {
233: /* If the requested length is larger than the buffer, and if there
234: is no mark/reset activity, do not bother to copy the bytes into
235: the local buffer. In this way buffered streams will cascade
236: 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: * Skips over and discards <code>n</code> bytes of data from the
253: * input stream. The <code>skip</code> method may, for a variety of
254: * reasons, end up skipping over some smaller number of bytes,
255: * possibly zero. The actual number of bytes skipped is returned.
256: * <p>
257: * The <code>skip</code> method of <code>BufferedInputStream</code>
258: * compares the number of bytes it has available in its buffer,
259: * <i>k</i>, where <i>k</i> = <code>count - pos</code>,
260: * with <code>n</code>. If <code>n</code> ≤ <i>k</i>,
261: * then the <code>pos</code> field is incremented by <code>n</code>.
262: * Otherwise, the <code>pos</code> field is incremented to have the
263: * value <code>count</code>, and the remaining bytes are skipped by
264: * calling the <code>skip</code> method on the underlying input
265: * stream, supplying the argument <code>n -</code> <i>k</i>.
266: *
267: * @param n the number of bytes to be skipped.
268: * @return the actual number of bytes skipped.
269: * @exception IOException if an I/O error occurs.
270: * @since JDK1.0
271: */
272: public synchronized long skip(long n) throws IOException {
273: if (n < 0)
274: return 0;
275:
276: long avail = count - pos;
277:
278: if (avail >= n) {
279: pos += n;
280: return n;
281: }
282:
283: pos += avail;
284: return avail + in.skip(n - avail);
285: }
286:
287: /**
288: * Returns the number of bytes that can be read from this input
289: * stream without blocking.
290: * <p>
291: * The <code>available</code> method of
292: * <code>BufferedInputStream</code> returns the sum of the the number
293: * of bytes remaining to be read in the buffer
294: * (<code>count - pos</code>)
295: * and the result of calling the <code>available</code> method of the
296: * underlying input stream.
297: *
298: * @return the number of bytes that can be read from this input
299: * stream without blocking.
300: * @exception IOException if an I/O error occurs.
301: * @see java.io.FilterInputStream#in
302: * @since JDK1.0
303: */
304: public synchronized int available() throws IOException {
305: return (count - pos) + in.available();
306: }
307:
308: /**
309: * Marks the current position in this input stream. A subsequent
310: * call to the <code>reset</code> method repositions the stream at
311: * the last marked position so that subsequent reads re-read the same
312: * bytes.
313: * <p>
314: * The <code>readlimit</code> argument tells the input stream to
315: * allow that many bytes to be read before the mark position gets
316: * invalidated.
317: *
318: * @param readlimit the maximum limit of bytes that can be read before
319: * the mark position becomes invalid.
320: * @see java.io.BufferedInputStream#reset()
321: * @since JDK1.0
322: */
323: public synchronized void mark(int readlimit) {
324: marklimit = readlimit;
325: markpos = pos;
326: }
327:
328: /**
329: * Repositions this stream to the position at the time the
330: * <code>mark</code> method was last called on this input stream.
331: * <p>
332: * If the stream has not been marked, or if the mark has been invalidated,
333: * an IOException is thrown. Stream marks are intended to be used in
334: * situations where you need to read ahead a little to see what's in
335: * the stream. Often this is most easily done by invoking some
336: * general parser. If the stream is of the type handled by the
337: * parser, it just chugs along happily. If the stream is not of
338: * that type, the parser should toss an exception when it fails. If an
339: * exception gets tossed within readlimit bytes, the parser will allow the
340: * outer code to reset the stream and to try another parser.
341: *
342: * @exception IOException if this stream has not been marked or
343: * if the mark has been invalidated.
344: * @see java.io.BufferedInputStream#mark(int)
345: * @since JDK1.0
346: */
347: public synchronized void reset() throws IOException {
348: if (markpos < 0)
349: throw new IOException("Resetting to invalid mark");
350: pos = markpos;
351: }
352:
353: /**
354: * Tests if this input stream supports the <code>mark</code>
355: * and <code>reset</code> methods. The <code>markSupported</code>
356: * method of <code>BufferedInputStream</code> returns
357: * <code>true</code>.
358: *
359: * @return a <code>boolean</code> indicating if this stream type supports
360: * the <code>mark</code> and <code>reset</code> methods.
361: * @see java.io.InputStream#mark(int)
362: * @see java.io.InputStream#reset()
363: * @since JDK1.0
364:
365: */
366: public boolean markSupported() {
367: return true;
368: }
369:
370: /**
371: * Figures out how many bytes past the end of the multipart we read.
372: * It then resets the markpos to either just past the end boundary
373: * if we found it, or back enough from the current position so we
374: * can always be sure to find the boundary.
375: *
376: * @param search the search string (end boundary)
377: * @param search_cmp the compiled info of the search string
378: * @return how many bytes past the end of the boundary we went.
379: */
380: int pastEnd(byte[] search, int[] search_cmp) {
381: int idx = Util.findStr(search, search_cmp, buf, markpos, pos);
382: if (idx == -1)
383: markpos = pos - search.length;
384: else {
385: markpos = idx + search.length;
386: idx = pos - markpos;
387: }
388:
389: return idx;
390: }
391:
392: /**
393: * Initialises the mark and sets the marklimit to the buffer length.
394: */
395: void initMark() {
396: mark(buf.length);
397: }
398: }
|