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 | |