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#[macro_export]
54macro_rules! raw_cstr {
55 ($s: expr) => {
56 $crate::cstr!($s).as_ptr()
57 };
58}
59
60#[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 use super::*;
95
96 #[test]
97 fn test_raw_cstr() {
98 const FMT: &str = "%d\n";
99 let fmt = raw_cstr!(FMT);
100 let len = FMT.len() + 1;
101 let bytes: &[u8] = unsafe { core::slice::from_raw_parts(fmt.cast(), len) };
102 assert_eq!(bytes, b"%d\n\0");
103 }
104
105 #[test]
106 fn test_cstr_runtime() {
107 let to_cstr = ToCStr("hello");
109 assert_eq!(to_cstr.output_len(), 6); let buf: [u8; 6] = to_cstr.const_eval();
112 assert_eq!(&buf, b"hello\0");
113
114 let to_cstr_empty = ToCStr("");
116 assert_eq!(to_cstr_empty.output_len(), 1);
117 let buf2: [u8; 1] = to_cstr_empty.const_eval();
118 assert_eq!(&buf2, b"\0");
119
120 let to_cstr_long = ToCStr("test string");
122 assert_eq!(to_cstr_long.output_len(), 12);
123 let buf3: [u8; 12] = to_cstr_long.const_eval();
124 assert_eq!(&buf3, b"test string\0");
125 }
126}