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 }