public static boolean readWithExtra(ByteBuff buf, FSDataInputStream dis, int necessaryLen,
    int extraLen) throws IOException {
    if (!isByteBufferReadable(dis)) {
      // If InputStream does not support the ByteBuffer read, just read to heap and copy bytes to
      // the destination ByteBuff.
      byte[] heapBuf = new byte[necessaryLen + extraLen];
      boolean ret = readWithExtraOnHeap(dis, heapBuf, 0, necessaryLen, extraLen);
      copyToByteBuff(heapBuf, 0, heapBuf.length, buf);
      return ret;
    }
    int directBytesRead = 0, heapBytesRead = 0;
    ByteBuffer[] buffers = buf.nioByteBuffers();
    int bytesRead = 0;
    int remain = necessaryLen + extraLen;
    int idx = 0;
    ByteBuffer cur = buffers[idx];
    try {
      while (bytesRead < necessaryLen) {
        while (!cur.hasRemaining()) {
          if (++idx >= buffers.length) {
            throw new IOException(
              "Not enough ByteBuffers to read the reminding " + remain + "bytes");
          }
          cur = buffers[idx];
        }
        cur.limit(cur.position() + Math.min(remain, cur.remaining()));
        int ret = dis.read(cur);
        if (ret < 0) {
          throw new IOException("Premature EOF from inputStream (read returned " + ret
            + ", was trying to read " + necessaryLen + " necessary bytes and " + extraLen
            + " extra bytes, successfully read " + bytesRead);
        }
        bytesRead += ret;
        remain -= ret;
        if (cur.isDirect()) {
          directBytesRead += ret;
        } else {
          heapBytesRead += ret;
        }
      }
    } finally {
      final Span span = Span.current();
      span.addEvent("BlockIOUtils.readWithExtra",
        getDirectAndHeapBytesReadAttributes(span, directBytesRead, heapBytesRead));
    }
    return (extraLen > 0) && (bytesRead == necessaryLen + extraLen);
  }
Read bytes into ByteBuffers directly, those buffers either contains the extraLen bytes or only
contains necessaryLen bytes, which depends on how much bytes do the last time we read.
@param buf          the destination {@link ByteBuff}.
@param dis          input stream to read.
@param necessaryLen bytes which we must read
@param extraLen     bytes which we may read
@return if the returned flag is true, then we've finished to read the extraLen into our
        ByteBuffers, otherwise we've not read the extraLen bytes yet.
@throws IOException if failed to read the necessary bytes.
hbase-common/src/main/java/org/apache/hadoop/hbase/io/util/BlockIOUtils.java