1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    *
5    *
6    *
7    * The contents of this file are subject to the terms of the Liferay Enterprise
8    * Subscription License ("License"). You may not use this file except in
9    * compliance with the License. You can obtain a copy of the License by
10   * contacting Liferay, Inc. See the License for the specific language governing
11   * permissions and limitations under the License, including but not limited to
12   * distribution rights of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.kernel.io.unsync;
24  
25  import java.io.IOException;
26  import java.io.InputStream;
27  
28  /**
29   * <a href="UnsyncBufferedInputStream.java.html"><b><i>View Source</i></b></a>
30   *
31   * <p>
32   * See http://support.liferay.com/browse/LPS-6648.
33   * </p>
34   *
35   * @author Shuyang Zhou
36   */
37  public class UnsyncBufferedInputStream extends UnsyncFilterInputStream {
38  
39      public UnsyncBufferedInputStream(InputStream inputStream) {
40          this(inputStream, _DEFAULT_BUFFER_SIZE);
41      }
42  
43      public UnsyncBufferedInputStream(InputStream inputStream, int size) {
44          super(inputStream);
45  
46          buffer = new byte[size];
47      }
48  
49      public int available() throws IOException {
50          if (inputStream == null) {
51              throw new IOException("Input stream is null");
52          }
53  
54          return inputStream.available() + (firstInvalidIndex - index);
55      }
56  
57      public void close() throws IOException {
58          if (inputStream != null) {
59              inputStream.close();
60  
61              inputStream = null;
62          }
63  
64          buffer = null;
65      }
66  
67      public void mark(int readLimit) {
68          markLimit = readLimit;
69          markIndex = index;
70      }
71  
72      public boolean markSupported() {
73          return true;
74      }
75  
76      public int read() throws IOException {
77          if (inputStream == null) {
78              throw new IOException("Input stream is null");
79          }
80  
81          if (index >= firstInvalidIndex) {
82              readUnderlyingInputStream();
83  
84              if (index >= firstInvalidIndex) {
85                  return -1;
86              }
87          }
88  
89          return buffer[index++] & 0xff;
90      }
91  
92      public int read(byte[] byteArray) throws IOException {
93          return read(byteArray, 0, byteArray.length);
94      }
95  
96      public int read(byte[] byteArray, int offset, int length)
97          throws IOException {
98  
99          if (inputStream == null) {
100             throw new IOException("Input stream is null");
101         }
102 
103         if (length <= 0) {
104             return 0;
105         }
106 
107         int read = 0;
108 
109         while (true) {
110             int available = firstInvalidIndex - index;
111 
112             if ((available + read) >= length) {
113 
114                 // Enough data, stop reading
115 
116                 int leftSize = length - read;
117 
118                 System.arraycopy(buffer, index, byteArray, read, leftSize);
119 
120                 index += leftSize;
121 
122                 return length;
123             }
124 
125             if (available <= 0) {
126 
127                 // No more data in buffer, continue reading
128 
129                 readUnderlyingInputStream();
130 
131                 available = firstInvalidIndex - index;
132 
133                 if (available <= 0) {
134 
135                     // Cannot read any more, stop reading
136 
137                     if (read == 0) {
138                         return -1;
139                     }
140                     else {
141                         return read;
142                     }
143                 }
144             }
145             else {
146 
147                 // Copy all in-memory data, continue reading
148 
149                 System.arraycopy(
150                     buffer, index, byteArray, read, available);
151 
152                 index += available;
153                 read += available;
154             }
155         }
156     }
157     public void reset() throws IOException {
158         if (inputStream == null) {
159             throw new IOException("Input stream is null");
160         }
161 
162         if (markIndex < 0) {
163             throw new IOException("Resetting to invalid mark");
164         }
165 
166         index = markIndex;
167     }
168 
169     public long skip(long skip) throws IOException {
170         if (inputStream == null) {
171             throw new IOException("Input stream is null");
172         }
173 
174         if (skip <= 0) {
175             return 0;
176         }
177         long available = firstInvalidIndex - index;
178 
179         if (available > 0) {
180 
181             // Skip the data in buffer
182 
183             if (available < skip) {
184                 skip = available;
185             }
186         }
187         else {
188 
189             // Skip the underlying input stream
190 
191             if (markIndex < 0) {
192 
193                 // No mark required, skip
194 
195                 skip = inputStream.skip(skip);
196             }
197             else {
198 
199                 // Mark required, save the skipped data
200 
201                 readUnderlyingInputStream();
202 
203                 available = firstInvalidIndex - index;
204 
205                 if (available > 0) {
206 
207                     // Skip the data in buffer
208 
209                     if (available < skip) {
210                         skip = available;
211                     }
212                 }
213             }
214         }
215 
216         index += skip;
217 
218         return skip;
219     }
220 
221     protected void readUnderlyingInputStream() throws IOException {
222         if (markIndex < 0) {
223 
224             // No mark required, fill the buffer
225 
226             index = firstInvalidIndex = 0;
227 
228             int number = inputStream.read(buffer);
229 
230             if (number > 0) {
231                 firstInvalidIndex = number;
232             }
233 
234             return;
235         }
236 
237         // Mark required
238 
239         if (index >= buffer.length) {
240 
241             // Buffer is full, clean up or grow
242 
243             if ((firstInvalidIndex - markIndex) > markLimit) {
244 
245                 // Passed mark limit, get rid of all cache data
246 
247                 markIndex = -1;
248                 index = 0;
249             }
250             else if (markIndex > _MAX_MARK_WASTE_SIZE) {
251 
252                 // There are more than _MAX_MARK_WASTE_SIZE free space at the
253                 // beginning of buffer, clean up by shuffling the buffer
254 
255                 int realDataSize = index - markIndex;
256 
257                 System.arraycopy(
258                     buffer, markIndex, buffer, 0, realDataSize);
259 
260                 markIndex = 0;
261                 index = realDataSize;
262             }
263             else {
264 
265                 // Grow the buffer because we cannot get rid of cache data and
266                 // it is inefficient to shuffle the buffer
267 
268                 int newBufferSize = index << 1;
269 
270                 if ((newBufferSize - _MAX_MARK_WASTE_SIZE) > markLimit) {
271 
272                     // Make thew new buffer size larger than the mark limit
273 
274                     newBufferSize = markLimit + _MAX_MARK_WASTE_SIZE;
275                 }
276 
277                 byte[] newBuffer = new byte[newBufferSize];
278 
279                 System.arraycopy(buffer, 0, newBuffer, 0, index);
280 
281                 buffer = newBuffer;
282             }
283         }
284 
285         // Read underlying input stream since the buffer has more space
286 
287         firstInvalidIndex = index;
288 
289         int number = inputStream.read(buffer, index, buffer.length - index);
290 
291         if (number > 0) {
292             firstInvalidIndex += number;
293         }
294     }
295 
296     protected byte[] buffer;
297     protected int firstInvalidIndex;
298     protected int index;
299     protected int markIndex = -1;
300     protected int markLimit;
301 
302     private static int _DEFAULT_BUFFER_SIZE = 8192;
303 
304     private static int _MAX_MARK_WASTE_SIZE = 4096;
305 
306 }