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, F>(path: &Path, f: F) -> io::Result<T> |
19 | where |
20 | F: FnOnce(&CStr) -> io::Result<T>, |
21 | { |
22 | run_with_cstr(path.as_os_str().as_encoded_bytes(), f) |
23 | } |
24 | |
25 | #[inline ] |
26 | pub fn run_with_cstr<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
27 | where |
28 | F: FnOnce(&CStr) -> io::Result<T>, |
29 | { |
30 | if bytes.len() >= MAX_STACK_ALLOCATION { |
31 | return run_with_cstr_allocating(bytes, f); |
32 | } |
33 | |
34 | let mut buf: MaybeUninit<[u8; 384]> = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit(); |
35 | let buf_ptr: *mut u8 = buf.as_mut_ptr() as *mut u8; |
36 | |
37 | unsafe { |
38 | ptr::copy_nonoverlapping(src:bytes.as_ptr(), dst:buf_ptr, count:bytes.len()); |
39 | buf_ptr.add(bytes.len()).write(val:0); |
40 | } |
41 | |
42 | match CStr::from_bytes_with_nul(bytes:unsafe { slice::from_raw_parts(data:buf_ptr, len:bytes.len() + 1) }) { |
43 | Ok(s: &CStr) => f(s), |
44 | Err(_) => Err(NUL_ERR), |
45 | } |
46 | } |
47 | |
48 | #[cold ] |
49 | #[inline (never)] |
50 | fn run_with_cstr_allocating<T, F>(bytes: &[u8], f: F) -> io::Result<T> |
51 | where |
52 | F: FnOnce(&CStr) -> io::Result<T>, |
53 | { |
54 | match CString::new(bytes) { |
55 | Ok(s: CString) => f(&s), |
56 | Err(_) => Err(NUL_ERR), |
57 | } |
58 | } |
59 | |