1use 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
26pub 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
48pub 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 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 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 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
802pub 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 f2.seek(std::io::SeekFrom::Start(0)).await.unwrap();
1123 f2.write_all(&[0; 10]).await.unwrap();
1124
1125 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 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 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}