xref: /openbmc/qemu/rust/qemu-api/src/c_str.rs (revision 718e255f)
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