1 package be
.nikiroo
.utils
;
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
{
19 protected byte[] buffer
;
20 protected boolean eof
;
22 private boolean closed
;
23 private InputStream in
;
24 private int openCounter
;
26 // special use, prefetched next buffer
27 private byte[] buffer2
;
30 private byte[] originalBuffer
;
32 private long bytesRead
;
35 * Create a new {@link BufferedInputStream} that wraps the given
36 * {@link InputStream}.
39 * the {@link InputStream} to wrap
41 public BufferedInputStream(InputStream in
) {
44 this.buffer
= new byte[4096];
45 this.originalBuffer
= this.buffer
;
51 * Create a new {@link BufferedInputStream} that wraps the given bytes array
55 * the array to wrap, cannot be NULL
57 public BufferedInputStream(byte[] in
) {
58 this(in
, 0, in
.length
);
62 * Create a new {@link BufferedInputStream} that wraps the given bytes array
66 * the array to wrap, cannot be NULL
68 * the offset to start the reading at
70 * the number of bytes to take into account in the array,
71 * starting from the offset
73 * @throws NullPointerException
74 * if the array is NULL
75 * @throws IndexOutOfBoundsException
76 * if the offset and length do not correspond to the given array
78 public BufferedInputStream(byte[] in
, int offset
, int length
) {
80 throw new NullPointerException();
81 } else if (offset
< 0 || length
< 0 || length
> in
.length
- offset
) {
82 throw new IndexOutOfBoundsException();
88 this.originalBuffer
= this.buffer
;
94 * Return this very same {@link BufferedInputStream}, but keep a counter of
95 * how many streams were open this way. When calling
96 * {@link BufferedInputStream#close()}, decrease this counter if it is not
97 * already zero instead of actually closing the stream.
99 * You are now responsible for it — you <b>must</b> close it.
101 * This method allows you to use a wrapping stream around this one and still
102 * close the wrapping stream.
104 * @return the same stream, but you are now responsible for closing it
106 * @throws IOException
107 * in case of I/O error or if the stream is closed
109 public synchronized InputStream
open() throws IOException
{
115 // max is buffer.size !
116 public boolean startsWiths(String search
) throws IOException
{
117 return startsWith(search
.getBytes("UTF-8"));
120 // max is buffer.size !
121 public boolean startsWith(byte[] search
) throws IOException
{
122 if (search
.length
> originalBuffer
.length
) {
123 throw new IOException(
124 "This stream does not support searching for more than "
125 + buffer
.length
+ " bytes");
130 if (available() < search
.length
) {
134 if (available() >= search
.length
) {
136 return startsWith(search
, buffer
, pos
);
139 if (buffer2
== null && buffer
.length
== originalBuffer
.length
) {
140 buffer2
= Arrays
.copyOf(buffer
, buffer
.length
* 2);
142 pos2
= buffer
.length
;
143 len2
= in
.read(buffer2
, pos2
, buffer
.length
);
148 // Note: here, len/len2 = INDEX of last good byte
152 if (available() + (len2
- pos2
) >= search
.length
) {
153 return startsWith(search
, buffer2
, pos2
);
161 * The number of bytes read from the under-laying {@link InputStream}.
163 * @return the number of bytes
165 public long getBytesRead() {
170 * Check if this stream is totally spent (no more data to read or to
173 * Note: an empty stream that is still not started will return FALSE, as we
174 * don't know yet if it is empty.
176 * @return TRUE if it is
178 public boolean eof() {
179 return closed
|| (len
< 0 && !hasMoreData());
183 public int read() throws IOException
{
191 return buffer
[pos
++];
195 public int read(byte[] b
) throws IOException
{
196 return read(b
, 0, b
.length
);
200 public int read(byte[] b
, int boff
, int blen
) throws IOException
{
204 throw new NullPointerException();
205 } else if (boff
< 0 || blen
< 0 || blen
> b
.length
- boff
) {
206 throw new IndexOutOfBoundsException();
207 } else if (blen
== 0) {
212 while (hasMoreData() && done
< blen
) {
215 int now
= Math
.min(blen
, len
) - pos
;
217 System
.arraycopy(buffer
, pos
, b
, boff
, now
);
224 return done
> 0 ? done
: -1;
228 public long skip(long n
) throws IOException
{
234 while (hasMoreData() && n
> 0) {
237 long inBuffer
= Math
.min(n
, available());
247 public int available() {
252 return Math
.max(0, len
- pos
);
256 * Closes this stream and releases any system resources associated with the
259 * Including the under-laying {@link InputStream}.
261 * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
262 * prior to this one, it will just decrease the internal count of how many
263 * open streams it held and do nothing else. The stream will actually be
264 * closed when you have called {@link BufferedInputStream#close()} once more
265 * than {@link BufferedInputStream#open()}.
267 * @exception IOException
268 * in case of I/O error
271 public synchronized void close() throws IOException
{
276 * Closes this stream and releases any system resources associated with the
279 * Including the under-laying {@link InputStream} if
280 * <tt>incudingSubStream</tt> is true.
282 * You can call this method multiple times, it will not cause an
283 * {@link IOException} for subsequent calls.
285 * <b>Note:</b> if you called the {@link BufferedInputStream#open()} method
286 * prior to this one, it will just decrease the internal count of how many
287 * open streams it held and do nothing else. The stream will actually be
288 * closed when you have called {@link BufferedInputStream#close()} once more
289 * than {@link BufferedInputStream#open()}.
291 * @exception IOException
292 * in case of I/O error
294 public synchronized void close(boolean includingSubStream
)
297 if (openCounter
> 0) {
301 if (includingSubStream
&& in
!= null) {
309 * Check if we still have some data in the buffer and, if not, fetch some.
311 * @return TRUE if we fetched some data, FALSE if there are still some in
314 * @throws IOException
315 * in case of I/O error
317 protected boolean preRead() throws IOException
{
318 boolean hasRead
= false;
319 if (!eof
&& in
!= null && pos
>= len
) {
321 if (buffer2
!= null) {
330 buffer
= originalBuffer
;
332 len
= read(in
, buffer
);
349 * Read the under-laying stream into the local buffer.
352 * the under-laying {@link InputStream}
354 * the buffer we use in this {@link BufferedInputStream}
356 * @return the number of bytes read
358 * @throws IOException
359 * in case of I/O error
361 protected int read(InputStream in
, byte[] buffer
) throws IOException
{
362 return in
.read(buffer
);
366 * We have more data available in the buffer or we can fetch more.
368 * @return TRUE if it is the case, FALSE if not
370 protected boolean hasMoreData() {
371 return !closed
&& !(eof
&& pos
>= len
);
375 * Check that the stream was not closed, and throw an {@link IOException} if
378 * @throws IOException
381 protected void checkClose() throws IOException
{
383 throw new IOException(
384 "This NextableInputStream was closed, you cannot use it anymore.");
388 // buffer must be > search
389 static protected boolean startsWith(byte[] search
, byte[] buffer
, int offset
) {
391 for (int i
= 0; i
< search
.length
; i
++) {
392 if (search
[i
] != buffer
[offset
+ i
]) {