1use std::{
15 fmt,
16 future::Future,
17 io,
18 mem::{self, MaybeUninit},
19 pin::Pin,
20 sync::atomic::{AtomicU64, Ordering},
21 task::{Context, Poll, ready},
22};
23
24use bytes::BytesMut;
25use futures::io::{AsyncRead, AsyncSeek, AsyncWrite};
26
27use super::{File, FileInner};
28use crate::{client, direct, error::Result};
29
30pub(crate) enum Either<D, C> {
32 Direct(D),
33 Client(C),
34}
35
36impl<T, D, C> Future for Either<D, C>
37where
38 D: Future<Output = T> + Unpin,
39 C: Future<Output = T> + Unpin,
40{
41 type Output = T;
42
43 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
44 match self.get_mut() {
45 Self::Direct(op) => Pin::new(op).poll(cx),
46 Self::Client(op) => Pin::new(op).poll(cx),
47 }
48 }
49}
50
51impl Either<direct::Body, client::Body> {
52 fn poll_read_uninit(
53 &mut self,
54 cx: &mut Context<'_>,
55 buf: &mut [MaybeUninit<u8>],
56 ) -> Poll<io::Result<usize>> {
57 match self {
58 Self::Direct(body) => Poll::Ready(Ok(body.read_uninit(buf))),
59 Self::Client(body) => body.poll_read_uninit(cx, buf),
60 }
61 }
62}
63
64pub(crate) trait WriteOp {
66 fn poll_send(&mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>>;
68 fn poll_finish(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<(usize, u64)>>;
71}
72
73impl<D: WriteOp, C: WriteOp> WriteOp for Either<D, C> {
74 fn poll_send(&mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
75 match self {
76 Self::Direct(op) => op.poll_send(cx, buf),
77 Self::Client(op) => op.poll_send(cx, buf),
78 }
79 }
80
81 fn poll_finish(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<(usize, u64)>> {
82 match self {
83 Self::Direct(op) => op.poll_finish(cx),
84 Self::Client(op) => op.poll_finish(cx),
85 }
86 }
87}
88
89type ReadOp = Either<direct::ReadOp, client::ReadOp>;
90type Body = Either<direct::Body, client::Body>;
91type Write = Either<direct::WriteOp, client::WriteOp>;
92type SizeOp = Either<direct::SizeOp, client::SizeOp>;
93
94#[derive(Default)]
99pub(super) struct Cursor {
100 op: Op,
101 scratch: BytesMut,
104}
105
106#[derive(Default)]
107enum Op {
108 #[default]
109 Idle,
110 Read(ReadOp, u64),
112 Body {
114 body: Body,
115 at: u64,
118 delivered: bool,
121 },
122 Write {
123 op: Write,
124 target: usize,
125 sent: usize,
126 reported: usize,
130 },
131 Size(SizeOp, i64),
133}
134
135impl fmt::Debug for Cursor {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 let op = match self.op {
138 Op::Idle => "idle",
139 Op::Read(..) => "read",
140 Op::Body { .. } => "body",
141 Op::Write { .. } => "write",
142 Op::Size(..) => "size",
143 };
144 f.debug_struct("Cursor")
145 .field("op", &op)
146 .finish_non_exhaustive()
147 }
148}
149
150impl Cursor {
151 fn drop_body(&mut self) {
153 if !matches!(self.op, Op::Body { .. }) {
154 return;
155 }
156 if let Op::Body {
157 body: Either::Direct(body),
158 ..
159 } = mem::take(&mut self.op)
160 {
161 self.scratch = body.into_scratch();
162 }
163 }
164
165 fn end_write(&mut self) {
167 if let Op::Write {
168 op: Either::Direct(op),
169 ..
170 } = mem::take(&mut self.op)
171 {
172 self.scratch = op.into_scratch();
173 }
174 }
175
176 fn is_busy(&self) -> bool {
177 !matches!(self.op, Op::Idle | Op::Body { .. })
178 }
179
180 async fn cancel(&mut self) {
184 match mem::take(&mut self.op) {
185 Op::Read(Either::Client(op), _) => op.cancel().await,
186 Op::Write {
187 op: Either::Client(op),
188 ..
189 } => op.cancel().await,
190 Op::Size(Either::Client(op), _) => op.cancel().await,
191 _ => {}
192 }
193 }
194}
195
196fn offset_delta(base: u64, delta: i64) -> io::Result<u64> {
198 base.checked_add_signed(delta).ok_or_else(|| {
199 io::Error::new(
200 io::ErrorKind::InvalidInput,
201 "seek would move the cursor outside the representable range",
202 )
203 })
204}
205
206fn in_progress(what: &str) -> io::Error {
207 io::Error::other(format!(
208 "file {what} while another operation is in progress"
209 ))
210}
211
212fn poll_read_op(op: &mut ReadOp, cx: &mut Context<'_>) -> Poll<io::Result<Body>> {
215 Poll::Ready(match op {
216 Either::Direct(op) => ready!(Pin::new(op).poll(cx)).map(Either::Direct),
217 Either::Client(op) => ready!(Pin::new(op).poll(cx)).map(Either::Client),
218 })
219}
220
221impl FileInner {
222 pub(crate) fn position(&self) -> &AtomicU64 {
224 match self {
225 Self::Direct(file) => file.position(),
226 Self::Client(file) => file.position(),
227 }
228 }
229
230 fn start_read(&self, offset: u64, len: usize, scratch: &mut BytesMut) -> Result<ReadOp> {
231 Ok(match self {
232 Self::Direct(file) => Either::Direct(file.start_read(offset, len, scratch)?),
233 Self::Client(file) => Either::Client(file.start_read(offset, len)?),
234 })
235 }
236
237 fn start_write(
238 &self,
239 offset: u64,
240 len: usize,
241 scratch: &mut BytesMut,
242 ) -> Result<(Write, usize)> {
243 Ok(match self {
244 Self::Direct(file) => {
245 let (op, target) = file.start_write(offset, len, scratch)?;
246 (Either::Direct(op), target)
247 }
248 Self::Client(file) => (Either::Client(file.start_write(offset)?), len),
249 })
250 }
251
252 fn start_size(&self) -> Result<SizeOp> {
253 Ok(match self {
254 Self::Direct(file) => Either::Direct(file.start_size()?),
255 Self::Client(file) => Either::Client(file.start_size()?),
256 })
257 }
258}
259
260impl File {
261 fn parts(&mut self) -> (&FileInner, &mut Cursor) {
262 (&self.inner, self.cursor.get_or_insert_default())
263 }
264
265 pub(super) fn check_idle(&self) -> Result<()> {
267 match &self.cursor {
268 Some(cursor) if cursor.is_busy() => {
269 Err(io::Error::other(format!("file operation {cursor:?} is still pending")).into())
270 }
271 _ => Ok(()),
272 }
273 }
274
275 pub(super) async fn cancel_cursor(&mut self) {
277 if let Some(cursor) = self.cursor.as_mut() {
278 cursor.cancel().await;
279 }
280 }
281}
282
283impl File {
284 pub fn poll_read_uninit(
292 &mut self,
293 cx: &mut Context<'_>,
294 buf: &mut [MaybeUninit<u8>],
295 ) -> Poll<io::Result<usize>> {
296 if buf.is_empty() {
297 return Poll::Ready(Ok(0));
298 }
299 let (inner, cursor) = self.parts();
300 let pos = inner.position();
301 loop {
302 match &mut cursor.op {
303 Op::Body { at, .. } if pos.load(Ordering::Relaxed) != *at => cursor.drop_body(),
304 Op::Body {
305 body,
306 at,
307 delivered,
308 } => {
309 let read = match ready!(body.poll_read_uninit(cx, buf)) {
310 Ok(read) => read,
311 Err(error) => {
312 cursor.drop_body();
313 return Poll::Ready(Err(error));
314 }
315 };
316 if read > 0 {
317 *delivered = true;
318 *at += read as u64;
319 pos.store(*at, Ordering::Relaxed);
320 return Poll::Ready(Ok(read));
321 }
322 let end_of_file = !*delivered;
323 cursor.drop_body();
324 if end_of_file {
325 return Poll::Ready(Ok(0));
326 }
327 }
328 Op::Read(op, at) => {
329 let at = *at;
330 let result = ready!(poll_read_op(op, cx));
331 cursor.op = Op::Idle;
332 cursor.op = Op::Body {
333 body: result?,
334 at,
335 delivered: false,
336 };
337 }
338 Op::Idle => {
339 let at = pos.load(Ordering::Relaxed);
340 let op = inner.start_read(at, buf.len(), &mut cursor.scratch)?;
341 cursor.op = Op::Read(op, at);
342 }
343 _ => return Poll::Ready(Err(in_progress("read polled"))),
344 }
345 }
346 }
347}
348
349impl AsyncRead for File {
350 fn poll_read(
351 self: Pin<&mut Self>,
352 cx: &mut Context<'_>,
353 buf: &mut [u8],
354 ) -> Poll<io::Result<usize>> {
355 let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit<u8>]) };
358 self.get_mut().poll_read_uninit(cx, buf)
359 }
360}
361
362impl AsyncWrite for File {
363 fn poll_write(
364 self: Pin<&mut Self>,
365 cx: &mut Context<'_>,
366 buf: &[u8],
367 ) -> Poll<io::Result<usize>> {
368 if buf.is_empty() {
369 return Poll::Ready(Ok(0));
370 }
371 let (inner, cursor) = self.get_mut().parts();
372 loop {
373 match &mut cursor.op {
374 Op::Body { .. } => cursor.drop_body(),
376 Op::Idle => {
377 let (op, target) = inner.start_write(
378 inner.position().load(Ordering::Relaxed),
379 buf.len(),
380 &mut cursor.scratch,
381 )?;
382 cursor.op = Op::Write {
383 op,
384 target,
385 sent: 0,
386 reported: 0,
387 };
388 }
389 Op::Write {
390 op,
391 target,
392 sent,
393 reported,
394 } => {
395 if *sent < *target {
396 let chunk = &buf[..buf.len().min(*target - *sent)];
397 let sent_now = match ready!(op.poll_send(cx, chunk)) {
398 Ok(0) => Err(io::ErrorKind::WriteZero.into()),
399 result => result,
400 };
401 let sent_now = match sent_now {
402 Ok(sent_now) => sent_now,
403 Err(error) => {
404 cursor.end_write();
405 return Poll::Ready(Err(error));
406 }
407 };
408 *sent += sent_now;
409 if *sent < *target {
410 *reported += sent_now;
411 return Poll::Ready(Ok(sent_now));
412 }
413 }
414 let result = ready!(op.poll_finish(cx));
415 let reported = *reported;
416 cursor.end_write();
417 let (written, end) = result?;
418 inner.position().store(end, Ordering::Relaxed);
419 return Poll::Ready(written.checked_sub(reported).ok_or_else(|| {
420 io::Error::new(
421 io::ErrorKind::InvalidData,
422 "file write acknowledged fewer bytes than were accepted",
423 )
424 }));
425 }
426 _ => return Poll::Ready(Err(in_progress("write polled"))),
427 }
428 }
429 }
430
431 fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
434 let (inner, cursor) = self.get_mut().parts();
435 match &mut cursor.op {
436 Op::Idle | Op::Body { .. } => Poll::Ready(Ok(())),
437 Op::Read(op, at) => {
438 let at = *at;
439 let result = ready!(poll_read_op(op, cx));
440 cursor.op = Op::Idle;
441 cursor.op = Op::Body {
442 body: result?,
443 at,
444 delivered: false,
445 };
446 Poll::Ready(Ok(()))
447 }
448 Op::Write { op, .. } => {
449 let result = ready!(op.poll_finish(cx));
450 cursor.end_write();
451 let (_, end) = result?;
452 inner.position().store(end, Ordering::Relaxed);
453 Poll::Ready(Ok(()))
454 }
455 Op::Size(op, delta) => {
456 let delta = *delta;
457 let result = ready!(Pin::new(op).poll(cx));
458 cursor.op = Op::Idle;
459 let end = offset_delta(result?, delta)?;
460 inner.position().store(end, Ordering::Relaxed);
461 Poll::Ready(Ok(()))
462 }
463 }
464 }
465
466 fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
468 self.poll_flush(cx)
469 }
470}
471
472impl AsyncSeek for File {
473 fn poll_seek(
476 self: Pin<&mut Self>,
477 cx: &mut Context<'_>,
478 position: io::SeekFrom,
479 ) -> Poll<io::Result<u64>> {
480 let (inner, cursor) = self.get_mut().parts();
481 let pos = inner.position();
482 loop {
483 match (&mut cursor.op, position) {
484 (Op::Size(op, delta), io::SeekFrom::End(requested)) if *delta == requested => {
485 let result = ready!(Pin::new(op).poll(cx));
486 cursor.op = Op::Idle;
487 pos.store(offset_delta(result?, requested)?, Ordering::Relaxed);
488 return Poll::Ready(Ok(pos.load(Ordering::Relaxed)));
489 }
490 _ => cursor.drop_body(),
491 }
492 if cursor.is_busy() {
493 return Poll::Ready(Err(in_progress("seek started")));
494 }
495 match position {
496 io::SeekFrom::Start(offset) => pos.store(offset, Ordering::Relaxed),
499 io::SeekFrom::Current(delta) => {
500 pos.store(
501 offset_delta(pos.load(Ordering::Relaxed), delta)?,
502 Ordering::Relaxed,
503 );
504 }
505 io::SeekFrom::End(delta) => {
509 cursor.op = Op::Size(inner.start_size()?, delta);
510 continue;
511 }
512 }
513 return Poll::Ready(Ok(pos.load(Ordering::Relaxed)));
514 }
515 }
516}