21
21
import java .nio .channels .WritableByteChannel ;
22
22
23
23
import com .google .common .base .Preconditions ;
24
- import com .google .common .primitives .Ints ;
25
24
import io .netty .buffer .ByteBuf ;
26
25
import io .netty .channel .FileRegion ;
27
26
import io .netty .util .AbstractReferenceCounted ;
28
27
import io .netty .util .ReferenceCountUtil ;
29
28
30
29
/**
31
- * A wrapper message that holds two separate pieces (a header and a body) to avoid
32
- * copying the body's content.
30
+ * A wrapper message that holds two separate pieces (a header and a body).
31
+ *
32
+ * The header must be a ByteBuf, while the body can be a ByteBuf or a FileRegion.
33
33
*/
34
34
class MessageWithHeader extends AbstractReferenceCounted implements FileRegion {
35
35
@@ -63,32 +63,36 @@ public long transfered() {
63
63
return totalBytesTransferred ;
64
64
}
65
65
66
+ /**
67
+ * This code is more complicated than you would think because we might require multiple
68
+ * transferTo invocations in order to transfer a single MessageWithHeader to avoid busy waiting.
69
+ *
70
+ * The contract is that the caller will ensure position is properly set to the total number
71
+ * of bytes transferred so far (i.e. value returned by transfered()).
72
+ */
66
73
@ Override
67
- public long transferTo (WritableByteChannel target , long position ) throws IOException {
74
+ public long transferTo (final WritableByteChannel target , final long position ) throws IOException {
68
75
Preconditions .checkArgument (position == totalBytesTransferred , "Invalid position." );
69
- long written = 0 ;
70
-
71
- if (position < headerLength ) {
72
- written += copyByteBuf (header , target );
76
+ // Bytes written for header in this call.
77
+ long writtenHeader = 0 ;
78
+ if (header .readableBytes () > 0 ) {
79
+ writtenHeader = copyByteBuf (header , target );
80
+ totalBytesTransferred += writtenHeader ;
73
81
if (header .readableBytes () > 0 ) {
74
- totalBytesTransferred += written ;
75
- return written ;
82
+ return writtenHeader ;
76
83
}
77
84
}
78
85
86
+ // Bytes written for body in this call.
87
+ long writtenBody = 0 ;
79
88
if (body instanceof FileRegion ) {
80
- // Adjust the position. If the write is happening as part of the same call where the header
81
- // (or some part of it) is written, `position` will be less than the header size, so we want
82
- // to start from position 0 in the FileRegion object. Otherwise, we start from the position
83
- // requested by the caller.
84
- long bodyPos = position > headerLength ? position - headerLength : 0 ;
85
- written += ((FileRegion )body ).transferTo (target , bodyPos );
89
+ writtenBody = ((FileRegion ) body ).transferTo (target , totalBytesTransferred - headerLength );
86
90
} else if (body instanceof ByteBuf ) {
87
- written + = copyByteBuf ((ByteBuf ) body , target );
91
+ writtenBody = copyByteBuf ((ByteBuf ) body , target );
88
92
}
93
+ totalBytesTransferred += writtenBody ;
89
94
90
- totalBytesTransferred += written ;
91
- return written ;
95
+ return writtenHeader + writtenBody ;
92
96
}
93
97
94
98
@ Override
@@ -102,5 +106,4 @@ private int copyByteBuf(ByteBuf buf, WritableByteChannel target) throws IOExcept
102
106
buf .skipBytes (written );
103
107
return written ;
104
108
}
105
-
106
109
}
0 commit comments