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