@Override
  public int read(byte[] b, int off, int len) throws IOException {

    // Sanity checks taken from InputStream
    if (b == null) {
      throw new NullPointerException();
    } else if (off < 0 || len < 0 || len > b.length - off) {
      throw new IndexOutOfBoundsException();
    } else if (len == 0) {
      return 0;
    }

    if (trailerBuffer == null) {
      fillTrailerBuffer();
    }

    byte[] inputBuffer = new byte[len];
    int numBytesRead = in.read(inputBuffer);

    if (numBytesRead == EOF_VALUE) {
      return numBytesRead;
    }

    if (trailerSize == 0) {
      System.arraycopy(inputBuffer, 0, b, off, numBytesRead);
      return numBytesRead;
    }

    if (numBytesRead <= trailerSize) {
      // Need some of the trailer
      System.arraycopy(trailerBuffer, 0, b, off, numBytesRead);

      // Now need to shift rear of trailer to front
      System.arraycopy(trailerBuffer, numBytesRead, trailerBuffer, 0,
          trailerSize - numBytesRead);

      // Now need to fill rear of trailer
      System.arraycopy(inputBuffer, 0, trailerBuffer, trailerSize
          - numBytesRead, numBytesRead);

    } else {
      // Need all the trailer
      System.arraycopy(trailerBuffer, 0, b, off, trailerSize);
      off += trailerSize;

      // Need the remaining data, except enough to fill the buffer
      System.arraycopy(inputBuffer, 0, b, off, numBytesRead - trailerSize);

      // Finally, fill the buffer from the remaining input
      System.arraycopy(inputBuffer, numBytesRead - trailerSize, trailerBuffer,
          0, trailerSize);
    }

    return numBytesRead;
  }
@ Override public int read ( byte [ ] b , int off , int len ) throws IOException { if ( b == null ) { throw new NullPointerException ( ) ; } else if ( off < 0 || len < 0 || len > b . length - off ) { throw new IndexOutOfBoundsException ( ) ; } else if ( len == 0 ) { return 0 ; } if ( trailerBuffer == null ) { fillTrailerBuffer ( ) ; } byte [ ] inputBuffer = new byte [ len ] ; int numBytesRead = in . read ( inputBuffer ) ; if ( numBytesRead == EOF_VALUE ) { return numBytesRead ; } if ( trailerSize == 0 ) { System . arraycopy ( inputBuffer , 0 , b , off , numBytesRead ) ; return numBytesRead ; } if ( numBytesRead <= trailerSize ) { System . arraycopy ( trailerBuffer , 0 , b , off , numBytesRead ) ; System . arraycopy ( trailerBuffer , numBytesRead , trailerBuffer , 0 , trailerSize - numBytesRead ) ; System . arraycopy ( inputBuffer , 0 , trailerBuffer , trailerSize - numBytesRead , numBytesRead ) ; } else { System . arraycopy ( trailerBuffer , 0 , b , off , trailerSize ) ; off += trailerSize ; System . arraycopy ( inputBuffer , 0 , b , off , numBytesRead - trailerSize ) ; System . arraycopy ( inputBuffer , numBytesRead - trailerSize , trailerBuffer , 0 , trailerSize ) ; } return numBytesRead ; }
Reads up to {@code len} non-trailer bytes from the underlying stream into
the array {@code b}, beginning at offset {@code off}.

@param b
the buffer into which the data is read.
@param off
the start offset in array {@code b} at which the data is written.
@param len
the maximum number of bytes to read.
@return the total number of bytes read into the buffer, or {@code -1} if
there is no more data because the end of the stream has been
reached.

@throws IOException
If the first byte cannot be read for any reason other than end of
file, or if the input stream has been closed, or if there are
insufficient bytes in the underlying stream to read the trailer,
or if some other I/O error occurs.