1 // Copyright 2024 Red Hat, Inc. 2 // Author(s): Paolo Bonzini <pbonzini@redhat.com> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 #[macro_export] 6 /// Given a string constant _without_ embedded or trailing NULs, return 7 /// a `CStr`. 8 /// 9 /// Needed for compatibility with Rust <1.77. 10 macro_rules! c_str { 11 ($str:expr) => {{ 12 const STRING: &str = concat!($str, "\0"); 13 const BYTES: &[u8] = STRING.as_bytes(); 14 15 // "for" is not allowed in const context... oh well, 16 // everybody loves some lisp. This could be turned into 17 // a procedural macro if this is a problem; alternatively 18 // Rust 1.72 makes CStr::from_bytes_with_nul a const function. 19 const fn f(b: &[u8], i: usize) { 20 if i == b.len() - 1 { 21 } else if b[i] == 0 { 22 panic!("c_str argument contains NUL") 23 } else { 24 f(b, i + 1) 25 } 26 } 27 f(BYTES, 0); 28 29 // SAFETY: absence of NULs apart from the final byte was checked above 30 unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES) } 31 }}; 32 } 33 34 #[cfg(test)] 35 mod tests { 36 use std::ffi::CStr; 37 38 use crate::c_str; 39 40 #[test] test_cstr_macro()41 fn test_cstr_macro() { 42 let good = c_str!(""); 43 let good_bytes = b"\xf0\x9f\xa6\x80\0"; 44 assert_eq!(good.to_bytes_with_nul(), good_bytes); 45 } 46 47 #[test] test_cstr_macro_const()48 fn test_cstr_macro_const() { 49 const GOOD: &CStr = c_str!(""); 50 const GOOD_BYTES: &[u8] = b"\xf0\x9f\xa6\x80\0"; 51 assert_eq!(GOOD.to_bytes_with_nul(), GOOD_BYTES); 52 } 53 } 54