| 1 | use crate::ffi::{CStr, CString}; |
| 2 | use crate::mem::MaybeUninit; |
| 3 | use crate::path::Path; |
| 4 | use crate::{io, ptr, slice}; |
| 5 | |
| 6 | // Make sure to stay under 4096 so the compiler doesn't insert a probe frame: |
| 7 | // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html |
| 8 | #[cfg (not(target_os = "espidf" ))] |
| 9 | const MAX_STACK_ALLOCATION: usize = 384; |
| 10 | #[cfg (target_os = "espidf" )] |
| 11 | const MAX_STACK_ALLOCATION: usize = 32; |
| 12 | |
| 13 | const NUL_ERR: io::Error = |
| 14 | io::const_error!(io::ErrorKind::InvalidInput, "file name contained an unexpected NUL byte" ); |
| 15 | |
| 16 | #[inline ] |
| 17 | pub fn run_path_with_cstr<T>(path: &Path, f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> { |
| 18 | run_with_cstr(path.as_os_str().as_encoded_bytes(), f) |
| 19 | } |
| 20 | |
| 21 | #[inline ] |
| 22 | pub fn run_with_cstr<T>(bytes: &[u8], f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> { |
| 23 | // Dispatch and dyn erase the closure type to prevent mono bloat. |
| 24 | // See https://github.com/rust-lang/rust/pull/121101. |
| 25 | if bytes.len() >= MAX_STACK_ALLOCATION { |
| 26 | run_with_cstr_allocating(bytes, f) |
| 27 | } else { |
| 28 | unsafe { run_with_cstr_stack(bytes, f) } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | /// # Safety |
| 33 | /// |
| 34 | /// `bytes` must have a length less than `MAX_STACK_ALLOCATION`. |
| 35 | unsafe fn run_with_cstr_stack<T>( |
| 36 | bytes: &[u8], |
| 37 | f: &dyn Fn(&CStr) -> io::Result<T>, |
| 38 | ) -> io::Result<T> { |
| 39 | let mut buf: MaybeUninit<[u8; 384]> = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); |
| 40 | let buf_ptr: *mut u8 = buf.as_mut_ptr() as *mut u8; |
| 41 | |
| 42 | unsafe { |
| 43 | ptr::copy_nonoverlapping(src:bytes.as_ptr(), dst:buf_ptr, count:bytes.len()); |
| 44 | buf_ptr.add(bytes.len()).write(val:0); |
| 45 | } |
| 46 | |
| 47 | match CStr::from_bytes_with_nul(bytes:unsafe { slice::from_raw_parts(data:buf_ptr, len:bytes.len() + 1) }) { |
| 48 | Ok(s: &CStr) => f(s), |
| 49 | Err(_) => Err(NUL_ERR), |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | #[cold ] |
| 54 | #[inline (never)] |
| 55 | fn run_with_cstr_allocating<T>(bytes: &[u8], f: &dyn Fn(&CStr) -> io::Result<T>) -> io::Result<T> { |
| 56 | match CString::new(bytes) { |
| 57 | Ok(s: CString) => f(&s), |
| 58 | Err(_) => Err(NUL_ERR), |
| 59 | } |
| 60 | } |
| 61 | |