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