• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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]
26 macro_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]
test_cstr()57 fn 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]
test_invalid_cstr()68 fn test_invalid_cstr() {
69     let _ = cstr!("hello\0world");
70 }
71 
72 #[test]
73 #[should_panic]
test_invalid_empty_cstr()74 fn test_invalid_empty_cstr() {
75     let _ = cstr!("\0");
76 }
77