public Vectors readNextSlice() throws IOException {
      if (offsetReader == null) {
        return null; // This means the reader has already been closed.
      }
      try {
        while (offsetReader.next()) {
          hasAnyData = true;
          Writable value = offsetReader.getCurrentRow();
          lastStartOffset = offsetReader.getCurrentRowStartOffset();
          if (firstStartOffset == Long.MIN_VALUE) {
            firstStartOffset = lastStartOffset;
          }
          writer.writeOneRow(value);

          if (maySplitTheSplit && ++rowsPerSlice == targetSliceRowCount) {
            assert offsetReader.hasOffsets();
            writer.flushIntermediateData();
            long fileOffset = offsetReader.getCurrentRowEndOffset();
            // Must support offsets to be able to split.
            if (firstStartOffset < 0 || lastStartOffset < 0 || fileOffset < 0) {
              throw new AssertionError("Unable to get offsets from "
                  + offsetReader.getClass().getSimpleName());
            }
            writer.setCurrentStripeOffsets(
                currentKnownTornStart, firstStartOffset, lastStartOffset, fileOffset);
            writer.writeIntermediateFooter();

            // Split starting at row start will not read that row.
            currentKnownTornStart = lastStartOffset;
            // Row offsets will be determined from the reader (we could set the first from last).
            lastStartOffset = Long.MIN_VALUE;
            firstStartOffset = Long.MIN_VALUE;
            rowsPerSlice = 0;
            return new Vectors(writer.extractCurrentVrbs());
          }
        }
        try {
          if (rowsPerSlice > 0 || (!maySplitTheSplit && hasAnyData)) {
            long fileOffset = -1;
            if (!offsetReader.hasOffsets()) {
              // The reader doesn't support offsets. We adjust offsets to match future splits.
              // If cached split was starting at row start, that row would be skipped, so +1 byte.
              firstStartOffset = split.getStart() + 1;
              // Last row starting at the end of the split would be read.
              lastStartOffset = split.getStart() + split.getLength();
              // However, it must end after the split end, otherwise the next one would have been read.
              fileOffset = lastStartOffset + 1;
              if (LlapIoImpl.CACHE_LOGGER.isDebugEnabled()) {
                LlapIoImpl.CACHE_LOGGER.debug("Cache offsets based on the split - 'first row' at "
                  + firstStartOffset + "; 'last row' at " + lastStartOffset + ", " + fileOffset);
              }
            } else {
              fileOffset = offsetReader.getCurrentRowEndOffset();
              assert firstStartOffset >= 0 && lastStartOffset >= 0 && fileOffset >= 0;
            }
            writer.setCurrentStripeOffsets(
                currentKnownTornStart, firstStartOffset, lastStartOffset, fileOffset);
            // Close the writer to finalize the metadata.
            writer.close();
            return new Vectors(writer.extractCurrentVrbs());
          } else {
            writer.close();
            return null; // There's no more data.
          }
        } finally {
          closeOffsetReader();
        }
      } catch (Exception ex) {
        closeOffsetReader();
        throw (ex instanceof IOException) ? (IOException)ex : new IOException(ex);
      }
    }
This class only exists because Java doesn't have yield return. The original method
before this change only needed yield return-s sprinkled here and there; however,
Java developers are usually paid by class, so here we go.
llap-server/src/java/org/apache/hadoop/hive/llap/io/encoded/SerDeEncodedDataReader.java