mirror of
https://git.asonix.dog/asonix/pict-rs
synced 2024-12-23 03:41:23 +00:00
Enable io-uring
This commit is contained in:
parent
226cbd1b0a
commit
2318fd9dca
6 changed files with 497 additions and 20 deletions
41
Cargo.lock
generated
41
Cargo.lock
generated
|
@ -120,20 +120,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-rt"
|
name = "actix-rt"
|
||||||
version = "2.2.0"
|
version = "2.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc7d7cd957c9ed92288a7c3c96af81fa5291f65247a76a34dac7b6af74e52ba0"
|
checksum = "ea360596a50aa9af459850737f99293e5cb9114ae831118cb6026b3bbc7583ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-macros",
|
"actix-macros",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-uring",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-server"
|
name = "actix-server"
|
||||||
version = "2.0.0-beta.5"
|
version = "2.0.0-beta.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "26369215fcc3b0176018b3b68756a8bcc275bb000e6212e454944913a1f9bf87"
|
checksum = "7367665785765b066ad16e1086d26a087f696bc7c42b6f93004ced6cfcf1eeca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
"actix-service",
|
"actix-service",
|
||||||
|
@ -142,7 +143,6 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"slab",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -872,6 +872,16 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "io-uring"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d75829ed9377bab6c90039fe47b9d84caceb4b5063266142e21bcce6550cda8"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
@ -1162,6 +1172,7 @@ version = "0.3.0-alpha.38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-form-data",
|
"actix-form-data",
|
||||||
"actix-rt",
|
"actix-rt",
|
||||||
|
"actix-server",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"awc",
|
"awc",
|
||||||
|
@ -1182,6 +1193,7 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"time 0.3.3",
|
"time 0.3.3",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-uring",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-actix-web",
|
"tracing-actix-web",
|
||||||
|
@ -1473,6 +1485,12 @@ version = "1.0.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -1936,6 +1954,19 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-uring"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09f54af096a39b937631659f1a5da60ab7c1af334025c33b87ed913072c3c8a9"
|
||||||
|
dependencies = [
|
||||||
|
"io-uring",
|
||||||
|
"libc",
|
||||||
|
"scoped-tls",
|
||||||
|
"slab",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
|
|
|
@ -9,10 +9,14 @@ repository = "https://git.asonix.dog/asonix/pict-rs"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
io-uring = ["actix-rt/io-uring", "actix-server/io-uring", "tokio-uring"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-form-data = "0.6.0-beta.1"
|
actix-form-data = "0.6.0-beta.1"
|
||||||
actix-rt = "2.2.0"
|
actix-rt = "2.2.0"
|
||||||
|
actix-server = "2.0.0-beta.6"
|
||||||
actix-web = { version = "4.0.0-beta.8", default-features = false }
|
actix-web = { version = "4.0.0-beta.8", default-features = false }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
awc = { version = "3.0.0-beta.7", default-features = false, features = ["rustls"] }
|
awc = { version = "3.0.0-beta.7", default-features = false, features = ["rustls"] }
|
||||||
|
@ -33,6 +37,7 @@ structopt = "0.3.14"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
time = { version = "0.3.0", features = ["serde"] }
|
time = { version = "0.3.0", features = ["serde"] }
|
||||||
tokio = { version = "1", default-features = false, features = ["fs", "io-util", "process", "sync"] }
|
tokio = { version = "1", default-features = false, features = ["fs", "io-util", "process", "sync"] }
|
||||||
|
tokio-uring = { version = "0.1", optional = true }
|
||||||
tokio-util = { version = "0.6", default-features = false, features = ["codec"] }
|
tokio-util = { version = "0.6", default-features = false, features = ["codec"] }
|
||||||
tracing = "0.1.15"
|
tracing = "0.1.15"
|
||||||
tracing-error = "0.1.2"
|
tracing-error = "0.1.2"
|
||||||
|
|
423
src/file.rs
Normal file
423
src/file.rs
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
#[cfg(feature = "io-uring")]
|
||||||
|
pub(crate) use io_uring::File;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "io-uring"))]
|
||||||
|
pub(crate) use tokio::fs::File;
|
||||||
|
|
||||||
|
#[cfg(feature = "io-uring")]
|
||||||
|
mod io_uring {
|
||||||
|
use std::{
|
||||||
|
convert::TryInto,
|
||||||
|
fs::Metadata,
|
||||||
|
future::Future,
|
||||||
|
io::SeekFrom,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll, Waker},
|
||||||
|
};
|
||||||
|
use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||||
|
|
||||||
|
type IoFuture =
|
||||||
|
Pin<Box<dyn Future<Output = (tokio_uring::fs::File, std::io::Result<usize>, Vec<u8>)>>>;
|
||||||
|
|
||||||
|
type FlushFuture = Pin<Box<dyn Future<Output = (tokio_uring::fs::File, std::io::Result<()>)>>>;
|
||||||
|
|
||||||
|
type ShutdownFuture = Pin<Box<dyn Future<Output = std::io::Result<()>>>>;
|
||||||
|
|
||||||
|
type SeekFuture = Pin<Box<dyn Future<Output = std::io::Result<u64>>>>;
|
||||||
|
|
||||||
|
enum FileState {
|
||||||
|
Reading { future: IoFuture },
|
||||||
|
Writing { future: IoFuture },
|
||||||
|
Syncing { future: FlushFuture },
|
||||||
|
Seeking { future: SeekFuture },
|
||||||
|
Shutdown { future: ShutdownFuture },
|
||||||
|
Pending,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileState {
|
||||||
|
fn take(&mut self) -> Self {
|
||||||
|
std::mem::replace(self, FileState::Pending)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct File {
|
||||||
|
path: PathBuf,
|
||||||
|
inner: Option<tokio_uring::fs::File>,
|
||||||
|
cursor: usize,
|
||||||
|
wakers: Vec<Waker>,
|
||||||
|
state: FileState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub(crate) async fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
|
||||||
|
tracing::info!("Opening io-uring file");
|
||||||
|
Ok(File {
|
||||||
|
path: path.as_ref().to_owned(),
|
||||||
|
inner: Some(tokio_uring::fs::File::open(path).await?),
|
||||||
|
cursor: 0,
|
||||||
|
wakers: vec![],
|
||||||
|
state: FileState::Pending,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn create(path: impl AsRef<Path>) -> std::io::Result<Self> {
|
||||||
|
tracing::info!("Creating io-uring file");
|
||||||
|
Ok(File {
|
||||||
|
path: path.as_ref().to_owned(),
|
||||||
|
inner: Some(tokio_uring::fs::File::create(path).await?),
|
||||||
|
cursor: 0,
|
||||||
|
wakers: vec![],
|
||||||
|
state: FileState::Pending,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn metadata(&self) -> std::io::Result<Metadata> {
|
||||||
|
tokio::fs::metadata(&self.path).await
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_read(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
mut future: IoFuture,
|
||||||
|
) -> Poll<std::io::Result<()>> {
|
||||||
|
match Pin::new(&mut future).poll(cx) {
|
||||||
|
Poll::Ready((file, Ok(bytes_read), vec)) => {
|
||||||
|
self.cursor += bytes_read;
|
||||||
|
self.inner = Some(file);
|
||||||
|
buf.put_slice(&vec[0..bytes_read]);
|
||||||
|
|
||||||
|
// Wake tasks waiting on read to complete
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Ok(()))
|
||||||
|
}
|
||||||
|
Poll::Ready((file, Err(err), _vec)) => {
|
||||||
|
self.inner = Some(file);
|
||||||
|
// Wake tasks waiting on read to complete
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Err(err))
|
||||||
|
}
|
||||||
|
Poll::Pending => {
|
||||||
|
self.state = FileState::Reading { future };
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_write(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
mut future: IoFuture,
|
||||||
|
) -> Poll<std::io::Result<usize>> {
|
||||||
|
match Pin::new(&mut future).poll(cx) {
|
||||||
|
Poll::Ready((file, Ok(bytes_written), _vec)) => {
|
||||||
|
self.cursor += bytes_written;
|
||||||
|
self.inner = Some(file);
|
||||||
|
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Ok(bytes_written))
|
||||||
|
}
|
||||||
|
Poll::Ready((file, Err(err), _vec)) => {
|
||||||
|
self.inner = Some(file);
|
||||||
|
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Err(err))
|
||||||
|
}
|
||||||
|
Poll::Pending => {
|
||||||
|
self.state = FileState::Writing { future };
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
mut future: FlushFuture,
|
||||||
|
) -> Poll<std::io::Result<()>> {
|
||||||
|
match Pin::new(&mut future).poll(cx) {
|
||||||
|
Poll::Ready((file, res)) => {
|
||||||
|
self.inner = Some(file);
|
||||||
|
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(res)
|
||||||
|
}
|
||||||
|
Poll::Pending => {
|
||||||
|
self.state = FileState::Syncing { future };
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
mut future: ShutdownFuture,
|
||||||
|
) -> Poll<std::io::Result<()>> {
|
||||||
|
match Pin::new(&mut future).poll(cx) {
|
||||||
|
Poll::Ready(res) => {
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(res)
|
||||||
|
}
|
||||||
|
Poll::Pending => {
|
||||||
|
self.state = FileState::Shutdown { future };
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_seek(
|
||||||
|
&mut self,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
mut future: SeekFuture,
|
||||||
|
) -> Poll<std::io::Result<u64>> {
|
||||||
|
match Pin::new(&mut future).poll(cx) {
|
||||||
|
Poll::Ready(Ok(new_position)) => {
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(position) = new_position.try_into() {
|
||||||
|
self.cursor = position;
|
||||||
|
Poll::Ready(Ok(new_position))
|
||||||
|
} else {
|
||||||
|
Poll::Ready(Err(std::io::ErrorKind::Other.into()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Poll::Ready(Err(err)) => {
|
||||||
|
for waker in self.wakers.drain(..) {
|
||||||
|
waker.wake();
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Ready(Err(err))
|
||||||
|
}
|
||||||
|
Poll::Pending => {
|
||||||
|
self.state = FileState::Seeking { future };
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_read(&mut self, buf: &mut ReadBuf<'_>) -> IoFuture {
|
||||||
|
let bytes_to_read = buf.remaining().min(65_536);
|
||||||
|
|
||||||
|
let vec = vec![0u8; bytes_to_read];
|
||||||
|
|
||||||
|
let file = self.inner.take().unwrap();
|
||||||
|
let position: u64 = self.cursor.try_into().unwrap();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let (res, vec) = file.read_at(vec, position).await;
|
||||||
|
(file, res, vec)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_write(&mut self, buf: &[u8]) -> IoFuture {
|
||||||
|
let vec = buf.to_vec();
|
||||||
|
|
||||||
|
let file = self.inner.take().unwrap();
|
||||||
|
let position: u64 = self.cursor.try_into().unwrap();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let (res, vec) = file.write_at(vec, position).await;
|
||||||
|
(file, res, vec)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_flush(&mut self) -> FlushFuture {
|
||||||
|
let file = self.inner.take().unwrap();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let res = file.sync_all().await;
|
||||||
|
(file, res)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_shutdown(&mut self) -> ShutdownFuture {
|
||||||
|
let file = self.inner.take().unwrap();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
file.sync_all().await?;
|
||||||
|
file.close().await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_seek(&self, from_end: i64) -> SeekFuture {
|
||||||
|
let path = self.path.clone();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let meta = tokio::fs::metadata(path).await?;
|
||||||
|
let end = meta.len();
|
||||||
|
|
||||||
|
if from_end < 0 {
|
||||||
|
let from_end = (-1) * from_end;
|
||||||
|
let from_end: u64 =
|
||||||
|
from_end.try_into().map_err(|_| std::io::ErrorKind::Other)?;
|
||||||
|
|
||||||
|
return Ok(end + from_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
let from_end: u64 = from_end.try_into().map_err(|_| std::io::ErrorKind::Other)?;
|
||||||
|
|
||||||
|
if from_end > end {
|
||||||
|
return Err(std::io::ErrorKind::Other.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(end - from_end)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_waker<T>(&mut self, cx: &mut Context<'_>) -> Poll<T> {
|
||||||
|
let already_registered = self.wakers.iter().any(|waker| cx.waker().will_wake(waker));
|
||||||
|
|
||||||
|
if !already_registered {
|
||||||
|
self.wakers.push(cx.waker().clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncRead for File {
|
||||||
|
fn poll_read(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &mut ReadBuf<'_>,
|
||||||
|
) -> Poll<std::io::Result<()>> {
|
||||||
|
match self.state.take() {
|
||||||
|
FileState::Pending => {
|
||||||
|
let future = (*self).prepare_read(buf);
|
||||||
|
|
||||||
|
(*self).poll_read(cx, buf, future)
|
||||||
|
}
|
||||||
|
FileState::Reading { future } => (*self).poll_read(cx, buf, future),
|
||||||
|
_ => (*self).register_waker(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncWrite for File {
|
||||||
|
fn poll_write(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
buf: &[u8],
|
||||||
|
) -> Poll<std::io::Result<usize>> {
|
||||||
|
match self.state.take() {
|
||||||
|
FileState::Pending => {
|
||||||
|
let future = (*self).prepare_write(buf);
|
||||||
|
|
||||||
|
(*self).poll_write(cx, future)
|
||||||
|
}
|
||||||
|
FileState::Writing { future } => (*self).poll_write(cx, future),
|
||||||
|
_ => (*self).register_waker(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||||
|
match self.state.take() {
|
||||||
|
FileState::Pending => {
|
||||||
|
let future = (*self).prepare_flush();
|
||||||
|
|
||||||
|
(*self).poll_flush(cx, future)
|
||||||
|
}
|
||||||
|
FileState::Syncing { future } => (*self).poll_flush(cx, future),
|
||||||
|
_ => (*self).register_waker(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_shutdown(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<std::io::Result<()>> {
|
||||||
|
match self.state.take() {
|
||||||
|
FileState::Pending => {
|
||||||
|
let future = (*self).prepare_shutdown();
|
||||||
|
|
||||||
|
(*self).poll_shutdown(cx, future)
|
||||||
|
}
|
||||||
|
FileState::Shutdown { future } => (*self).poll_shutdown(cx, future),
|
||||||
|
_ => (*self).register_waker(cx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsyncSeek for File {
|
||||||
|
fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> std::io::Result<()> {
|
||||||
|
match position {
|
||||||
|
SeekFrom::Start(from_start) => {
|
||||||
|
self.cursor = from_start.try_into().unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
SeekFrom::End(from_end) => match self.state.take() {
|
||||||
|
FileState::Pending => {
|
||||||
|
let future = self.prepare_seek(from_end);
|
||||||
|
|
||||||
|
self.state = FileState::Seeking { future };
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(std::io::ErrorKind::Other.into()),
|
||||||
|
},
|
||||||
|
SeekFrom::Current(from_current) => {
|
||||||
|
if from_current < 0 {
|
||||||
|
let to_subtract = (-1) * from_current;
|
||||||
|
let to_subtract: usize = to_subtract
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| std::io::ErrorKind::Other)?;
|
||||||
|
|
||||||
|
if to_subtract > self.cursor {
|
||||||
|
return Err(std::io::ErrorKind::Other.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cursor -= to_subtract;
|
||||||
|
} else {
|
||||||
|
let from_current: usize = from_current
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| std::io::ErrorKind::Other)?;
|
||||||
|
|
||||||
|
self.cursor += from_current;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll_complete(
|
||||||
|
mut self: Pin<&mut Self>,
|
||||||
|
cx: &mut Context<'_>,
|
||||||
|
) -> Poll<std::io::Result<u64>> {
|
||||||
|
match self.state.take() {
|
||||||
|
FileState::Pending => Poll::Ready(Ok(self
|
||||||
|
.cursor
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| std::io::ErrorKind::Other)?)),
|
||||||
|
FileState::Seeking { future } => (*self).poll_seek(cx, future),
|
||||||
|
_ => Poll::Ready(Err(std::io::ErrorKind::Other.into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,6 +44,7 @@ mod config;
|
||||||
mod error;
|
mod error;
|
||||||
mod exiftool;
|
mod exiftool;
|
||||||
mod ffmpeg;
|
mod ffmpeg;
|
||||||
|
mod file;
|
||||||
mod magick;
|
mod magick;
|
||||||
mod middleware;
|
mod middleware;
|
||||||
mod migrate;
|
mod migrate;
|
||||||
|
@ -231,7 +232,7 @@ async fn safe_save_file(path: PathBuf, mut bytes: web::Bytes) -> Result<(), Erro
|
||||||
|
|
||||||
// Open the file for writing
|
// Open the file for writing
|
||||||
debug!("Creating {:?}", path);
|
debug!("Creating {:?}", path);
|
||||||
let mut file = tokio::fs::File::create(&path).await?;
|
let mut file = crate::file::File::create(&path).await?;
|
||||||
|
|
||||||
// try writing
|
// try writing
|
||||||
debug!("Writing to {:?}", path);
|
debug!("Writing to {:?}", path);
|
||||||
|
@ -541,7 +542,7 @@ async fn process(
|
||||||
|
|
||||||
let permit = PROCESS_SEMAPHORE.acquire().await?;
|
let permit = PROCESS_SEMAPHORE.acquire().await?;
|
||||||
|
|
||||||
let file = tokio::fs::File::open(original_path.clone()).await?;
|
let file = crate::file::File::open(original_path.clone()).await?;
|
||||||
|
|
||||||
let mut processed_reader =
|
let mut processed_reader =
|
||||||
crate::magick::process_image_write_read(file, thumbnail_args, format)?;
|
crate::magick::process_image_write_read(file, thumbnail_args, format)?;
|
||||||
|
@ -708,7 +709,7 @@ async fn ranged_file_resp(
|
||||||
if range_header.is_empty() {
|
if range_header.is_empty() {
|
||||||
return Err(UploadError::Range.into());
|
return Err(UploadError::Range.into());
|
||||||
} else if range_header.len() == 1 {
|
} else if range_header.len() == 1 {
|
||||||
let file = tokio::fs::File::open(path).await?;
|
let file = crate::file::File::open(path).await?;
|
||||||
|
|
||||||
let meta = file.metadata().await?;
|
let meta = file.metadata().await?;
|
||||||
|
|
||||||
|
@ -724,7 +725,7 @@ async fn ranged_file_resp(
|
||||||
}
|
}
|
||||||
//No Range header in the request - return the entire document
|
//No Range header in the request - return the entire document
|
||||||
None => {
|
None => {
|
||||||
let file = tokio::fs::File::open(path).await?;
|
let file = crate::file::File::open(path).await?;
|
||||||
let stream = Box::pin(crate::stream::bytes_stream(file)) as LocalBoxStream<'_, _>;
|
let stream = Box::pin(crate::stream::bytes_stream(file)) as LocalBoxStream<'_, _>;
|
||||||
(HttpResponse::Ok(), stream)
|
(HttpResponse::Ok(), stream)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ impl Range {
|
||||||
|
|
||||||
pub(crate) async fn chop_file(
|
pub(crate) async fn chop_file(
|
||||||
&self,
|
&self,
|
||||||
mut file: tokio::fs::File,
|
mut file: crate::file::File,
|
||||||
) -> Result<LocalBoxStream<'static, Result<Bytes, Error>>, Error> {
|
) -> Result<LocalBoxStream<'static, Result<Bytes, Error>>, Error> {
|
||||||
match self {
|
match self {
|
||||||
Range::Start(start) => {
|
Range::Start(start) => {
|
||||||
|
|
|
@ -991,7 +991,7 @@ pub(crate) async fn safe_save_reader(
|
||||||
|
|
||||||
debug!("Writing stream to {:?}", to);
|
debug!("Writing stream to {:?}", to);
|
||||||
|
|
||||||
let mut file = tokio::fs::File::create(to).await?;
|
let mut file = crate::file::File::create(to).await?;
|
||||||
|
|
||||||
tokio::io::copy(input, &mut file).await?;
|
tokio::io::copy(input, &mut file).await?;
|
||||||
|
|
||||||
|
@ -1025,7 +1025,7 @@ where
|
||||||
|
|
||||||
let to1 = to.clone();
|
let to1 = to.clone();
|
||||||
let fut = async move {
|
let fut = async move {
|
||||||
let mut file = tokio::fs::File::create(to1).await?;
|
let mut file = crate::file::File::create(to1).await?;
|
||||||
|
|
||||||
while let Some(res) = stream.next().await {
|
while let Some(res) = stream.next().await {
|
||||||
let mut bytes = res?;
|
let mut bytes = res?;
|
||||||
|
@ -1086,11 +1086,28 @@ mod test {
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
macro_rules! test_on_arbiter {
|
||||||
|
($fut:expr) => {
|
||||||
|
actix_rt::System::new().block_on(async move {
|
||||||
|
let arbiter = actix_rt::Arbiter::new();
|
||||||
|
|
||||||
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
|
|
||||||
|
arbiter.spawn(async move {
|
||||||
|
let handle = actix_rt::spawn($fut);
|
||||||
|
|
||||||
|
let _ = tx.send(handle.await.unwrap());
|
||||||
|
});
|
||||||
|
|
||||||
|
rx.await.unwrap()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hasher_works() {
|
fn hasher_works() {
|
||||||
let hash = actix_rt::System::new()
|
let hash = test_on_arbiter!(async move {
|
||||||
.block_on(async move {
|
let file1 = crate::file::File::open("./client-examples/earth.gif").await?;
|
||||||
let file1 = tokio::fs::File::open("./client-examples/earth.gif").await?;
|
|
||||||
|
|
||||||
let mut hasher = Hasher::new(file1, Sha256::new());
|
let mut hasher = Hasher::new(file1, Sha256::new());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue