Skip to content

Commit 30ca832

Browse files
authored
Make some functions less-generic to reduce binary bloat (#503)
* refactor: Extract FramedWrite::buffer to a less generic function Should cut out another 23 KiB (since I see it duplicated) * refactor: Extract some duplicated code to a function * refactor: Extract part of flush into a less generic function * refactor: Extract a less generic part of connection * refactor: Factor out a less generic part of Connection::poll2 * refactor: Extract a non-generic part of handshake2 * refactor: Don't duplicate Streams code on Peer (-3.5%) The `P: Peer` parameter is rarely used and there is already a mechanism for using it dynamically. * refactor: Make recv_frame less generic (-2.3%) * Move out part of Connection::poll * refactor: Extract parts of Connection * refactor: Extract a non-generic part of reclaim_frame * comments
1 parent 6357e32 commit 30ca832

File tree

7 files changed

+931
-656
lines changed

7 files changed

+931
-656
lines changed

src/client.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,20 @@ where
11241124

11251125
// ===== impl Connection =====
11261126

1127+
async fn bind_connection<T>(io: &mut T) -> Result<(), crate::Error>
1128+
where
1129+
T: AsyncRead + AsyncWrite + Unpin,
1130+
{
1131+
tracing::debug!("binding client connection");
1132+
1133+
let msg: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
1134+
io.write_all(msg).await.map_err(crate::Error::from_io)?;
1135+
1136+
tracing::debug!("client connection bound");
1137+
1138+
Ok(())
1139+
}
1140+
11271141
impl<T, B> Connection<T, B>
11281142
where
11291143
T: AsyncRead + AsyncWrite + Unpin,
@@ -1133,12 +1147,7 @@ where
11331147
mut io: T,
11341148
builder: Builder,
11351149
) -> Result<(SendRequest<B>, Connection<T, B>), crate::Error> {
1136-
tracing::debug!("binding client connection");
1137-
1138-
let msg: &'static [u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
1139-
io.write_all(msg).await.map_err(crate::Error::from_io)?;
1140-
1141-
tracing::debug!("client connection bound");
1150+
bind_connection(&mut io).await?;
11421151

11431152
// Create the codec
11441153
let mut codec = Codec::new(io);

src/codec/framed_write.rs

Lines changed: 147 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ pub struct FramedWrite<T, B> {
2323
/// Upstream `AsyncWrite`
2424
inner: T,
2525

26+
encoder: Encoder<B>,
27+
}
28+
29+
#[derive(Debug)]
30+
struct Encoder<B> {
2631
/// HPACK encoder
2732
hpack: hpack::Encoder,
2833

@@ -74,12 +79,14 @@ where
7479
let is_write_vectored = inner.is_write_vectored();
7580
FramedWrite {
7681
inner,
77-
hpack: hpack::Encoder::default(),
78-
buf: Cursor::new(BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY)),
79-
next: None,
80-
last_data_frame: None,
81-
max_frame_size: frame::DEFAULT_MAX_FRAME_SIZE,
82-
is_write_vectored,
82+
encoder: Encoder {
83+
hpack: hpack::Encoder::default(),
84+
buf: Cursor::new(BytesMut::with_capacity(DEFAULT_BUFFER_CAPACITY)),
85+
next: None,
86+
last_data_frame: None,
87+
max_frame_size: frame::DEFAULT_MAX_FRAME_SIZE,
88+
is_write_vectored,
89+
},
8390
}
8491
}
8592

@@ -88,11 +95,11 @@ where
8895
/// Calling this function may result in the current contents of the buffer
8996
/// to be flushed to `T`.
9097
pub fn poll_ready(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
91-
if !self.has_capacity() {
98+
if !self.encoder.has_capacity() {
9299
// Try flushing
93100
ready!(self.flush(cx))?;
94101

95-
if !self.has_capacity() {
102+
if !self.encoder.has_capacity() {
96103
return Poll::Pending;
97104
}
98105
}
@@ -105,6 +112,128 @@ where
105112
/// `poll_ready` must be called first to ensure that a frame may be
106113
/// accepted.
107114
pub fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
115+
self.encoder.buffer(item)
116+
}
117+
118+
/// Flush buffered data to the wire
119+
pub fn flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
120+
let span = tracing::trace_span!("FramedWrite::flush");
121+
let _e = span.enter();
122+
123+
loop {
124+
while !self.encoder.is_empty() {
125+
match self.encoder.next {
126+
Some(Next::Data(ref mut frame)) => {
127+
tracing::trace!(queued_data_frame = true);
128+
let mut buf = (&mut self.encoder.buf).chain(frame.payload_mut());
129+
ready!(write(
130+
&mut self.inner,
131+
self.encoder.is_write_vectored,
132+
&mut buf,
133+
cx,
134+
))?
135+
}
136+
_ => {
137+
tracing::trace!(queued_data_frame = false);
138+
ready!(write(
139+
&mut self.inner,
140+
self.encoder.is_write_vectored,
141+
&mut self.encoder.buf,
142+
cx,
143+
))?
144+
}
145+
}
146+
}
147+
148+
match self.encoder.unset_frame() {
149+
ControlFlow::Continue => (),
150+
ControlFlow::Break => break,
151+
}
152+
}
153+
154+
tracing::trace!("flushing buffer");
155+
// Flush the upstream
156+
ready!(Pin::new(&mut self.inner).poll_flush(cx))?;
157+
158+
Poll::Ready(Ok(()))
159+
}
160+
161+
/// Close the codec
162+
pub fn shutdown(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
163+
ready!(self.flush(cx))?;
164+
Pin::new(&mut self.inner).poll_shutdown(cx)
165+
}
166+
}
167+
168+
fn write<T, B>(
169+
writer: &mut T,
170+
is_write_vectored: bool,
171+
buf: &mut B,
172+
cx: &mut Context<'_>,
173+
) -> Poll<io::Result<()>>
174+
where
175+
T: AsyncWrite + Unpin,
176+
B: Buf,
177+
{
178+
// TODO(eliza): when tokio-util 0.5.1 is released, this
179+
// could just use `poll_write_buf`...
180+
const MAX_IOVS: usize = 64;
181+
let n = if is_write_vectored {
182+
let mut bufs = [IoSlice::new(&[]); MAX_IOVS];
183+
let cnt = buf.chunks_vectored(&mut bufs);
184+
ready!(Pin::new(writer).poll_write_vectored(cx, &bufs[..cnt]))?
185+
} else {
186+
ready!(Pin::new(writer).poll_write(cx, buf.chunk()))?
187+
};
188+
buf.advance(n);
189+
Ok(()).into()
190+
}
191+
192+
#[must_use]
193+
enum ControlFlow {
194+
Continue,
195+
Break,
196+
}
197+
198+
impl<B> Encoder<B>
199+
where
200+
B: Buf,
201+
{
202+
fn unset_frame(&mut self) -> ControlFlow {
203+
// Clear internal buffer
204+
self.buf.set_position(0);
205+
self.buf.get_mut().clear();
206+
207+
// The data frame has been written, so unset it
208+
match self.next.take() {
209+
Some(Next::Data(frame)) => {
210+
self.last_data_frame = Some(frame);
211+
debug_assert!(self.is_empty());
212+
ControlFlow::Break
213+
}
214+
Some(Next::Continuation(frame)) => {
215+
// Buffer the continuation frame, then try to write again
216+
let mut buf = limited_write_buf!(self);
217+
if let Some(continuation) = frame.encode(&mut self.hpack, &mut buf) {
218+
// We previously had a CONTINUATION, and after encoding
219+
// it, we got *another* one? Let's just double check
220+
// that at least some progress is being made...
221+
if self.buf.get_ref().len() == frame::HEADER_LEN {
222+
// If *only* the CONTINUATION frame header was
223+
// written, and *no* header fields, we're stuck
224+
// in a loop...
225+
panic!("CONTINUATION frame write loop; header value too big to encode");
226+
}
227+
228+
self.next = Some(Next::Continuation(continuation));
229+
}
230+
ControlFlow::Continue
231+
}
232+
None => ControlFlow::Break,
233+
}
234+
}
235+
236+
fn buffer(&mut self, item: Frame<B>) -> Result<(), UserError> {
108237
// Ensure that we have enough capacity to accept the write.
109238
assert!(self.has_capacity());
110239
let span = tracing::trace_span!("FramedWrite::buffer", frame = ?item);
@@ -185,93 +314,6 @@ where
185314
Ok(())
186315
}
187316

188-
/// Flush buffered data to the wire
189-
pub fn flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
190-
const MAX_IOVS: usize = 64;
191-
192-
let span = tracing::trace_span!("FramedWrite::flush");
193-
let _e = span.enter();
194-
195-
loop {
196-
while !self.is_empty() {
197-
match self.next {
198-
Some(Next::Data(ref mut frame)) => {
199-
tracing::trace!(queued_data_frame = true);
200-
let mut buf = (&mut self.buf).chain(frame.payload_mut());
201-
// TODO(eliza): when tokio-util 0.5.1 is released, this
202-
// could just use `poll_write_buf`...
203-
let n = if self.is_write_vectored {
204-
let mut bufs = [IoSlice::new(&[]); MAX_IOVS];
205-
let cnt = buf.chunks_vectored(&mut bufs);
206-
ready!(Pin::new(&mut self.inner).poll_write_vectored(cx, &bufs[..cnt]))?
207-
} else {
208-
ready!(Pin::new(&mut self.inner).poll_write(cx, buf.chunk()))?
209-
};
210-
buf.advance(n);
211-
}
212-
_ => {
213-
tracing::trace!(queued_data_frame = false);
214-
let n = if self.is_write_vectored {
215-
let mut iovs = [IoSlice::new(&[]); MAX_IOVS];
216-
let cnt = self.buf.chunks_vectored(&mut iovs);
217-
ready!(
218-
Pin::new(&mut self.inner).poll_write_vectored(cx, &mut iovs[..cnt])
219-
)?
220-
} else {
221-
ready!(Pin::new(&mut self.inner).poll_write(cx, &mut self.buf.chunk()))?
222-
};
223-
self.buf.advance(n);
224-
}
225-
}
226-
}
227-
228-
// Clear internal buffer
229-
self.buf.set_position(0);
230-
self.buf.get_mut().clear();
231-
232-
// The data frame has been written, so unset it
233-
match self.next.take() {
234-
Some(Next::Data(frame)) => {
235-
self.last_data_frame = Some(frame);
236-
debug_assert!(self.is_empty());
237-
break;
238-
}
239-
Some(Next::Continuation(frame)) => {
240-
// Buffer the continuation frame, then try to write again
241-
let mut buf = limited_write_buf!(self);
242-
if let Some(continuation) = frame.encode(&mut self.hpack, &mut buf) {
243-
// We previously had a CONTINUATION, and after encoding
244-
// it, we got *another* one? Let's just double check
245-
// that at least some progress is being made...
246-
if self.buf.get_ref().len() == frame::HEADER_LEN {
247-
// If *only* the CONTINUATION frame header was
248-
// written, and *no* header fields, we're stuck
249-
// in a loop...
250-
panic!("CONTINUATION frame write loop; header value too big to encode");
251-
}
252-
253-
self.next = Some(Next::Continuation(continuation));
254-
}
255-
}
256-
None => {
257-
break;
258-
}
259-
}
260-
}
261-
262-
tracing::trace!("flushing buffer");
263-
// Flush the upstream
264-
ready!(Pin::new(&mut self.inner).poll_flush(cx))?;
265-
266-
Poll::Ready(Ok(()))
267-
}
268-
269-
/// Close the codec
270-
pub fn shutdown(&mut self, cx: &mut Context) -> Poll<io::Result<()>> {
271-
ready!(self.flush(cx))?;
272-
Pin::new(&mut self.inner).poll_shutdown(cx)
273-
}
274-
275317
fn has_capacity(&self) -> bool {
276318
self.next.is_none() && self.buf.get_ref().remaining_mut() >= MIN_BUFFER_CAPACITY
277319
}
@@ -284,26 +326,32 @@ where
284326
}
285327
}
286328

329+
impl<B> Encoder<B> {
330+
fn max_frame_size(&self) -> usize {
331+
self.max_frame_size as usize
332+
}
333+
}
334+
287335
impl<T, B> FramedWrite<T, B> {
288336
/// Returns the max frame size that can be sent
289337
pub fn max_frame_size(&self) -> usize {
290-
self.max_frame_size as usize
338+
self.encoder.max_frame_size()
291339
}
292340

293341
/// Set the peer's max frame size.
294342
pub fn set_max_frame_size(&mut self, val: usize) {
295343
assert!(val <= frame::MAX_MAX_FRAME_SIZE as usize);
296-
self.max_frame_size = val as FrameSize;
344+
self.encoder.max_frame_size = val as FrameSize;
297345
}
298346

299347
/// Set the peer's header table size.
300348
pub fn set_header_table_size(&mut self, val: usize) {
301-
self.hpack.update_max_size(val);
349+
self.encoder.hpack.update_max_size(val);
302350
}
303351

304352
/// Retrieve the last data frame that has been sent
305353
pub fn take_last_data_frame(&mut self) -> Option<frame::Data<B>> {
306-
self.last_data_frame.take()
354+
self.encoder.last_data_frame.take()
307355
}
308356

309357
pub fn get_mut(&mut self) -> &mut T {

0 commit comments

Comments
 (0)