xref: /third_party/rust/crates/rustix/src/cstr.rs (revision b8a62b91)
1b8a62b91Sopenharmony_ci/// A macro for [`CStr`] literals.
2b8a62b91Sopenharmony_ci///
3b8a62b91Sopenharmony_ci/// This can make passing string literals to rustix APIs more efficient, since
4b8a62b91Sopenharmony_ci/// most underlying system calls with string arguments expect NUL-terminated
5b8a62b91Sopenharmony_ci/// strings, and passing strings to rustix as `CStr`s means that rustix doesn't
6b8a62b91Sopenharmony_ci/// need to copy them into a separate buffer to NUL-terminate them.
7b8a62b91Sopenharmony_ci///
8b8a62b91Sopenharmony_ci/// [`CStr`]: crate::ffi::CStr
9b8a62b91Sopenharmony_ci///
10b8a62b91Sopenharmony_ci/// # Examples
11b8a62b91Sopenharmony_ci///
12b8a62b91Sopenharmony_ci/// ```rust,no_run
13b8a62b91Sopenharmony_ci/// # #[cfg(feature = "fs")]
14b8a62b91Sopenharmony_ci/// # fn main() -> rustix::io::Result<()> {
15b8a62b91Sopenharmony_ci/// use rustix::cstr;
16b8a62b91Sopenharmony_ci/// use rustix::fs::{cwd, statat, AtFlags};
17b8a62b91Sopenharmony_ci///
18b8a62b91Sopenharmony_ci/// let metadata = statat(cwd(), cstr!("test.txt"), AtFlags::empty())?;
19b8a62b91Sopenharmony_ci/// # Ok(())
20b8a62b91Sopenharmony_ci/// # }
21b8a62b91Sopenharmony_ci/// # #[cfg(not(feature = "fs"))]
22b8a62b91Sopenharmony_ci/// # fn main() {}
23b8a62b91Sopenharmony_ci/// ```
24b8a62b91Sopenharmony_ci#[allow(unused_macros)]
25b8a62b91Sopenharmony_ci#[macro_export]
26b8a62b91Sopenharmony_cimacro_rules! cstr {
27b8a62b91Sopenharmony_ci    ($str:literal) => {{
28b8a62b91Sopenharmony_ci        // Check for NUL manually, to ensure safety.
29b8a62b91Sopenharmony_ci        //
30b8a62b91Sopenharmony_ci        // In release builds, with strings that don't contain NULs, this
31b8a62b91Sopenharmony_ci        // constant-folds away.
32b8a62b91Sopenharmony_ci        //
33b8a62b91Sopenharmony_ci        // We don't use std's `CStr::from_bytes_with_nul`; as of this writing,
34b8a62b91Sopenharmony_ci        // that function isn't defined as `#[inline]` in std and doesn't
35b8a62b91Sopenharmony_ci        // constant-fold away.
36b8a62b91Sopenharmony_ci        assert!(
37b8a62b91Sopenharmony_ci            !$str.bytes().any(|b| b == b'\0'),
38b8a62b91Sopenharmony_ci            "cstr argument contains embedded NUL bytes",
39b8a62b91Sopenharmony_ci        );
40b8a62b91Sopenharmony_ci
41b8a62b91Sopenharmony_ci        #[allow(unsafe_code, unused_unsafe)]
42b8a62b91Sopenharmony_ci        {
43b8a62b91Sopenharmony_ci            // Now that we know the string doesn't have embedded NULs, we can call
44b8a62b91Sopenharmony_ci            // `from_bytes_with_nul_unchecked`, which as of this writing is defined
45b8a62b91Sopenharmony_ci            // as `#[inline]` and completely optimizes away.
46b8a62b91Sopenharmony_ci            //
47b8a62b91Sopenharmony_ci            // Safety: We have manually checked that the string does not contain
48b8a62b91Sopenharmony_ci            // embedded NULs above, and we append or own NUL terminator here.
49b8a62b91Sopenharmony_ci            unsafe {
50b8a62b91Sopenharmony_ci                $crate::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes())
51b8a62b91Sopenharmony_ci            }
52b8a62b91Sopenharmony_ci        }
53b8a62b91Sopenharmony_ci    }};
54b8a62b91Sopenharmony_ci}
55b8a62b91Sopenharmony_ci
56b8a62b91Sopenharmony_ci#[test]
57b8a62b91Sopenharmony_cifn test_cstr() {
58b8a62b91Sopenharmony_ci    use crate::ffi::CString;
59b8a62b91Sopenharmony_ci    use alloc::borrow::ToOwned;
60b8a62b91Sopenharmony_ci    assert_eq!(cstr!(""), &*CString::new("").unwrap());
61b8a62b91Sopenharmony_ci    assert_eq!(cstr!("").to_owned(), CString::new("").unwrap());
62b8a62b91Sopenharmony_ci    assert_eq!(cstr!("hello"), &*CString::new("hello").unwrap());
63b8a62b91Sopenharmony_ci    assert_eq!(cstr!("hello").to_owned(), CString::new("hello").unwrap());
64b8a62b91Sopenharmony_ci}
65b8a62b91Sopenharmony_ci
66b8a62b91Sopenharmony_ci#[test]
67b8a62b91Sopenharmony_ci#[should_panic]
68b8a62b91Sopenharmony_cifn test_invalid_cstr() {
69b8a62b91Sopenharmony_ci    let _ = cstr!("hello\0world");
70b8a62b91Sopenharmony_ci}
71b8a62b91Sopenharmony_ci
72b8a62b91Sopenharmony_ci#[test]
73b8a62b91Sopenharmony_ci#[should_panic]
74b8a62b91Sopenharmony_cifn test_invalid_empty_cstr() {
75b8a62b91Sopenharmony_ci    let _ = cstr!("\0");
76b8a62b91Sopenharmony_ci}
77