/** Copy the required number of bytes into the save buffer. */
  private void copyToSaveBuffer(int bytesNeeded) {
    /* How much can we get from this current read buffer? */
    int bytesFromThisBuffer;

    if (bytesNeeded <= window.remaining()) {
      bytesFromThisBuffer = bytesNeeded;
    } else {
      bytesFromThisBuffer = window.remaining();
    }

    /* Gather it all into this save buffer. */
    ByteBuffer temp;

    /* Make sure the save buffer is big enough. */
    if (saveBuffer.capacity() - threadSafeBufferPosition(saveBuffer) < bytesFromThisBuffer) {
      /* Grow the save buffer. */
      temp = ByteBuffer.allocate(saveBuffer.capacity() + bytesFromThisBuffer);
      threadSafeBufferFlip(saveBuffer);
      temp.put(saveBuffer);
      saveBuffer = temp;
    }

    /*
     * Bulk copy only the required section from the read buffer into the
     * save buffer. We need from readBuffer.position() to
     * readBuffer.position() + bytesFromThisBuffer
     */
    temp = window.getBuffer().slice();
    temp.limit(bytesFromThisBuffer);
    saveBuffer.put(temp);
    window.incrementBufferPosition(bytesFromThisBuffer);
  }
  /**
   * Try to read a specified number of bytes.
   *
   * @param amountToRead is the number of bytes we need
   * @param collectData is true if we need to actually look at the data. If false, we know we're
   *     skipping this entry, and all we need to do is to count until we get to the right spot.
   * @return a byte buffer positioned at the head of the desired portion, or null if we reached eof.
   */
  private ByteBuffer readData(int amountToRead, boolean collectData)
      throws ChecksumException, EOFException, FileNotFoundException, DatabaseException {

    int alreadyRead = 0;
    ByteBuffer completeBuffer = null;
    saveBuffer.clear();

    while ((alreadyRead < amountToRead) && !eof) {

      int bytesNeeded = amountToRead - alreadyRead;
      if (window.hasRemaining()) {

        /* There's data in the window, process it. */
        if (collectData) {

          /*
           * Save data in a buffer for processing.
           */
          if ((alreadyRead > 0) || (window.remaining() < bytesNeeded)) {

            /* We need to piece an entry together. */
            copyToSaveBuffer(bytesNeeded);
            alreadyRead = threadSafeBufferPosition(saveBuffer);
            completeBuffer = saveBuffer;
          } else {

            /* A complete entry is available in this buffer. */
            completeBuffer = window.getBuffer();
            alreadyRead = amountToRead;
          }
        } else {

          /*
           * We're not processing the data, so need to save it. just
           * move buffer positions.
           */
          int positionIncrement =
              (window.remaining() > bytesNeeded) ? bytesNeeded : window.remaining();

          alreadyRead += positionIncrement;
          window.incrementBufferPosition(positionIncrement);
          completeBuffer = window.getBuffer();
        }
      } else {

        /*
         * Look for more data.
         */
        if (window.fillNext(singleFile, bytesNeeded)) {
          /* This call to fillNext slid the window to a new file. */
          nextEntryOffset = 0;
        }
      }
    }

    /* Flip the save buffer just in case we've been accumulating in it. */
    threadSafeBufferFlip(saveBuffer);

    return completeBuffer;
  }