From 817fa605ee6a2549fe8e6057ec23a8309d42d2e9 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Tue, 8 Apr 2025 15:43:38 +0200 Subject: [PATCH] fs: avoid some copies in `tokio::fs::write` (#7199) --- tokio/src/fs/write.rs | 2 +- tokio/src/util/as_ref.rs | 38 ++++++++++++++++++++++++++++++++++ tokio/src/util/mod.rs | 6 ++++++ tokio/src/util/typeid.rs | 44 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 tokio/src/util/as_ref.rs create mode 100644 tokio/src/util/typeid.rs diff --git a/tokio/src/fs/write.rs b/tokio/src/fs/write.rs index 28606fb36..f5d18e843 100644 --- a/tokio/src/fs/write.rs +++ b/tokio/src/fs/write.rs @@ -25,7 +25,7 @@ use std::{io, path::Path}; /// ``` pub async fn write(path: impl AsRef, contents: impl AsRef<[u8]>) -> io::Result<()> { let path = path.as_ref().to_owned(); - let contents = contents.as_ref().to_owned(); + let contents = crate::util::as_ref::upgrade(contents); asyncify(move || std::fs::write(path, contents)).await } diff --git a/tokio/src/util/as_ref.rs b/tokio/src/util/as_ref.rs new file mode 100644 index 000000000..464975d60 --- /dev/null +++ b/tokio/src/util/as_ref.rs @@ -0,0 +1,38 @@ +use super::typeid; + +#[derive(Debug)] +pub(crate) enum OwnedBuf { + Vec(Vec), + #[cfg(feature = "io-util")] + Bytes(bytes::Bytes), +} + +impl AsRef<[u8]> for OwnedBuf { + fn as_ref(&self) -> &[u8] { + match self { + Self::Vec(vec) => vec, + #[cfg(feature = "io-util")] + Self::Bytes(bytes) => bytes, + } + } +} + +pub(crate) fn upgrade>(buf: B) -> OwnedBuf { + let buf = match unsafe { typeid::try_transmute::>(buf) } { + Ok(vec) => return OwnedBuf::Vec(vec), + Err(original_buf) => original_buf, + }; + + let buf = match unsafe { typeid::try_transmute::(buf) } { + Ok(string) => return OwnedBuf::Vec(string.into_bytes()), + Err(original_buf) => original_buf, + }; + + #[cfg(feature = "io-util")] + let buf = match unsafe { typeid::try_transmute::(buf) } { + Ok(bytes) => return OwnedBuf::Bytes(bytes), + Err(original_buf) => original_buf, + }; + + OwnedBuf::Vec(buf.as_ref().to_owned()) +} diff --git a/tokio/src/util/mod.rs b/tokio/src/util/mod.rs index 6dce66f25..328c4094a 100644 --- a/tokio/src/util/mod.rs +++ b/tokio/src/util/mod.rs @@ -2,6 +2,9 @@ cfg_io_driver! { pub(crate) mod bit; } +#[cfg(feature = "fs")] +pub(crate) mod as_ref; + #[cfg(feature = "rt")] pub(crate) mod atomic_cell; @@ -81,6 +84,9 @@ cfg_rt_multi_thread! { pub(crate) mod trace; +#[cfg(feature = "fs")] +pub(crate) mod typeid; + pub(crate) mod error; #[cfg(feature = "io-util")] diff --git a/tokio/src/util/typeid.rs b/tokio/src/util/typeid.rs new file mode 100644 index 000000000..1cd14b59c --- /dev/null +++ b/tokio/src/util/typeid.rs @@ -0,0 +1,44 @@ +use std::{ + any::TypeId, + marker::PhantomData, + mem::{self, ManuallyDrop}, +}; + +// SAFETY: this function does not compare lifetimes. Values returned as `Ok` +// may have their lifetimes extended. +pub(super) unsafe fn try_transmute(x: Src) -> Result { + if nonstatic_typeid::() == TypeId::of::() { + let x = ManuallyDrop::new(x); + Ok(mem::transmute_copy::(&x)) + } else { + Err(x) + } +} + +// https://github.com/dtolnay/typeid/blob/b06a3c08a0eaccc7df6091ade1ae4e3fb53609d5/src/lib.rs#L197-L222 +#[inline(always)] +fn nonstatic_typeid() -> TypeId +where + T: ?Sized, +{ + trait NonStaticAny { + fn get_type_id(&self) -> TypeId + where + Self: 'static; + } + + impl NonStaticAny for PhantomData { + #[inline(always)] + fn get_type_id(&self) -> TypeId + where + Self: 'static, + { + TypeId::of::() + } + } + + let phantom_data = PhantomData::; + NonStaticAny::get_type_id(unsafe { + mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(&phantom_data) + }) +}