From 7ed0d45c9783785c475b571e2f2912119e07fbb1 Mon Sep 17 00:00:00 2001 From: Tobias Stoeckmann Date: Tue, 12 Mar 2024 19:49:53 +0100 Subject: [PATCH] Reuse Rng during temp name creation The fastrand Rng should be created manually to avoid TLS deallocation issues. The fastrand::alphanumeric function uses a TLS storage internally. With current Rust and fastrand implementation, this is no problem. Yet, we can speed up the process a bit by not accessing the storage for every single random char. --- src/util.rs | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/util.rs b/src/util.rs index ee81bbe7e..10882f17a 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,20 +4,7 @@ use std::{io, iter::repeat_with}; use crate::error::IoResultExt; -fn tmpname(prefix: &OsStr, suffix: &OsStr, rand_len: usize) -> OsString { - let capacity = prefix - .len() - .saturating_add(suffix.len()) - .saturating_add(rand_len); - let mut buf = OsString::with_capacity(capacity); - buf.push(prefix); - let mut char_buf = [0u8; 4]; - for c in repeat_with(fastrand::alphanumeric).take(rand_len) { - buf.push(c.encode_utf8(&mut char_buf)); - } - buf.push(suffix); - buf -} +use fastrand::Rng; pub fn create_helper( base: &Path, @@ -27,12 +14,29 @@ pub fn create_helper( permissions: Option<&std::fs::Permissions>, mut f: impl FnMut(PathBuf, Option<&std::fs::Permissions>) -> io::Result, ) -> io::Result { + let capacity = prefix + .len() + .saturating_add(random_len) + .saturating_add(suffix.len()); + let mut buf = OsString::with_capacity(capacity); + if random_len == 0 { - let path = base.join(tmpname(prefix, suffix, random_len)); + buf.push(prefix); + buf.push(suffix); + let path = base.join(buf); f(path, permissions) } else { + let mut char_buf = [0u8; 4]; + let mut rng = Rng::new(); + for _ in 0..crate::NUM_RETRIES { - let path = base.join(tmpname(prefix, suffix, random_len)); + buf.push(prefix); + for c in repeat_with(|| rng.alphanumeric()).take(random_len) { + buf.push(c.encode_utf8(&mut char_buf)); + } + buf.push(suffix); + let path = base.join(&buf); + buf.clear(); return match f(path, permissions) { Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => continue, // AddrInUse can happen if we're creating a UNIX domain socket and