Add 'src/be/nikiroo/utils/' from commit '46add0670fdee4bd936a13fe2448c5e20a7ffd0a'
[fanfix.git] / src / be / nikiroo / utils / streams / BufferedInputStream.java
index be4b24d2ec9b477b3556df34fd607923c8c30bf5..683fa55865aff5ee4b6d442638721372c99e0e02 100644 (file)
@@ -4,6 +4,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 
+import be.nikiroo.utils.StringUtils;
+
 /**
  * A simple {@link InputStream} that is buffered with a bytes array.
  * <p>
@@ -14,13 +16,22 @@ import java.util.Arrays;
  * @author niki
  */
 public class BufferedInputStream extends InputStream {
+       /**
+        * The size of the internal buffer (can be different if you pass your own
+        * buffer, of course).
+        * <p>
+        * A second buffer of twice the size can sometimes be created as needed for
+        * the {@link BufferedInputStream#startsWith(byte[])} search operation.
+        */
+       static private final int BUFFER_SIZE = 4096;
+
        /** The current position in the buffer. */
        protected int start;
        /** The index of the last usable position of the buffer. */
        protected int stop;
        /** The buffer itself. */
        protected byte[] buffer;
-       /** An End-Of-File (or buffer, here) marker. */
+       /** An End-Of-File (or {@link InputStream}, here) marker. */
        protected boolean eof;
 
        private boolean closed;
@@ -45,7 +56,7 @@ public class BufferedInputStream extends InputStream {
        public BufferedInputStream(InputStream in) {
                this.in = in;
 
-               this.buffer = new byte[4096];
+               this.buffer = new byte[BUFFER_SIZE];
                this.originalBuffer = this.buffer;
                this.start = 0;
                this.stop = 0;
@@ -94,6 +105,15 @@ public class BufferedInputStream extends InputStream {
                this.stop = length;
        }
 
+       /**
+        * The internal buffer size (can be useful to know for search methods).
+        * 
+        * @return the size of the internal buffer, in bytes.
+        */
+       public int getInternalBufferSize() {
+               return originalBuffer.length;
+       }
+
        /**
         * Return this very same {@link BufferedInputStream}, but keep a counter of
         * how many streams were open this way. When calling
@@ -116,6 +136,50 @@ public class BufferedInputStream extends InputStream {
                return this;
        }
 
+       /**
+        * Check if the current content (until eof) is equal to the given search
+        * term.
+        * <p>
+        * Note: the search term size <b>must</b> be smaller or equal the internal
+        * buffer size.
+        * 
+        * @param search
+        *            the term to search for
+        * 
+        * @return TRUE if the content that will be read starts with it
+        * 
+        * @throws IOException
+        *             in case of I/O error or if the size of the search term is
+        *             greater than the internal buffer
+        */
+       public boolean is(String search) throws IOException {
+               return is(StringUtils.getBytes(search));
+       }
+
+       /**
+        * Check if the current content (until eof) is equal to the given search
+        * term.
+        * <p>
+        * Note: the search term size <b>must</b> be smaller or equal the internal
+        * buffer size.
+        * 
+        * @param search
+        *            the term to search for
+        * 
+        * @return TRUE if the content that will be read starts with it
+        * 
+        * @throws IOException
+        *             in case of I/O error or if the size of the search term is
+        *             greater than the internal buffer
+        */
+       public boolean is(byte[] search) throws IOException {
+               if (startsWith(search)) {
+                       return (stop - start) == search.length;
+               }
+
+               return false;
+       }
+
        /**
         * Check if the current content (what will be read next) starts with the
         * given search term.
@@ -132,14 +196,17 @@ public class BufferedInputStream extends InputStream {
         *             in case of I/O error or if the size of the search term is
         *             greater than the internal buffer
         */
-       public boolean startsWiths(String search) throws IOException {
-               return startsWith(search.getBytes("UTF-8"));
+       public boolean startsWith(String search) throws IOException {
+               return startsWith(StringUtils.getBytes(search));
        }
 
        /**
         * Check if the current content (what will be read next) starts with the
         * given search term.
         * <p>
+        * An empty string will always return true (unless the stream is closed,
+        * which would throw an {@link IOException}).
+        * <p>
         * Note: the search term size <b>must</b> be smaller or equal the internal
         * buffer size.
         * 
@@ -168,13 +235,13 @@ public class BufferedInputStream extends InputStream {
                if (available() >= search.length) {
                        // Easy path
                        return StreamUtils.startsWith(search, buffer, start, stop);
-               } else if (!eof) {
+               } else if (in != null && !eof) {
                        // Harder path
                        if (buffer2 == null && buffer.length == originalBuffer.length) {
                                buffer2 = Arrays.copyOf(buffer, buffer.length * 2);
 
                                pos2 = buffer.length;
-                               len2 = in.read(buffer2, pos2, buffer.length);
+                               len2 = read(in, buffer2, pos2, buffer.length);
                                if (len2 > 0) {
                                        bytesRead += len2;
                                }
@@ -199,13 +266,39 @@ public class BufferedInputStream extends InputStream {
        }
 
        /**
-        * Check if this stream is totally spent (no more data to read or to
+        * Check if this stream is spent (no more data to read or to
         * process).
         * 
         * @return TRUE if it is
+        * 
+        * @throws IOException
+        *             in case of I/O error
         */
-       public boolean eof() {
-               return closed || (stop < 0 && !hasMoreData());
+       public boolean eof() throws IOException {
+               if (closed) {
+                       return true;
+               }
+
+               preRead();
+               return !hasMoreData();
+       }
+
+       /**
+        * Read the whole {@link InputStream} until the end and return the number of
+        * bytes read.
+        * 
+        * @return the number of bytes read
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       public long end() throws IOException {
+               long skipped = 0;
+               while (hasMoreData()) {
+                       skipped += skip(buffer.length);
+               }
+
+               return skipped;
        }
 
        @Override
@@ -241,7 +334,7 @@ public class BufferedInputStream extends InputStream {
                while (hasMoreData() && done < blen) {
                        preRead();
                        if (hasMoreData()) {
-                               int now = Math.min(blen, stop) - start;
+                               int now = Math.min(blen - done, stop - start);
                                if (now > 0) {
                                        System.arraycopy(buffer, start, b, boff + done, now);
                                        start += now;
@@ -348,7 +441,7 @@ public class BufferedInputStream extends InputStream {
         */
        protected boolean preRead() throws IOException {
                boolean hasRead = false;
-               if (!eof && in != null && start >= stop) {
+               if (in != null && !eof && start >= stop) {
                        start = 0;
                        if (buffer2 != null) {
                                buffer = buffer2;
@@ -361,7 +454,7 @@ public class BufferedInputStream extends InputStream {
                        } else {
                                buffer = originalBuffer;
 
-                               stop = read(in, buffer);
+                               stop = read(in, buffer, 0, buffer.length);
                                if (stop > 0) {
                                        bytesRead += stop;
                                }
@@ -384,23 +477,33 @@ public class BufferedInputStream extends InputStream {
         *            the under-laying {@link InputStream}
         * @param buffer
         *            the buffer we use in this {@link BufferedInputStream}
+        * @param off
+        *            the offset
+        * @param len
+        *            the length in bytes
         * 
         * @return the number of bytes read
         * 
         * @throws IOException
         *             in case of I/O error
         */
-       protected int read(InputStream in, byte[] buffer) throws IOException {
-               return in.read(buffer);
+       protected int read(InputStream in, byte[] buffer, int off, int len)
+                       throws IOException {
+               return in.read(buffer, off, len);
        }
 
        /**
-        * We have more data available in the buffer or we can fetch more.
+        * We have more data available in the buffer <b>or</b> we can, maybe, fetch
+        * more.
         * 
         * @return TRUE if it is the case, FALSE if not
         */
        protected boolean hasMoreData() {
-               return !closed && !(eof && start >= stop);
+               if (closed) {
+                       return false;
+               }
+
+               return (start < stop) || !eof;
        }
 
        /**