Skip to main content

maelstrom_util/
io.rs

1//! Useful [`Read`]ers.
2
3use crate::ext::OptionExt as _;
4use byteorder::{BigEndian, ReadBytesExt as _, WriteBytesExt as _};
5use lru::LruCache;
6use maelstrom_base::Sha256Digest;
7use maelstrom_linux::{self as linux};
8use pin_project::pin_project;
9use sha2::{Digest as _, Sha256};
10use slog::{warn, Logger};
11use std::{
12    cmp,
13    fs::File,
14    future::Future,
15    io::{self, Chain, Read, Repeat, Take, Write},
16    num::NonZeroUsize,
17    os::fd::{self, AsRawFd},
18    pin::{pin, Pin},
19    task::{ready, Context, Poll},
20};
21use tokio::io::{
22    self as tokio_io, unix::AsyncFd, AsyncRead, AsyncSeek, AsyncSeekExt as _, AsyncWrite, ReadBuf,
23    ReadHalf, WriteHalf,
24};
25
26/// A [`Read`]er wrapper that will always reads a specific number of bytes, except on error. If the
27/// inner, wrapped, reader returns EOF before the specified number of bytes have been returned,
28/// this reader will pad the remaining bytes with zeros. If the inner reader returns more bytes
29/// than the specified number, this reader will return EOF early, like [Read::take].
30pub struct FixedSizeReader<InnerT>(Take<Chain<InnerT, Repeat>>);
31
32impl<InnerT: Read> FixedSizeReader<InnerT> {
33    pub fn new(inner: InnerT, limit: u64) -> Self {
34        FixedSizeReader(inner.chain(io::repeat(0)).take(limit))
35    }
36
37    pub fn into_inner(self) -> InnerT {
38        self.0.into_inner().into_inner().0
39    }
40}
41
42impl<InnerT: Read> Read for FixedSizeReader<InnerT> {
43    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
44        self.0.read(buf)
45    }
46}
47
48/// A IO wrapper that computes the SHA-256 digest of the bytes that are read from or written to it.
49pub struct Sha256Stream<InnerT> {
50    inner: InnerT,
51    hasher: Sha256,
52}
53
54impl<InnerT> Sha256Stream<InnerT> {
55    pub fn new(inner: InnerT) -> Self {
56        Self {
57            inner,
58            hasher: Sha256::new(),
59        }
60    }
61
62    /// Deconstruct the reader and return the inner reader and computed digest.
63    pub fn finalize(self) -> (InnerT, Sha256Digest) {
64        (self.inner, Sha256Digest::new(self.hasher.finalize().into()))
65    }
66}
67
68impl<InnerT: Read> Read for Sha256Stream<InnerT> {
69    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
70        let size = self.inner.read(buf)?;
71        self.hasher.update(&buf[..size]);
72        Ok(size)
73    }
74}
75
76impl<InnerT: Write> Write for Sha256Stream<InnerT> {
77    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
78        let size = self.inner.write(buf)?;
79        self.hasher.update(&buf[..size]);
80        Ok(size)
81    }
82
83    fn flush(&mut self) -> io::Result<()> {
84        self.inner.flush()
85    }
86}
87
88impl<InnerT: AsyncRead + Unpin> AsyncRead for Sha256Stream<InnerT> {
89    fn poll_read(
90        self: Pin<&mut Self>,
91        cx: &mut Context<'_>,
92        dst: &mut ReadBuf<'_>,
93    ) -> Poll<io::Result<()>> {
94        let start_len = dst.filled().len();
95        let me = self.get_mut();
96        let result = AsyncRead::poll_read(pin!(&mut me.inner), cx, dst);
97        if matches!(result, Poll::Ready(Ok(_))) {
98            me.hasher.update(&dst.filled()[start_len..]);
99        }
100        result
101    }
102}
103
104impl<InnerT: AsyncWrite + Unpin> AsyncWrite for Sha256Stream<InnerT> {
105    fn poll_write(
106        self: Pin<&mut Self>,
107        cx: &mut Context<'_>,
108        src: &[u8],
109    ) -> Poll<io::Result<usize>> {
110        let me = self.get_mut();
111        let result = AsyncWrite::poll_write(pin!(&mut me.inner), cx, src);
112        if let Poll::Ready(Ok(size)) = &result {
113            me.hasher.update(&src[..*size]);
114        }
115        result
116    }
117
118    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
119        let me = self.get_mut();
120        AsyncWrite::poll_flush(pin!(&mut me.inner), cx)
121    }
122
123    fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
124        let me = self.get_mut();
125        AsyncWrite::poll_shutdown(pin!(&mut me.inner), cx)
126    }
127}
128
129struct Chunk<ReaderT> {
130    reader: io::Take<ReaderT>,
131}
132
133impl<ReaderT: io::Read> Chunk<ReaderT> {
134    fn new(mut reader: ReaderT) -> io::Result<Option<Self>> {
135        let size = reader.read_u32::<BigEndian>()?;
136        Ok((size != 0).then(|| Self {
137            reader: reader.take(size as u64),
138        }))
139    }
140
141    fn into_inner(self) -> ReaderT {
142        self.reader.into_inner()
143    }
144}
145
146impl<ReaderT: io::Read> io::Read for Chunk<ReaderT> {
147    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
148        self.reader.read(buffer)
149    }
150}
151
152pub struct ChunkedReader<ReaderT> {
153    reader: Option<ReaderT>,
154    chunk: Option<Chunk<ReaderT>>,
155}
156
157impl<ReaderT> ChunkedReader<ReaderT> {
158    pub fn new(reader: ReaderT) -> Self {
159        Self {
160            reader: Some(reader),
161            chunk: None,
162        }
163    }
164}
165
166impl<ReaderT: io::Read> io::Read for ChunkedReader<ReaderT> {
167    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
168        if let Some(mut chunk) = self.chunk.take() {
169            let read = chunk.read(buffer)?;
170            return if read == 0 {
171                self.reader = Some(chunk.into_inner());
172                self.read(buffer)
173            } else {
174                self.chunk.replace(chunk);
175                Ok(read)
176            };
177        } else if let Some(reader) = self.reader.take() {
178            if let Some(chunk) = Chunk::new(reader)? {
179                self.chunk = Some(chunk);
180                return self.read(buffer);
181            }
182        }
183        Ok(0)
184    }
185}
186
187pub struct ChunkedWriter<WriterT> {
188    writer: WriterT,
189    chunk: Vec<u8>,
190    max_chunk_size: usize,
191}
192
193impl<WriterT> ChunkedWriter<WriterT> {
194    pub fn new(writer: WriterT, max_chunk_size: usize) -> Self {
195        Self {
196            writer,
197            chunk: vec![0; 4],
198            max_chunk_size,
199        }
200    }
201}
202
203impl<WriterT: io::Write> ChunkedWriter<WriterT> {
204    fn send_chunk(&mut self) -> io::Result<()> {
205        let size = (self.chunk.len() - 4).try_into().unwrap();
206        (&mut self.chunk[..4]).write_u32::<BigEndian>(size).unwrap();
207        self.writer.write_all(&self.chunk)?;
208        self.chunk.resize(4, 0);
209        Ok(())
210    }
211
212    fn remaining_chunk_space(&self) -> usize {
213        self.max_chunk_size - (self.chunk.len() - 4)
214    }
215
216    pub fn finish(mut self) -> io::Result<()> {
217        use std::io::Write as _;
218
219        self.flush()?;
220        self.writer.write_u32::<BigEndian>(0)?;
221        Ok(())
222    }
223}
224
225impl<WriterT: io::Write> io::Write for ChunkedWriter<WriterT> {
226    fn write(&mut self, mut input: &[u8]) -> io::Result<usize> {
227        let to_read = std::cmp::min(self.remaining_chunk_space(), input.len()) as u64;
228        let written = std::io::copy(&mut io::Read::take(&mut input, to_read), &mut self.chunk)
229            .unwrap() as usize;
230
231        if self.remaining_chunk_space() == 0 {
232            self.send_chunk()?;
233        }
234        if !input.is_empty() {
235            return Ok(written + self.write(input)?);
236        }
237
238        Ok(written)
239    }
240
241    fn flush(&mut self) -> io::Result<()> {
242        if self.chunk.len() > 4 {
243            self.send_chunk()?;
244        }
245        self.writer.flush()
246    }
247}
248
249#[derive(Default, Debug)]
250enum BufferedStreamState {
251    #[default]
252    Idle,
253    Seeking {
254        chunk_index: u64,
255    },
256    Reading {
257        chunk_index: u64,
258        amount_read: usize,
259        new_chunk: Vec<u8>,
260    },
261}
262
263#[derive(Debug)]
264struct BufferedStreamChunk {
265    data: Vec<u8>,
266    dirty_start: usize,
267}
268
269impl BufferedStreamChunk {
270    fn new(data: Vec<u8>) -> Self {
271        Self {
272            dirty_start: data.len(),
273            data,
274        }
275    }
276
277    fn is_dirty(&self) -> bool {
278        self.dirty_start < self.data.len()
279    }
280
281    fn dirty(&self) -> &[u8] {
282        &self.data[self.dirty_start..]
283    }
284}
285
286pub struct BufferedStream<StreamT> {
287    chunk_size: usize,
288    chunks: LruCache<u64, BufferedStreamChunk>,
289    position: u64,
290    inner_position: u64,
291    stream: StreamT,
292    state: BufferedStreamState,
293    length: u64,
294}
295
296impl<StreamT: AsyncSeek + Unpin> BufferedStream<StreamT> {
297    pub async fn new(
298        chunk_size: usize,
299        capacity: NonZeroUsize,
300        mut stream: StreamT,
301    ) -> io::Result<Self> {
302        stream.seek(std::io::SeekFrom::End(0)).await?;
303        let length = stream.stream_position().await?;
304        stream.seek(std::io::SeekFrom::Start(0)).await?;
305        Ok(Self {
306            chunk_size,
307            chunks: LruCache::new(capacity),
308            position: 0,
309            inner_position: 0,
310            stream,
311            state: Default::default(),
312            length,
313        })
314    }
315}
316
317impl<StreamT> BufferedStream<StreamT> {
318    pub fn get_ref(&self) -> &StreamT {
319        &self.stream
320    }
321}
322
323impl<StreamT: AsyncRead + AsyncWrite + AsyncSeek + Unpin> BufferedStream<StreamT> {
324    fn attempt_cached_read(&mut self, dst: &mut ReadBuf<'_>) -> bool {
325        let chunk_index = self.position / self.chunk_size as u64;
326        if let Some(entry) = self.chunks.get(&chunk_index) {
327            let chunk_offset = self.position as usize % self.chunk_size;
328            if chunk_offset >= entry.data.len() {
329                return true;
330            }
331            let end = std::cmp::min(entry.data.len(), chunk_offset + dst.remaining());
332            dst.put_slice(&entry.data[chunk_offset..end]);
333            self.position += (end - chunk_offset) as u64;
334            true
335        } else {
336            false
337        }
338    }
339
340    fn fill_cache(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
341        if self.chunks.len() == usize::from(self.chunks.cap()) {
342            ready!(self.flush_lru(cx))?;
343        }
344
345        let chunk_start = (self.position / self.chunk_size as u64) * self.chunk_size as u64;
346        let chunk_index = self.position / self.chunk_size as u64;
347
348        if !matches!(&self.state, BufferedStreamState::Reading { .. })
349            && self.inner_position != chunk_start
350        {
351            ready!(self.seek_inner(cx, chunk_index))?;
352            assert!(self.is_idle());
353        }
354
355        loop {
356            match &mut self.state {
357                BufferedStreamState::Idle => {
358                    self.state = BufferedStreamState::Reading {
359                        chunk_index,
360                        amount_read: 0,
361                        new_chunk: vec![0; self.chunk_size],
362                    };
363                }
364                BufferedStreamState::Reading {
365                    chunk_index: existing,
366                    amount_read,
367                    new_chunk,
368                } => {
369                    assert_eq!(*existing, chunk_index);
370                    let mut buf = ReadBuf::new(&mut new_chunk[*amount_read..]);
371                    ready!(AsyncRead::poll_read(pin!(&mut self.stream), cx, &mut buf))?;
372                    let just_read = buf.filled().len();
373                    self.inner_position += just_read as u64;
374                    *amount_read += just_read;
375                    if *amount_read == self.chunk_size || just_read == 0 {
376                        new_chunk.resize(*amount_read, 0);
377                        let new_chunk = BufferedStreamChunk::new(std::mem::take(new_chunk));
378                        self.chunks.push(chunk_index, new_chunk).assert_is_none();
379                        self.state = BufferedStreamState::Idle;
380                        return Poll::Ready(Ok(()));
381                    } else {
382                        self.state = BufferedStreamState::Reading {
383                            chunk_index,
384                            amount_read: *amount_read,
385                            new_chunk: std::mem::take(new_chunk),
386                        };
387                    }
388                }
389                _ => unreachable!(),
390            }
391        }
392    }
393
394    fn is_idle(&self) -> bool {
395        matches!(&self.state, BufferedStreamState::Idle)
396    }
397
398    fn attempt_write_to_cache(&mut self, buf: &[u8]) -> Option<usize> {
399        let chunk_index = self.position / self.chunk_size as u64;
400        if let Some(entry) = self.chunks.get_mut(&chunk_index) {
401            let chunk_offset = self.position as usize % self.chunk_size;
402            let read_end = chunk_offset + buf.len();
403            if entry.data.len() < self.chunk_size && read_end > entry.data.len() {
404                let extend_by = read_end - entry.data.len();
405                entry.data.resize(
406                    std::cmp::min(entry.data.len() + extend_by, self.chunk_size),
407                    0,
408                );
409            }
410            let end = std::cmp::min(entry.data.len(), read_end);
411            let read_size = end - chunk_offset;
412            entry.data[chunk_offset..end].clone_from_slice(&buf[..read_size]);
413            self.position += read_size as u64;
414            if self.position > self.length {
415                self.length = self.position;
416            }
417            entry.dirty_start = 0;
418            return Some(read_size);
419        }
420        None
421    }
422
423    fn seek_inner(&mut self, cx: &mut Context<'_>, chunk_index: u64) -> Poll<io::Result<()>> {
424        let chunk_start = chunk_index * self.chunk_size as u64;
425
426        loop {
427            match &mut self.state {
428                BufferedStreamState::Idle => {
429                    ready!(pin!(&mut self.stream).poll_complete(cx))?;
430                    pin!(&mut self.stream).start_seek(std::io::SeekFrom::Start(chunk_start))?;
431                    self.state = BufferedStreamState::Seeking { chunk_index };
432                }
433                BufferedStreamState::Seeking {
434                    chunk_index: existing,
435                } => {
436                    assert_eq!(*existing, chunk_index);
437                    ready!(pin!(&mut self.stream).poll_complete(cx))?;
438                    self.inner_position = chunk_start;
439                    self.state = BufferedStreamState::Idle;
440                    return Poll::Ready(Ok(()));
441                }
442                _ => unreachable!(),
443            }
444        }
445    }
446
447    fn flush_chunk(&mut self, cx: &mut Context<'_>, chunk_index: u64) -> Poll<io::Result<()>> {
448        let chunk = self.chunks.peek_mut(&chunk_index).unwrap();
449        if !chunk.is_dirty() {
450            return Poll::Ready(Ok(()));
451        }
452
453        let pos = chunk_index * self.chunk_size as u64 + chunk.dirty_start as u64;
454        if self.inner_position != pos {
455            ready!(self.seek_inner(cx, chunk_index))?;
456        }
457
458        let chunk = self.chunks.peek_mut(&chunk_index).unwrap();
459        let amount_written = ready!(AsyncWrite::poll_write(
460            pin!(&mut self.stream),
461            cx,
462            chunk.dirty()
463        ))?;
464        self.inner_position += amount_written as u64;
465        chunk.dirty_start += amount_written;
466
467        Poll::Ready(Ok(()))
468    }
469
470    fn flush_from_front(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
471        let mut indices: Vec<_> = self.chunks.iter().map(|(i, _)| *i).collect();
472        indices.sort();
473
474        for chunk_index in indices {
475            ready!(self.flush_chunk(cx, chunk_index))?;
476        }
477        Poll::Ready(Ok(()))
478    }
479
480    fn flush_lru(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
481        let (chunk_index, _) = self.chunks.peek_lru().unwrap();
482        let chunk_index = *chunk_index;
483        ready!(self.flush_chunk(cx, chunk_index))?;
484
485        self.chunks.pop(&chunk_index).unwrap();
486        Poll::Ready(Ok(()))
487    }
488
489    pub fn into_inner(self) -> StreamT {
490        self.stream
491    }
492}
493
494impl<StreamT: AsyncRead + AsyncWrite + AsyncSeek + Unpin> AsyncRead for BufferedStream<StreamT> {
495    fn poll_read(
496        self: Pin<&mut Self>,
497        cx: &mut Context<'_>,
498        dst: &mut ReadBuf<'_>,
499    ) -> Poll<io::Result<()>> {
500        let me = self.get_mut();
501        if me.attempt_cached_read(dst) {
502            assert!(me.is_idle());
503            return Poll::Ready(Ok(()));
504        }
505
506        ready!(me.fill_cache(cx))?;
507
508        assert!(me.attempt_cached_read(dst));
509
510        assert!(me.is_idle());
511        Poll::Ready(Ok(()))
512    }
513}
514
515impl<StreamT: AsyncRead + AsyncWrite + AsyncSeek + Unpin> AsyncWrite for BufferedStream<StreamT> {
516    fn poll_write(
517        self: Pin<&mut Self>,
518        cx: &mut Context<'_>,
519        buf: &[u8],
520    ) -> Poll<io::Result<usize>> {
521        let me = self.get_mut();
522
523        if let Some(size) = me.attempt_write_to_cache(buf) {
524            assert!(me.is_idle());
525            return Poll::Ready(Ok(size));
526        }
527
528        ready!(me.fill_cache(cx))?;
529
530        assert!(me.is_idle());
531        Poll::Ready(Ok(me.attempt_write_to_cache(buf).unwrap()))
532    }
533
534    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
535        let me = self.get_mut();
536        ready!(me.flush_from_front(cx))?;
537        AsyncWrite::poll_flush(pin!(&mut me.stream), cx)
538    }
539
540    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
541        unimplemented!()
542    }
543}
544
545impl<StreamT: Unpin> AsyncSeek for BufferedStream<StreamT> {
546    fn start_seek(self: Pin<&mut Self>, position: std::io::SeekFrom) -> io::Result<()> {
547        let me = self.get_mut();
548
549        match position {
550            std::io::SeekFrom::Start(pos) => me.position = pos,
551            std::io::SeekFrom::End(offset) => {
552                me.position = (me.length as i64 + offset) as u64;
553            }
554            std::io::SeekFrom::Current(offset) => {
555                me.position = (me.position as i64 + offset) as u64;
556            }
557        }
558        Ok(())
559    }
560
561    fn poll_complete(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
562        let me = self.get_mut();
563        Poll::Ready(Ok(me.position))
564    }
565}
566
567struct Splicer {
568    pipe_in: linux::OwnedFd,
569    pipe_out: linux::OwnedFd,
570    written: usize,
571    pipe_size: usize,
572}
573
574impl Splicer {
575    fn new() -> io::Result<Self> {
576        let (pipe_out, pipe_in) = linux::pipe()?;
577        let pipe_max_s = std::fs::read_to_string("/proc/sys/fs/pipe-max-size")?;
578        let pipe_size = pipe_max_s.trim().parse().unwrap();
579        linux::fcntl_setpipe_sz(&pipe_in, pipe_size)?;
580        Ok(Self {
581            pipe_in,
582            pipe_out,
583            written: 0,
584            pipe_size,
585        })
586    }
587
588    pub fn write(&mut self, bytes: &[u8]) -> io::Result<()> {
589        assert!(
590            self.written + bytes.len() <= self.pipe_size,
591            "attempt to write more than pipe can hold"
592        );
593        linux::write(&self.pipe_in, bytes)?;
594        self.written += bytes.len();
595        Ok(())
596    }
597
598    pub fn write_fd(
599        &mut self,
600        fd: linux::Fd,
601        offset: Option<u64>,
602        length: usize,
603    ) -> io::Result<usize> {
604        assert!(
605            self.written + length <= self.pipe_size,
606            "attempt to write more than pipe can hold"
607        );
608        let written = linux::splice(&fd, offset, &self.pipe_in, None, length)?;
609        self.written += written;
610        Ok(written)
611    }
612
613    pub fn copy_to_fd(&mut self, fd: linux::Fd, offset: Option<u64>) -> io::Result<()> {
614        let copied = linux::splice(&self.pipe_out, None, &fd, offset, self.written)?;
615        if copied != self.written {
616            // If we get a short write, it means we are out of space.
617            return Err(linux::Errno::ENOSPC.into());
618        }
619        self.written = 0;
620        Ok(())
621    }
622
623    pub fn reset_pipe(&mut self) -> io::Result<()> {
624        (self.pipe_out, self.pipe_in) = linux::pipe()?;
625        let pipe_max_s = std::fs::read_to_string("/proc/sys/fs/pipe-max-size")?;
626        let pipe_size = pipe_max_s.trim().parse().unwrap();
627        linux::fcntl_setpipe_sz(&self.pipe_in, pipe_size)?;
628        self.written = 0;
629        self.pipe_size = pipe_size;
630        Ok(())
631    }
632
633    pub fn buffer_size(&self) -> usize {
634        self.pipe_size
635    }
636}
637
638struct SlowWriter {
639    buffer: Vec<u8>,
640    chunk_size: usize,
641}
642
643impl SlowWriter {
644    fn new(chunk_size: usize) -> Self {
645        Self {
646            buffer: vec![],
647            chunk_size,
648        }
649    }
650
651    pub fn write(&mut self, bytes: &[u8]) -> io::Result<()> {
652        self.buffer.extend(bytes);
653        Ok(())
654    }
655
656    /// We aren't allowed to seek the given `fd` here, so we must do splice with a tiny pipe
657    /// instead.
658    fn offset_write_fd(
659        &mut self,
660        fd: linux::Fd,
661        mut offset: u64,
662        length: usize,
663    ) -> io::Result<usize> {
664        let (pipe_out, pipe_in) = linux::pipe()?;
665        let pipe_size = linux::fcntl_getpipe_sz(&pipe_in)?;
666        let chunk_size = std::cmp::min(self.chunk_size, pipe_size);
667
668        let mut remaining = length;
669        while remaining > 0 {
670            let end = self.buffer.len();
671            let to_read = std::cmp::min(chunk_size, remaining);
672            self.buffer.resize(end + to_read, 0);
673            let in_pipe = linux::splice(&fd, Some(offset), &pipe_in, None, to_read)?;
674            offset += in_pipe as u64;
675
676            let in_buffer = linux::read(&pipe_out, &mut self.buffer[end..])?;
677            assert_eq!(in_pipe, in_buffer);
678            remaining -= in_buffer;
679            self.buffer.resize(end + in_buffer, 0);
680            if in_buffer == 0 {
681                break;
682            }
683        }
684        Ok(length - remaining)
685    }
686
687    pub fn write_fd(
688        &mut self,
689        fd: linux::Fd,
690        offset: Option<u64>,
691        length: usize,
692    ) -> io::Result<usize> {
693        if let Some(offset) = offset {
694            return self.offset_write_fd(fd, offset, length);
695        }
696
697        let mut remaining = length;
698        while remaining > 0 {
699            let end = self.buffer.len();
700            let to_read = std::cmp::min(self.chunk_size, remaining);
701            self.buffer.resize(end + to_read, 0);
702            let read = linux::read(&fd, &mut self.buffer[end..])?;
703            remaining -= read;
704            self.buffer.resize(end + read, 0);
705            if read == 0 {
706                break;
707            }
708        }
709        Ok(length - remaining)
710    }
711
712    pub fn copy_to_fd(&mut self, fd: linux::Fd, offset: Option<u64>) -> io::Result<()> {
713        if let Some(offset) = offset {
714            linux::lseek(&fd, i64::try_from(offset).unwrap(), linux::Whence::SeekSet)?;
715        }
716
717        let written = linux::write(&fd, &self.buffer[..])?;
718        assert_eq!(written, self.buffer.len());
719        self.buffer = vec![];
720
721        Ok(())
722    }
723
724    pub fn buffer_size(&self) -> usize {
725        SLOW_WRITE_BUFFER_SIZE
726    }
727}
728
729enum SpliceOrFallback {
730    Splice(Splicer),
731    Fallback(SlowWriter),
732}
733
734const SLOW_WRITE_BUFFER_SIZE: usize = 1024 * 1024;
735const SLOW_WRITE_CHUNK_SIZE: usize = 1024 * 1024;
736
737pub struct MaybeFastWriter(SpliceOrFallback);
738
739impl MaybeFastWriter {
740    pub fn new(log: &Logger) -> Self {
741        match Splicer::new() {
742            Ok(splicer) => Self(SpliceOrFallback::Splice(splicer)),
743            Err(err) => {
744                warn!(log, "error getting pipe memory, cannot splice"; "error" => %err);
745                Self(SpliceOrFallback::Fallback(SlowWriter::new(
746                    SLOW_WRITE_CHUNK_SIZE,
747                )))
748            }
749        }
750    }
751
752    pub fn buffer_size(&self) -> usize {
753        match &self.0 {
754            SpliceOrFallback::Splice(splicer) => splicer.buffer_size(),
755            SpliceOrFallback::Fallback(writer) => writer.buffer_size(),
756        }
757    }
758
759    fn reset_splicer(&mut self, err: io::Error) -> io::Error {
760        let SpliceOrFallback::Splice(splicer) = &mut self.0 else {
761            unreachable!()
762        };
763        if splicer.reset_pipe().is_err() {
764            self.0 = SpliceOrFallback::Fallback(SlowWriter::new(SLOW_WRITE_CHUNK_SIZE));
765        }
766        err
767    }
768
769    pub fn write(&mut self, bytes: &[u8]) -> io::Result<()> {
770        match &mut self.0 {
771            SpliceOrFallback::Splice(splicer) => {
772                splicer.write(bytes).map_err(|e| self.reset_splicer(e))
773            }
774            SpliceOrFallback::Fallback(writer) => writer.write(bytes),
775        }
776    }
777
778    pub fn write_fd(
779        &mut self,
780        fd: linux::Fd,
781        offset: Option<u64>,
782        length: usize,
783    ) -> io::Result<usize> {
784        match &mut self.0 {
785            SpliceOrFallback::Splice(splicer) => splicer
786                .write_fd(fd, offset, length)
787                .map_err(|e| self.reset_splicer(e)),
788            SpliceOrFallback::Fallback(writer) => writer.write_fd(fd, offset, length),
789        }
790    }
791
792    pub fn copy_to_fd(&mut self, fd: linux::Fd, offset: Option<u64>) -> io::Result<()> {
793        match &mut self.0 {
794            SpliceOrFallback::Splice(splicer) => splicer
795                .copy_to_fd(fd, offset)
796                .map_err(|e| self.reset_splicer(e)),
797            SpliceOrFallback::Fallback(writer) => writer.copy_to_fd(fd, offset),
798        }
799    }
800}
801
802/// A wrapper for a raw, non-blocking fd that allows it to be read from async code.
803pub struct AsyncFile(AsyncFd<File>);
804
805impl AsyncFile {
806    pub fn new(fd: linux::OwnedFd) -> anyhow::Result<Self> {
807        Ok(Self(AsyncFd::new(File::from(fd::OwnedFd::from(fd)))?))
808    }
809
810    pub fn into_split(self) -> (ReadHalf<Self>, WriteHalf<Self>) {
811        tokio_io::split(self)
812    }
813}
814
815impl AsyncRead for AsyncFile {
816    fn poll_read(
817        self: Pin<&mut Self>,
818        cx: &mut Context<'_>,
819        buf: &mut ReadBuf<'_>,
820    ) -> Poll<io::Result<()>> {
821        loop {
822            let mut guard = ready!(self.0.poll_read_ready(cx))?;
823
824            let unfilled = buf.initialize_unfilled();
825            match guard.try_io(|inner| inner.get_ref().read(unfilled)) {
826                Ok(Ok(len)) => {
827                    buf.advance(len);
828                    return Poll::Ready(Ok(()));
829                }
830                Ok(Err(err)) => return Poll::Ready(Err(err)),
831                Err(_would_block) => continue,
832            }
833        }
834    }
835}
836
837impl AsyncWrite for AsyncFile {
838    fn poll_write(
839        self: Pin<&mut Self>,
840        cx: &mut Context<'_>,
841        buf: &[u8],
842    ) -> Poll<io::Result<usize>> {
843        loop {
844            let mut guard = ready!(self.0.poll_write_ready(cx))?;
845
846            match guard.try_io(|inner| inner.get_ref().write(buf)) {
847                Ok(result) => return Poll::Ready(result),
848                Err(_would_block) => continue,
849            }
850        }
851    }
852
853    fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
854        Poll::Ready(Ok(()))
855    }
856
857    fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
858        Poll::Ready(Ok(()))
859    }
860}
861
862pub struct ErrorReader;
863
864impl std::io::Read for ErrorReader {
865    fn read(&mut self, _buf: &mut [u8]) -> io::Result<usize> {
866        Err(std::io::Error::new(std::io::ErrorKind::Other, "test error"))
867    }
868}
869
870impl AsyncRead for ErrorReader {
871    fn poll_read(
872        self: Pin<&mut Self>,
873        _: &mut Context<'_>,
874        _: &mut ReadBuf<'_>,
875    ) -> Poll<io::Result<()>> {
876        Poll::Ready(Err(std::io::Error::new(
877            std::io::ErrorKind::Other,
878            "test error",
879        )))
880    }
881}
882
883pub fn copy_using_splice(
884    reader: &mut impl AsRawFd,
885    writer: &mut impl AsRawFd,
886    to_copy: u64,
887    log: &Logger,
888) -> io::Result<u64> {
889    let mut buffer = MaybeFastWriter::new(log);
890    let read_fd = linux::Fd::from_raw(reader.as_raw_fd());
891    let write_fd = linux::Fd::from_raw(writer.as_raw_fd());
892
893    let mut copied = 0;
894    while copied < to_copy {
895        let to_read = cmp::min(buffer.buffer_size(), (to_copy - copied) as usize);
896        let read = buffer.write_fd(read_fd, None, to_read)?;
897        if read == 0 {
898            break;
899        }
900        buffer.copy_to_fd(write_fd, None)?;
901        copied += read as u64;
902    }
903
904    Ok(copied)
905}
906
907#[pin_project(project = LazyReadProj)]
908pub enum LazyRead<InitT, ReadT> {
909    Uninitialized(#[pin] InitT),
910    Initialized(#[pin] ReadT),
911}
912
913impl<InitT, ReadT> LazyRead<InitT, ReadT> {
914    pub fn new(init: InitT) -> Self {
915        Self::Uninitialized(init)
916    }
917}
918
919impl<InitT: Future<Output = io::Result<ReadT>>, ReadT: AsyncRead> AsyncRead
920    for LazyRead<InitT, ReadT>
921{
922    fn poll_read(
923        mut self: Pin<&mut Self>,
924        cx: &mut Context<'_>,
925        dst: &mut ReadBuf<'_>,
926    ) -> Poll<io::Result<()>> {
927        match self.as_mut().project() {
928            LazyReadProj::Uninitialized(init_fut) => {
929                let read = ready!(init_fut.poll(cx))?;
930                self.set(Self::Initialized(read));
931                self.poll_read(cx, dst)
932            }
933            LazyReadProj::Initialized(read) => pin!(read).poll_read(cx, dst),
934        }
935    }
936}
937
938#[cfg(test)]
939mod tests {
940    use super::*;
941    use tokio::io::AsyncReadExt as _;
942
943    fn calculate_read_hash(mut input: &[u8]) -> Sha256Digest {
944        let mut reader = Sha256Stream::new(&mut input);
945        std::io::copy(&mut reader, &mut std::io::sink()).unwrap();
946        reader.finalize().1
947    }
948
949    fn calculate_write_hash(mut input: &[u8]) -> Sha256Digest {
950        let mut writer = Sha256Stream::new(std::io::sink());
951        std::io::copy(&mut input, &mut writer).unwrap();
952        writer.finalize().1
953    }
954
955    async fn calculate_async_read_hash(mut input: &[u8]) -> Sha256Digest {
956        let mut reader = Sha256Stream::new(&mut input);
957        tokio::io::copy(&mut reader, &mut tokio::io::sink())
958            .await
959            .unwrap();
960        reader.finalize().1
961    }
962
963    async fn calculate_async_write_hash(mut input: &[u8]) -> Sha256Digest {
964        let mut writer = Sha256Stream::new(tokio::io::sink());
965        tokio::io::copy(&mut input, &mut writer).await.unwrap();
966        writer.finalize().1
967    }
968
969    #[tokio::test]
970    async fn sha256_stream_impls_consistent() {
971        let test_bytes = Vec::from_iter([1, 2, 3, 4, 5, 6, 7].into_iter().cycle().take(1000));
972        let read_hash = calculate_read_hash(&test_bytes);
973        let write_hash = calculate_write_hash(&test_bytes);
974        let async_read_hash = calculate_async_read_hash(&test_bytes).await;
975        let async_write_hash = calculate_async_write_hash(&test_bytes).await;
976
977        assert_eq!(read_hash, write_hash);
978        assert_eq!(read_hash, async_read_hash);
979        assert_eq!(read_hash, async_write_hash);
980    }
981
982    fn test_chunked_reader(input: &[u8], expected: &[&[u8]]) -> io::Result<()> {
983        let mut reader = ChunkedReader::new(input);
984        for e in expected {
985            let mut actual = vec![0; e.len()];
986            reader.read_exact(&mut actual[..])?;
987            assert_eq!(&actual, e);
988        }
989
990        let mut rest = vec![];
991        reader.read_to_end(&mut rest)?;
992        assert!(rest.is_empty(), "{rest:?}");
993
994        Ok(())
995    }
996
997    #[test]
998    fn chunked_reader() {
999        test_chunked_reader(
1000            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 2, 6, 7, 0, 0, 0, 0],
1001            &[&[1, 2, 3], &[4, 5, 6], &[7]],
1002        )
1003        .unwrap();
1004
1005        test_chunked_reader(
1006            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 2, 6, 7, 0, 0, 0, 0],
1007            &[&[1, 2, 3, 4, 5], &[6, 7]],
1008        )
1009        .unwrap();
1010
1011        test_chunked_reader(
1012            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 2, 6, 7, 0, 0, 0, 0],
1013            &[&[1, 2, 3, 4, 5, 6, 7]],
1014        )
1015        .unwrap();
1016
1017        test_chunked_reader(
1018            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 2, 6, 7],
1019            &[&[1, 2, 3], &[4, 5, 6], &[7]],
1020        )
1021        .unwrap_err();
1022    }
1023
1024    fn test_chunk_writer(input: &[&[u8]], expected: &[u8]) {
1025        use std::io::Write as _;
1026
1027        let mut written = vec![];
1028        let mut writer = ChunkedWriter::new(&mut written, 5);
1029        for i in input {
1030            writer.write_all(i).unwrap();
1031        }
1032
1033        writer.finish().unwrap();
1034        assert_eq!(written, expected,);
1035    }
1036
1037    #[test]
1038    fn chunk_writer() {
1039        test_chunk_writer(
1040            &[&[1, 2, 3, 4, 5, 6, 7, 8]],
1041            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 3, 6, 7, 8, 0, 0, 0, 0],
1042        );
1043
1044        test_chunk_writer(
1045            &[&[1, 2], &[3, 4], &[5, 6, 7, 8]],
1046            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 3, 6, 7, 8, 0, 0, 0, 0],
1047        );
1048        test_chunk_writer(&[&[1, 2]], &[0, 0, 0, 2, 1, 2, 0, 0, 0, 0]);
1049
1050        test_chunk_writer(
1051            &[&[1, 2, 3, 4, 5]],
1052            &[0, 0, 0, 5, 1, 2, 3, 4, 5, 0, 0, 0, 0],
1053        );
1054    }
1055
1056    #[test]
1057    fn chunk_reader_and_writer() {
1058        use std::io::{Read as _, Write as _};
1059
1060        let test_data = Vec::from_iter((0u8..=255).cycle().take(1000));
1061        let mut encoded = vec![];
1062        let mut writer = ChunkedWriter::new(&mut encoded, 7);
1063        writer.write_all(&test_data).unwrap();
1064        writer.finish().unwrap();
1065
1066        let mut reader = ChunkedReader::new(&encoded[..]);
1067        let mut decoded = vec![];
1068        reader.read_to_end(&mut decoded).unwrap();
1069
1070        assert_eq!(&decoded, &test_data);
1071    }
1072
1073    #[tokio::test]
1074    async fn buffered_read() {
1075        use tokio::io::AsyncReadExt as _;
1076
1077        for chunk_size in 1..10 {
1078            let underlying: Vec<_> = (0..10).collect();
1079            let mut stream = BufferedStream::new(
1080                chunk_size,
1081                10.try_into().unwrap(),
1082                std::io::Cursor::new(underlying),
1083            )
1084            .await
1085            .unwrap();
1086
1087            let mut buf = [0; 5];
1088
1089            stream.read_exact(&mut buf).await.unwrap();
1090            assert_eq!(&buf[..], &[0, 1, 2, 3, 4]);
1091
1092            stream.read_exact(&mut buf).await.unwrap();
1093            assert_eq!(&buf[..], &[5, 6, 7, 8, 9]);
1094        }
1095    }
1096
1097    #[tokio::test]
1098    async fn buffered_read_cached() {
1099        use tokio::io::{AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _};
1100
1101        let tmp = tempfile::tempdir().unwrap();
1102        let fs = crate::async_fs::Fs::new();
1103        let mut f1 = fs
1104            .create_file_read_write(tmp.path().join("foo"))
1105            .await
1106            .unwrap();
1107
1108        let mut f2 = f1.try_clone().await.unwrap();
1109        f2.write_all(&(0..10).collect::<Vec<_>>()).await.unwrap();
1110        f2.flush().await.unwrap();
1111
1112        f1.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1113        let mut stream = BufferedStream::new(4, 10.try_into().unwrap(), f1)
1114            .await
1115            .unwrap();
1116
1117        let mut buf = [0; 3];
1118        stream.read_exact(&mut buf).await.unwrap();
1119        assert_eq!(&buf[..], &[0, 1, 2]);
1120
1121        // Write zeros to the underlying stream
1122        f2.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1123        f2.write_all(&[0; 10]).await.unwrap();
1124
1125        // We should still get the same thing
1126        stream.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1127        stream.read_exact(&mut buf).await.unwrap();
1128        assert_eq!(&buf[..], &[0, 1, 2]);
1129    }
1130
1131    #[tokio::test]
1132    async fn buffered_write_then_read() {
1133        use tokio::io::{AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _};
1134
1135        for chunk_size in 1..10 {
1136            let underlying = vec![0; 10];
1137            let mut stream = BufferedStream::new(
1138                chunk_size,
1139                10.try_into().unwrap(),
1140                std::io::Cursor::new(underlying),
1141            )
1142            .await
1143            .unwrap();
1144
1145            stream.write_all(&[0, 1, 2, 3, 4]).await.unwrap();
1146            stream.write_all(&[5, 6, 7, 8, 9]).await.unwrap();
1147            stream.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1148
1149            let mut buf = [0; 5];
1150
1151            stream.read_exact(&mut buf).await.unwrap();
1152            assert_eq!(&buf[..], &[0, 1, 2, 3, 4]);
1153
1154            stream.read_exact(&mut buf).await.unwrap();
1155            assert_eq!(&buf[..], &[5, 6, 7, 8, 9]);
1156        }
1157    }
1158
1159    #[tokio::test]
1160    async fn buffered_write_then_flush() {
1161        use tokio::io::{AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _};
1162
1163        let tmp = tempfile::tempdir().unwrap();
1164        let fs = crate::async_fs::Fs::new();
1165        let mut f1 = fs
1166            .create_file_read_write(tmp.path().join("foo"))
1167            .await
1168            .unwrap();
1169
1170        let mut f2 = f1.try_clone().await.unwrap();
1171
1172        f1.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1173        let mut stream = BufferedStream::new(4, 10.try_into().unwrap(), f1)
1174            .await
1175            .unwrap();
1176
1177        stream.write_all(&[0, 1, 2, 3, 4]).await.unwrap();
1178
1179        // nothing should be written yet
1180        let file_len = f2.metadata().await.unwrap().len();
1181        assert_eq!(file_len, 0);
1182
1183        stream.flush().await.unwrap();
1184
1185        let mut buf = [0; 5];
1186
1187        f2.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1188        f2.read_exact(&mut buf).await.unwrap();
1189        assert_eq!(&buf[..], &[0, 1, 2, 3, 4]);
1190    }
1191
1192    #[tokio::main]
1193    async fn buffered_stream_simex_test(
1194        sim: &mut maelstrom_simex::Simulation,
1195        read_size: usize,
1196        write_size: usize,
1197        file_size: usize,
1198    ) {
1199        use tokio::io::{AsyncReadExt as _, AsyncSeekExt as _, AsyncWriteExt as _};
1200
1201        let tmp = tempfile::tempdir().unwrap();
1202        let fs = crate::async_fs::Fs::new();
1203        let f1 = fs
1204            .create_file_read_write(tmp.path().join("foo"))
1205            .await
1206            .unwrap();
1207
1208        let mut stream = BufferedStream::new(4, 2.try_into().unwrap(), f1)
1209            .await
1210            .unwrap();
1211        let mut shadow: Vec<_> = (1..(u8::try_from(file_size).unwrap())).collect();
1212
1213        stream.write_all(&shadow[..]).await.unwrap();
1214        stream.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1215
1216        const ITERATIONS: u8 = 5;
1217        for i in 0..ITERATIONS {
1218            match sim.choose(["read", "write", "flush", "seek"]).unwrap() {
1219                "read" => {
1220                    let pos = stream.stream_position().await.unwrap() as usize;
1221                    let read_length = std::cmp::min(read_size, shadow.len() - pos);
1222
1223                    let mut buf = vec![0; read_length];
1224                    stream.read_exact(&mut buf).await.unwrap();
1225
1226                    assert_eq!(&shadow[pos..(pos + read_length)], &buf);
1227                }
1228                "write" => {
1229                    let pos = stream.stream_position().await.unwrap() as usize;
1230                    let data = vec![i; write_size];
1231                    stream.write_all(&data[..]).await.unwrap();
1232                    let end = pos + write_size;
1233                    if end > shadow.len() {
1234                        shadow.resize(end, 0);
1235                    }
1236                    shadow[pos..(pos + write_size)].copy_from_slice(&data[..]);
1237                }
1238                "seek" => {
1239                    let pos = ((shadow.len() as u64) / (ITERATIONS - 1) as u64) * i as u64;
1240                    stream.seek(std::io::SeekFrom::Start(pos)).await.unwrap();
1241                }
1242                "flush" => {
1243                    stream.flush().await.unwrap();
1244                }
1245                _ => unreachable!(),
1246            }
1247        }
1248
1249        stream.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1250        let mut verify = vec![0; shadow.len()];
1251        stream.read_exact(&mut verify).await.unwrap();
1252        assert_eq!(shadow, verify);
1253
1254        stream.flush().await.unwrap();
1255        let mut file = stream.into_inner();
1256        file.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1257        file.read_exact(&mut verify).await.unwrap();
1258
1259        assert_eq!(shadow, verify);
1260    }
1261
1262    #[test]
1263    fn buffered_stream_simex_read_10_write_15_size_50() {
1264        maelstrom_simex::SimulationExplorer::default().for_each(|mut sim| {
1265            buffered_stream_simex_test(&mut sim, 10, 15, 50);
1266        })
1267    }
1268
1269    #[test]
1270    fn buffered_stream_simex_read_3_write_3_size_10() {
1271        maelstrom_simex::SimulationExplorer::default().for_each(|mut sim| {
1272            buffered_stream_simex_test(&mut sim, 3, 3, 10);
1273        })
1274    }
1275
1276    fn maybe_fast_writer_test(mut writer: MaybeFastWriter, len1: usize, len2: usize) {
1277        use std::io::Write as _;
1278        use std::io::{Seek as _, SeekFrom};
1279        use std::os::fd::AsRawFd as _;
1280
1281        let buf1 = Vec::from_iter((0u8..0xFFu8).cycle().take(len1));
1282        let buf2 = Vec::from_iter((0u8..0xFFu8).cycle().take(len2));
1283
1284        writer.write(&buf1).unwrap();
1285
1286        let tmp = tempfile::tempdir().unwrap();
1287        let fs = crate::fs::Fs::new();
1288        let mut f1 = fs.create_file_read_write(tmp.path().join("f1")).unwrap();
1289        f1.write_all(b"xx").unwrap();
1290        f1.write_all(&buf2).unwrap();
1291        f1.write_all(b"xx").unwrap();
1292        f1.flush().unwrap();
1293        let fd = linux::Fd::from_raw(f1.as_raw_fd());
1294        writer.write_fd(fd, Some(2), buf2.len()).unwrap();
1295
1296        let mut f2 = fs.create_file_read_write(tmp.path().join("f2")).unwrap();
1297        let fd = linux::Fd::from_raw(f2.as_raw_fd());
1298        writer.copy_to_fd(fd, None).unwrap();
1299
1300        f2.seek(SeekFrom::Start(0)).unwrap();
1301        let mut read = vec![];
1302        f2.read_to_end(&mut read).unwrap();
1303
1304        let expected = Vec::from_iter(buf1.iter().copied().chain(buf2.iter().copied()));
1305        assert_eq!(&read[..], expected);
1306
1307        // Do it again, we should have reset
1308        writer.write(&buf1).unwrap();
1309        let fd = linux::Fd::from_raw(f1.as_raw_fd());
1310        writer.write_fd(fd, Some(2), buf2.len()).unwrap();
1311
1312        let mut f3 = fs.create_file_read_write(tmp.path().join("f3")).unwrap();
1313        f3.write_all(b"xx").unwrap();
1314        f3.flush().unwrap();
1315
1316        let fd = linux::Fd::from_raw(f3.as_raw_fd());
1317        writer.copy_to_fd(fd, Some(0)).unwrap();
1318
1319        f3.seek(SeekFrom::Start(0)).unwrap();
1320        let mut read = vec![];
1321        f3.read_to_end(&mut read).unwrap();
1322        assert_eq!(&read[..], expected);
1323    }
1324
1325    #[test]
1326    fn splicer() {
1327        for i in 1..15 {
1328            for j in 1..15 {
1329                maybe_fast_writer_test(
1330                    MaybeFastWriter(SpliceOrFallback::Splice(Splicer::new().unwrap())),
1331                    i,
1332                    j,
1333                );
1334            }
1335        }
1336    }
1337
1338    #[test]
1339    fn slow_writer() {
1340        for i in 1..15 {
1341            for j in 1..15 {
1342                maybe_fast_writer_test(
1343                    MaybeFastWriter(SpliceOrFallback::Fallback(SlowWriter::new(5))),
1344                    i,
1345                    j,
1346                );
1347            }
1348        }
1349    }
1350
1351    #[tokio::test]
1352    async fn lazy_async_read_success() {
1353        let mut read = LazyRead::new(Box::pin(async move { Ok(&[1u8, 2, 3, 4][..]) }));
1354        let mut buf = vec![0u8; 4];
1355        read.read_exact(&mut buf).await.unwrap();
1356        assert_eq!(&buf[..], &[1, 2, 3, 4][..]);
1357    }
1358
1359    #[tokio::test]
1360    async fn lazy_async_read_error() {
1361        let mut read = LazyRead::new(Box::pin(async move {
1362            io::Result::<&[u8]>::Err(io::Error::new(io::ErrorKind::Other, "test error"))
1363        }));
1364        let mut buf = vec![0u8; 4];
1365        read.read_exact(&mut buf).await.unwrap_err();
1366    }
1367}