Skip to main content

vortex_array/
mask_future.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::future::Future;
5use std::ops::Range;
6use std::sync::Arc;
7
8use futures::FutureExt;
9use futures::TryFutureExt;
10use futures::future::BoxFuture;
11use futures::future::Shared;
12use vortex_error::SharedVortexResult;
13use vortex_error::VortexError;
14use vortex_error::VortexResult;
15use vortex_error::vortex_panic;
16use vortex_mask::Mask;
17
18/// A future that resolves to a mask.
19#[derive(Clone)]
20pub struct MaskFuture {
21    inner: Shared<BoxFuture<'static, SharedVortexResult<Mask>>>,
22    len: usize,
23}
24
25impl MaskFuture {
26    /// Create a new MaskFuture from a future that returns a mask.
27    pub fn new<F>(len: usize, fut: F) -> Self
28    where
29        F: Future<Output = VortexResult<Mask>> + Send + 'static,
30    {
31        Self {
32            inner: fut
33                .inspect(move |r| {
34                    if let Ok(mask) = r
35                        && mask.len() != len {
36                            vortex_panic!("MaskFuture created with future that returned mask of incorrect length (expected {}, got {})", len, mask.len());
37                        }
38                })
39                .map_err(Arc::new)
40                .boxed()
41                .shared(),
42            len,
43        }
44    }
45
46    /// Returns the length of the mask.
47    pub fn len(&self) -> usize {
48        self.len
49    }
50
51    /// Returns true if the mask is empty.
52    pub fn is_empty(&self) -> bool {
53        self.len == 0
54    }
55
56    /// Create a MaskFuture from a ready mask.
57    pub fn ready(mask: Mask) -> Self {
58        let fut = Self::new(mask.len(), async move { Ok(mask) });
59        // Complete the shared future now: it is ready on its first poll, and once complete,
60        // `peek` sees it.
61        let _ready = fut.inner.clone().now_or_never();
62        fut
63    }
64
65    /// Returns the mask if the future has completed, without polling it.
66    ///
67    /// [`MaskFuture::ready`] and slices of a completed mask have completed from the start. A
68    /// reader can check their mask before it requests any data, for example to skip unselected
69    /// rows. A failed future returns `None`; awaiting it reports the error.
70    pub fn peek(&self) -> Option<&Mask> {
71        self.inner.peek().and_then(|result| result.as_ref().ok())
72    }
73
74    /// Create a MaskFuture that resolves to a mask with all values set to true.
75    pub fn new_true(row_count: usize) -> Self {
76        Self::ready(Mask::new_true(row_count))
77    }
78
79    /// Create a MaskFuture that resolves to a slice of the original mask.
80    pub fn slice(&self, range: Range<usize>) -> Self {
81        // Slicing the whole mask is the identity. Cloning shares the existing future instead of
82        // allocating another boxed, shared one that would await it only to hand the mask back.
83        if range.start == 0 && range.end == self.len {
84            return self.clone();
85        }
86
87        // A slice of a completed mask is complete too.
88        if let Some(mask) = self.peek() {
89            return Self::ready(mask.slice(range));
90        }
91
92        let inner = self.inner.clone();
93        Self::new(range.len(), async move { Ok(inner.await?.slice(range)) })
94    }
95
96    pub fn inspect(
97        self,
98        f: impl FnOnce(&SharedVortexResult<Mask>) + 'static + Send + Sync,
99    ) -> Self {
100        let len = self.len;
101
102        Self {
103            inner: self.inner.inspect(f).boxed().shared(),
104            len,
105        }
106    }
107}
108
109impl Future for MaskFuture {
110    type Output = VortexResult<Mask>;
111
112    fn poll(
113        mut self: std::pin::Pin<&mut Self>,
114        cx: &mut std::task::Context<'_>,
115    ) -> std::task::Poll<Self::Output> {
116        self.inner.poll_unpin(cx).map_err(VortexError::from)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use vortex_buffer::BitBuffer;
123
124    use super::*;
125
126    /// Slicing resolves to the same mask the equivalent [`Mask::slice`] would produce, for both
127    /// the full range (which takes the identity fast path) and a sub-range.
128    #[test]
129    fn slice_resolves_to_sliced_mask() -> VortexResult<()> {
130        futures::executor::block_on(async {
131            let mask = Mask::from_buffer(BitBuffer::from_iter([true, false, true, true, false]));
132            let fut = MaskFuture::ready(mask.clone());
133
134            let full = fut.slice(0..mask.len());
135            assert_eq!(full.len(), mask.len());
136            assert_eq!(full.await?, mask);
137
138            let partial = fut.slice(0..mask.len() - 1);
139            assert_eq!(partial.len(), mask.len() - 1);
140            assert_eq!(partial.await?, Mask::from_iter([true, false, true, true]));
141            Ok(())
142        })
143    }
144
145    #[test]
146    fn ready_masks_and_their_slices_can_be_peeked() -> VortexResult<()> {
147        let mask = Mask::from_buffer(BitBuffer::from_iter([true, false, true, false]));
148        let fut = MaskFuture::ready(mask.clone());
149        assert_eq!(fut.peek(), Some(&mask));
150        assert_eq!(MaskFuture::new_true(3).peek(), Some(&Mask::new_true(3)));
151
152        let slice = fut.slice(1..3);
153        assert_eq!(slice.peek(), Some(&mask.slice(1..3)));
154        // A ready future returns its mask on the first poll.
155        assert_eq!(slice.now_or_never().transpose()?, Some(mask.slice(1..3)));
156        Ok(())
157    }
158
159    #[test]
160    fn pending_masks_can_be_peeked_once_complete() -> VortexResult<()> {
161        let mask = Mask::from_buffer(BitBuffer::from_iter([true, false, true]));
162        let pending = MaskFuture::new(3, async move { Ok(mask) });
163        assert!(pending.peek().is_none());
164        assert!(pending.slice(0..2).peek().is_none());
165
166        let mask = futures::executor::block_on(pending.clone())?;
167        assert_eq!(pending.peek(), Some(&mask));
168        assert_eq!(pending.slice(0..2).peek(), Some(&mask.slice(0..2)));
169        Ok(())
170    }
171}