io/src/main/java/com/stanfy/enroscar/io/PoolableBufferedInputStream.java
@Override
  public synchronized int read(final byte[] buffer, final int start, final int length) throws IOException {
    int offset = start;
    // Use local ref since buf may be invalidated by an unsynchronized
    // close()
    byte[] localBuf = buf;
    if (localBuf == null) {
      throw new IOException("Stream is closed");
    }
    // avoid int overflow
    // BEGIN android-changed
    // Exception priorities (in case of multiple errors) differ from
    // RI, but are spec-compliant.
    // made implicit null check explicit, used (offset | length) < 0
    // instead of (offset < 0) || (length < 0) to safe one operation
    if (buffer == null) {
      throw new NullPointerException("Buffer is null");
    }
    if ((offset | length) < 0 || offset > buffer.length - length) {
      throw new IndexOutOfBoundsException("Bad offsets");
    }
    // END android-changed
    if (length == 0) {
      return 0;
    }
    final InputStream localIn = in;
    if (localIn == null) {
      throw new IOException("Stream is closed");
    }

    int required;
    if (pos < count) {
      /* There are bytes available in the buffer. */
      final int copylength = count - pos >= length ? length : count - pos;
      System.arraycopy(localBuf, pos, buffer, offset, copylength);
      pos += copylength;
      if (copylength == length || localIn.available() == 0) {
        return copylength;
      }
      offset += copylength;
      required = length - copylength;
    } else {
      required = length;
    }

    while (true) {
      int read;
      /*
       * If we're not marked and the required size is greater than the
       * buffer, simply read the bytes directly bypassing the buffer.
       */
      if (markpos == -1 && required >= localBuf.length) {
        read = localIn.read(buffer, offset, required);
        if (read == -1) {
          return required == length ? -1 : length - required;
        }
      } else {
        if (fillbuf(localIn, localBuf) == -1) {
          return required == length ? -1 : length - required;
        }
        // localBuf may have been invalidated by fillbuf
        if (localBuf != buf) {
          localBuf = buf;
          if (localBuf == null) {
            throw new IOException("Stream is closed");
          }
        }

        read = count - pos >= required ? required : count - pos;
        System.arraycopy(localBuf, pos, buffer, offset, read);
        pos += read;
      }
      required -= read;
      if (required == 0) {
        return length;
      }
      if (localIn.available() == 0) {
        return length - required;
      }
      offset += read;
    }
  }
@Override
  public synchronized int read(final byte[] buffer, final int start, final int length) throws IOException {
    int offset = start;
    // Use local ref since buf may be invalidated by an unsynchronized
    // close()
    byte[] localBuf = buf;
    if (localBuf == null) {
      throw new IOException("Stream is closed");
    }
    // avoid int overflow
    // BEGIN android-changed
    // Exception priorities (in case of multiple errors) differ from
    // RI, but are spec-compliant.
    // made implicit null check explicit, used (offset | length) < 0
    // instead of (offset < 0) || (length < 0) to safe one operation
    if (buffer == null) {
      throw new NullPointerException("Buffer is null");
    }
    if ((offset | length) < 0 || offset > buffer.length - length) {
      throw new IndexOutOfBoundsException("Bad offsets");
    }
    // END android-changed
    if (length == 0) {
      return 0;
    }
    final InputStream localIn = in;
    if (localIn == null) {
      throw new IOException("Stream is closed");
    }

    int required;
    if (pos < count) {
      /* There are bytes available in the buffer. */
      final int copylength = count - pos >= length ? length : count - pos;
      System.arraycopy(localBuf, pos, buffer, offset, copylength);
      pos += copylength;
      if (copylength == length || localIn.available() == 0) {
        return copylength;
      }
      offset += copylength;
      required = length - copylength;
    } else {
      required = length;
    }

    while (true) {
      int read;
      /*
       * If we're not marked and the required size is greater than the
       * buffer, simply read the bytes directly bypassing the buffer.
       */
      if (markpos == -1 && required >= localBuf.length) {
        read = localIn.read(buffer, offset, required);
        if (read == -1) {
          return required == length ? -1 : length - required;
        }
      } else {
        if (fillbuf(localIn, localBuf) == -1) {
          return required == length ? -1 : length - required;
        }
        // localBuf may have been invalidated by fillbuf
        if (localBuf != buf) {
          localBuf = buf;
          if (localBuf == null) {
            throw new IOException("Stream is closed");
          }
        }

        read = count - pos >= required ? required : count - pos;
        System.arraycopy(localBuf, pos, buffer, offset, read);
        pos += read;
      }
      required -= read;
      if (required == 0) {
        return length;
      }
      if (localIn.available() == 0) {
        return length - required;
      }
      offset += read;
    }
  }
@ Override public synchronized int read ( final byte [ ] buffer , final int start , final int length ) throws IOException { int offset = start ; // Use local ref since buf may be invalidated by an unsynchronized
 // close()
 byte [ ] localBuf = buf ; if ( localBuf == null ) { throw new IOException ( "Stream is closed" ) ; } // avoid int overflow
 // BEGIN android-changed
 // Exception priorities (in case of multiple errors) differ from
 // RI, but are spec-compliant.
 // made implicit null check explicit, used (offset | length) < 0
 // instead of (offset < 0) || (length < 0) to safe one operation
 if ( buffer == null ) { throw new NullPointerException ( "Buffer is null" ) ; } if ( ( offset | length ) < 0 || offset > buffer . length - length ) { throw new IndexOutOfBoundsException ( "Bad offsets" ) ; } // END android-changed
 if ( length == 0 ) { return 0 ; } final InputStream localIn = in ; if ( localIn == null ) { throw new IOException ( "Stream is closed" ) ; } int required ; if ( pos < count ) { /* There are bytes available in the buffer. */ final int copylength = count - pos >= length ? length : count - pos ; System . arraycopy ( localBuf , pos , buffer , offset , copylength ) ; pos += copylength ; if ( copylength == length || localIn . available ( ) == 0 ) { return copylength ; } offset += copylength ; required = length - copylength ; } else { required = length ; } while ( true ) { int read ; /*
       * If we're not marked and the required size is greater than the
       * buffer, simply read the bytes directly bypassing the buffer.
       */ if ( markpos == - 1 && required >= localBuf . length ) { read = localIn . read ( buffer , offset , required ) ; if ( read == - 1 ) { return required == length ? - 1 : length - required ; } } else { if ( fillbuf ( localIn , localBuf ) == - 1 ) { return required == length ? - 1 : length - required ; } // localBuf may have been invalidated by fillbuf
 if ( localBuf != buf ) { localBuf = buf ; if ( localBuf == null ) { throw new IOException ( "Stream is closed" ) ; } } read = count - pos >= required ? required : count - pos ; System . arraycopy ( localBuf , pos , buffer , offset , read ) ; pos += read ; } required -= read ; if ( required == 0 ) { return length ; } if ( localIn . available ( ) == 0 ) { return length - required ; } offset += read ; } }
Reads at most {@code length} bytes from this stream and stores them in
byte array {@code buffer} starting at offset {@code offset}. Returns the
number of bytes actually read or -1 if no bytes were read and the end of
the stream was encountered. If all the buffered bytes have been used, a
mark has not been set and the requested number of bytes is larger than
the receiver's buffer size, this implementation bypasses the buffer and
simply places the results directly into {@code buffer}.

@param buffer
the byte array in which to store the bytes read.
@param start
the initial position in {@code buffer} to store the bytes read
from this stream.
@param length
the maximum number of bytes to store in {@code buffer}.
@return the number of bytes actually read or -1 if end of stream.
@throws IndexOutOfBoundsException
if {@code offset < 0} or {@code length < 0}, or if
{@code offset + length} is greater than the size of
{@code buffer}.
@throws IOException
if the stream is already closed or another IOException
occurs.
Reads at most { @code length } bytes from this stream and stores them in byte array { @code buffer } starting at offset { @code offset } . Returns the number of bytes actually read or - 1 if no bytes were read and the end of the stream was encountered . If all the buffered bytes have been used a mark has not been set and the requested number of bytes is larger than the receiver s buffer size this implementation bypasses the buffer and simply places the results directly into { @code buffer } .