public ReadResult readBytes(long tailChunkAddress) throws IOException {
    forceAppender(myPath);

    checkCancellation();
    long startedTime = ourDumpChunkRemovalTime ? System.nanoTime() : 0;

    RAReader reader = myCompactionModeReader;
    FileAccessorCache.Handle<RAReader> readerHandle = null;
    if (myCompressedAppendableFile != null) {
      reader = new ReaderOverCompressedFile(myCompressedAppendableFile);
    }
    if (reader == null) {
      readerHandle = ourReadersCache.get(myPath);
      reader = readerHandle.get();
    }

    int chunkCount = 0;
    byte[] result = null;
    try {
      long chunk = tailChunkAddress;
      while (chunk != 0) {
        if (chunk < 0 || chunk > mySize) throw new CorruptedException(myPath);

        byte[] buffer = myBuffer.getBuffer(ourBufferLength);
        int len = (int)Math.min(ourBufferLength, mySize - chunk);

        reader.get(chunk, buffer, 0, len);
        DataInputStream inputStream = toDataInputStream(buffer, 0, len);

        final int chunkSize = readChunkSize(inputStream);
        final long prevChunkAddress = readPrevChunkAddress(chunk, inputStream);
        final int headerOffset = len - inputStream.available();

        byte[] b = new byte[(result != null ? result.length : 0) + chunkSize];
        if (result != null) System.arraycopy(result, 0, b, b.length - result.length, result.length);
        result = b;

        checkPreconditions(result, chunkSize);
        if (chunkSize < ourBufferLength - headerOffset) {
          System.arraycopy(buffer, headerOffset, result, 0, chunkSize);
        }
        else {
          reader.get(chunk + headerOffset, result, 0, chunkSize);
        }

        if (prevChunkAddress >= chunk) throw new CorruptedException(myPath);

        chunk = prevChunkAddress;
        chunkCount++;

        if (prevChunkAddress != 0) {
          checkCancellation();
          assert !myOptions.myHasNoChunks;
        }
        if (result.length > mySize && myCompressedAppendableFile == null) {
          throw new CorruptedException(myPath);
        }
      }
    }
    catch (OutOfMemoryError error) {
      throw new CorruptedException(myPath);
    }
    finally {
      if (readerHandle != null) {
        readerHandle.release();
      }
    }

    if (chunkCount > 1) {
      checkCancellation();

      myChunksReadingTime += (ourDumpChunkRemovalTime ? System.nanoTime() : 0) - startedTime;
      myChunks += chunkCount;
      myChunksOriginalBytes += result.length;
    }

    return new ReadResult(result, chunkCount);
  }
public ReadResult readBytes(long tailChunkAddress)