42f0d9d24c123be0fad89cce35f43bb9b5831855
1 package be
.nikiroo
.utils
.streams
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.util
.Arrays
;
8 * A simple {@link InputStream} that is buffered with a bytes array.
10 * It is mostly intended to be used as a base class to create new
11 * {@link InputStream}s with special operation modes, and to give some default
16 public class BufferedInputStream
extends InputStream
{
18 * The size of the internal buffer (can be different if you pass your own
21 * A second buffer of twice the size can sometimes be created as needed for
22 * the {@link BufferedInputStream#startsWith(byte[])} search operation.
24 static private final int BUFFER_SIZE
= 4096;
26 /** The current position in the buffer. */
28 /** The index of the last usable position of the buffer. */
30 /** The buffer itself. */
31 protected byte[] buffer
;
32 /** An End-Of-File (or {@link InputStream}, here) marker. */
33 protected boolean eof
;
35 private boolean closed
;
36 private InputStream in
;
37 private int openCounter
;
39 // special use, prefetched next buffer
40 private byte[] buffer2
;
43 private byte[] originalBuffer
;
45 private long bytesRead
;
48 * Create a new {@link BufferedInputStream} that wraps the given
49 * {@link InputStream}.
52 * the {@link InputStream} to wrap
54 public BufferedInputStream(InputStream in
) {
57 this.buffer
= new byte[BUFFER_SIZE
];
58 this.originalBuffer
= this.buffer
;
64 * Create a new {@link BufferedInputStream} that wraps the given bytes array
68 * the array to wrap, cannot be NULL
70 public BufferedInputStream(byte[] in
) {
71 this(in
, 0, in
.length
);
75 * Create a new {@link BufferedInputStream} that wraps the given bytes array
79 * the array to wrap, cannot be NULL
81 * the offset to start the reading at
83 * the number of bytes to take into account in the array,
84 * starting from the offset
86 * @throws NullPointerException
87 * if the array is NULL
88 * @throws IndexOutOfBoundsException
89 * if the offset and length do not correspond to the given array
91 public BufferedInputStream(byte[] in
, int offset
, int length
) {
93 throw new NullPointerException();
94 } else if (offset
< 0 || length
< 0 || length
> in
.length
- offset
) {
95 throw new IndexOutOfBoundsException();
101 this.originalBuffer
= this.buffer
;
107 * Return this very same {@link BufferedInputStream}, but keep a counter of
108 * how many streams were open this way. When calling
109 * {@link BufferedInputStream#close()}, decrease this counter if it is not
110 * already zero instead of actually closing the stream.
112 * You are now responsible for it — you <b>must</b> close it.
114 * This method allows you to use a wrapping stream around this one and still
115 * close the wrapping stream.
117 * @return the same stream, but you are now responsible for closing it
119 * @throws IOException
120 * in case of I/O error or if the stream is closed
122 public synchronized InputStream
open() throws IOException
{
129 * Check if the current content (what will be read next) starts with the
132 * Note: the search term size <b>must</b> be smaller or equal the internal
136 * the term to search for
138 * @return TRUE if the content that will be read starts with it
140 * @throws IOException
141 * in case of I/O error or if the size of the search term is
142 * greater than the internal buffer
144 public boolean startsWiths(String search
) throws IOException
{
145 return startsWith(search
.getBytes("UTF-8"));
149 * Check if the current content (what will be read next) starts with the
152 * Note: the search term size <b>must</b> be smaller or equal the internal
156 * the term to search for
158 * @return TRUE if the content that will be read starts with it
160 * @throws IOException
161 * in case of I/O error or if the size of the search term is
162 * greater than the internal buffer
164 public boolean startsWith(byte[] search
) throws IOException
{
165 if (search
.length
> originalBuffer
.length
) {
166 throw new IOException(
167 "This stream does not support searching for more than "
168 + buffer
.length
+ " bytes");
173 if (available() < search
.length
) {
177 if (available() >= search
.length
) {
179 return StreamUtils
.startsWith(search
, buffer
, start
, stop
);
182 if (buffer2
== null && buffer
.length
== originalBuffer
.length
) {
183 buffer2
= Arrays
.copyOf(buffer
, buffer
.length
* 2);
185 pos2
= buffer
.length
;
186 len2
= read(in
, buffer2
, pos2
, buffer
.length
);
191 // Note: here, len/len2 = INDEX of last good byte
195 return StreamUtils
.startsWith(search
, buffer2
, pos2
, len2
);
202 * The number of bytes read from the under-laying {@link InputStream}.
204 * @return the number of bytes
206 public long getBytesRead() {
211 * Check if this stream is totally spent (no more data to read or to
214 * @return TRUE if it is
216 * @throws IOException
217 * in case of I/O error
219 public boolean eof() throws IOException
{
225 return !hasMoreData();
229 public int read() throws IOException
{
237 return buffer
[start
++];
241 public int read(byte[] b
) throws IOException
{
242 return read(b
, 0, b
.length
);
246 public int read(byte[] b
, int boff
, int blen
) throws IOException
{
250 throw new NullPointerException();
251 } else if (boff
< 0 || blen
< 0 || blen
> b
.length
- boff
) {
252 throw new IndexOutOfBoundsException();
253 } else if (blen
== 0) {
258 while (hasMoreData() && done
< blen
) {
261 int now
= Math
.min(blen
- done
, stop
- start
);
263 System
.arraycopy(buffer
, start
, b
, boff
+ done
, now
);
270 return done
> 0 ? done
: -1;
274 public long skip(long n
) throws IOException
{
280 while (hasMoreData() && n
> 0) {
283 long inBuffer
= Math
.min(n
, available());
293 public int available() {
298 return Math
.max(0, stop
- start
);
302 * Closes this stream and releases any system resources associated with the
305 * Including the under-laying {@link InputStream}.
307 * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
308 * prior to this one, it will just decrease the internal count of how many
309 * open streams it held and do nothing else. The stream will actually be
310 * closed when you have called {@link BufferedInputStream#close()} once more
311 * than {@link BufferedInputStream#open()}.
313 * @exception IOException
314 * in case of I/O error
317 public synchronized void close() throws IOException
{
322 * Closes this stream and releases any system resources associated with the
325 * Including the under-laying {@link InputStream} if
326 * <tt>incudingSubStream</tt> is true.
328 * You can call this method multiple times, it will not cause an
329 * {@link IOException} for subsequent calls.
331 * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
332 * prior to this one, it will just decrease the internal count of how many
333 * open streams it held and do nothing else. The stream will actually be
334 * closed when you have called {@link BufferedInputStream#close()} once more
335 * than {@link BufferedInputStream#open()}.
337 * @param includingSubStream
338 * also close the under-laying stream
340 * @exception IOException
341 * in case of I/O error
343 public synchronized void close(boolean includingSubStream
)
346 if (openCounter
> 0) {
350 if (includingSubStream
&& in
!= null) {
358 * Check if we still have some data in the buffer and, if not, fetch some.
360 * @return TRUE if we fetched some data, FALSE if there are still some in
363 * @throws IOException
364 * in case of I/O error
366 protected boolean preRead() throws IOException
{
367 boolean hasRead
= false;
368 if (in
!= null && !eof
&& start
>= stop
) {
370 if (buffer2
!= null) {
379 buffer
= originalBuffer
;
381 stop
= read(in
, buffer
, 0, buffer
.length
);
398 * Read the under-laying stream into the local buffer.
401 * the under-laying {@link InputStream}
403 * the buffer we use in this {@link BufferedInputStream}
407 * the length in bytes
409 * @return the number of bytes read
411 * @throws IOException
412 * in case of I/O error
414 protected int read(InputStream in
, byte[] buffer
, int off
, int len
)
416 return in
.read(buffer
, off
, len
);
420 * We have more data available in the buffer <b>or</b> we can, maybe, fetch
423 * @return TRUE if it is the case, FALSE if not
425 protected boolean hasMoreData() {
430 return (start
< stop
) || !eof
;
434 * Check that the stream was not closed, and throw an {@link IOException} if
437 * @throws IOException
440 protected void checkClose() throws IOException
{
442 throw new IOException(
443 "This BufferedInputStream was closed, you cannot use it anymore.");