protected void testPreviousOffsetInternals() throws IOException {
    // TODO: parameterize these nested loops.
    Configuration conf = TEST_UTIL.getConfiguration();
    for (Compression.Algorithm algo : COMPRESSION_ALGORITHMS) {
      for (boolean pread : BOOLEAN_VALUES) {
        for (boolean cacheOnWrite : BOOLEAN_VALUES) {
          Random rand = defaultRandom();
          LOG.info("testPreviousOffset: Compression algorithm={}, pread={}, cacheOnWrite={}",
            algo.toString(), pread, cacheOnWrite);
          Path path = new Path(TEST_UTIL.getDataTestDir(), "prev_offset");
          List<Long> expectedOffsets = new ArrayList<>();
          List<Long> expectedPrevOffsets = new ArrayList<>();
          List<BlockType> expectedTypes = new ArrayList<>();
          List<ByteBuffer> expectedContents = cacheOnWrite ? new ArrayList<>() : null;
          long totalSize = writeBlocks(TEST_UTIL.getConfiguration(), rand, algo, path,
            expectedOffsets, expectedPrevOffsets, expectedTypes, expectedContents);

          FSDataInputStream is = fs.open(path);
          HFileContext meta =
            new HFileContextBuilder().withHBaseCheckSum(true).withIncludesMvcc(includesMemstoreTS)
              .withIncludesTags(includesTag).withCompression(algo).build();
          ReaderContext context =
            new ReaderContextBuilder().withInputStreamWrapper(new FSDataInputStreamWrapper(is))
              .withFileSize(totalSize).withFilePath(path).withFileSystem(fs).build();
          HFileBlock.FSReader hbr = new HFileBlock.FSReaderImpl(context, meta, alloc, conf);
          long curOffset = 0;
          for (int i = 0; i < NUM_TEST_BLOCKS; ++i) {
            if (!pread) {
              assertEquals(is.getPos(),
                curOffset + (i == 0 ? 0 : HConstants.HFILEBLOCK_HEADER_SIZE));
            }

            assertEquals(expectedOffsets.get(i).longValue(), curOffset);
            if (detailedLogging) {
              LOG.info("Reading block #" + i + " at offset " + curOffset);
            }
            HFileBlock b = hbr.readBlockData(curOffset, -1, pread, false, false);
            if (detailedLogging) {
              LOG.info("Block #" + i + ": " + b);
            }
            assertEquals("Invalid block #" + i + "'s type:", expectedTypes.get(i),
              b.getBlockType());
            assertEquals("Invalid previous block offset for block " + i + " of " + "type "
              + b.getBlockType() + ":", (long) expectedPrevOffsets.get(i), b.getPrevBlockOffset());
            b.sanityCheck();
            assertEquals(curOffset, b.getOffset());

            // Now re-load this block knowing the on-disk size. This tests a
            // different branch in the loader.
            HFileBlock b2 =
              hbr.readBlockData(curOffset, b.getOnDiskSizeWithHeader(), pread, false, false);
            b2.sanityCheck();

            assertEquals(b.getBlockType(), b2.getBlockType());
            assertEquals(b.getOnDiskSizeWithoutHeader(), b2.getOnDiskSizeWithoutHeader());
            assertEquals(b.getOnDiskSizeWithHeader(), b2.getOnDiskSizeWithHeader());
            assertEquals(b.getUncompressedSizeWithoutHeader(),
              b2.getUncompressedSizeWithoutHeader());
            assertEquals(b.getPrevBlockOffset(), b2.getPrevBlockOffset());
            assertEquals(curOffset, b2.getOffset());
            assertEquals(b.getBytesPerChecksum(), b2.getBytesPerChecksum());
            assertEquals(b.getOnDiskDataSizeWithHeader(), b2.getOnDiskDataSizeWithHeader());
            assertEquals(0, HFile.getAndResetChecksumFailuresCount());
            assertRelease(b2);

            curOffset += b.getOnDiskSizeWithHeader();

            if (cacheOnWrite) {
              // NOTE: cache-on-write testing doesn't actually involve a BlockCache. It simply
              // verifies that the unpacked value read back off disk matches the unpacked value
              // generated before writing to disk.
              HFileBlock newBlock = b.unpack(meta, hbr);
              // neither b's unpacked nor the expectedContents have checksum.
              // they should be identical
              ByteBuff bufRead = newBlock.getBufferReadOnly();
              ByteBuffer bufExpected = expectedContents.get(i);
              byte[] bytesRead = bufRead.toBytes();
              boolean bytesAreCorrect = Bytes.compareTo(bytesRead, 0, bytesRead.length,
                bufExpected.array(), bufExpected.arrayOffset(), bufExpected.limit()) == 0;
              String wrongBytesMsg = "";

              if (!bytesAreCorrect) {
                // Optimization: only construct an error message in case we
                // will need it.
                wrongBytesMsg = "Expected bytes in block #" + i + " (algo=" + algo + ", pread="
                  + pread + ", cacheOnWrite=" + cacheOnWrite + "):\n";
                wrongBytesMsg += Bytes.toStringBinary(bufExpected.array(),
                  bufExpected.arrayOffset(), Math.min(32 + 10, bufExpected.limit())) + ", actual:\n"
                  + Bytes.toStringBinary(bytesRead, 0, Math.min(32 + 10, bytesRead.length));
                if (detailedLogging) {
                  LOG.warn(
                    "expected header" + HFileBlock.toStringHeader(new SingleByteBuff(bufExpected))
                      + "\nfound    header" + HFileBlock.toStringHeader(bufRead));
                  LOG.warn("bufread offset " + bufRead.arrayOffset() + " limit " + bufRead.limit()
                    + " expected offset " + bufExpected.arrayOffset() + " limit "
                    + bufExpected.limit());
                  LOG.warn(wrongBytesMsg);
                }
              }
              assertTrue(wrongBytesMsg, bytesAreCorrect);
              assertRelease(newBlock);
              if (newBlock != b) {
                assertRelease(b);
              }
            } else {
              assertRelease(b);
            }
          }
          assertEquals(curOffset, fs.getFileStatus(path).getLen());
          is.close();
        }
      }
    }
  }
Convert a few next bytes in the given buffer at the given position to string. Used for error
messages.
hbase-server/src/test/java/org/apache/hadoop/hbase/io/hfile/TestHFileBlock.java