1use core::{
7 convert::TryFrom,
8 fmt::{Debug, Display},
9};
10
11use alloc::sync::Arc;
12
13use scale_decode::DecodeAsType;
14use scale_encode::EncodeAsType;
15use scale_info::{
16 TypeInfo,
17 scale::{Decode, Encode},
18};
19
20use crate::limited::{LimitedStr, LimitedVec, LimitedVecError};
21
22const RUNTIME_MAX_ALLOC_SIZE: usize = 512 * 0x10000;
28
29const RUNTIME_MAX_BUFF_SIZE: usize = RUNTIME_MAX_ALLOC_SIZE / 2;
31
32pub struct PayloadSlice {
34 start: usize,
36 end: usize,
38 payload: Arc<Payload>,
40}
41
42impl PayloadSlice {
43 pub fn try_new(start: u32, end: u32, payload: Arc<Payload>) -> Option<Self> {
45 if start > end || end > payload.len_u32() {
47 return None;
48 }
49
50 Some(Self {
51 start: start as usize,
52 end: end as usize,
53 payload,
54 })
55 }
56
57 pub fn slice(&self) -> &[u8] {
59 &self.payload[self.start..self.end]
60 }
61}
62
63pub type RuntimeBuffer = LimitedVec<u8, RUNTIME_MAX_BUFF_SIZE>;
65
66pub const MAX_PAYLOAD_SIZE: usize = 8 * 1024 * 1024;
68
69const _: () = assert!(MAX_PAYLOAD_SIZE <= u32::MAX as usize);
71
72pub type Payload = LimitedVec<u8, MAX_PAYLOAD_SIZE>;
74
75impl Payload {
76 pub fn len_u32(&self) -> u32 {
78 self.len() as u32
80 }
81}
82
83#[derive(
85 Clone,
86 Default,
87 Eq,
88 Hash,
89 Ord,
90 PartialEq,
91 PartialOrd,
92 Decode,
93 DecodeAsType,
94 Encode,
95 EncodeAsType,
96 TypeInfo,
97 derive_more::From,
98 derive_more::Into,
99)]
100#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
101pub struct PanicBuffer(Payload);
102
103impl PanicBuffer {
104 pub fn inner(&self) -> &Payload {
106 &self.0
107 }
108
109 fn to_limited_str(&self) -> Option<LimitedStr<'_>> {
110 let s = core::str::from_utf8(&self.0).ok()?;
111 LimitedStr::try_from(s).ok()
112 }
113}
114
115impl From<LimitedStr<'_>> for PanicBuffer {
116 fn from(value: LimitedStr) -> Self {
117 const _: () = assert!(<LimitedStr<'_>>::MAX_LEN <= MAX_PAYLOAD_SIZE);
118 Payload::try_from(value.into_inner().into_owned().into_bytes())
119 .map(Self)
120 .unwrap_or_else(|LimitedVecError| {
121 unreachable!("`LimitedStr` is always smaller than maximum payload size",)
122 })
123 }
124}
125
126impl Display for PanicBuffer {
127 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
128 if let Some(s) = self.to_limited_str() {
129 Display::fmt(&s, f)
130 } else {
131 Display::fmt(&self.0, f)
132 }
133 }
134}
135
136impl Debug for PanicBuffer {
137 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
138 if let Some(s) = self.to_limited_str() {
139 Debug::fmt(s.as_str(), f)
140 } else {
141 Debug::fmt(&self.0, f)
142 }
143 }
144}
145
146#[cfg(test)]
147mod test {
148 use super::{PanicBuffer, Payload};
149 use alloc::format;
150 use core::convert::TryFrom;
151
152 fn panic_buf(bytes: &[u8]) -> PanicBuffer {
153 Payload::try_from(bytes).map(PanicBuffer).unwrap()
154 }
155
156 #[test]
157 fn panic_buffer_debug() {
158 let buf = panic_buf(b"Hello, world!");
159 assert_eq!(format!("{buf:?}"), r#""Hello, world!""#);
160
161 let buf = panic_buf(b"\xE0\x80\x80");
162 assert_eq!(format!("{buf:?}"), "0xe08080");
163 }
164
165 #[test]
166 fn panic_buffer_display() {
167 let buf = panic_buf(b"Hello, world!");
168 assert_eq!(format!("{buf}"), "Hello, world!");
169
170 let buf = panic_buf(b"\xE0\x80\x80");
171 assert_eq!(format!("{buf}"), "0xe08080");
172 }
173}