Repository: tokio-rs/tokio
File: tokio-stream/src/stream_ext/chunks_timeout.rs (Rust)

Code before:
            cap: max_size,
        }
    }

    fn take(mut self: Pin<&mut Self>) -> Vec<S::Item> {
        let cap = self.cap;
        let this = self.as_mut().project();
        *this.poll_deadline = false;

        std::mem::replace(this.items, Vec::with_capacity(cap))
    }
}

impl<S: Stream> Stream for ChunksTimeout<S> {
    type Item = Vec<S::Item>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut me = self.as_mut().project();
        loop {
            match me.stream.as_mut().poll_next(cx) {
                Poll::Pending => break,
                Poll::Ready(Some(item)) => {
                    if me.items.is_empty() {
                        me.deadline.as_mut().reset(Instant::now() + *me.duration);
                        *me.poll_deadline = true;
                    }
                    me.items.push(item);
                    if me.items.len() >= *me.cap {
                        return Poll::Ready(Some(self.take()));
                    }
                }
                Poll::Ready(None) => {
                    let last = if me.items.is_empty() {
                        None
                    } else {
                        let full_buf = std::mem::take(me.items);
                        Some(full_buf)
                    };

                    return Poll::Ready(last);
                }
            }
        }

        if *me.poll_deadline {
            ready!(me.deadline.poll(cx));
            return Poll::Ready(Some(self.take()));
        }

        Poll::Pending
    }

Review comment (suggestion):
If you change `take` to return `std::mem::take(this.items)` and then add the following line:
```suggestion
                    if me.items.is_empty() {
                        me.deadline.as_mut().reset(Instant::now() + *me.duration);
                        me.items.reserve_exact(*me.cap);
                    }
```
Then we don't allocate a vector if the inner stream returns `None` right after a call to `take`.

Code after:
    }
}

impl<S: Stream> Stream for ChunksTimeout<S> {
    type Item = Vec<S::Item>;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let mut me = self.as_mut().project();
        loop {
            match me.stream.as_mut().poll_next(cx) {
                Poll::Pending => break,
                Poll::Ready(Some(item)) => {
                    if me.items.is_empty() {
                        me.deadline.as_mut().reset(Instant::now() + *me.duration);
                        me.items.reserve_exact(*me.cap);
                    }
                    me.items.push(item);
                    if me.items.len() >= *me.cap {
                        return Poll::Ready(Some(std::mem::take(me.items)));
                    }
                }
                Poll::Ready(None) => {
                    // Returning Some here is only correct because we fuse the inner stream.
                    let last = if me.items.is_empty() {
                        None
                    } else {
                        Some(std::mem::take(me.items))
                    };

                    return Poll::Ready(last);
                }
            }
        }

        if !me.items.is_empty() {
            ready!(me.deadline.poll(cx));
            return Poll::Ready(Some(std::mem::take(me.items)));
        }

        Poll::Pending
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let chunk_len = if self.items.is_empty() { 0 } else { 1 };
        let (lower, upper) = self.stream.size_hint();
        let lower = (lower / self.cap).saturating_add(chunk_len);
        let upper = upper.and_then(|x| x.checked_add(chunk_len));
        (lower, upper)
    }
}