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 Shutdown,
30}
31
32struct FinishActivePoll<'a>(Pin<&'a mut WriterState>);
33
34impl<'a> Future for FinishActivePoll<'a> {
35 type Output = std::io::Result<Option<PollOperation>>;
36
37 fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
38 match &mut *self.0 {
39 WriterState::Poll(fut, _) => match fut.poll_unpin(cx) {
40 Poll::Ready(Ok(new_state)) => {
41 debug_assert!(!matches!(&new_state, WriterState::Poll(..)));
42
43 let WriterState::Poll(_, operation) =
44 std::mem::replace(&mut *self.0, new_state)
45 else {
46 unreachable!()
47 };
48
49 Poll::Ready(Ok(Some(operation)))
50 },
51 Poll::Ready(Err(e)) => {
52 *self.0 = WriterState::Finished;
53 Poll::Ready(Err(e))
54 },
55 Poll::Pending => Poll::Pending,
56 },
57
58 WriterState::Ready(_) | WriterState::Finished => Poll::Ready(Ok(None)),
59 }
60 }
61}
62
63impl CloudWriterIoTraitWrap {
64 fn finish_active_poll(&mut self) -> FinishActivePoll<'_> {
65 FinishActivePoll(Pin::new(&mut self.state))
66 }
67
68 fn take_writer_from_ready_state(&mut self) -> Option<Box<CloudWriter>> {
69 if !matches!(&self.state, WriterState::Ready(_)) {
70 return None;
71 }
72
73 let WriterState::Ready(writer) = std::mem::replace(&mut self.state, WriterState::Finished)
74 else {
75 unreachable!()
76 };
77
78 Some(writer)
79 }
80
81 fn get_writer_mut_from_ready_state(&mut self) -> Option<&mut CloudWriter> {
82 if let WriterState::Ready(writer) = &mut self.state {
83 Some(writer.as_mut())
84 } else {
85 None
86 }
87 }
88
89 pub async fn write_all_owned(&mut self, bytes: Bytes) -> std::io::Result<()> {
90 self.finish_active_poll().await?;
91
92 self.get_writer_mut_from_ready_state()
93 .unwrap()
94 .write_all_owned(bytes)
95 .await?;
96
97 Ok(())
98 }
99
100 pub async fn into_cloud_writer(mut self) -> std::io::Result<CloudWriter> {
101 self.finish_active_poll().await?;
102
103 match self.state {
104 WriterState::Ready(writer) => Ok(*writer),
105 WriterState::Poll(..) => unreachable!(),
106 WriterState::Finished => panic!(),
107 }
108 }
109
110 pub fn as_cloud_writer(&mut self) -> std::io::Result<&mut CloudWriter> {
111 if !matches!(self.state, WriterState::Ready(_)) {
112 match &mut self.state {
113 WriterState::Ready(_) => unreachable!(),
114 WriterState::Poll(..) => ASYNC.block_in_place_on(self.finish_active_poll())?,
115 WriterState::Finished => panic!(),
116 };
117 }
118
119 let WriterState::Ready(writer) = &mut self.state else {
120 panic!()
121 };
122
123 Ok(writer)
124 }
125}
126
127impl From<CloudWriter> for CloudWriterIoTraitWrap {
128 fn from(writer: CloudWriter) -> Self {
129 Self {
130 state: WriterState::Ready(Box::new(writer)),
131 }
132 }
133}
134
135impl std::io::Write for CloudWriterIoTraitWrap {
136 fn write(&mut self, mut buf: &[u8]) -> std::io::Result<usize> {
137 let total_buf_len = buf.len();
138 let buf: &mut &[u8] = &mut buf;
139
140 if let Some(writer) = self.get_writer_mut_from_ready_state() {
141 let full = writer.fill_buffer_from_slice(buf);
142
143 if !full {
144 assert!(buf.is_empty());
145 return Ok(total_buf_len);
146 }
147 }
148
149 ASYNC.block_in_place_on(async {
150 self.finish_active_poll().await?;
151
152 let writer = self.get_writer_mut_from_ready_state().unwrap();
153
154 loop {
155 writer.flush_full_chunk().await?;
156
157 if !writer.fill_buffer_from_slice(buf) {
158 break;
159 }
160 }
161
162 assert!(buf.is_empty());
163
164 Ok(total_buf_len)
165 })
166 }
167
168 fn flush(&mut self) -> std::io::Result<()> {
169 match &self.state {
171 WriterState::Poll(..) => ASYNC
172 .block_in_place_on(self.finish_active_poll())
173 .map(|_| ()),
174 WriterState::Ready(_) | WriterState::Finished => Ok(()),
175 }
176 }
177}
178
179impl WritableTrait for CloudWriterIoTraitWrap {
180 fn close(&mut self) -> std::io::Result<()> {
181 ASYNC.block_in_place_on(async {
182 self.finish_active_poll().await?;
183
184 let mut writer = self.take_writer_from_ready_state().unwrap();
185 writer.finish().await?;
186
187 Ok(())
188 })
189 }
190
191 fn sync_all(&self) -> std::io::Result<()> {
192 Ok(())
193 }
194
195 fn sync_data(&self) -> std::io::Result<()> {
196 Ok(())
197 }
198}
199
200impl tokio::io::AsyncWrite for CloudWriterIoTraitWrap {
201 fn poll_write(
202 mut self: Pin<&mut Self>,
203 cx: &mut std::task::Context<'_>,
204 buf: &[u8],
205 ) -> std::task::Poll<std::io::Result<usize>> {
206 loop {
207 let offset = match ready!(self.finish_active_poll().poll_unpin(cx))? {
208 Some(PollOperation::Write { slice_ptr, written })
209 if slice_ptr == buf.as_ptr() as usize =>
210 {
211 written
212 },
213 Some(_) => panic!(),
214 None => 0,
215 };
216
217 let writer = self.get_writer_mut_from_ready_state().unwrap();
218
219 let offset_buf: &mut &[u8] = &mut &buf[offset..];
220
221 let full = writer.fill_buffer_from_slice(offset_buf);
222
223 if !full {
224 assert!(offset_buf.is_empty());
225 return Poll::Ready(Ok(buf.len()));
226 };
227
228 let new_offset = buf.len() - offset_buf.len();
229
230 let mut writer = self.take_writer_from_ready_state().unwrap();
231
232 self.state = WriterState::Poll(
233 Box::pin(async move {
234 writer.flush_full_chunk().await?;
235 Ok(WriterState::Ready(writer))
236 }),
237 PollOperation::Write {
238 slice_ptr: buf.as_ptr() as usize,
239 written: new_offset,
240 },
241 );
242 }
243 }
244
245 fn poll_flush(
246 mut self: Pin<&mut Self>,
247 cx: &mut std::task::Context<'_>,
248 ) -> std::task::Poll<std::io::Result<()>> {
249 match ready!(self.finish_active_poll().poll_unpin(cx))? {
251 Some(_) => panic!(),
252 None => Poll::Ready(Ok(())),
253 }
254 }
255
256 fn poll_shutdown(
257 mut self: Pin<&mut Self>,
258 cx: &mut std::task::Context<'_>,
259 ) -> std::task::Poll<std::io::Result<()>> {
260 loop {
261 match ready!(self.finish_active_poll().poll_unpin(cx))? {
262 Some(PollOperation::Shutdown) => return Poll::Ready(Ok(())),
263 Some(_) => panic!(),
264 None => {
265 let mut writer = self.take_writer_from_ready_state().unwrap();
266
267 self.state = WriterState::Poll(
268 Box::pin(async move {
269 writer.finish().await?;
270 Ok(WriterState::Finished)
271 }),
272 PollOperation::Shutdown,
273 );
274 },
275 }
276 }
277 }
278}