Skip to content

Commit

Permalink
Channel-to-channel transfer without passing all data through Java heap
Browse files Browse the repository at this point in the history
  • Loading branch information
mkarg committed Jun 8, 2023
1 parent b2a5271 commit 1401b71
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 2 deletions.
58 changes: 56 additions & 2 deletions src/java.base/share/classes/sun/nio/ch/ChannelInputStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
* @author Mark Reinhold
*/
class ChannelInputStream extends InputStream {
private static final int DEFAULT_BUFFER_SIZE = 8192;
private static final int DEFAULT_BUFFER_SIZE = 16384;

private final ReadableByteChannel ch;
private ByteBuffer bb;
Expand Down Expand Up @@ -265,11 +265,43 @@ public long transferTo(OutputStream out) throws IOException {
return transfer(rbc, fc);
}

// ReableByteChannel -> WritableByteChannel
if (out instanceof ChannelOutputStream cos) {
ReadableByteChannel rbc = ch;
WritableByteChannel wbc = cos.channel();

if (rbc instanceof SelectableChannel rsc) {
synchronized (rsc.blockingLock()) {
if (!rsc.isBlocking())
throw new IllegalBlockingModeException();
if (wbc instanceof SelectableChannel wsc) {
synchronized (wsc.blockingLock()) {
if (!wsc.isBlocking())
throw new IllegalBlockingModeException();
return transfer(rbc, wbc);
}
}

return transfer(rbc, wbc);
}
}

if (wbc instanceof SelectableChannel wsc) {
synchronized (wsc.blockingLock()) {
if (!wsc.isBlocking())
throw new IllegalBlockingModeException();
return transfer(rbc, wbc);
}
}

return transfer(rbc, wbc);
}

return super.transferTo(out);
}

/**
* Transfers all bytes from a channel's file to a target writeable byte channel.
* Transfers all bytes from a channel's file to a target writable byte channel.
* If the writeable byte channel is a selectable channel then it must be in
* blocking mode.
*/
Expand Down Expand Up @@ -307,6 +339,28 @@ private static long transfer(ReadableByteChannel src, FileChannel dst) throws IO
return pos - initialPos;
}

/**
* Transfers all bytes from a readable byte channel to a writable byte channel.
* If the readable or writable byte channel is a selectable channel then it must be in
* blocking mode.
*/
private static long transfer(ReadableByteChannel src, WritableByteChannel dst) throws IOException {
long bytesWritten = 0L;
ByteBuffer bb = Util.getTemporaryDirectBuffer(DEFAULT_BUFFER_SIZE);
try {
for (int bytesRead = src.read(bb); bytesRead > -1; bytesRead = src.read(bb)) {
bb.flip();
while (bb.hasRemaining())
dst.write(bb);
bb.clear();
bytesWritten += bytesRead;
}
return bytesWritten;
} finally {
Util.releaseTemporaryDirectBuffer(bb);
}
}

@Override
public void close() throws IOException {
ch.close();
Expand Down
4 changes: 4 additions & 0 deletions test/jdk/java/nio/channels/Channels/TransferTo.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public static Object[][] streamCombinations() {
// optimized case
{fileChannelInput(), writableByteChannelOutput()},

// tests ReadableFileChannel transfer to WritableByteChannelOutput
// optimized case
{readableByteChannelInput(), writableByteChannelOutput()},

// tests InputStream.transferTo(OutputStream) default case
{readableByteChannelInput(), defaultOutput()}
};
Expand Down

0 comments on commit 1401b71

Please sign in to comment.