const_str/__ctfe/
to_str.rs1#![allow(unsafe_code)]
2
3use super::str::StrBuf;
4use crate::utf8::CharEncodeUtf8;
5
6pub struct ToStr<T>(pub T);
7
8impl ToStr<&str> {
9 pub const fn output_len(&self) -> usize {
10 self.0.len()
11 }
12
13 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
14 StrBuf::from_str(self.0)
15 }
16}
17
18impl ToStr<bool> {
19 const fn bool_to_str(b: bool) -> &'static str {
20 if b {
21 "true"
22 } else {
23 "false"
24 }
25 }
26
27 pub const fn output_len(&self) -> usize {
28 Self::bool_to_str(self.0).len()
29 }
30
31 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
32 let bytes: &[u8] = Self::bool_to_str(self.0).as_bytes();
33 let buf = crate::bytes::merge([0; N], bytes);
34 unsafe { StrBuf::new_unchecked(buf) }
35 }
36}
37
38impl ToStr<char> {
39 pub const fn output_len(&self) -> usize {
40 self.0.len_utf8()
41 }
42
43 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
44 let ch = CharEncodeUtf8::new(self.0);
45 let buf = crate::bytes::merge([0; N], ch.as_bytes());
46 unsafe { StrBuf::new_unchecked(buf) }
47 }
48}
49
50macro_rules! impl_integer_to_str {
51 ($unsigned: ty, $signed: ty) => {
52 impl ToStr<$unsigned> {
53 pub const fn output_len(&self) -> usize {
54 let mut x = self.0;
55 let mut ans = 1;
56 while x > 9 {
57 ans += 1;
58 x /= 10;
59 }
60 ans
61 }
62
63 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
64 let mut buf = [0; N];
65 let mut pos = 0;
66 let mut x = self.0;
67 loop {
68 buf[pos] = b'0' + (x % 10) as u8;
69 pos += 1;
70 x /= 10;
71 if x == 0 {
72 break;
73 }
74 }
75 assert!(pos == N);
76 let buf = crate::bytes::reversed(buf);
77 unsafe { StrBuf::new_unchecked(buf) }
78 }
79 }
80
81 impl ToStr<$signed> {
82 pub const fn output_len(&self) -> usize {
83 let x = self.0;
84 let abs_len = ToStr(x.unsigned_abs()).output_len();
85 abs_len + (x < 0) as usize
86 }
87
88 pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
89 let mut buf = [0; N];
90 let mut pos = 0;
91
92 let mut x = self.0.unsigned_abs();
93
94 loop {
95 buf[pos] = b'0' + (x % 10) as u8;
96 pos += 1;
97 x /= 10;
98 if x == 0 {
99 break;
100 }
101 }
102
103 if self.0 < 0 {
104 buf[pos] = b'-';
105 pos += 1;
106 }
107
108 assert!(pos == N);
109 let buf = crate::bytes::reversed(buf);
110 unsafe { StrBuf::new_unchecked(buf) }
111 }
112 }
113 };
114}
115
116impl_integer_to_str!(u8, i8);
117impl_integer_to_str!(u16, i16);
118impl_integer_to_str!(u32, i32);
119impl_integer_to_str!(u64, i64);
120impl_integer_to_str!(u128, i128);
121impl_integer_to_str!(usize, isize);
122
123#[macro_export]
155macro_rules! to_str {
156 ($x: expr) => {{
157 const OUTPUT_LEN: usize = $crate::__ctfe::ToStr($x).output_len();
158 const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
159 $crate::__ctfe::ToStr($x).const_eval();
160 OUTPUT_BUF.as_str()
161 }};
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 #[test]
169 fn test_to_str() {
170 macro_rules! test_to_str {
171 ($ty: ty, $x: expr) => {{
172 const X: $ty = $x;
173 const OUTPUT_LEN: usize = ToStr(X).output_len();
174 const OUTPUT_BUF: StrBuf<OUTPUT_LEN> = ToStr(X).const_eval();
175
176 let output = OUTPUT_BUF.as_str();
177 let ans = X.to_string();
178 assert_eq!(OUTPUT_LEN, ans.len());
179 assert_eq!(output, ans);
180 }};
181 }
182
183 test_to_str!(&str, "lovelive superstar");
184
185 test_to_str!(bool, true);
186 test_to_str!(bool, false);
187
188 test_to_str!(char, '鲤');
189 test_to_str!(char, '鱼');
190
191 test_to_str!(u8, 0);
192 test_to_str!(u16, 0);
193 test_to_str!(u32, 0);
194 test_to_str!(u64, 0);
195 test_to_str!(u128, 0);
196
197 test_to_str!(u8, 10);
198 test_to_str!(u8, 128);
199 test_to_str!(u8, u8::MAX);
200
201 test_to_str!(u64, 1);
202 test_to_str!(u64, 10);
203 test_to_str!(u64, 42);
204 test_to_str!(u64, u64::MAX);
205
206 test_to_str!(u128, u128::MAX);
207
208 test_to_str!(i8, 0);
209 test_to_str!(i16, 0);
210 test_to_str!(i32, 0);
211 test_to_str!(i64, 0);
212 test_to_str!(i128, 0);
213
214 test_to_str!(i8, -10);
215 test_to_str!(i8, -42);
216 test_to_str!(i8, i8::MAX);
217 test_to_str!(i8, i8::MIN);
218
219 test_to_str!(i64, 1);
220 test_to_str!(i64, 10);
221 test_to_str!(i64, -42);
222 test_to_str!(i64, i64::MAX);
223 test_to_str!(i64, i64::MIN);
224
225 test_to_str!(i128, i128::MAX);
226 test_to_str!(i128, i128::MIN);
227 }
228
229 #[test]
230 fn test_to_str_runtime() {
231 let to_str_bool = ToStr(true);
233 let buf = to_str_bool.const_eval::<4>();
234 assert_eq!(buf.as_str(), "true");
235
236 let to_str_char = ToStr('A');
237 let buf2 = to_str_char.const_eval::<1>();
238 assert_eq!(buf2.as_str(), "A");
239
240 let to_str_str = ToStr("hello");
241 let buf3 = to_str_str.const_eval::<5>();
242 assert_eq!(buf3.as_str(), "hello");
243
244 let to_str_u8 = ToStr(42u8);
246 let len = to_str_u8.output_len();
247 assert_eq!(len, 2);
248
249 let to_str_i8 = ToStr(-42i8);
250 let len2 = to_str_i8.output_len();
251 assert_eq!(len2, 3);
252
253 let to_str_u64 = ToStr(12345u64);
254 let len3 = to_str_u64.output_len();
255 assert_eq!(len3, 5);
256
257 let to_str_i64 = ToStr(-9876i64);
258 let len4 = to_str_i64.output_len();
259 assert_eq!(len4, 5);
260
261 let to_str_u8_max = ToStr(u8::MAX);
263 let buf_max = to_str_u8_max.const_eval::<3>();
264 assert_eq!(buf_max.as_str(), "255");
265
266 let to_str_i8_min = ToStr(i8::MIN);
267 let buf_min = to_str_i8_min.const_eval::<4>();
268 assert_eq!(buf_min.as_str(), "-128");
269
270 let to_str_str = ToStr("test");
272 assert_eq!(to_str_str.output_len(), 4);
273
274 let to_str_bool_true = ToStr(true);
275 assert_eq!(to_str_bool_true.output_len(), 4);
276
277 let to_str_bool_false = ToStr(false);
278 assert_eq!(to_str_bool_false.output_len(), 5);
279
280 let to_str_char = ToStr('A');
281 assert_eq!(to_str_char.output_len(), 1);
282
283 let to_str_char_utf8 = ToStr('你');
284 assert_eq!(to_str_char_utf8.output_len(), 3);
285 }
286}