const_str/__ctfe/
cstr.rs

1#![allow(unsafe_code)]
2
3pub struct ToCStr<T>(pub T);
4
5impl ToCStr<&str> {
6    const fn check_nul(&self) {
7        let bytes = self.0.as_bytes();
8        let mut i = 0;
9        while i < bytes.len() {
10            assert!(bytes[i] != 0);
11            i += 1;
12        }
13    }
14
15    pub const fn output_len(&self) -> usize {
16        self.check_nul();
17        self.0.len() + 1
18    }
19
20    pub const fn const_eval<const N: usize>(&self) -> [u8; N] {
21        let mut buf = [0; N];
22        let mut pos = 0;
23        let bytes = self.0.as_bytes();
24        let mut i = 0;
25        while i < bytes.len() {
26            assert!(bytes[i] != 0);
27            buf[pos] = bytes[i];
28            pos += 1;
29            i += 1;
30        }
31        pos += 1;
32        assert!(pos == N);
33        buf
34    }
35}
36
37/// Converts a string slice to [`*const c_char`](core::ffi::c_char).
38///
39/// The C-style string is guaranteed to be terminated by a nul byte.
40/// This trailing nul byte will be appended by this macro.
41/// The provided data should not contain any nul bytes in it.
42///
43/// This macro is [const-context only](./index.html#const-context-only).
44///
45/// See also [`cstr!`](crate::cstr)
46///
47/// # Examples
48///
49/// ```
50/// use core::ffi::c_char;
51/// const PRINTF_FMT: *const c_char = const_str::raw_cstr!("%d\n");
52/// ```
53#[macro_export]
54macro_rules! raw_cstr {
55    ($s: expr) => {
56        $crate::cstr!($s).as_ptr()
57    };
58}
59
60/// Converts a string slice to [`&CStr`](core::ffi::CStr).
61///
62/// The C-style string is guaranteed to be terminated by a nul byte.
63/// This trailing nul byte will be appended by this macro.
64/// The provided data should not contain any nul bytes in it.
65///
66/// This macro is [const-context only](./index.html#const-context-only).
67///
68/// See also [`raw_cstr!`](crate::raw_cstr).
69///
70/// Note that Rust has supported [C string literals][c-str-literal] since [1.77.0][rust-1-77-0].
71///
72/// [c-str-literal]: https://doc.rust-lang.org/reference/tokens.html#c-string-and-raw-c-string-literals
73/// [rust-1-77-0]: https://blog.rust-lang.org/2024/03/21/Rust-1.77.0.html#c-string-literals
74///
75/// # Examples
76///
77/// ```
78/// use core::ffi::CStr;;
79/// const PRINTF_FMT: &CStr = const_str::cstr!("%d\n");
80/// ```
81#[macro_export]
82macro_rules! cstr {
83    ($s:expr) => {{
84        const OUTPUT_LEN: ::core::primitive::usize = $crate::__ctfe::ToCStr($s).output_len();
85        const OUTPUT_BUF: [u8; OUTPUT_LEN] = $crate::__ctfe::ToCStr($s).const_eval();
86        const OUTPUT: &::core::ffi::CStr =
87            unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(&OUTPUT_BUF) };
88        OUTPUT
89    }};
90}
91
92#[cfg(test)]
93mod tests {
94    #[test]
95    fn test_raw_cstr() {
96        const FMT: &str = "%d\n";
97        let fmt = raw_cstr!(FMT);
98        let len = FMT.len() + 1;
99        let bytes: &[u8] = unsafe { core::slice::from_raw_parts(fmt.cast(), len) };
100        assert_eq!(bytes, b"%d\n\0");
101    }
102}