1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * The contents of this file are subject to the terms of the Liferay Enterprise
5    * Subscription License ("License"). You may not use this file except in
6    * compliance with the License. You can obtain a copy of the License by
7    * contacting Liferay, Inc. See the License for the specific language governing
8    * permissions and limitations under the License, including but not limited to
9    * distribution rights of the Software.
10   *
11   *
12   *
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(
111                     buffer, index, byteArray, offset + read, leftSize);
112 
113                 index += leftSize;
114 
115                 return length;
116             }
117 
118             if (available <= 0) {
119 
120                 // No more data in buffer, continue reading
121 
122                 readUnderlyingInputStream();
123 
124                 available = firstInvalidIndex - index;
125 
126                 if (available <= 0) {
127 
128                     // Cannot read any more, stop reading
129 
130                     if (read == 0) {
131                         return -1;
132                     }
133                     else {
134                         return read;
135                     }
136                 }
137             }
138             else {
139 
140                 // Copy all in-memory data, continue reading
141 
142                 System.arraycopy(
143                     buffer, index, byteArray, offset + read, available);
144 
145                 index += available;
146                 read += available;
147             }
148         }
149     }
150     public void reset() throws IOException {
151         if (inputStream == null) {
152             throw new IOException("Input stream is null");
153         }
154 
155         if (markIndex < 0) {
156             throw new IOException("Resetting to invalid mark");
157         }
158 
159         index = markIndex;
160     }
161 
162     public long skip(long skip) throws IOException {
163         if (inputStream == null) {
164             throw new IOException("Input stream is null");
165         }
166 
167         if (skip <= 0) {
168             return 0;
169         }
170         long available = firstInvalidIndex - index;
171 
172         if (available > 0) {
173 
174             // Skip the data in buffer
175 
176             if (available < skip) {
177                 skip = available;
178             }
179         }
180         else {
181 
182             // Skip the underlying input stream
183 
184             if (markIndex < 0) {
185 
186                 // No mark required, skip
187 
188                 skip = inputStream.skip(skip);
189             }
190             else {
191 
192                 // Mark required, save the skipped data
193 
194                 readUnderlyingInputStream();
195 
196                 available = firstInvalidIndex - index;
197 
198                 if (available > 0) {
199 
200                     // Skip the data in buffer
201 
202                     if (available < skip) {
203                         skip = available;
204                     }
205                 }
206             }
207         }
208 
209         index += skip;
210 
211         return skip;
212     }
213 
214     protected void readUnderlyingInputStream() throws IOException {
215         if (markIndex < 0) {
216 
217             // No mark required, fill the buffer
218 
219             index = firstInvalidIndex = 0;
220 
221             int number = inputStream.read(buffer);
222 
223             if (number > 0) {
224                 firstInvalidIndex = number;
225             }
226 
227             return;
228         }
229 
230         // Mark required
231 
232         if (index >= buffer.length) {
233 
234             // Buffer is full, clean up or grow
235 
236             if ((firstInvalidIndex - markIndex) > markLimit) {
237 
238                 // Passed mark limit, get rid of all cache data
239 
240                 markIndex = -1;
241                 index = 0;
242             }
243             else if (markIndex > _MAX_MARK_WASTE_SIZE) {
244 
245                 // There are more than _MAX_MARK_WASTE_SIZE free space at the
246                 // beginning of buffer, clean up by shuffling the buffer
247 
248                 int realDataSize = index - markIndex;
249 
250                 System.arraycopy(
251                     buffer, markIndex, buffer, 0, realDataSize);
252 
253                 markIndex = 0;
254                 index = realDataSize;
255             }
256             else {
257 
258                 // Grow the buffer because we cannot get rid of cache data and
259                 // it is inefficient to shuffle the buffer
260 
261                 int newBufferSize = index << 1;
262 
263                 if ((newBufferSize - _MAX_MARK_WASTE_SIZE) > markLimit) {
264 
265                     // Make thew new buffer size larger than the mark limit
266 
267                     newBufferSize = markLimit + _MAX_MARK_WASTE_SIZE;
268                 }
269 
270                 byte[] newBuffer = new byte[newBufferSize];
271 
272                 System.arraycopy(buffer, 0, newBuffer, 0, index);
273 
274                 buffer = newBuffer;
275             }
276         }
277 
278         // Read underlying input stream since the buffer has more space
279 
280         firstInvalidIndex = index;
281 
282         int number = inputStream.read(buffer, index, buffer.length - index);
283 
284         if (number > 0) {
285             firstInvalidIndex += number;
286         }
287     }
288 
289     protected byte[] buffer;
290     protected int firstInvalidIndex;
291     protected int index;
292     protected int markIndex = -1;
293     protected int markLimit;
294 
295     private static int _DEFAULT_BUFFER_SIZE = 8192;
296 
297     private static int _MAX_MARK_WASTE_SIZE = 4096;
298 
299 }