-
Notifications
You must be signed in to change notification settings - Fork 12.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reduce CString allocations in std as much as possible
Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
- Loading branch information
1 parent
33d3519
commit 86974b8
Showing
13 changed files
with
397 additions
and
286 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,3 +11,7 @@ | |
#![allow(dead_code)] | ||
|
||
pub mod alloc; | ||
pub mod small_c_string; | ||
|
||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use crate::ffi::{CStr, CString}; | ||
use crate::mem::MaybeUninit; | ||
use crate::path::Path; | ||
use crate::slice; | ||
use crate::{io, ptr}; | ||
|
||
// Make sure to stay under 4096 so the compiler doesn't insert a probe frame: | ||
// https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html | ||
#[cfg(not(target_os = "espidf"))] | ||
const MAX_STACK_ALLOCATION: usize = 384; | ||
#[cfg(target_os = "espidf")] | ||
const MAX_STACK_ALLOCATION: usize = 32; | ||
|
||
const NUL_ERR: io::Error = | ||
io::const_io_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte"); | ||
|
||
#[inline] | ||
pub fn run_path_with_cstr<T, F>(path: &Path, f: F) -> io::Result<T> | ||
where | ||
F: FnOnce(&CStr) -> io::Result<T>, | ||
{ | ||
run_with_cstr(path.as_os_str().bytes(), f) | ||
} | ||
|
||
#[inline] | ||
pub fn run_with_cstr<T, F>(bytes: &[u8], f: F) -> io::Result<T> | ||
where | ||
F: FnOnce(&CStr) -> io::Result<T>, | ||
{ | ||
if bytes.len() >= MAX_STACK_ALLOCATION { | ||
return run_with_cstr_allocating(bytes, f); | ||
} | ||
|
||
let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); | ||
let buf_ptr = buf.as_mut_ptr() as *mut u8; | ||
|
||
unsafe { | ||
ptr::copy_nonoverlapping(bytes.as_ptr(), buf_ptr, bytes.len()); | ||
buf_ptr.add(bytes.len()).write(0); | ||
} | ||
|
||
match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, bytes.len() + 1) }) { | ||
Ok(s) => f(s), | ||
Err(_) => Err(NUL_ERR), | ||
} | ||
} | ||
|
||
#[cold] | ||
#[inline(never)] | ||
fn run_with_cstr_allocating<T, F>(bytes: &[u8], f: F) -> io::Result<T> | ||
where | ||
F: FnOnce(&CStr) -> io::Result<T>, | ||
{ | ||
match CString::new(bytes) { | ||
Ok(s) => f(&s), | ||
Err(_) => Err(NUL_ERR), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
use crate::ffi::CString; | ||
use crate::hint::black_box; | ||
use crate::path::Path; | ||
use crate::sys::common::small_c_string::run_path_with_cstr; | ||
use core::iter::repeat; | ||
|
||
#[test] | ||
fn stack_allocation_works() { | ||
let path = Path::new("abc"); | ||
let result = run_path_with_cstr(path, |p| { | ||
assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); | ||
Ok(42) | ||
}); | ||
assert_eq!(result.unwrap(), 42); | ||
} | ||
|
||
#[test] | ||
fn stack_allocation_fails() { | ||
let path = Path::new("ab\0"); | ||
assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); | ||
} | ||
|
||
#[test] | ||
fn heap_allocation_works() { | ||
let path = repeat("a").take(384).collect::<String>(); | ||
let path = Path::new(&path); | ||
let result = run_path_with_cstr(path, |p| { | ||
assert_eq!(p, &*CString::new(path.as_os_str().bytes()).unwrap()); | ||
Ok(42) | ||
}); | ||
assert_eq!(result.unwrap(), 42); | ||
} | ||
|
||
#[test] | ||
fn heap_allocation_fails() { | ||
let mut path = repeat("a").take(384).collect::<String>(); | ||
path.push('\0'); | ||
let path = Path::new(&path); | ||
assert!(run_path_with_cstr::<(), _>(path, |_| unreachable!()).is_err()); | ||
} | ||
|
||
#[bench] | ||
fn bench_stack_path_alloc(b: &mut test::Bencher) { | ||
let path = repeat("a").take(383).collect::<String>(); | ||
let p = Path::new(&path); | ||
b.iter(|| { | ||
run_path_with_cstr(p, |cstr| { | ||
black_box(cstr); | ||
Ok(()) | ||
}) | ||
.unwrap(); | ||
}); | ||
} | ||
|
||
#[bench] | ||
fn bench_heap_path_alloc(b: &mut test::Bencher) { | ||
let path = repeat("a").take(384).collect::<String>(); | ||
let p = Path::new(&path); | ||
b.iter(|| { | ||
run_path_with_cstr(p, |cstr| { | ||
black_box(cstr); | ||
Ok(()) | ||
}) | ||
.unwrap(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.