@Override
  public ByteBuffer read(long offset, long length) throws IOException {
    if (length == 0 || offset >= mFileSize) {
      return EMPTY_BYTE_BUFFER;
    }

    // cap length to the remaining of block, as the caller may pass in a longer length than what
    // is left in the block, but expect as many bytes as there is
    length = Math.min(length, mFileSize - offset);
    ensureReadable(offset, length);

    // must not use pooled buffer, see interface implementation note
    ByteBuffer buffer = ByteBuffer.allocateDirect((int) length);
    ByteBuf buf = Unpooled.wrappedBuffer(buffer);
    // Unpooled.wrappedBuffer returns a buffer with writer index set to capacity, so writable
    // bytes is 0, needs explicit clear
    buf.clear();
    ReadTargetBuffer targetBuffer = new NettyBufTargetBuffer(buf);
    int bytesRead = mPositionReader.read(offset, targetBuffer, (int) length);
    if (bytesRead < 0) {
      return EMPTY_BYTE_BUFFER;
    }
    buffer.position(0);
    buffer.limit(bytesRead);
    return buffer;
  }
@Test
  public void read() throws IOException {
    int testNum = Math.min(mFileLen, mMinTestNum);
    for (int i = 0; i < testNum; i++) {
      int offset = mRandom.nextInt(mFileLen);
      int readLength = mRandom.nextInt(mFileLen - offset);
      byte[] tmpBytes = Files.readAllBytes(Paths.get(mTestFileName));
      byte[] bytes = Arrays.copyOfRange(tmpBytes, offset, offset + readLength);
      ByteBuffer realByteBuffer = ByteBuffer.wrap(bytes);
      ByteBuffer byteBuffer = mPagedFileReader.read(offset, readLength);
      Assert.assertEquals(realByteBuffer, byteBuffer);
    }
  }