# HG changeset patch # User dfuchs # Date 1522417708 -3600 # Node ID eef94a3576a40ebcc092b9ecf58d4e4f78988038 # Parent e453d21310bd6dec4644ffc367a5eed9e2b58205 http-client-branch: review comment - SocketTube should use slices diff -r e453d21310bd -r eef94a3576a4 src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java Thu Mar 29 21:45:58 2018 +0100 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/SocketTube.java Fri Mar 30 14:48:28 2018 +0100 @@ -901,9 +901,19 @@ // ===================================================================== // static final int MAX_BUFFERS = 3; static final List EOF = List.of(); + static final List NOTHING = List.of(Utils.EMPTY_BYTEBUFFER); + // readAvailable() will read bytes into the 'current' ByteBuffer until + // the ByteBuffer is full, or 0 or -1 (EOF) is returned by read(). + // When that happens, a slice of the data that has been read so far + // is inserted into the returned buffer list, and if the current buffer + // has remaining space, that space will be used to read more data when + // the channel becomes readable again. + private volatile ByteBuffer current; private List readAvailable() throws IOException { - ByteBuffer buf = buffersSource.get(); + ByteBuffer buf = current; + buf = (buf == null || !buf.hasRemaining()) + ? (current = buffersSource.get()) : buf; assert buf.hasRemaining(); int read; @@ -916,7 +926,7 @@ break; } } catch (IOException x) { - if (buf.position() == pos && (list == null || list.isEmpty())) { + if (buf.position() == pos && list == null) { // no bytes have been read, just throw... throw x; } else { @@ -932,32 +942,53 @@ // returned if read == -1. If some data has already been read, // then it must be returned. -1 will be returned next time // the caller attempts to read something. - if (list == null && read == -1) { // eof - list = EOF; - break; + if (list == null) { + // nothing read - list was null - return EOF or NOTHING + list = read == -1 ? EOF : NOTHING; } + break; } + + // check whether this buffer has still some free space available. + // if so, we will keep it for the next round. + final boolean hasRemaining = buf.hasRemaining(); + + // creates a slice to add to the list + int limit = buf.limit(); buf.limit(buf.position()); buf.position(pos); - if (list == null) { - list = List.of(buf); - } else { - if (!(list instanceof ArrayList)) { - list = new ArrayList<>(list); - } - list.add(buf); - } + ByteBuffer slice = buf.slice(); + + // restore buffer state to what it was before creating the slice + buf.position(buf.limit()); + buf.limit(limit); + + // add the buffer to the list + list = addToList(list, slice.asReadOnlyBuffer()); if (read <= 0 || list.size() == MAX_BUFFERS) { break; } - buf = buffersSource.get(); + buf = hasRemaining ? buf : (current = buffersSource.get()); pos = buf.position(); assert buf.hasRemaining(); } return list; } + private List addToList(List list, T item) { + int size = list == null ? 0 : list.size(); + switch (size) { + case 0: return List.of(item); + case 1: return List.of(list.get(0), item); + case 2: return List.of(list.get(0), list.get(1), item); + default: // slow path if MAX_BUFFERS > 3 + ArrayList res = new ArrayList<>(list); + res.add(item); + return res; + } + } + private long writeAvailable(List bytes) throws IOException { ByteBuffer[] srcs = bytes.toArray(Utils.EMPTY_BB_ARRAY); final long remaining = Utils.remaining(srcs);