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/// ```
13/// # #[cfg(feature = "fs")]
14/// # fn main() -> rustix::io::Result<()> {
15/// use rustix::cstr;
16/// use rustix::fs::{statat, AtFlags, CWD};
17///
18/// let metadata = statat(CWD, cstr!("Cargo.toml"), 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
44 // call `from_bytes_with_nul_unchecked`, which as of this writing
45 // is defined as `#[inline]` and completely optimizes away.
46 //
47 // SAFETY: We have manually checked that the string does not
48 // contain embedded NULs above, and we append or own NUL terminator
49 // here.
50 unsafe {
51 $crate::ffi::CStr::from_bytes_with_nul_unchecked(concat!($str, "\0").as_bytes())
52 }
53 }
54 }};
55}
56
57#[test]
58fn test_cstr() {
59 use crate::ffi::CString;
60 use alloc::borrow::ToOwned;
61 assert_eq!(cstr!(""), &*CString::new("").unwrap());
62 assert_eq!(cstr!("").to_owned(), CString::new("").unwrap());
63 assert_eq!(cstr!("hello"), &*CString::new("hello").unwrap());
64 assert_eq!(cstr!("hello").to_owned(), CString::new("hello").unwrap());
65}
66
67#[test]
68#[should_panic]
69fn test_invalid_cstr() {
70 let _ = cstr!("hello\0world");
71}
72
73#[test]
74#[should_panic]
75fn test_invalid_empty_cstr() {
76 let _ = cstr!("\0");
77}
78