Skip to main content

polars_io/cloud/cloud_writer/
io_trait_wrap.rs

1use std::pin::Pin;
2use std::task::{Poll, ready};
3
4use bytes::Bytes;
5use futures::FutureExt;
6use polars_core::runtime::ASYNC;
7
8use crate::cloud::cloud_writer::CloudWriter;
9use crate::utils::file::WritableTrait;
10
11/// Wrapper on [`CloudWriter`] that implements [`std::io::Write`] and [`tokio::io::AsyncWrite`].
12pub struct CloudWriterIoTraitWrap {
13    state: WriterState,
14}
15
16enum WriterState {
17    Ready(Box<CloudWriter>),
18    Poll(
19        Pin<Box<dyn Future<Output = std::io::Result<WriterState>> + Send + 'static>>,
20        PollOperation,
21    ),
22    Finished,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
26enum PollOperation {
27    // (slice_addr, slice_len)
28    Write { slice_ptr: usize, written: usize },
29    Flush,
30    Shutdown,
31}
32
33struct FinishActivePoll<'a>(Pin<&'a mut WriterState>);
34
35impl<'a> Future for FinishActivePoll<'a> {
36    type Output = std::io::Result<Option<PollOperation>>;
37
38    fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
39        match &mut *self.0 {
40            WriterState::Poll(fut, _) => match fut.poll_unpin(cx) {
41                Poll::Ready(Ok(new_state)) => {
42                    debug_assert!(!matches!(&new_state, WriterState::Poll(..)));
43
44                    let WriterState::Poll(_, operation) =
45                        std::mem::replace(&mut *self.0, new_state)
46                    else {
47                        unreachable!()
48                    };
49
50                    Poll::Ready(Ok(Some(operation)))
51                },
52                Poll::Ready(Err(e)) => {
53                    *self.0 = WriterState::Finished;
54                    Poll::Ready(Err(e))
55                },
56                Poll::Pending => Poll::Pending,
57            },
58
59            WriterState::Ready(_) | WriterState::Finished => Poll::Ready(Ok(None)),
60        }
61    }
62}
63
64impl CloudWriterIoTraitWrap {
65    fn finish_active_poll(&mut self) -> FinishActivePoll<'_> {
66        FinishActivePoll(Pin::new(&mut self.state))
67    }
68
69    fn take_writer_from_ready_state(&mut self) -> Option<Box<CloudWriter>> {
70        if !matches!(&self.state, WriterState::Ready(_)) {
71            return None;
72        }
73
74        let WriterState::Ready(writer) = std::mem::replace(&mut self.state, WriterState::Finished)
75        else {
76            unreachable!()
77        };
78
79        Some(writer)
80    }
81
82    fn get_writer_mut_from_ready_state(&mut self) -> Option<&mut CloudWriter> {
83        if let WriterState::Ready(writer) = &mut self.state {
84            Some(writer.as_mut())
85        } else {
86            None
87        }
88    }
89
90    pub async fn write_all_owned(&mut self, bytes: Bytes) -> std::io::Result<()> {
91        self.finish_active_poll().await?;
92
93        self.get_writer_mut_from_ready_state()
94            .unwrap()
95            .write_all_owned(bytes)
96            .await?;
97
98        Ok(())
99    }
100
101    pub async fn write_multiple_owned_unbuffered<I, T>(&mut self, bytes: I) -> std::io::Result<()>
102    where
103        I: IntoIterator<Item = T>,
104        Bytes: From<T>,
105    {
106        self.finish_active_poll().await?;
107
108        self.get_writer_mut_from_ready_state()
109            .unwrap()
110            .write_multiple_owned_unbuffered(bytes)
111            .await?;
112
113        Ok(())
114    }
115
116    pub async fn into_cloud_writer(mut self) -> std::io::Result<CloudWriter> {
117        self.finish_active_poll().await?;
118
119        match self.state {
120            WriterState::Ready(writer) => Ok(*writer),
121            WriterState::Poll(..) => unreachable!(),
122            WriterState::Finished => panic!(),
123        }
124    }
125
126    pub fn as_cloud_writer(&mut self) -> std::io::Result<&mut CloudWriter> {
127        if !matches!(self.state, WriterState::Ready(_)) {
128            match &mut self.state {
129                WriterState::Ready(_) => unreachable!(),
130                WriterState::Poll(..) => ASYNC.block_in_place_on(self.finish_active_poll())?,
131                WriterState::Finished => panic!(),
132            };
133        }
134
135        let WriterState::Ready(writer) = &mut self.state else {
136            panic!()
137        };
138
139        Ok(writer)
140    }
141}
142
143impl From<CloudWriter> for CloudWriterIoTraitWrap {
144    fn from(writer: CloudWriter) -> Self {
145        Self {
146            state: WriterState::Ready(Box::new(writer)),
147        }
148    }
149}
150
151impl std::io::Write for CloudWriterIoTraitWrap {
152    fn write(&mut self, mut buf: &[u8]) -> std::io::Result<usize> {
153        let total_buf_len = buf.len();
154        let buf: &mut &[u8] = &mut buf;
155
156        if let Some(writer) = self.get_writer_mut_from_ready_state() {
157            let full = writer.fill_buffer_from_slice(buf);
158
159            if !full {
160                assert!(buf.is_empty());
161                return Ok(total_buf_len);
162            }
163        }
164
165        ASYNC.block_in_place_on(async {
166            self.finish_active_poll().await?;
167
168            let writer = self.get_writer_mut_from_ready_state().unwrap();
169
170            loop {
171                writer.flush_full_chunk().await?;
172
173                if !writer.fill_buffer_from_slice(buf) {
174                    break;
175                }
176            }
177
178            assert!(buf.is_empty());
179
180            Ok(total_buf_len)
181        })
182    }
183
184    fn flush(&mut self) -> std::io::Result<()> {
185        if self
186            .get_writer_mut_from_ready_state()
187            .is_some_and(|w| !w.has_buffered_bytes())
188        {
189            return Ok(());
190        }
191
192        ASYNC.block_in_place_on(async {
193            self.finish_active_poll().await?;
194
195            self.get_writer_mut_from_ready_state()
196                .unwrap()
197                .flush()
198                .await?;
199
200            Ok(())
201        })
202    }
203}
204
205impl WritableTrait for CloudWriterIoTraitWrap {
206    fn close(&mut self) -> std::io::Result<()> {
207        ASYNC.block_in_place_on(async {
208            self.finish_active_poll().await?;
209
210            let mut writer = self.take_writer_from_ready_state().unwrap();
211            writer.finish().await?;
212
213            Ok(())
214        })
215    }
216
217    fn sync_all(&self) -> std::io::Result<()> {
218        Ok(())
219    }
220
221    fn sync_data(&self) -> std::io::Result<()> {
222        Ok(())
223    }
224}
225
226impl tokio::io::AsyncWrite for CloudWriterIoTraitWrap {
227    fn poll_write(
228        mut self: Pin<&mut Self>,
229        cx: &mut std::task::Context<'_>,
230        buf: &[u8],
231    ) -> std::task::Poll<std::io::Result<usize>> {
232        loop {
233            let offset = match ready!(self.finish_active_poll().poll_unpin(cx))? {
234                Some(PollOperation::Write { slice_ptr, written })
235                    if slice_ptr == buf.as_ptr() as usize =>
236                {
237                    written
238                },
239                Some(_) => panic!(),
240                None => 0,
241            };
242
243            let writer = self.get_writer_mut_from_ready_state().unwrap();
244
245            let offset_buf: &mut &[u8] = &mut &buf[offset..];
246
247            let full = writer.fill_buffer_from_slice(offset_buf);
248
249            if !full {
250                assert!(offset_buf.is_empty());
251                return Poll::Ready(Ok(buf.len()));
252            };
253
254            let new_offset = buf.len() - offset_buf.len();
255
256            let mut writer = self.take_writer_from_ready_state().unwrap();
257
258            self.state = WriterState::Poll(
259                Box::pin(async move {
260                    writer.flush_full_chunk().await?;
261                    Ok(WriterState::Ready(writer))
262                }),
263                PollOperation::Write {
264                    slice_ptr: buf.as_ptr() as usize,
265                    written: new_offset,
266                },
267            );
268        }
269    }
270
271    fn poll_flush(
272        mut self: Pin<&mut Self>,
273        cx: &mut std::task::Context<'_>,
274    ) -> std::task::Poll<std::io::Result<()>> {
275        loop {
276            match ready!(self.finish_active_poll().poll_unpin(cx))? {
277                Some(PollOperation::Flush) => return Poll::Ready(Ok(())),
278                Some(_) => panic!(),
279                None => {
280                    let mut writer = self.take_writer_from_ready_state().unwrap();
281
282                    self.state = WriterState::Poll(
283                        Box::pin(async move {
284                            writer.flush().await?;
285                            Ok(WriterState::Ready(writer))
286                        }),
287                        PollOperation::Flush,
288                    )
289                },
290            }
291        }
292    }
293
294    fn poll_shutdown(
295        mut self: Pin<&mut Self>,
296        cx: &mut std::task::Context<'_>,
297    ) -> std::task::Poll<std::io::Result<()>> {
298        loop {
299            match ready!(self.finish_active_poll().poll_unpin(cx))? {
300                Some(PollOperation::Shutdown) => return Poll::Ready(Ok(())),
301                Some(_) => panic!(),
302                None => {
303                    let mut writer = self.take_writer_from_ready_state().unwrap();
304
305                    self.state = WriterState::Poll(
306                        Box::pin(async move {
307                            writer.finish().await?;
308                            Ok(WriterState::Finished)
309                        }),
310                        PollOperation::Shutdown,
311                    );
312                },
313            }
314        }
315    }
316}