pict-rs/src/file.rs

353 lines
11 KiB
Rust
Raw Normal View History

use std::path::Path;
use futures_core::Stream;
use tokio_util::bytes::Bytes;
2021-10-13 04:16:31 +00:00
#[cfg(feature = "io-uring")]
pub(crate) use io_uring::File;
#[cfg(not(feature = "io-uring"))]
2021-10-14 00:06:53 +00:00
pub(crate) use tokio_file::File;
2021-10-13 04:16:31 +00:00
2024-03-10 04:53:46 +00:00
use crate::future::WithPollTimer;
pub(crate) async fn write_from_stream(
path: impl AsRef<Path>,
stream: impl Stream<Item = std::io::Result<Bytes>>,
) -> std::io::Result<()> {
2024-03-10 04:53:46 +00:00
let mut file = File::create(path).with_poll_timer("create-file").await?;
file.write_from_stream(stream)
.with_poll_timer("write-from-stream")
.await?;
file.close().await?;
Ok(())
}
2021-10-14 00:06:53 +00:00
#[cfg(not(feature = "io-uring"))]
mod tokio_file {
2024-03-10 04:53:46 +00:00
use crate::{future::WithPollTimer, store::file_store::FileError, Either};
2021-10-14 00:06:53 +00:00
use actix_web::web::{Bytes, BytesMut};
2023-08-23 16:59:42 +00:00
use futures_core::Stream;
2021-11-01 02:11:35 +00:00
use std::{io::SeekFrom, path::Path};
use streem::IntoStreamer;
2024-03-10 04:53:46 +00:00
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
use tokio_util::{
bytes::Buf,
codec::{BytesCodec, FramedRead},
};
2021-10-14 00:06:53 +00:00
pub(crate) struct File {
inner: tokio::fs::File,
}
2021-10-13 04:16:31 +00:00
2021-10-14 00:06:53 +00:00
impl File {
pub(crate) async fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
Ok(File {
2022-04-07 17:56:40 +00:00
inner: tracing::trace_span!(parent: None, "Open File")
.in_scope(|| tokio::fs::File::open(path))
.await?,
2021-10-14 00:06:53 +00:00
})
}
2021-10-13 04:16:31 +00:00
2021-10-14 00:06:53 +00:00
pub(crate) async fn create(path: impl AsRef<Path>) -> std::io::Result<Self> {
Ok(File {
2022-04-07 17:56:40 +00:00
inner: tracing::trace_span!(parent: None, "Create File")
.in_scope(|| tokio::fs::File::create(path))
.await?,
2021-10-14 00:06:53 +00:00
})
}
2021-11-01 02:11:35 +00:00
pub(crate) async fn write_from_stream<S>(&mut self, stream: S) -> std::io::Result<()>
2021-10-23 19:14:12 +00:00
where
S: Stream<Item = std::io::Result<Bytes>>,
{
2023-08-23 16:59:42 +00:00
let stream = std::pin::pin!(stream);
let mut stream = stream.into_streamer();
2021-10-23 19:14:12 +00:00
2024-03-10 04:53:46 +00:00
while let Some(mut bytes) = stream.try_next().with_poll_timer("try-next").await? {
tracing::trace!("write_from_stream: looping");
2024-03-10 04:53:46 +00:00
while bytes.has_remaining() {
self.inner
.write_buf(&mut bytes)
.with_poll_timer("write-buf")
.await?;
2021-10-23 19:14:12 +00:00
2024-03-10 04:53:46 +00:00
crate::sync::cooperate().await;
}
2021-10-23 19:14:12 +00:00
}
Ok(())
}
pub(crate) async fn close(self) -> std::io::Result<()> {
Ok(())
}
2021-10-14 00:06:53 +00:00
pub(crate) async fn read_to_stream(
mut self,
from_start: Option<u64>,
len: Option<u64>,
2021-10-23 04:48:56 +00:00
) -> Result<impl Stream<Item = std::io::Result<Bytes>>, FileError> {
2021-10-14 00:06:53 +00:00
let obj = match (from_start, len) {
(Some(lower), Some(upper)) => {
self.inner.seek(SeekFrom::Start(lower)).await?;
Either::left(self.inner.take(upper))
2021-10-14 00:06:53 +00:00
}
(None, Some(upper)) => Either::left(self.inner.take(upper)),
2021-10-14 00:06:53 +00:00
(Some(lower), None) => {
self.inner.seek(SeekFrom::Start(lower)).await?;
Either::right(self.inner)
2021-10-14 00:06:53 +00:00
}
(None, None) => Either::right(self.inner),
2021-10-14 00:06:53 +00:00
};
Ok(crate::stream::map_ok(
FramedRead::new(obj, BytesCodec::new()),
BytesMut::freeze,
))
2021-10-13 04:16:31 +00:00
}
}
2021-10-14 00:06:53 +00:00
}
#[cfg(feature = "io-uring")]
mod io_uring {
use crate::store::file_store::FileError;
use actix_web::web::{Bytes, BytesMut};
2023-08-23 16:59:42 +00:00
use futures_core::Stream;
2021-10-14 00:06:53 +00:00
use std::{
convert::TryInto,
fs::Metadata,
path::{Path, PathBuf},
};
use streem::IntoStreamer;
2021-10-14 00:06:53 +00:00
use tokio_uring::{
2021-10-14 00:50:07 +00:00
buf::{IoBuf, IoBufMut},
2021-10-14 00:06:53 +00:00
BufResult,
};
2021-10-13 04:16:31 +00:00
pub(crate) struct File {
path: PathBuf,
2021-10-14 00:06:53 +00:00
inner: tokio_uring::fs::File,
2021-10-13 04:16:31 +00:00
}
impl File {
pub(crate) async fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
tracing::debug!("Opening io-uring file: {:?}", path.as_ref());
2021-10-13 04:16:31 +00:00
Ok(File {
path: path.as_ref().to_owned(),
2022-04-07 17:56:40 +00:00
inner: tracing::trace_span!(parent: None, "Open File")
.in_scope(|| tokio_uring::fs::File::open(path))
.await?,
2021-10-13 04:16:31 +00:00
})
}
pub(crate) async fn create(path: impl AsRef<Path>) -> std::io::Result<Self> {
tracing::debug!("Creating io-uring file: {:?}", path.as_ref());
2021-10-13 04:16:31 +00:00
Ok(File {
path: path.as_ref().to_owned(),
2022-04-07 17:56:40 +00:00
inner: tracing::trace_span!(parent: None, "Create File")
.in_scope(|| tokio_uring::fs::File::create(path))
.await?,
2021-10-13 04:16:31 +00:00
})
}
2021-10-23 04:48:56 +00:00
async fn metadata(&self) -> std::io::Result<Metadata> {
2021-10-13 04:16:31 +00:00
tokio::fs::metadata(&self.path).await
}
2021-11-01 02:11:35 +00:00
pub(crate) async fn write_from_stream<S>(&mut self, stream: S) -> std::io::Result<()>
2021-10-23 19:14:12 +00:00
where
S: Stream<Item = std::io::Result<Bytes>>,
{
2023-08-23 16:59:42 +00:00
let stream = std::pin::pin!(stream);
let mut stream = stream.into_streamer();
2021-10-23 19:14:12 +00:00
let mut cursor: u64 = 0;
while let Some(res) = stream.next().await {
tracing::trace!("write_from_stream while: looping");
let mut buf = res?;
2021-10-23 19:14:12 +00:00
let len = buf.len();
let mut position = 0;
loop {
tracing::trace!("write_from_stream: looping");
2021-10-23 19:14:12 +00:00
if position == len {
break;
}
let position_u64: u64 = position.try_into().unwrap();
let (res, slice) = self
.write_at(buf.slice(position..len), cursor + position_u64)
.await;
let n = res?;
if n == 0 {
return Err(std::io::ErrorKind::UnexpectedEof.into());
}
position += n;
buf = slice.into_inner();
}
let position: u64 = position.try_into().unwrap();
cursor += position;
}
self.inner.sync_all().await?;
Ok(())
}
pub(crate) async fn close(self) -> std::io::Result<()> {
self.inner.close().await
}
2021-10-14 00:06:53 +00:00
pub(crate) async fn read_to_stream(
self,
from_start: Option<u64>,
len: Option<u64>,
2021-10-23 04:48:56 +00:00
) -> Result<impl Stream<Item = std::io::Result<Bytes>>, FileError> {
Ok(bytes_stream(self, from_start, len))
2021-10-14 00:06:53 +00:00
}
2021-10-13 04:16:31 +00:00
2021-10-14 00:06:53 +00:00
async fn read_at<T: IoBufMut>(&self, buf: T, pos: u64) -> BufResult<usize, T> {
self.inner.read_at(buf, pos).await
}
2021-10-13 04:16:31 +00:00
2021-10-14 00:06:53 +00:00
async fn write_at<T: IoBuf>(&self, buf: T, pos: u64) -> BufResult<usize, T> {
self.inner.write_at(buf, pos).await
2021-10-13 04:16:31 +00:00
}
}
fn bytes_stream(
file: File,
from_start: Option<u64>,
len: Option<u64>,
) -> impl Stream<Item = std::io::Result<Bytes>> {
streem::try_from_fn(|yielder| async move {
let file_size = file.metadata().await?.len();
let mut cursor = from_start.unwrap_or(0);
let remaining_size = file_size.saturating_sub(cursor);
let read_until = len.unwrap_or(remaining_size) + cursor;
let mut bytes = BytesMut::new();
loop {
tracing::trace!("bytes_stream: looping");
let max_size = read_until.saturating_sub(cursor);
2021-10-13 04:16:31 +00:00
if max_size == 0 {
break;
}
2021-10-13 04:16:31 +00:00
let capacity = max_size.min(65_356) as usize;
2021-10-13 04:16:31 +00:00
if bytes.capacity() < capacity {
bytes.reserve(capacity - bytes.capacity());
}
2021-10-13 04:16:31 +00:00
let (result, mut buf_) = file.read_at(bytes, cursor).await;
2021-10-13 04:16:31 +00:00
let n = match result {
Ok(n) => n,
Err(e) => return Err(e),
};
2021-10-13 04:16:31 +00:00
bytes = buf_.split_off(n);
let n: u64 = n.try_into().map_err(|_| std::io::ErrorKind::Other)?;
cursor += n;
2021-10-13 04:16:31 +00:00
yielder.yield_ok(buf_.into()).await;
2021-10-13 04:16:31 +00:00
}
Ok(())
})
2021-10-13 04:16:31 +00:00
}
2021-10-14 00:50:07 +00:00
#[cfg(test)]
mod tests {
use std::io::Read;
2024-03-09 21:38:39 +00:00
use streem::IntoStreamer;
use tokio::io::AsyncWriteExt;
2021-10-14 00:50:07 +00:00
2023-09-29 16:54:43 +00:00
macro_rules! test_async {
2021-10-14 00:50:07 +00:00
($fut:expr) => {
2023-09-29 16:54:43 +00:00
actix_web::rt::System::new()
.block_on(async move { crate::sync::spawn("tests", $fut).await.unwrap() })
2021-10-14 00:50:07 +00:00
};
}
const EARTH_GIF: &'static str = "client-examples/earth.gif";
#[test]
fn read() {
let tmp = "/tmp/read-test";
2023-09-29 16:54:43 +00:00
test_async!(async move {
2024-03-09 21:38:39 +00:00
let file = super::File::open(EARTH_GIF).await.unwrap();
2021-10-14 00:50:07 +00:00
let mut tmp_file = tokio::fs::File::create(tmp).await.unwrap();
2024-03-09 21:38:39 +00:00
let stream = file.read_to_stream(None, None).await.unwrap();
let stream = std::pin::pin!(stream);
let mut stream = stream.into_streamer();
while let Some(mut bytes) = stream.try_next().await.unwrap() {
tmp_file.write_all_buf(&mut bytes).await.unwrap();
}
2021-10-14 00:50:07 +00:00
});
let mut source = std::fs::File::open(EARTH_GIF).unwrap();
let mut dest = std::fs::File::open(tmp).unwrap();
let mut source_bytes = Vec::new();
source.read_to_end(&mut source_bytes).unwrap();
2021-10-14 00:50:07 +00:00
let mut dest_bytes = Vec::new();
dest.read_to_end(&mut dest_bytes).unwrap();
2021-10-14 00:50:07 +00:00
drop(dest);
std::fs::remove_file(tmp).unwrap();
assert_eq!(source_bytes.len(), dest_bytes.len());
assert_eq!(source_bytes, dest_bytes);
2021-10-14 00:50:07 +00:00
}
#[test]
fn write() {
let tmp = "/tmp/write-test";
2023-09-29 16:54:43 +00:00
test_async!(async move {
2024-03-10 04:53:46 +00:00
let file = tokio::fs::File::open(EARTH_GIF).await.unwrap();
2021-10-14 00:50:07 +00:00
let mut tmp_file = super::File::create(tmp).await.unwrap();
2024-03-10 04:53:46 +00:00
tmp_file
.write_from_stream(tokio_util::io::ReaderStream::new(file))
.await
.unwrap();
2021-10-14 00:50:07 +00:00
});
let mut source = std::fs::File::open(EARTH_GIF).unwrap();
let mut dest = std::fs::File::open(tmp).unwrap();
let mut source_bytes = Vec::new();
source.read_to_end(&mut source_bytes).unwrap();
2021-10-14 00:50:07 +00:00
let mut dest_bytes = Vec::new();
dest.read_to_end(&mut dest_bytes).unwrap();
2021-10-14 00:50:07 +00:00
drop(dest);
std::fs::remove_file(tmp).unwrap();
assert_eq!(source_bytes.len(), dest_bytes.len());
assert_eq!(source_bytes, dest_bytes);
2021-10-14 00:50:07 +00:00
}
}
2021-10-13 04:16:31 +00:00
}