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