View Javadoc

1   /*
2    * @(#)BufferedInputStream.java 1.43 03/01/23
3    *
4    * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
5    * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6    */
7   
8   package de.netseeker.ejoe.io;
9   
10  import java.io.FilterInputStream;
11  import java.io.IOException;
12  import java.io.InputStream;
13  
14  /***
15   * A <code>BufferedInputStream</code> adds functionality to another input stream-namely, the ability to buffer the
16   * input and to support the <code>mark</code> and <code>reset</code> methods. When the
17   * <code>BufferedInputStream</code> is created, an internal buffer array is created. As bytes from the stream are read
18   * or skipped, the internal buffer is refilled as necessary from the contained input stream, many bytes at a time. The
19   * <code>mark</code> operation remembers a point in the input stream and the <code>reset</code> operation causes all
20   * the bytes read since the most recent <code>mark</code> operation to be reread before new bytes are taken from the
21   * contained input stream.
22   * 
23   * @author Arthur van Hoff
24   * @version 1.43, 01/23/03
25   * @since JDK1.0
26   */
27  public class FastBufferedInputStream extends FilterInputStream
28  {
29  
30      private static int defaultBufferSize = 2048;
31  
32      /***
33       * The internal buffer array where the data is stored. When necessary, it may be replaced by another array of a
34       * different size.
35       */
36      protected byte     buf[];
37  
38      /***
39       * The index one greater than the index of the last valid byte in the buffer. This value is always in the range
40       * <code>0</code> through <code>buf.length</code>; elements <code>buf[0]</code> through <code>buf[count-1]
41       * </code>contain
42       * buffered input data obtained from the underlying input stream.
43       */
44      protected int      count;
45  
46      /***
47       * The current position in the buffer. This is the index of the next character to be read from the <code>buf</code>
48       * array.
49       * <p>
50       * This value is always in the range <code>0</code> through <code>count</code>. If it is less than
51       * <code>count</code>, then <code>buf[pos]</code> is the next byte to be supplied as input; if it is equal to
52       * <code>count</code>, then the next <code>read</code> or <code>skip</code> operation will require more bytes
53       * to be read from the contained input stream.
54       * 
55       * @see java.io.BufferedInputStream#buf
56       */
57      protected int      pos;
58  
59      /***
60       * The value of the <code>pos</code> field at the time the last <code>mark</code> method was called.
61       * <p>
62       * This value is always in the range <code>-1</code> through <code>pos</code>. If there is no marked position
63       * in the input stream, this field is <code>-1</code>. If there is a marked position in the input stream, then
64       * <code>buf[markpos]</code> is the first byte to be supplied as input after a <code>reset</code> operation. If
65       * <code>markpos</code> is not <code>-1</code>, then all bytes from positions <code>buf[markpos]</code>
66       * through <code>buf[pos-1]</code> must remain in the buffer array (though they may be moved to another place in
67       * the buffer array, with suitable adjustments to the values of <code>count</code>, <code>pos</code>, and
68       * <code>markpos</code>); they may not be discarded unless and until the difference between <code>pos</code>
69       * and <code>markpos</code> exceeds <code>marklimit</code>.
70       * 
71       * @see java.io.BufferedInputStream#mark(int)
72       * @see java.io.BufferedInputStream#pos
73       */
74      protected int      markpos           = -1;
75  
76      /***
77       * The maximum read ahead allowed after a call to the <code>mark</code> method before subsequent calls to the
78       * <code>reset</code> method fail. Whenever the difference between <code>pos</code> and <code>markpos</code>
79       * exceeds <code>marklimit</code>, then the mark may be dropped by setting <code>markpos</code> to
80       * <code>-1</code>.
81       * 
82       * @see java.io.BufferedInputStream#mark(int)
83       * @see java.io.BufferedInputStream#reset()
84       */
85      protected int      marklimit;
86  
87      /***
88       * Check to make sure that this stream has not been closed
89       */
90      private void ensureOpen() throws IOException
91      {
92          if ( in == null ) throw new IOException( "Stream closed" );
93      }
94  
95      /***
96       * Creates a <code>FastBufferedInputStream</code> and saves its argument, the input stream <code>in</code>, for
97       * later use. An internal buffer array is created and stored in <code>buf</code>.
98       * 
99       * @param in the underlying input stream.
100      */
101     public FastBufferedInputStream(InputStream in)
102     {
103         this( in, defaultBufferSize );
104     }
105 
106     /***
107      * Creates a <code>FastBufferedInputStream</code> with the specified buffer size, and saves its argument, the
108      * input stream <code>in</code>, for later use. An internal buffer array of length <code>size</code> is created
109      * and stored in <code>buf</code>.
110      * 
111      * @param in the underlying input stream.
112      * @param size the buffer size.
113      * @exception IllegalArgumentException if size <= 0.
114      */
115     public FastBufferedInputStream(InputStream in, int size)
116     {
117         super( in );
118         if ( size <= 0 )
119         {
120             throw new IllegalArgumentException( "Buffer size <= 0" );
121         }
122         buf = new byte[size];
123     }
124 
125     /***
126      * Fills the buffer with more data, taking into account shuffling and other tricks for dealing with marks. Assumes
127      * that it is being called by a synchronized method. This method also assumes that all data has already been read
128      * in, hence pos > count.
129      */
130     private void fill() throws IOException
131     {
132         if ( markpos < 0 )
133             pos = 0; /* no mark: throw away the buffer */
134         else if ( pos >= buf.length ) /* no room left in buffer */
135         if ( markpos > 0 )
136         { /* can throw away early part of the buffer */
137             int sz = pos - markpos;
138             System.arraycopy( buf, markpos, buf, 0, sz );
139             pos = sz;
140             markpos = 0;
141         }
142         else if ( buf.length >= marklimit )
143         {
144             markpos = -1; /* buffer got too big, invalidate mark */
145             pos = 0; /* drop buffer contents */
146         }
147         else
148         { /* grow buffer */
149             int nsz = pos * 2;
150             if ( nsz > marklimit ) nsz = marklimit;
151             byte nbuf[] = new byte[nsz];
152             System.arraycopy( buf, 0, nbuf, 0, pos );
153             buf = nbuf;
154         }
155         count = pos;
156         int n = in.read( buf, pos, buf.length - pos );
157         if ( n > 0 ) count = n + pos;
158     }
159 
160     /***
161      * See the general contract of the <code>read</code> method of <code>InputStream</code>.
162      * 
163      * @return the next byte of data, or <code>-1</code> if the end of the stream is reached.
164      * @exception IOException if an I/O error occurs.
165      * @see java.io.FilterInputStream#in
166      */
167     public int read() throws IOException
168     {
169         ensureOpen();
170         if ( pos >= count )
171         {
172             fill();
173             if ( pos >= count ) return -1;
174         }
175         return buf[pos++] & 0xff;
176     }
177 
178     /***
179      * Read characters into a portion of an array, reading from the underlying stream at most once if necessary.
180      */
181     private int read1( byte[] b, int off, int len ) throws IOException
182     {
183         int avail = count - pos;
184         if ( avail <= 0 )
185         {
186             /*
187              * If the requested length is at least as large as the buffer, and if there is no mark/reset activity, do
188              * not bother to copy the bytes into the local buffer. In this way buffered streams will cascade harmlessly.
189              */
190             if ( len >= buf.length && markpos < 0 )
191             {
192                 return in.read( b, off, len );
193             }
194             fill();
195             avail = count - pos;
196             if ( avail <= 0 ) return -1;
197         }
198         int cnt = (avail < len) ? avail : len;
199         System.arraycopy( buf, pos, b, off, cnt );
200         pos += cnt;
201         return cnt;
202     }
203 
204     /***
205      * Reads bytes from this byte-input stream into the specified byte array, starting at the given offset.
206      * <p>
207      * This method implements the general contract of the corresponding
208      * <code>{@link InputStream#read(byte[], int, int) read}</code> method of the <code>{@link InputStream}</code>
209      * class. As an additional convenience, it attempts to read as many bytes as possible by repeatedly invoking the
210      * <code>read</code> method of the underlying stream. This iterated <code>read</code> continues until one of the
211      * following conditions becomes true:
212      * <ul>
213      * <li> The specified number of bytes have been read,
214      * <li> The <code>read</code> method of the underlying stream returns <code>-1</code>, indicating end-of-file,
215      * or
216      * <li> The <code>available</code> method of the underlying stream returns zero, indicating that further input
217      * requests would block.
218      * </ul>
219      * If the first <code>read</code> on the underlying stream returns <code>-1</code> to indicate end-of-file then
220      * this method returns <code>-1</code>. Otherwise this method returns the number of bytes actually read.
221      * <p>
222      * Subclasses of this class are encouraged, but not required, to attempt to read as many bytes as possible in the
223      * same fashion.
224      * 
225      * @param b destination buffer.
226      * @param off offset at which to start storing bytes.
227      * @param len maximum number of bytes to read.
228      * @return the number of bytes read, or <code>-1</code> if the end of the stream has been reached.
229      * @exception IOException if an I/O error occurs.
230      */
231     public int read( byte b[], int off, int len ) throws IOException
232     {
233         ensureOpen();
234         if ( (off | len | (off + len) | (b.length - (off + len))) < 0 )
235         {
236             throw new IndexOutOfBoundsException();
237         }
238         else if ( len == 0 )
239         {
240             return 0;
241         }
242 
243         int n = read1( b, off, len );
244         if ( n <= 0 ) return n;
245         while ( (n < len) && (in.available() > 0) )
246         {
247             int n1 = read1( b, off + n, len - n );
248             if ( n1 <= 0 ) break;
249             n += n1;
250         }
251         return n;
252     }
253 
254     /***
255      * See the general contract of the <code>skip</code> method of <code>InputStream</code>.
256      * 
257      * @param n the number of bytes to be skipped.
258      * @return the actual number of bytes skipped.
259      * @exception IOException if an I/O error occurs.
260      */
261     public long skip( long n ) throws IOException
262     {
263         ensureOpen();
264         if ( n <= 0 )
265         {
266             return 0;
267         }
268         long avail = count - pos;
269 
270         if ( avail <= 0 )
271         {
272             // If no mark position set then don't keep in buffer
273             if ( markpos < 0 ) return in.skip( n );
274 
275             // Fill in buffer to save bytes for reset
276             fill();
277             avail = count - pos;
278             if ( avail <= 0 ) return 0;
279         }
280 
281         long skipped = (avail < n) ? avail : n;
282         pos += skipped;
283         return skipped;
284     }
285 
286     /***
287      * Returns the number of bytes that can be read from this input stream without blocking.
288      * <p>
289      * The <code>available</code> method of <code>BufferedInputStream</code> returns the sum of the the number of
290      * bytes remaining to be read in the buffer (<code>count&nbsp;- pos</code>) and the result of calling the
291      * <code>available</code> method of the underlying input stream.
292      * 
293      * @return the number of bytes that can be read from this input stream without blocking.
294      * @exception IOException if an I/O error occurs.
295      * @see java.io.FilterInputStream#in
296      */
297     public int available() throws IOException
298     {
299         ensureOpen();
300         return (count - pos) + in.available();
301     }
302 
303     /***
304      * See the general contract of the <code>mark</code> method of <code>InputStream</code>.
305      * 
306      * @param readlimit the maximum limit of bytes that can be read before the mark position becomes invalid.
307      * @see java.io.BufferedInputStream#reset()
308      */
309     public void mark( int readlimit )
310     {
311         marklimit = readlimit;
312         markpos = pos;
313     }
314 
315     /***
316      * See the general contract of the <code>reset</code> method of <code>InputStream</code>.
317      * <p>
318      * If <code>markpos</code> is <code>-1</code> (no mark has been set or the mark has been invalidated), an
319      * <code>IOException</code> is thrown. Otherwise, <code>pos</code> is set equal to <code>markpos</code>.
320      * 
321      * @exception IOException if this stream has not been marked or if the mark has been invalidated.
322      * @see java.io.BufferedInputStream#mark(int)
323      */
324     public void reset() throws IOException
325     {
326         ensureOpen();
327         if ( markpos < 0 ) throw new IOException( "Resetting to invalid mark" );
328         pos = markpos;
329     }
330 
331     /***
332      * Tests if this input stream supports the <code>mark</code> and <code>reset</code> methods. The
333      * <code>markSupported</code> method of <code>BufferedInputStream</code> returns <code>true</code>.
334      * 
335      * @return a <code>boolean</code> indicating if this stream type supports the <code>mark</code> and
336      *         <code>reset</code> methods.
337      * @see java.io.InputStream#mark(int)
338      * @see java.io.InputStream#reset()
339      */
340     public boolean markSupported()
341     {
342         return true;
343     }
344 
345     /***
346      * Closes this input stream and releases any system resources associated with the stream.
347      * 
348      * @exception IOException if an I/O error occurs.
349      */
350     public void close() throws IOException
351     {
352         if ( in == null ) return;
353         in.close();
354         in = null;
355         buf = null;
356     }
357 }