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 }