1use core::{
22 convert::TryFrom,
23 fmt::{Debug, Display},
24};
25
26use alloc::sync::Arc;
27
28use parity_scale_codec::{Compact, MaxEncodedLen};
29use scale_decode::DecodeAsType;
30use scale_encode::EncodeAsType;
31use scale_info::{
32 TypeInfo,
33 scale::{Decode, Encode},
34};
35
36use crate::limited::{LimitedStr, LimitedVec, LimitedVecError};
37
38const RUNTIME_MAX_ALLOC_SIZE: usize = 512 * 0x10000;
44
45const RUNTIME_MAX_BUFF_SIZE: usize = RUNTIME_MAX_ALLOC_SIZE / 2;
47
48pub struct PayloadSlice {
50 start: usize,
52 end: usize,
54 payload: Arc<Payload>,
56}
57
58impl PayloadSlice {
59 pub fn try_new(start: u32, end: u32, payload: Arc<Payload>) -> Option<Self> {
61 if start > end || end > payload.len_u32() {
63 return None;
64 }
65
66 Some(Self {
67 start: start as usize,
68 end: end as usize,
69 payload,
70 })
71 }
72
73 pub fn slice(&self) -> &[u8] {
75 &self.payload[self.start..self.end]
76 }
77}
78
79pub type RuntimeBuffer = LimitedVec<u8, RUNTIME_MAX_BUFF_SIZE>;
81
82pub const MAX_PAYLOAD_SIZE: usize = 8 * 1024 * 1024;
84
85const _: () = assert!(MAX_PAYLOAD_SIZE <= u32::MAX as usize);
87
88pub type Payload = LimitedVec<u8, MAX_PAYLOAD_SIZE>;
90
91impl Payload {
92 pub fn len_u32(&self) -> u32 {
94 self.len() as u32
96 }
97}
98
99impl MaxEncodedLen for Payload {
100 fn max_encoded_len() -> usize {
101 Compact::<u32>::max_encoded_len() + MAX_PAYLOAD_SIZE
102 }
103}
104
105#[derive(
107 Clone,
108 Default,
109 Eq,
110 Hash,
111 Ord,
112 PartialEq,
113 PartialOrd,
114 Decode,
115 DecodeAsType,
116 Encode,
117 EncodeAsType,
118 TypeInfo,
119 derive_more::From,
120 derive_more::Into,
121)]
122#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
123pub struct PanicBuffer(Payload);
124
125impl PanicBuffer {
126 pub fn inner(&self) -> &Payload {
128 &self.0
129 }
130
131 fn to_limited_str(&self) -> Option<LimitedStr<'_>> {
132 let s = core::str::from_utf8(&self.0).ok()?;
133 LimitedStr::try_from(s).ok()
134 }
135}
136
137impl From<LimitedStr<'_>> for PanicBuffer {
138 fn from(value: LimitedStr) -> Self {
139 const _: () = assert!(<LimitedStr<'_>>::MAX_LEN <= MAX_PAYLOAD_SIZE);
140 Payload::try_from(value.into_inner().into_owned().into_bytes())
141 .map(Self)
142 .unwrap_or_else(|LimitedVecError| {
143 unreachable!("`LimitedStr` is always smaller than maximum payload size",)
144 })
145 }
146}
147
148impl Display for PanicBuffer {
149 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150 if let Some(s) = self.to_limited_str() {
151 Display::fmt(&s, f)
152 } else {
153 Display::fmt(&self.0, f)
154 }
155 }
156}
157
158impl Debug for PanicBuffer {
159 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
160 if let Some(s) = self.to_limited_str() {
161 Debug::fmt(s.as_str(), f)
162 } else {
163 Debug::fmt(&self.0, f)
164 }
165 }
166}
167
168#[cfg(test)]
169mod test {
170 use super::{PanicBuffer, Payload};
171 use alloc::format;
172 use core::convert::TryFrom;
173
174 fn panic_buf(bytes: &[u8]) -> PanicBuffer {
175 Payload::try_from(bytes).map(PanicBuffer).unwrap()
176 }
177
178 #[test]
179 fn panic_buffer_debug() {
180 let buf = panic_buf(b"Hello, world!");
181 assert_eq!(format!("{buf:?}"), r#""Hello, world!""#);
182
183 let buf = panic_buf(b"\xE0\x80\x80");
184 assert_eq!(format!("{buf:?}"), "0xe08080");
185 }
186
187 #[test]
188 fn panic_buffer_display() {
189 let buf = panic_buf(b"Hello, world!");
190 assert_eq!(format!("{buf}"), "Hello, world!");
191
192 let buf = panic_buf(b"\xE0\x80\x80");
193 assert_eq!(format!("{buf}"), "0xe08080");
194 }
195}