View Javadoc

1   /*
2    * @(#)BufferedInputStream.java				0.3-3 06/05/2001
3    *
4    *  This file is part of the HTTPClient package
5    *  Copyright (C) 1996-2001 Ronald Tschalär
6    *
7    *  This library is free software; you can redistribute it and/or
8    *  modify it under the terms of the GNU Lesser General Public
9    *  License as published by the Free Software Foundation; either
10   *  version 2 of the License, or (at your option) any later version.
11   *
12   *  This library is distributed in the hope that it will be useful,
13   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   *  Lesser General Public License for more details.
16   *
17   *  You should have received a copy of the GNU Lesser General Public
18   *  License along with this library; if not, write to the Free
19   *  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20   *  MA 02111-1307, USA
21   *
22   *  For questions, suggestions, bug-reports, enhancement-requests etc.
23   *  I may be contacted at:
24   *
25   *  ronald@innovation.ch
26   *
27   *  The HTTPClient's home page is located at:
28   *
29   *  http://www.innovation.ch/java/HTTPClient/ 
30   *
31   */
32  
33  package HTTPClient;
34  
35  import java.io.InputStream;
36  import java.io.FilterInputStream;
37  import java.io.IOException;
38  
39  /**
40   * This class is similar to java.io.BufferedInputStream, except that it fixes
41   * certain bugs and provides support for finding multipart boundaries.
42   *
43   * <P>Note: none of the methods here are synchronized because we assume the
44   * caller is already taking care of that.
45   *
46   * @version	0.3-3  06/05/2001
47   * @author	Ronald Tschalär
48   */
49  class BufferedInputStream extends FilterInputStream
50  {
51      /** our read buffer */
52      private byte[] buffer = new byte[2000];
53      /** the next byte in the buffer at which to read */
54      private int    pos = 0;
55      /** the end of the valid data in the buffer */
56      private int    end = 0;
57      /** the current mark position, or -1 if none */
58      private int    mark_pos = -1;
59      /**
60       * the large read threashhold: reads larger than this aren't buffered if
61       * both the current buffer is empty and no mark has been set. This is just
62       * an attempt to balance copying vs. multiple reads.
63       */
64      private int    lr_thrshld = 1500;
65  
66  
67      /**
68       * Create a new BufferedInputStream around the given input stream.
69       *
70       * @param stream  the underlying input stream to use
71       */
72      BufferedInputStream(InputStream stream)
73      {
74  	super(stream);
75      }
76  
77      /**
78       * Read a single byte.
79       *
80       * @return the read byte, or -1 if the end of the stream has been reached
81       * @exception IOException if thrown by the underlying stream
82       */
83      public int read() throws IOException
84      {
85  	if (pos >= end)
86  	    fillBuff();
87  
88  	return (end > pos) ? (buffer[pos++] & 0xFF) : -1;
89      }
90  
91      /**
92       * Read a buffer full.
93       *
94       * @param buf  the buffer to read into
95       * @param off  the offset within <var>buf</var> at which to start writing
96       * @param len  the number of bytes to read
97       * @return the number of bytes read
98       * @exception IOException if thrown by the underlying stream
99       */
100     public int read(byte[] buf, int off, int len) throws IOException
101     {
102 	if (len <= 0)
103 	    return 0;
104 
105 	// optimize for large reads
106 	if (pos >= end  &&  len >= lr_thrshld  &&  mark_pos < 0)
107 	    return in.read(buf, off, len);
108 
109 	if (pos >= end)
110 	    fillBuff();
111 
112 	if (pos >= end)
113 	    return -1;
114 
115 	int left = end - pos;
116 	if (len > left)
117 	    len = left;
118 	System.arraycopy(buffer, pos, buf, off, len);
119 	pos += len;
120 
121 	return len;
122     }
123 
124     /**
125      * Skip the given number of bytes in the stream.
126      *
127      * @param n   the number of bytes to skip
128      * @return the actual number of bytes skipped
129      * @exception IOException if thrown by the underlying stream
130      */
131     public long skip(long n) throws IOException
132     {
133 	if (n <= 0)
134 	    return 0;
135 
136 	int left = end - pos;
137 	if (n <= left)
138 	{
139 	    pos += n;
140 	    return n;
141 	}
142 	else
143 	{
144 	    pos = end;
145 	    return left + in.skip(n - left);
146 	}
147     }
148 
149     /**
150      * Fill buffer by reading from the underlying stream. This assumes the
151      * current buffer is empty, i.e. pos == end.
152      */
153     private final void fillBuff() throws IOException
154     {
155 	if (mark_pos > 0)	// keep the marked stuff around if possible
156 	{
157 	    // only copy if we don't have any space left
158 	    if (end >= buffer.length)
159 	    {
160 		System.arraycopy(buffer, mark_pos, buffer, 0, end - mark_pos);
161 		pos = end - mark_pos;
162 	    }
163 	}
164 	else if (mark_pos == 0  &&  end < buffer.length)
165 	    ;			// pos == end, so we just fill what's left
166 	else
167 	    pos = 0;		// try to fill complete buffer
168 
169 	// make sure our state is consistent even if read() throws InterruptedIOException
170 	end = pos;
171 
172 	int got = in.read(buffer, pos, buffer.length - pos);
173 	if (got > 0)
174 	    end = pos + got;
175     }
176 
177     /**
178      * @return the number of bytes available for reading without blocking
179      * @exception IOException if the buffer is empty and the underlying stream has been
180      *                        closed
181      */
182     public int available() throws IOException
183     {
184 	int avail = end - pos;  
185 	if (avail == 0)
186 	    return in.available();
187 
188 	try
189 	    { avail += in.available(); }
190 	catch (IOException ignored)
191 	    { /* ignore this because we have something available */ }
192 	return avail;
193     }
194 
195     /**
196      * Mark the current read position so that we can start searching for the end boundary.
197      */
198     void markForSearch()
199     {
200 	mark_pos = pos;
201     }
202 
203     /**
204      * Figures out how many bytes past the end of the multipart we read. If we
205      * found the end, it then resets the read pos to just past the end of the
206      * boundary and unsets the mark; if not found, is sets the mark_pos back
207      * enough from the current position so we can always be sure to find the
208      * boundary.
209      *
210      * @param search     the search string (end boundary)
211      * @param search_cmp the compiled info of the search string
212      * @return how many bytes past the end of the boundary we went; -1 if we
213      *         haven't gone passed it yet.
214      */
215     int pastEnd(byte[] search, int[] search_cmp)
216     {
217 	int idx = Util.findStr(search, search_cmp, buffer, mark_pos, pos);
218 	if (idx == -1)
219 	    mark_pos = (pos > search.length) ? pos - search.length : 0;
220 	else
221 	{
222 	    int eos  = idx + search.length;
223 	    idx      = pos - eos;
224 	    pos      = eos;
225 	    mark_pos = -1;
226 	}
227 
228 	return idx;
229     }
230 }