1use crate::{
2 fmt::FmtKind,
3 panic_val::{PanicClass, PanicVal, StrFmt},
4 utils::{bytes_up_to, string_cap, WasTruncated},
5};
6
7#[cold]
44#[inline(never)]
45#[track_caller]
46pub const fn concat_panic(args: &[&[PanicVal<'_>]]) -> ! {
47 if let Err(_) = panic_inner::<1024>(args) {}
53
54 if let Err(_) = panic_inner::<{ 1024 * 6 }>(args) {}
55
56 match panic_inner::<MAX_PANIC_MSG_LEN>(args) {
57 Ok(x) => match x {},
58 Err(_) => panic!(
59 "\
60 unreachable:\n\
61 the `write_panicval_to_buffer` macro must not return Err when \
62 $capacity == $max_capacity\
63 "
64 ),
65 }
66}
67
68pub const MAX_PANIC_MSG_LEN: usize = 32768;
73
74macro_rules! write_panicval {
76 (
77 $outer_label:lifetime,
78 $mout:ident, $lout:ident, $tct:expr,
79 (
80 $len:expr,
81 $capacity:expr,
82 $max_capacity:expr,
83 $not_enough_space:expr,
84 $write_buffer:ident,
85 $write_buffer_checked:ident,
86 )
87 ) => {
88 let rem_space = $capacity - $len;
89 let (strfmt, class, was_truncated) = $tct;
90 let StrFmt {
91 leftpad: mut lpad,
92 rightpad: mut rpad,
93 fmt_kind,
94 } = strfmt;
95
96 let ranged = match class {
97 PanicClass::PreFmt(str) => str,
98 PanicClass::Int(int) => {
99 if int.len() <= string_cap::MEDIUM {
100 $mout = int.fmt::<{ string_cap::MEDIUM }>();
101 $mout.ranged()
102 } else {
103 $lout = int.fmt::<{ string_cap::LARGE }>();
104 $lout.ranged()
105 }
106 }
107 #[cfg(feature = "non_basic")]
108 PanicClass::Slice(_) => unreachable!(),
109 };
110
111 let trunc_end = ranged.start + was_truncated.get_length(ranged.len());
112
113 while lpad != 0 {
114 $write_buffer! {b' '}
115 lpad -= 1;
116 }
117
118 if let FmtKind::Display = fmt_kind {
119 let mut i = ranged.start;
120 while i < trunc_end {
121 $write_buffer! {ranged.bytes[i]}
122 i += 1;
123 }
124 } else if rem_space != 0 {
125 $write_buffer! {b'"'}
126 let mut i = 0;
127 while i < trunc_end {
128 use crate::debug_str_fmt::{hex_as_ascii, ForEscaping};
129
130 let c = ranged.bytes[i];
131 let mut written_c = c;
132 if ForEscaping::is_escaped(c) {
133 $write_buffer! {b'\\'}
134 if ForEscaping::is_backslash_escaped(c) {
135 written_c = ForEscaping::get_backslash_escape(c);
136 } else {
137 $write_buffer! {b'x'}
138 $write_buffer! {hex_as_ascii(c >> 4)}
139 written_c = hex_as_ascii(c & 0b1111);
140 };
141 }
142 $write_buffer! {written_c}
143
144 i += 1;
145 }
146 if let WasTruncated::No = was_truncated {
147 $write_buffer_checked! {b'"'}
148 }
149 }
150
151 while rpad != 0 {
152 $write_buffer! {b' '}
153 rpad -= 1;
154 }
155
156 if let WasTruncated::Yes(_) = was_truncated {
157 if $capacity < $max_capacity {
158 return $not_enough_space;
159 } else {
160 break $outer_label;
161 }
162 }
163 };
164}
165
166macro_rules! write_to_buffer_inner {
167 (
168 $args:ident
169 (
170 $len:expr,
171 $capacity:expr,
172 $($_rem:tt)*
173 )
174 $wptb_args:tt
175 ) => {
176 let mut args = $args;
177
178 let mut mout;
179 let mut lout;
180
181 'outer: while let [mut outer, ref nargs @ ..] = args {
182 while let [arg, nouter @ ..] = outer {
183 let tct = arg.to_class_truncated($capacity - $len);
184 match tct.1 {
185 #[cfg(feature = "non_basic")]
186 #[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
187 PanicClass::Slice(slice) => {
188 let mut iter = slice.iter();
189
190 'iter: loop {
191 let (two_args, niter) = iter.next();
192
193 let mut two_args: &[_] = &two_args;
194 while let [arg, ntwo_args @ ..] = two_args {
195 let tct = arg.to_class_truncated($capacity - $len);
196 write_panicval! {'outer, mout, lout, tct, $wptb_args}
197 two_args = ntwo_args;
198 }
199
200 match niter {
201 Some(x) => iter = x,
202 None => break 'iter,
203 }
204 }
205 }
206 _ => {
207 write_panicval! {'outer, mout, lout, tct, $wptb_args}
208 }
209 }
210
211 outer = nouter;
212 }
213 args = nargs;
214 }
215 };
216}
217
218macro_rules! write_to_buffer {
219 ($args:ident $wptb_args:tt) => {
220 write_to_buffer_inner! {
221 $args
222 $wptb_args
223 $wptb_args
224 }
225 };
226}
227
228macro_rules! make_buffer_writer_macros {
229 ($buffer:ident, $len:ident) => {
230 macro_rules! write_buffer {
231 ($value:expr) => {
232 __write_array! {$buffer, $len, $value}
233 };
234 }
235 macro_rules! write_buffer_checked {
236 ($value:expr) => {
237 __write_array_checked! {$buffer, $len, $value}
238 };
239 }
240 };
241}
242
243#[cold]
244#[inline(never)]
245#[track_caller]
246const fn panic_inner<const LEN: usize>(args: &[&[PanicVal<'_>]]) -> Result<Never, NotEnoughSpace> {
247 let mut buffer = [0u8; LEN];
248 let mut len = 0usize;
249
250 make_buffer_writer_macros! {buffer, len}
251
252 write_to_buffer! {
253 args
254 (
255 len, LEN, MAX_PANIC_MSG_LEN, Err(NotEnoughSpace),
256 write_buffer, write_buffer_checked,
257 )
258 }
259
260 unsafe {
261 let buffer = bytes_up_to(&buffer, len);
262 let str = core::str::from_utf8_unchecked(buffer);
263 panic!("{}", str)
264 }
265}
266
267#[doc(hidden)]
268#[derive(Debug)]
269pub struct NotEnoughSpace;
270enum Never {}
271
272#[cfg(feature = "test")]
273use crate::test_utils::TestString;
274
275#[doc(hidden)]
276#[cfg(feature = "test")]
277pub fn format_panic_message<const LEN: usize>(
278 args: &[&[PanicVal<'_>]],
279 capacity: usize,
280 max_capacity: usize,
281) -> Result<TestString<LEN>, NotEnoughSpace> {
282 let mut buffer = [0u8; LEN];
283 let mut len = 0usize;
284 {
285 let buffer = &mut buffer[..capacity];
287
288 make_buffer_writer_macros! {buffer, len}
289
290 write_to_buffer! {
291 args
292 (
293 len, capacity, max_capacity, Err(NotEnoughSpace),
294 write_buffer, write_buffer_checked,
295 )
296 }
297 }
298
299 Ok(TestString { buffer, len })
300}
301
302#[cfg(feature = "non_basic")]
303#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
304#[doc(hidden)]
305pub(crate) const fn make_panic_string<const LEN: usize>(
306 args: &[&[PanicVal<'_>]],
307) -> Result<crate::ArrayString<LEN>, NotEnoughSpace> {
308 let mut buffer = [0u8; LEN];
309 let mut len = 0usize;
310
311 make_buffer_writer_macros! {buffer, len}
312
313 write_to_buffer! {
314 args
315 (len, LEN, LEN + 1, Err(NotEnoughSpace), write_buffer, write_buffer_checked,)
316 }
317
318 assert!(len as u32 as usize == len, "the panic message is too large");
319
320 Ok(crate::ArrayString {
321 buffer,
322 len: len as u32,
323 })
324}
325
326#[cfg(feature = "non_basic")]
327#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
328#[doc(hidden)]
329#[track_caller]
330pub const fn make_panic_string_unwrapped<const LEN: usize>(
331 args: &[&[PanicVal<'_>]],
332) -> crate::ArrayString<LEN> {
333 match make_panic_string(args) {
334 Ok(x) => x,
335 Err(_) => panic!("arguments are too large to fit in LEN"),
336 }
337}
338
339#[cfg(feature = "non_basic")]
340#[cfg_attr(feature = "docsrs", doc(cfg(feature = "non_basic")))]
341#[doc(hidden)]
342pub const fn compute_length(args: &[&[PanicVal<'_>]]) -> usize {
343 let mut len = 0usize;
344
345 macro_rules! add_to_len {
346 ($value:expr) => {{
347 let _: u8 = $value;
348 len += 1;
349 }};
350 }
351
352 write_to_buffer! {
353 args
354 (
355 len, usize::MAX - 1, usize::MAX, usize::MAX,
356 add_to_len, add_to_len,
357 )
358 }
359
360 len
361}