Skip to main content

pollable_map/
stream.rs

1pub mod set;
2#[cfg(all(feature = "std", feature = "timeout"))]
3pub mod timeout_map;
4#[cfg(all(feature = "std", feature = "timeout"))]
5pub mod timeout_set;
6
7use crate::common::InnerMap;
8use alloc::boxed::Box;
9use core::pin::Pin;
10use core::task::{Context, Poll, Waker};
11use futures::stream::{FusedStream, SelectAll};
12use futures::{Stream, StreamExt};
13
14/// Combining multiple streams into one, with each stream having a unique key.
15pub struct StreamMap<K, S> {
16    list: SelectAll<Pin<Box<InnerMap<K, S>>>>,
17    empty: bool,
18    terminate_on_empty: bool,
19    waker: Option<Waker>,
20}
21
22impl<K, T> Default for StreamMap<K, T>
23where
24    K: Clone,
25    T: Stream,
26{
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32impl<K, T> StreamMap<K, T>
33where
34    K: Clone,
35    T: Stream,
36{
37    /// Creates an empty [`StreamMap`]
38    pub fn new() -> Self {
39        Self {
40            list: SelectAll::new(),
41            empty: true,
42            terminate_on_empty: false,
43            waker: None,
44        }
45    }
46
47    /// Set flag to terminate stream after all streams are completed
48    pub fn set_terminate_on_empty(&mut self, terminate: bool) {
49        self.terminate_on_empty = terminate;
50    }
51}
52
53impl<K, T> StreamMap<K, T>
54where
55    K: Clone + PartialEq,
56    T: Stream,
57{
58    /// Insert a stream into the map with a unique key.
59    /// The function will return true if the map does not have the key present,
60    /// otherwise it will return false
61    pub fn insert(&mut self, key: K, stream: T) -> bool {
62        if self.contains_key(&key) {
63            return false;
64        }
65
66        let st = Box::pin(InnerMap::new(key, stream));
67        self.list.push(st);
68
69        if let Some(waker) = self.waker.take() {
70            waker.wake();
71        }
72
73        self.empty = false;
74        true
75    }
76
77    /// Mark stream with assigned key to wake up on successful yield.
78    /// Will return false if stream does not exist or if value is the same as
79    /// previously set.
80    pub fn set_wake_on_success(&mut self, key: &K, wake_on_success: bool) -> bool {
81        self.list
82            .iter_mut()
83            .find(|st| st.as_ref().key_pin().eq(key))
84            .is_some_and(|st| st.as_mut().set_wake_on_success_pin(wake_on_success))
85    }
86
87    /// An iterator visiting all key-value pairs in arbitrary order.
88    pub fn iter(&self) -> impl Iterator<Item = (&K, &T)> {
89        self.list
90            .iter()
91            .filter_map(|st| st.as_ref().key_value_pin_ref())
92            .map(|(key, stream)| (key, stream.get_ref()))
93    }
94
95    /// An iterator visiting all key-value pairs with a pinned valued in arbitrary order
96    pub fn iter_pin(&mut self) -> impl Iterator<Item = (&K, Pin<&mut T>)> {
97        self.list
98            .iter_mut()
99            .filter_map(|st| st.as_mut().key_value_pin())
100    }
101
102    /// Returns an iterator visiting all keys in arbitrary order.
103    pub fn keys(&self) -> impl Iterator<Item = &K> {
104        self.list
105            .iter()
106            .filter_map(|st| st.as_ref().key_value_pin_ref().map(|(key, _)| key))
107    }
108
109    /// An iterator visiting all values in arbitrary order.
110    pub fn values(&self) -> impl Iterator<Item = &T> {
111        self.list
112            .iter()
113            .filter_map(|st| st.as_ref().inner_pin_ref())
114            .map(Pin::get_ref)
115    }
116
117    /// Returns `true` if the map contains a stream for the specified key.
118    pub fn contains_key(&self, key: &K) -> bool {
119        self.list
120            .iter()
121            .filter(|st| st.as_ref().inner_pin_ref().is_some())
122            .any(|st| st.as_ref().key_pin().eq(key))
123    }
124
125    /// Clears the map.
126    pub fn clear(&mut self) {
127        self.list.clear();
128    }
129
130    /// Returns a reference to the stream corresponding to the key.
131    pub fn get(&self, key: &K) -> Option<&T> {
132        self.list
133            .iter()
134            .find(|st| st.as_ref().key_pin().eq(key))
135            .and_then(|st| st.as_ref().inner_pin_ref())
136            .map(Pin::get_ref)
137    }
138
139    /// Returns a pinned stream corresponding to the key.
140    pub fn get_pinned(&mut self, key: &K) -> Option<Pin<&mut T>> {
141        self.list
142            .iter_mut()
143            .find(|st| st.as_ref().key_pin().eq(key))
144            .and_then(|st| st.as_mut().inner_pin())
145    }
146
147    /// Returns the number of streams in the map.
148    pub fn len(&self) -> usize {
149        self.list
150            .iter()
151            .filter(|st| st.as_ref().inner_pin_ref().is_some())
152            .count()
153    }
154
155    /// Return `true` map contains no elements.
156    pub fn is_empty(&self) -> bool {
157        self.len() == 0
158    }
159}
160
161impl<K, T> StreamMap<K, T>
162where
163    K: Clone + PartialEq,
164    T: Stream + Unpin,
165{
166    /// An iterator visiting all key-value pairs mutably in arbitrary order.
167    pub fn iter_mut(&mut self) -> impl Iterator<Item = (&K, &mut T)> {
168        self.iter_pin()
169            .map(|(key, stream)| (key, Pin::get_mut(stream)))
170    }
171
172    /// An iterator visiting all values mutably in arbitrary order.
173    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> {
174        self.iter_pin().map(|(_, stream)| Pin::get_mut(stream))
175    }
176
177    /// Returns a mutable stream corresponding to the key.
178    pub fn get_mut(&mut self, key: &K) -> Option<&mut T> {
179        self.get_pinned(key).map(Pin::get_mut)
180    }
181
182    /// Returns a mutable stream or default value if it does not exist.
183    pub fn get_mut_or_default(&mut self, key: &K) -> &mut T
184    where
185        T: Default,
186    {
187        self.insert(key.clone(), T::default());
188        self.get_mut(key).expect("valid entry")
189    }
190
191    /// Removes a key from the map, returning the stream.
192    pub fn remove(&mut self, key: &K) -> Option<T> {
193        self.list
194            .iter_mut()
195            .find(|st| st.as_ref().key_pin().eq(key))
196            .and_then(|st| st.as_mut().take_inner_pin())
197    }
198}
199
200impl<K, T> FromIterator<(K, T)> for StreamMap<K, T>
201where
202    K: Clone + PartialEq,
203    T: Stream,
204{
205    fn from_iter<I: IntoIterator<Item = (K, T)>>(iter: I) -> Self {
206        let mut maps = Self::new();
207        for (key, val) in iter {
208            maps.insert(key, val);
209        }
210        maps
211    }
212}
213
214impl<K, T> Stream for StreamMap<K, T>
215where
216    K: Clone,
217    T: Stream,
218{
219    type Item = (K, T::Item);
220
221    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
222        if self.list.is_empty() {
223            if self.terminate_on_empty {
224                return Poll::Ready(None);
225            }
226            self.waker = Some(cx.waker().clone());
227            return Poll::Pending;
228        }
229
230        loop {
231            match self.list.poll_next_unpin(cx) {
232                Poll::Ready(Some((key, Some(item)))) => return Poll::Ready(Some((key, item))),
233                // We continue in case there is any progress on the set of streams
234                Poll::Ready(Some((_key, None))) => continue,
235                Poll::Ready(None) => {
236                    // While we could allow the stream to continue to be pending, it would make more sense to notify that the stream
237                    // is empty without needing to explicitly check while polling the actual "map" itself
238                    // So we would mark a field to notify that the state is finished and return `Poll::Ready(None)` so the stream
239                    // can be terminated while on the next poll, we could let it be return pending.
240                    // We do this so that we are not returning `Poll::Ready(None)` each time the map is polled
241                    // as that may be seen as UB and may cause an increase in cpu usage
242                    if self.empty {
243                        self.waker = Some(cx.waker().clone());
244                        return Poll::Pending;
245                    }
246
247                    self.empty = true;
248                    return Poll::Ready(None);
249                }
250                Poll::Pending => {
251                    // Returning `None` does not mean the stream is actually terminated
252                    self.waker = Some(cx.waker().clone());
253                    return Poll::Pending;
254                }
255            }
256        }
257    }
258
259    fn size_hint(&self) -> (usize, Option<usize>) {
260        self.list.size_hint()
261    }
262}
263
264impl<K, T> FusedStream for StreamMap<K, T>
265where
266    K: Clone,
267    T: Stream,
268{
269    fn is_terminated(&self) -> bool {
270        self.terminate_on_empty && self.list.is_terminated()
271    }
272}
273
274#[cfg(test)]
275mod test {
276    use crate::stream::StreamMap;
277    use core::pin::Pin;
278    use core::task::{Context, Poll};
279    use futures::stream::empty;
280    use futures::{Stream, StreamExt};
281
282    struct Once<T> {
283        value: T,
284    }
285
286    impl<T> Once<T> {
287        pub fn new(value: T) -> Self {
288            Self { value }
289        }
290
291        pub fn get(&self) -> &T {
292            &self.value
293        }
294
295        pub fn set(&mut self, val: T) {
296            self.value = val;
297        }
298    }
299
300    impl<T> Stream for Once<T>
301    where
302        T: Unpin,
303    {
304        type Item = T;
305        fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<Self::Item>> {
306            Poll::Pending
307        }
308    }
309
310    #[test]
311    fn existing_key() {
312        let mut map = StreamMap::new();
313        assert!(map.insert(1, empty::<()>()));
314        assert!(!map.insert(1, empty::<()>()));
315    }
316
317    #[test]
318    fn supports_unboxed_async_stream() {
319        let mut map = StreamMap::new();
320        let stream = futures::stream::unfold(0, |value| async move {
321            (value < 3).then_some((value, value + 1))
322        });
323        assert!(map.insert("numbers", stream));
324
325        futures::executor::block_on(async move {
326            assert_eq!(map.next().await, Some(("numbers", 0)));
327            assert_eq!(map.next().await, Some(("numbers", 1)));
328            assert_eq!(map.next().await, Some(("numbers", 2)));
329            assert_eq!(map.next().await, None);
330        });
331    }
332
333    #[test]
334    fn poll_multiple_keyed_streams() {
335        let mut map = StreamMap::new();
336        map.insert(1, futures::stream::once(async { 10 }).boxed());
337        map.insert(2, futures::stream::once(async { 20 }).boxed());
338
339        map.insert(3, futures::stream::iter(alloc::vec![30, 40, 50]).boxed());
340
341        futures::executor::block_on(async move {
342            assert_eq!(map.next().await, Some((1, 10)));
343            assert_eq!(map.next().await, Some((2, 20)));
344            assert_eq!(map.next().await, Some((3, 30)));
345            assert_eq!(map.next().await, Some((3, 40)));
346            assert_eq!(map.next().await, Some((3, 50)));
347            assert_eq!(map.next().await, None);
348            let pending =
349                futures::future::poll_fn(|cx| Poll::Ready(map.poll_next_unpin(cx).is_pending()))
350                    .await;
351            assert!(pending);
352        })
353    }
354
355    #[test]
356    fn get_from_map() {
357        let mut map = StreamMap::new();
358        map.insert(1, Once::new(10));
359        map.insert(2, Once::new(20));
360
361        {
362            let value0 = map.get(&1).expect("valid entry").get();
363            let value1 = map.get(&2).expect("valid entry").get();
364
365            assert_eq!(value0, &10);
366            assert_eq!(value1, &20);
367        }
368
369        {
370            map.get_mut(&1).expect("valid entry").set(100);
371            let value0 = map.get(&1).expect("valid entry").get();
372            assert_eq!(*value0, 100);
373        }
374    }
375}