polars_io/cloud/cloud_writer/
io_trait_wrap.rs1use 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
11pub 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 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}