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