const_str/__ctfe/
fmt.rs

1#![allow(unsafe_code)]
2
3use super::StrBuf;
4use super::ToStr;
5
6use crate::slice::advance;
7use crate::utf8::CharEscapeDebug;
8use crate::utf8::CharEscapeDebugArgs;
9
10#[derive(Clone, Copy)]
11pub struct FmtSpec {
12    pub alternate: bool,
13}
14
15pub struct Display<T>(pub T, pub FmtSpec);
16
17macro_rules! delegate_display {
18    ($($ty: ty,)+) => {
19        $(
20            impl Display<$ty> {
21                pub const fn output_len(&self) -> usize {
22                    ToStr(self.0).output_len()
23                }
24
25                pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
26                    ToStr(self.0).const_eval()
27                }
28            }
29        )+
30    };
31}
32
33delegate_display!(&str, char, bool, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize,);
34
35#[doc(hidden)]
36#[macro_export]
37macro_rules! __fmt_display {
38    ($x: expr, $spec: expr) => {{
39        const OUTPUT_LEN: usize = $crate::__ctfe::Display($x, $spec).output_len();
40        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
41            $crate::__ctfe::Display($x, $spec).const_eval();
42        OUTPUT_BUF.as_str()
43    }};
44}
45
46pub struct Debug<T>(pub T, pub FmtSpec);
47
48macro_rules! delegate_debug {
49    ($($ty: ty,)+) => {
50        $(
51            impl Debug<$ty> {
52                pub const fn output_len(&self) -> usize {
53                    ToStr(self.0).output_len()
54                }
55
56                pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
57                    ToStr(self.0).const_eval()
58                }
59            }
60        )+
61    };
62}
63
64delegate_debug!(bool, u8, u16, u32, u64, usize, i8, i16, i32, i64, isize,);
65
66impl Debug<char> {
67    pub const fn output_len(&self) -> usize {
68        let escape = CharEscapeDebug::new(
69            self.0,
70            CharEscapeDebugArgs {
71                escape_single_quote: true,
72                escape_double_quote: false,
73            },
74        );
75
76        escape.as_bytes().len() + 2
77    }
78
79    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
80        let mut buf = [0; N];
81        let mut pos = 0;
82
83        macro_rules! push {
84            ($x: expr) => {{
85                buf[pos] = $x;
86                pos += 1;
87            }};
88        }
89
90        push!(b'\'');
91        {
92            let e = CharEscapeDebug::new(
93                self.0,
94                CharEscapeDebugArgs {
95                    escape_single_quote: true,
96                    escape_double_quote: false,
97                },
98            );
99            let bytes = e.as_bytes();
100            let mut i = 0;
101            while i < bytes.len() {
102                push!(bytes[i]);
103                i += 1;
104            }
105        }
106        push!(b'\'');
107
108        assert!(pos == N);
109
110        unsafe { StrBuf::new_unchecked(buf) }
111    }
112}
113
114impl Debug<&str> {
115    pub const fn output_len(&self) -> usize {
116        let mut s = self.0.as_bytes();
117        let mut ans = 2;
118        while let Some((ch, count)) = crate::utf8::next_char(s) {
119            s = advance(s, count);
120            let e = CharEscapeDebug::new(
121                ch,
122                CharEscapeDebugArgs {
123                    escape_single_quote: false,
124                    escape_double_quote: true,
125                },
126            );
127            ans += e.as_bytes().len()
128        }
129        ans
130    }
131
132    pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
133        let mut buf = [0; N];
134        let mut pos = 0;
135
136        macro_rules! push {
137            ($x: expr) => {{
138                buf[pos] = $x;
139                pos += 1;
140            }};
141        }
142
143        push!(b'"');
144
145        let mut s = self.0.as_bytes();
146        while let Some((ch, count)) = crate::utf8::next_char(s) {
147            s = advance(s, count);
148            let e = CharEscapeDebug::new(
149                ch,
150                CharEscapeDebugArgs {
151                    escape_single_quote: false,
152                    escape_double_quote: true,
153                },
154            );
155            let bytes = e.as_bytes();
156            let mut i = 0;
157            while i < bytes.len() {
158                push!(bytes[i]);
159                i += 1;
160            }
161        }
162
163        push!(b'"');
164
165        assert!(pos == N);
166
167        unsafe { StrBuf::new_unchecked(buf) }
168    }
169}
170
171#[doc(hidden)]
172#[macro_export]
173macro_rules! __fmt_debug {
174    ($x: expr, $spec: expr) => {{
175        const OUTPUT_LEN: usize = $crate::__ctfe::Debug($x, $spec).output_len();
176        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
177            $crate::__ctfe::Debug($x, $spec).const_eval();
178        OUTPUT_BUF.as_str()
179    }};
180}
181
182struct Hex<T>(T, FmtSpec, bool);
183
184pub struct LowerHex<T>(pub T, pub FmtSpec);
185pub struct UpperHex<T>(pub T, pub FmtSpec);
186
187macro_rules! impl_integer_hex {
188    ($unsigned: ty, $signed: ty) => {
189        impl Hex<$unsigned> {
190            const fn output_len(&self) -> usize {
191                let mut x = self.0;
192                let mut ans = 0;
193                loop {
194                    ans += 1;
195                    x /= 16;
196                    if x == 0 {
197                        break;
198                    }
199                }
200                if self.1.alternate {
201                    ans += 2;
202                }
203                ans
204            }
205
206            const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
207                let mut buf = [0; N];
208                let mut pos = 0;
209                let mut x = self.0;
210                loop {
211                    let d = crate::ascii::num_to_hex_digit((x % 16) as u8);
212                    buf[pos] = if self.2 { d.to_ascii_uppercase() } else { d };
213                    pos += 1;
214                    x /= 16;
215                    if x == 0 {
216                        break;
217                    }
218                }
219                if self.1.alternate {
220                    buf[pos] = b'x';
221                    pos += 1;
222                    buf[pos] = b'0';
223                    pos += 1;
224                }
225                assert!(pos == N);
226                let buf = crate::bytes::reversed(buf);
227                unsafe { StrBuf::new_unchecked(buf) }
228            }
229        }
230
231        impl LowerHex<$unsigned> {
232            pub const fn output_len(&self) -> usize {
233                let h = Hex(self.0, self.1, false);
234                h.output_len()
235            }
236
237            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
238                let h = Hex(self.0, self.1, false);
239                h.const_eval()
240            }
241        }
242
243        impl UpperHex<$unsigned> {
244            pub const fn output_len(&self) -> usize {
245                let h = Hex(self.0, self.1, true);
246                h.output_len()
247            }
248
249            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
250                let h = Hex(self.0, self.1, true);
251                h.const_eval()
252            }
253        }
254
255        impl LowerHex<$signed> {
256            pub const fn output_len(&self) -> usize {
257                let h = Hex(self.0 as $unsigned, self.1, false);
258                h.output_len()
259            }
260
261            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
262                let h = Hex(self.0 as $unsigned, self.1, false);
263                h.const_eval()
264            }
265        }
266
267        impl UpperHex<$signed> {
268            pub const fn output_len(&self) -> usize {
269                let h = Hex(self.0 as $unsigned, self.1, true);
270                h.output_len()
271            }
272
273            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
274                let h = Hex(self.0 as $unsigned, self.1, true);
275                h.const_eval()
276            }
277        }
278    };
279}
280
281impl_integer_hex!(u8, i8);
282impl_integer_hex!(u16, i16);
283impl_integer_hex!(u32, i32);
284impl_integer_hex!(u64, i64);
285impl_integer_hex!(u128, i128);
286impl_integer_hex!(usize, isize);
287
288#[doc(hidden)]
289#[macro_export]
290macro_rules! __fmt_lowerhex {
291    ($x: expr, $spec: expr) => {{
292        const OUTPUT_LEN: usize = $crate::__ctfe::LowerHex($x, $spec).output_len();
293        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
294            $crate::__ctfe::LowerHex($x, $spec).const_eval();
295        OUTPUT_BUF.as_str()
296    }};
297}
298
299#[doc(hidden)]
300#[macro_export]
301macro_rules! __fmt_upperhex {
302    ($x: expr, $spec: expr) => {{
303        const OUTPUT_LEN: usize = $crate::__ctfe::UpperHex($x, $spec).output_len();
304        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
305            $crate::__ctfe::UpperHex($x, $spec).const_eval();
306        OUTPUT_BUF.as_str()
307    }};
308}
309
310pub struct Binary<T>(pub T, pub FmtSpec);
311
312macro_rules! impl_integer_binary {
313    ($unsigned: ty, $signed: ty) => {
314        impl Binary<$unsigned> {
315            pub const fn output_len(&self) -> usize {
316                let mut x = self.0;
317                let mut ans = 0;
318                loop {
319                    ans += 1;
320                    x /= 2;
321                    if x == 0 {
322                        break;
323                    }
324                }
325                if self.1.alternate {
326                    ans += 2;
327                }
328                ans
329            }
330
331            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
332                let mut buf = [0; N];
333                let mut pos = 0;
334                let mut x = self.0;
335                loop {
336                    buf[pos] = b'0' + (x % 2) as u8;
337                    pos += 1;
338                    x /= 2;
339                    if x == 0 {
340                        break;
341                    }
342                }
343                if self.1.alternate {
344                    buf[pos] = b'b';
345                    pos += 1;
346                    buf[pos] = b'0';
347                    pos += 1;
348                }
349                assert!(pos == N);
350                let buf = crate::bytes::reversed(buf);
351                unsafe { StrBuf::new_unchecked(buf) }
352            }
353        }
354
355        impl Binary<$signed> {
356            pub const fn output_len(&self) -> usize {
357                let b = Binary(self.0 as $unsigned, self.1);
358                b.output_len()
359            }
360
361            pub const fn const_eval<const N: usize>(&self) -> StrBuf<N> {
362                let b = Binary(self.0 as $unsigned, self.1);
363                b.const_eval()
364            }
365        }
366    };
367}
368
369impl_integer_binary!(u8, i8);
370impl_integer_binary!(u16, i16);
371impl_integer_binary!(u32, i32);
372impl_integer_binary!(u64, i64);
373impl_integer_binary!(u128, i128);
374impl_integer_binary!(usize, isize);
375
376#[doc(hidden)]
377#[macro_export]
378macro_rules! __fmt_binary {
379    ($x: expr, $spec: expr) => {{
380        const OUTPUT_LEN: usize = $crate::__ctfe::Binary($x, $spec).output_len();
381        const OUTPUT_BUF: $crate::__ctfe::StrBuf<OUTPUT_LEN> =
382            $crate::__ctfe::Binary($x, $spec).const_eval();
383        OUTPUT_BUF.as_str()
384    }};
385}