1 /*
2 * $Source$
3 * $Revision$
4 *
5 * Copyright (C) 2000 Myles Chippendale
6 *
7 * Part of Melati (http://melati.org), a framework for the rapid
8 * development of clean, maintainable web applications.
9 *
10 * Melati is free software; Permission is granted to copy, distribute
11 * and/or modify this software under the terms either:
12 *
13 * a) the GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the License, or (at your option)
15 * any later version,
16 *
17 * or
18 *
19 * b) any version of the Melati Software License, as published
20 * at http://melati.org
21 *
22 * You should have received a copy of the GNU General Public License and
23 * the Melati Software License along with this program;
24 * if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA to obtain the
26 * GNU General Public License and visit http://melati.org to obtain the
27 * Melati Software License.
28 *
29 * Feel free to contact the Developers of Melati (http://melati.org),
30 * if you would like to work out a different arrangement than the options
31 * outlined here. It is our intention to allow Melati to be used by as
32 * wide an audience as possible.
33 *
34 * This program is distributed in the hope that it will be useful,
35 * but WITHOUT ANY WARRANTY; without even the implied warranty of
36 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37 * GNU General Public License for more details.
38 *
39 * Contact details for copyright holder:
40 *
41 * Myles Chippendale <mylesc At paneris.org>
42 * http://paneris.org/
43 * 29 Stanley Road, Oxford, OX4 1QY, UK
44 */
45
46 package org.melati.util;
47
48 import java.io.BufferedInputStream;
49 import java.io.IOException;
50 import java.io.InputStream;
51
52 /**
53 * Like a <code>BufferedInputStream</code> except it has a new function
54 * {@link #readToDelimiter} which will only read bytes upto the start
55 * of any occurrence of the delimiter in the InputStream.
56 *
57 * @see java.io.BufferedInputStream
58 */
59
60 public class DelimitedBufferedInputStream extends BufferedInputStream {
61
62 private int potentialMatch = -1;
63
64 /**
65 * Creates a <code>DelimitedBufferedInputStream</code>
66 * and saves its argument, the input stream
67 * <code>in</code>, for later use. An internal
68 * buffer array is created and stored in <code>buf</code>.
69 *
70 * @param in the underlying input stream.
71 **/
72 public DelimitedBufferedInputStream(InputStream in) {
73 super(in);
74 }
75
76 /**
77 * Creates a <code>DelimitedBufferedInputStream</code>
78 * with the specified buffer size,
79 * and saves its argument, the input stream
80 * <code>in</code>, for later use. An internal
81 * buffer array of length <code>size</code>
82 * is created and stored in <code>buf</code>.
83 *
84 * @param in the underlying input stream.
85 * @param size the buffer size.
86 **/
87 public DelimitedBufferedInputStream(InputStream in, int size) {
88 super(in, size);
89 }
90
91 /**
92 * Fills the buffer with more data, taking into account
93 * shuffling and other tricks for dealing with marks.
94 * Assumes that it is being called by a synchronized method.
95 * This method also assumes that all data has already been read in,
96 * hence pos > count.
97 * <p>
98 * This code is copy'n'pasted from BufferedInputString (it's private)
99 **/
100 private void fill() throws IOException {
101 if (markpos < 0)
102 pos = 0; /* no mark: throw away the buffer */
103 else if (pos >= buf.length) /* no room left in buffer */
104 if (markpos > 0) { /* can throw away early part of the buffer */
105 int sz = pos - markpos;
106 System.arraycopy(buf, markpos, buf, 0, sz);
107 pos = sz;
108 markpos = 0;
109 }
110 else if (buf.length >= marklimit) {
111 markpos = -1; /* buffer got too big, invalidate mark */
112 pos = 0; /* drop buffer contents */
113 }
114 else { /* grow buffer */
115 int nsz = pos * 2;
116 if (nsz > marklimit)
117 nsz = marklimit;
118 byte nbuf[] = new byte[nsz];
119 System.arraycopy(buf, 0, nbuf, 0, pos);
120 buf = nbuf;
121 }
122
123 count = pos;
124 int n = in.read(buf, pos, buf.length - pos);
125 if (n > 0)
126 count = n + pos;
127 }
128
129 /**
130 * Check to make sure that this stream has not been closed.
131 * <p>
132 * This code is copy'n'pasted from BufferedInputString (it's private)
133 **/
134 private void ensureOpen() throws IOException {
135 if (in == null)
136 throw new IOException("Stream closed");
137 }
138
139 /**
140 * Read characters into a portion of an array, reading from the underlying
141 * stream at most twice if necessary.
142 **/
143 private int readToDelimiter1(byte[] b, int off, int len, byte delim[])
144 throws IOException {
145 int avail = count - pos;
146 if (avail <= 0) {
147 fill();
148 avail = count - pos;
149 if (avail <= 0) return -1;
150 }
151 int cnt = (avail < len) ? avail : len;
152
153 // indexOf sets potentialMatch
154 int found = indexOf(buf, delim, pos, avail);
155 int max = cnt;
156 if (found != -1)
157 max = found - pos;
158 else if (potentialMatch != -1)
159 max = potentialMatch - pos;
160
161 cnt = (max < cnt) ? max : cnt;
162 System.arraycopy(buf, pos, b, off, cnt);
163 pos += cnt;
164
165 /* We want to shuffle the buffer so that it contains
166 * at least delim.length bytes after potentialMatch,
167 * if we have read that far into the buffer
168 */
169 if (found == -1 && potentialMatch != -1 && pos == potentialMatch) {
170
171 /* the number of bytes to shift the buffer down by */
172 int halfmark = (markpos == -1)
173 ? potentialMatch
174 : markpos;
175
176 /*
177 the number of bytes we need to read to get all our potential
178 delim into the buffer
179 */
180 int extras = potentialMatch + delim.length - count;
181
182 /* the number of bytes we want to end up in buf */
183 int nsz = potentialMatch + delim.length - halfmark;
184 if (nsz > buf.length) {
185 // we need to grow the buffer
186 byte nbuf[] = new byte[nsz];
187 System.arraycopy(buf, halfmark, nbuf, 0, count - halfmark);
188 buf = nbuf;
189 } else {
190 System.arraycopy(buf, halfmark, buf, 0, count - halfmark);
191 }
192
193 pos = pos - halfmark;
194 count = count - halfmark;
195 markpos = (markpos == -1) ? -1 : 0;
196 int n = in.read(buf, count, buf.length - count);
197 if (n > 0)
198 count += n;
199
200 /* If, after reading, we don't have enough bytes to match delim,
201 add the new bytes to our output */
202 if (n < extras) {
203 int unfilled = len - cnt;
204 if (unfilled > 0) {
205 avail = count - pos;
206 int cnt2 = (unfilled < avail) ? unfilled : avail;
207 System.arraycopy(buf, pos, b, off+cnt, cnt2);
208 cnt += cnt2;
209 pos += cnt2;
210 }
211 }
212 }
213 return cnt;
214 }
215
216 /**
217 * Reads bytes from this byte-input stream into the specified byte array,
218 * starting at the given offset, but we stop at the first occurrence of
219 * delim.
220 *
221 * <p> This method implements the general contract of the corresponding
222 * <code>{@link InputStream#read(byte[], int, int) read}</code> method of
223 * the <code>{@link InputStream}</code> class. As an additional
224 * convenience, it attempts to read as many bytes as possible by repeatedly
225 * invoking the <code>read</code> method of the underlying stream. This
226 * iterated <code>read</code> continues until one of the following
227 * conditions becomes true: <ul>
228 *
229 * <li> The specified number of bytes have been read,
230 *
231 * <li> The <code>read</code> method of the underlying stream returns
232 * <code>-1</code>, indicating end-of-file, or
233 *
234 * <li> The <code>available</code> method of the underlying stream
235 * returns zero, indicating that further input requests would block.
236 *
237 * <li> We encounter an occurrence of delim in the underlying stream
238 *
239 * </ul> If the first <code>read</code> on the underlying stream returns
240 * <code>-1</code> to indicate end-of-file then this method returns
241 * <code>-1</code>. Otherwise this method returns the number of bytes
242 * actually read.
243 *
244 * <p> Subclasses of this class are encouraged, but not required, to
245 * attempt to read as many bytes as possible in the same fashion.
246 *
247 * @param b destination buffer.
248 * @param off offset at which to start storing bytes.
249 * @param len maximum number of bytes to read.
250 * @param delim the delimiter which we should stop reading at.
251 * @return the number of bytes read, or <code>-1</code> if the end of
252 * the stream has been reached.
253 * @exception IOException if an I/O error occurs.
254 **/
255 public synchronized int readToDelimiter(byte b[], int off,
256 int len, byte delim[])
257 throws IOException {
258 ensureOpen();
259 if ((off < 0) || (off > b.length) || (len < 0) ||
260 ((off + len) > b.length) || ((off + len) < 0)) {
261 throw new IndexOutOfBoundsException();
262 } else if (len == 0) {
263 return 0;
264 }
265
266 int n = readToDelimiter1(b, off, len, delim);
267 if (n <= 0) return n;
268 while ((n < len) && (in.available() > 0)) {
269 int n1 = readToDelimiter1(b, off + n, len - n, delim);
270 if (n1 <= 0) break;
271 n += n1;
272 }
273 return n;
274 }
275
276 /**
277 * Calculates length.
278 * <p>
279 * Returns the index within data1 of the
280 * byte array data2. It starts looking in data1 at fromIndex and only
281 * considers len bytes of data1
282 *
283 * @param data1 array to look in.
284 * @param data2 array to look for.
285 * @param fromIndex where to start looking from in data1.
286 * @return the index of data2 within data1 or
287 * -1 if data2 does not occur within data1
288 */
289 public int indexOf(byte[] data1, byte[] data2, int fromIndex) {
290 if (fromIndex < 0)
291 fromIndex = 0;
292 return indexOf(data1, data2, fromIndex, data1.length - fromIndex);
293 }
294
295 /**
296 * Returns the index within data1 of the
297 * byte array data2. It starts looking in data1 at fromIndex and only
298 * considers len bytes of data1
299 *
300 * @param data1 array to look in.
301 * @param data2 array to look for.
302 * @param fromIndex where to start looking from in data1.
303 * @param len maximum number of bytes of data1 to look in.
304 * @return the index of data2 within data1 or
305 * -1 if data2 does not occur within data1
306 **/
307 public int indexOf(byte[] data1, byte[] data2, int fromIndex, int len) {
308 byte[] v1 = data1;
309 byte[] v2 = data2;
310 if (len <= 0)
311 return -1;
312 if (fromIndex >= data1.length) {
313 if (data1.length == 0 && fromIndex == 0 && data2.length == 0)
314 return 0;
315 return -1;
316 }
317 if (fromIndex < 0)
318 fromIndex = 0;
319 if (data2.length == 0)
320 return fromIndex;
321 int end = (fromIndex + len > data1.length) ?
322 data1.length : fromIndex + len;
323 int maxStart = end - data2.length;
324 byte first = v2[0];
325 int i = fromIndex;
326
327 startSearchForFirstByte:
328 while (true) {
329 potentialMatch = -1;
330 while (i < end && v1[i] != first)
331 i++;
332 if (i >= end)
333 return -1;
334 if (i > maxStart) { // we might find an initial segment of delim
335 potentialMatch = i;
336 }
337 int j = i + 1;
338 int last = (potentialMatch == -1) ? i + data2.length : data1.length;
339 int k = 1;
340 while (j < last) {
341 if (v1[j++] != v2[k++]) {
342 i++;
343 continue startSearchForFirstByte;
344 }
345 }
346 return (potentialMatch == -1) ? i : -1;
347 }
348 }
349
350
351 /**
352 * Used in tests.
353 * @return the buffer length
354 */
355 public int getBufferLength() {
356 return buf.length;
357 }
358 /**
359 * Used in tests.
360 * @return the potentialMatch
361 */
362 public int getPotentialMatch() {
363 return potentialMatch;
364 }
365
366 }