code: public void read(byte[] bytes, long storageIndex) throws IOException {
          LOGGER.debug("Starting to read with param: " + "\nstorageIndex = " + storageIndex
              + "\nbytes.length = " + bytes.length);
  
          long startIndex = storageIndex / BYTES_IN_DATA;
          int startIndexOffset = (int)(storageIndex % BYTES_IN_DATA);
  
          long endIndex = (storageIndex + bytes.length) / BYTES_IN_DATA;
  
          int endIndexMax = (int)((storageIndex + bytes.length) % BYTES_IN_DATA);
  
          LOGGER.debug("startIndex: " + startIndex);
          LOGGER.debug("startIndexOffset: " + startIndexOffset);
          LOGGER.debug("endIndex: " + endIndex);
          LOGGER.debug("endIndexMax: " + endIndexMax);
  
          int bytesRead =
              bytes.length + startIndexOffset > BYTES_IN_DATA ? BYTES_IN_DATA - startIndexOffset : bytes.length;
  
          checkState(mRtx.moveTo(startIndex));
          byte[] data = mRtx.getValueOfCurrentData();
          System.arraycopy(data, startIndexOffset, bytes, 0, bytesRead);
  
          for (long i = startIndex + 1; i < endIndex; i++) {
              checkState(mRtx.moveTo(i));
              data = mRtx.getValueOfCurrentData();
              System.arraycopy(data, 0, bytes, bytesRead, data.length);
              bytesRead = bytesRead + data.length;
  
          }
  
          if (startIndex != endIndex && endIndex < mNodeNumbers) {
              checkState(mRtx.moveTo(endIndex));
              data = mRtx.getValueOfCurrentData();
              System.arraycopy(data, 0, bytes, bytesRead, endIndexMax);
  
              bytesRead += endIndexMax;
          }
  
          if (storageIndex % BYTES_IN_DATA == 0) {
              prefetch(storageIndex);
          }
          // Bytes read is the actual number of bytes that have been read.
          // The two lengths have to match, otherwise not enough bytes have been read (or too much?).
          checkState(bytesRead == bytes.length);
      }
docs: {@inheritDoc}