Skip to content

Commit 398a2f1

Browse files
authored
Merge pull request #164 from yoshuawuyts/concurrent-stream
2 parents 79903a6 + 8ef8cd0 commit 398a2f1

File tree

22 files changed

+1760
-22
lines changed

22 files changed

+1760
-22
lines changed

Cargo.toml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ readme = "README.md"
99
edition = "2021"
1010
keywords = []
1111
categories = []
12-
authors = [
13-
"Yoshua Wuyts <[email protected]>"
14-
]
12+
authors = ["Yoshua Wuyts <[email protected]>"]
1513

1614
[profile.bench]
1715
debug = true
@@ -33,17 +31,21 @@ std = ["alloc"]
3331
alloc = ["bitvec/alloc", "dep:slab", "dep:smallvec"]
3432

3533
[dependencies]
36-
bitvec = { version = "1.0.1", default-features = false }
34+
bitvec = { version = "1.0.1", default-features = false, features = ["alloc"] }
3735
futures-core = { version = "0.3", default-features = false }
36+
futures-lite = "1.12.0"
3837
pin-project = "1.0.8"
3938
slab = { version = "0.4.8", optional = true }
4039
smallvec = { version = "1.11.0", optional = true }
4140

4241
[dev-dependencies]
4342
async-std = { version = "1.12.0", features = ["attributes"] }
44-
criterion = { version = "0.3", features = ["async", "async_futures", "html_reports"] }
43+
criterion = { version = "0.3", features = [
44+
"async",
45+
"async_futures",
46+
"html_reports",
47+
] }
4548
futures = "0.3.25"
46-
futures-lite = "1.12.0"
4749
futures-time = "3.0.0"
4850
lending-stream = "1.0.0"
4951
rand = "0.8.5"

examples/happy_eyeballs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,5 @@ async fn open_tcp_socket(
4444
}
4545

4646
// Start connecting. If an attempt succeeds, cancel all others attempts.
47-
Ok(futures.race_ok().await?)
47+
futures.race_ok().await
4848
}

src/collections/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[cfg(feature = "alloc")]
2+
pub mod vec;

src/collections/vec.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! Parallel iterator types for [vectors][std::vec] (`Vec<T>`)
2+
//!
3+
//! You will rarely need to interact with this module directly unless you need
4+
//! to name one of the iterator types.
5+
//!
6+
//! [std::vec]: https://doc.rust-lang.org/stable/std/vec/use core::future::Ready;
7+
8+
use crate::concurrent_stream::{self, FromStream};
9+
use crate::prelude::*;
10+
use crate::utils::{from_iter, FromIter};
11+
#[cfg(all(feature = "alloc", not(feature = "std")))]
12+
use alloc::vec::Vec;
13+
use core::future::Ready;
14+
15+
pub use crate::future::join::vec::Join;
16+
pub use crate::future::race::vec::Race;
17+
pub use crate::future::race_ok::vec::{AggregateError, RaceOk};
18+
pub use crate::future::try_join::vec::TryJoin;
19+
pub use crate::stream::chain::vec::Chain;
20+
pub use crate::stream::merge::vec::Merge;
21+
pub use crate::stream::zip::vec::Zip;
22+
23+
/// Concurrent async iterator that moves out of a vector.
24+
#[derive(Debug)]
25+
pub struct IntoConcurrentStream<T>(FromStream<FromIter<alloc::vec::IntoIter<T>>>);
26+
27+
impl<T> ConcurrentStream for IntoConcurrentStream<T> {
28+
type Item = T;
29+
30+
type Future = Ready<T>;
31+
32+
async fn drive<C>(self, consumer: C) -> C::Output
33+
where
34+
C: concurrent_stream::Consumer<Self::Item, Self::Future>,
35+
{
36+
self.0.drive(consumer).await
37+
}
38+
39+
fn concurrency_limit(&self) -> Option<core::num::NonZeroUsize> {
40+
self.0.concurrency_limit()
41+
}
42+
}
43+
44+
impl<T> concurrent_stream::IntoConcurrentStream for Vec<T> {
45+
type Item = T;
46+
47+
type IntoConcurrentStream = IntoConcurrentStream<T>;
48+
49+
fn into_co_stream(self) -> Self::IntoConcurrentStream {
50+
let stream = from_iter(self);
51+
let co_stream = stream.co();
52+
IntoConcurrentStream(co_stream)
53+
}
54+
}
55+
56+
#[cfg(test)]
57+
mod test {
58+
use crate::prelude::*;
59+
60+
#[test]
61+
fn collect() {
62+
futures_lite::future::block_on(async {
63+
let v: Vec<_> = vec![1, 2, 3, 4, 5].into_co_stream().collect().await;
64+
assert_eq!(v, &[1, 2, 3, 4, 5]);
65+
});
66+
}
67+
}

src/concurrent_stream/enumerate.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use pin_project::pin_project;
2+
3+
use super::{ConcurrentStream, Consumer};
4+
use core::future::Future;
5+
use core::num::NonZeroUsize;
6+
use core::pin::Pin;
7+
use core::task::{ready, Context, Poll};
8+
9+
/// A concurrent iterator that yields the current count and the element during iteration.
10+
///
11+
/// This `struct` is created by the [`enumerate`] method on [`ConcurrentStream`]. See its
12+
/// documentation for more.
13+
///
14+
/// [`enumerate`]: ConcurrentStream::enumerate
15+
/// [`ConcurrentStream`]: trait.ConcurrentStream.html
16+
#[derive(Debug)]
17+
pub struct Enumerate<CS: ConcurrentStream> {
18+
inner: CS,
19+
}
20+
21+
impl<CS: ConcurrentStream> Enumerate<CS> {
22+
pub(crate) fn new(inner: CS) -> Self {
23+
Self { inner }
24+
}
25+
}
26+
27+
impl<CS: ConcurrentStream> ConcurrentStream for Enumerate<CS> {
28+
type Item = (usize, CS::Item);
29+
type Future = EnumerateFuture<CS::Future, CS::Item>;
30+
31+
async fn drive<C>(self, consumer: C) -> C::Output
32+
where
33+
C: Consumer<Self::Item, Self::Future>,
34+
{
35+
self.inner
36+
.drive(EnumerateConsumer {
37+
inner: consumer,
38+
count: 0,
39+
})
40+
.await
41+
}
42+
43+
fn concurrency_limit(&self) -> Option<NonZeroUsize> {
44+
self.inner.concurrency_limit()
45+
}
46+
47+
fn size_hint(&self) -> (usize, Option<usize>) {
48+
self.inner.size_hint()
49+
}
50+
}
51+
52+
#[pin_project]
53+
struct EnumerateConsumer<C> {
54+
#[pin]
55+
inner: C,
56+
count: usize,
57+
}
58+
impl<C, Item, Fut> Consumer<Item, Fut> for EnumerateConsumer<C>
59+
where
60+
Fut: Future<Output = Item>,
61+
C: Consumer<(usize, Item), EnumerateFuture<Fut, Item>>,
62+
{
63+
type Output = C::Output;
64+
65+
async fn send(self: Pin<&mut Self>, future: Fut) -> super::ConsumerState {
66+
let this = self.project();
67+
let count = *this.count;
68+
*this.count += 1;
69+
this.inner.send(EnumerateFuture::new(future, count)).await
70+
}
71+
72+
async fn progress(self: Pin<&mut Self>) -> super::ConsumerState {
73+
let this = self.project();
74+
this.inner.progress().await
75+
}
76+
77+
async fn flush(self: Pin<&mut Self>) -> Self::Output {
78+
let this = self.project();
79+
this.inner.flush().await
80+
}
81+
}
82+
83+
/// Takes a future and maps it to another future via a closure
84+
#[derive(Debug)]
85+
#[pin_project::pin_project]
86+
pub struct EnumerateFuture<FutT, T>
87+
where
88+
FutT: Future<Output = T>,
89+
{
90+
done: bool,
91+
#[pin]
92+
fut_t: FutT,
93+
count: usize,
94+
}
95+
96+
impl<FutT, T> EnumerateFuture<FutT, T>
97+
where
98+
FutT: Future<Output = T>,
99+
{
100+
fn new(fut_t: FutT, count: usize) -> Self {
101+
Self {
102+
done: false,
103+
fut_t,
104+
count,
105+
}
106+
}
107+
}
108+
109+
impl<FutT, T> Future for EnumerateFuture<FutT, T>
110+
where
111+
FutT: Future<Output = T>,
112+
{
113+
type Output = (usize, T);
114+
115+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
116+
let this = self.project();
117+
if *this.done {
118+
panic!("future has already been polled to completion once");
119+
}
120+
121+
let item = ready!(this.fut_t.poll(cx));
122+
*this.done = true;
123+
Poll::Ready((*this.count, item))
124+
}
125+
}
126+
127+
#[cfg(test)]
128+
mod test {
129+
// use crate::concurrent_stream::{ConcurrentStream, IntoConcurrentStream};
130+
use crate::prelude::*;
131+
use futures_lite::stream;
132+
use futures_lite::StreamExt;
133+
use std::num::NonZeroUsize;
134+
135+
#[test]
136+
fn enumerate() {
137+
futures_lite::future::block_on(async {
138+
let mut n = 0;
139+
stream::iter(std::iter::from_fn(|| {
140+
let v = n;
141+
n += 1;
142+
Some(v)
143+
}))
144+
.take(5)
145+
.co()
146+
.limit(NonZeroUsize::new(1))
147+
.enumerate()
148+
.for_each(|(index, n)| async move {
149+
assert_eq!(index, n);
150+
})
151+
.await;
152+
});
153+
}
154+
}

0 commit comments

Comments
 (0)