#[macro_export]
macro_rules! c {
($s:expr) => {{
const BYTES: &[u8] = $crate::__private::concat!($s, "\0").as_bytes();
const _: () = $crate::__private::const_c_str_check(BYTES);
#[allow(unused_unsafe)]
unsafe {
$crate::__private::CStr::from_bytes_with_nul_unchecked(BYTES)
}
}};
}
#[doc(hidden)]
pub const fn const_c_str_check(bytes: &[u8]) {
let mut i = bytes.len().saturating_sub(1);
assert!(!bytes.is_empty() && bytes[i] == 0, "input was not nul-terminated");
while i != 0 {
i -= 1;
let byte = bytes[i];
assert!(byte != 0, "input contained interior nul");
}
}
#[allow(
clippy::alloc_instead_of_core,
clippy::std_instead_of_alloc,
clippy::std_instead_of_core,
clippy::undocumented_unsafe_blocks,
clippy::wildcard_imports
)]
#[cfg(test)]
mod tests {
use core::ffi::CStr;
#[test]
fn test_c_macro() {
#[track_caller]
fn t(s: &CStr, raw: &[u8]) {
assert_eq!(s.to_bytes_with_nul(), raw);
}
t(c!(""), b"\0");
t(c!("a"), b"a\0");
t(c!("abc"), b"abc\0");
t(c!(concat!("abc", "d")), b"abcd\0");
}
#[test]
fn test_is_c_str() {
#[track_caller]
fn t(bytes: &[u8]) {
assert_eq!(
std::panic::catch_unwind(|| super::const_c_str_check(bytes)).is_ok(),
CStr::from_bytes_with_nul(bytes).is_ok()
);
}
t(b"\0");
t(b"a\0");
t(b"abc\0");
t(b"");
t(b"a");
t(b"abc");
t(b"\0a");
t(b"\0a\0");
t(b"ab\0c\0");
t(b"\0\0");
}
}