/** @see java.io.InputStream#read(byte[], int, int) */
  public int read(final byte b[], final int off, final int len) throws IOException {
    if (LOGGER.isLoggable(LOGGER_LEVEL)) {
      log(
          "InputBuffer %s read byte array of len: %s. Ready content: %s",
          this, len, inputContentBuffer);
    }

    if (closed) {
      throw new IOException();
    }

    if (len == 0) {
      return 0;
    }
    if (!inputContentBuffer.hasRemaining()) {
      if (fill(1) == -1) {
        return -1;
      }
    }

    int nlen = Math.min(inputContentBuffer.remaining(), len);
    inputContentBuffer.get(b, off, nlen);

    if (!checkMarkAfterRead(nlen)) {
      inputContentBuffer.shrink();
    }

    return nlen;
  }
  /**
   * Skips the specified number of bytes/characters.
   *
   * @see java.io.InputStream#skip(long)
   * @see java.io.Reader#skip(long)
   */
  public long skip(final long n) throws IOException {
    if (LOGGER.isLoggable(LOGGER_LEVEL)) {
      log("InputBuffer %s skip %s bytes. Ready content: %s", this, n, inputContentBuffer);
    }

    if (closed) {
      throw new IOException();
    }

    if (!processingChars) {
      if (n <= 0) {
        return 0L;
      }

      if (!inputContentBuffer.hasRemaining()) {
        if (fill((int) n) == -1) {
          return -1;
        }
      }
      if (inputContentBuffer.remaining() < n) {
        fill((int) n);
      }

      long nlen = Math.min(inputContentBuffer.remaining(), n);
      inputContentBuffer.position(inputContentBuffer.position() + (int) nlen);

      if (!checkMarkAfterRead(n)) {
        inputContentBuffer.shrink();
      }

      return nlen;
    } else {
      if (n < 0) { // required by java.io.Reader.skip()
        throw new IllegalArgumentException();
      }
      if (n == 0) {
        return 0L;
      }
      final CharBuffer skipBuffer = CharBuffer.allocate((int) n);
      if (fillChars((int) n, skipBuffer) == -1) {
        return 0;
      }
      return Math.min(skipBuffer.remaining(), n);
    }
  }