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