Skip to content

Commit 8cfc085

Browse files
authored
Do not store items field in ReadyChunks (#2656)
`items` is always empty when `poll_ready` exists, so there's no reason to store it inside `ReadyChunks`. This makes code a bit more efficient, but also makes code easier to understand.
1 parent 3c657e5 commit 8cfc085

File tree

2 files changed

+15
-33
lines changed

2 files changed

+15
-33
lines changed

futures-util/src/stream/stream/ready_chunks.rs

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use crate::stream::Fuse;
22
use alloc::vec::Vec;
3-
use core::mem;
43
use core::pin::Pin;
54
use futures_core::stream::{FusedStream, Stream};
65
use futures_core::task::{Context, Poll};
@@ -15,7 +14,6 @@ pin_project! {
1514
pub struct ReadyChunks<St: Stream> {
1615
#[pin]
1716
stream: Fuse<St>,
18-
items: Vec<St::Item>,
1917
cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475
2018
}
2119
}
@@ -24,11 +22,7 @@ impl<St: Stream> ReadyChunks<St> {
2422
pub(super) fn new(stream: St, capacity: usize) -> Self {
2523
assert!(capacity > 0);
2624

27-
Self {
28-
stream: super::Fuse::new(stream),
29-
items: Vec::with_capacity(capacity),
30-
cap: capacity,
31-
}
25+
Self { stream: super::Fuse::new(stream), cap: capacity }
3226
}
3327

3428
delegate_access_inner!(stream, St, (.));
@@ -40,40 +34,33 @@ impl<St: Stream> Stream for ReadyChunks<St> {
4034
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
4135
let mut this = self.project();
4236

37+
let mut items: Vec<St::Item> = Vec::new();
38+
4339
loop {
4440
match this.stream.as_mut().poll_next(cx) {
4541
// Flush all collected data if underlying stream doesn't contain
4642
// more ready values
4743
Poll::Pending => {
48-
return if this.items.is_empty() {
49-
Poll::Pending
50-
} else {
51-
Poll::Ready(Some(mem::replace(this.items, Vec::with_capacity(*this.cap))))
52-
}
44+
return if items.is_empty() { Poll::Pending } else { Poll::Ready(Some(items)) }
5345
}
5446

5547
// Push the ready item into the buffer and check whether it is full.
5648
// If so, replace our buffer with a new and empty one and return
5749
// the full one.
5850
Poll::Ready(Some(item)) => {
59-
this.items.push(item);
60-
if this.items.len() >= *this.cap {
61-
return Poll::Ready(Some(mem::replace(
62-
this.items,
63-
Vec::with_capacity(*this.cap),
64-
)));
51+
if items.is_empty() {
52+
items.reserve(*this.cap);
53+
}
54+
items.push(item);
55+
if items.len() >= *this.cap {
56+
return Poll::Ready(Some(items));
6557
}
6658
}
6759

6860
// Since the underlying stream ran out of values, return what we
6961
// have buffered, if we have anything.
7062
Poll::Ready(None) => {
71-
let last = if this.items.is_empty() {
72-
None
73-
} else {
74-
let full_buf = mem::take(this.items);
75-
Some(full_buf)
76-
};
63+
let last = if items.is_empty() { None } else { Some(items) };
7764

7865
return Poll::Ready(last);
7966
}
@@ -82,20 +69,15 @@ impl<St: Stream> Stream for ReadyChunks<St> {
8269
}
8370

8471
fn size_hint(&self) -> (usize, Option<usize>) {
85-
let chunk_len = usize::from(!self.items.is_empty());
8672
let (lower, upper) = self.stream.size_hint();
87-
let lower = (lower / self.cap).saturating_add(chunk_len);
88-
let upper = match upper {
89-
Some(x) => x.checked_add(chunk_len),
90-
None => None,
91-
};
73+
let lower = lower / self.cap;
9274
(lower, upper)
9375
}
9476
}
9577

9678
impl<St: FusedStream> FusedStream for ReadyChunks<St> {
9779
fn is_terminated(&self) -> bool {
98-
self.stream.is_terminated() && self.items.is_empty()
80+
self.stream.is_terminated()
9981
}
10082
}
10183

futures/tests/auto_traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1492,10 +1492,10 @@ pub mod stream {
14921492
assert_not_impl!(PollImmediate<PinnedStream>: Unpin);
14931493

14941494
assert_impl!(ReadyChunks<SendStream<()>>: Send);
1495-
assert_not_impl!(ReadyChunks<SendStream>: Send);
1495+
assert_impl!(ReadyChunks<SendStream>: Send);
14961496
assert_not_impl!(ReadyChunks<LocalStream>: Send);
14971497
assert_impl!(ReadyChunks<SyncStream<()>>: Sync);
1498-
assert_not_impl!(ReadyChunks<SyncStream>: Sync);
1498+
assert_impl!(ReadyChunks<SyncStream>: Sync);
14991499
assert_not_impl!(ReadyChunks<LocalStream>: Sync);
15001500
assert_impl!(ReadyChunks<UnpinStream>: Unpin);
15011501
assert_not_impl!(ReadyChunks<PinnedStream>: Unpin);

0 commit comments

Comments
 (0)