@Override
  public synchronized int read(ByteBuffer dst) throws IOException {
    if (!isopen) {
      throw new ClosedChannelException();
    }
    if (!headerRead) {
      if (!tryReadHeader()) {
        return 0;
      }
    }
    int startPos = dst.position();
    while (dst.remaining() > 0 && plaintextPosition < plaintextSize) {
      // Determine segmentNr for the plaintext to read and the offset in
      // the plaintext, where reading should start.
      int segmentNr = getSegmentNr(plaintextPosition);
      int segmentOffset;
      if (segmentNr == 0) {
         segmentOffset = (int) plaintextPosition;
      } else {
         segmentOffset = (int) ((plaintextPosition +  ciphertextOffset) % plaintextSegmentSize);
      }

      if (tryLoadSegment(segmentNr)) {
        plaintextSegment.position(segmentOffset);
        if (plaintextSegment.remaining() <= dst.remaining()) {
          plaintextPosition += plaintextSegment.remaining();
          dst.put(plaintextSegment);
        } else {
          int sliceSize = dst.remaining();
          ByteBuffer slice = plaintextSegment.duplicate();
          slice.limit(slice.position() + sliceSize);
          dst.put(slice);
          plaintextPosition += sliceSize;
          plaintextSegment.position(plaintextSegment.position() + sliceSize);
        }
      } else {
        break;
      }
    }
    int read = dst.position() - startPos;
    if (read == 0 && reachedEnd()) {
      return -1;
    }
    return read;
  }
Atomic read from a given position.

This method works in the same way as read(ByteBuffer), except that it starts at the given
position and does not modify the channel's position.
java_src/src/main/java/com/google/crypto/tink/subtle/StreamingAeadSeekableDecryptingChannel.java