use core::{
convert::TryFrom,
fmt::{Debug, Display},
};
use alloc::sync::Arc;
use parity_scale_codec::{Compact, MaxEncodedLen};
use scale_decode::DecodeAsType;
use scale_encode::EncodeAsType;
use scale_info::{
TypeInfo,
scale::{Decode, Encode},
};
use crate::limited::{LimitedStr, LimitedVec, LimitedVecError};
const RUNTIME_MAX_ALLOC_SIZE: usize = 512 * 0x10000;
const RUNTIME_MAX_BUFF_SIZE: usize = RUNTIME_MAX_ALLOC_SIZE / 2;
pub struct PayloadSlice {
start: usize,
end: usize,
payload: Arc<Payload>,
}
impl PayloadSlice {
pub fn try_new(start: u32, end: u32, payload: Arc<Payload>) -> Option<Self> {
if start > end || end > payload.len_u32() {
return None;
}
Some(Self {
start: start as usize,
end: end as usize,
payload,
})
}
pub fn slice(&self) -> &[u8] {
&self.payload[self.start..self.end]
}
}
pub type RuntimeBuffer = LimitedVec<u8, RUNTIME_MAX_BUFF_SIZE>;
pub const MAX_PAYLOAD_SIZE: usize = 8 * 1024 * 1024;
const _: () = assert!(MAX_PAYLOAD_SIZE <= u32::MAX as usize);
pub type Payload = LimitedVec<u8, MAX_PAYLOAD_SIZE>;
impl Payload {
pub fn len_u32(&self) -> u32 {
self.len() as u32
}
}
impl MaxEncodedLen for Payload {
fn max_encoded_len() -> usize {
Compact::<u32>::max_encoded_len() + MAX_PAYLOAD_SIZE
}
}
#[derive(
Clone,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
Decode,
DecodeAsType,
Encode,
EncodeAsType,
TypeInfo,
derive_more::From,
derive_more::Into,
)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
pub struct PanicBuffer(Payload);
impl PanicBuffer {
pub fn inner(&self) -> &Payload {
&self.0
}
fn to_limited_str(&self) -> Option<LimitedStr<'_>> {
let s = core::str::from_utf8(&self.0).ok()?;
LimitedStr::try_from(s).ok()
}
}
impl From<LimitedStr<'_>> for PanicBuffer {
fn from(value: LimitedStr) -> Self {
const _: () = assert!(<LimitedStr<'_>>::MAX_LEN <= MAX_PAYLOAD_SIZE);
Payload::try_from(value.into_inner().into_owned().into_bytes())
.map(Self)
.unwrap_or_else(|LimitedVecError| {
unreachable!("`LimitedStr` is always smaller than maximum payload size",)
})
}
}
impl Display for PanicBuffer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(s) = self.to_limited_str() {
Display::fmt(&s, f)
} else {
Display::fmt(&self.0, f)
}
}
}
impl Debug for PanicBuffer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if let Some(s) = self.to_limited_str() {
Debug::fmt(s.as_str(), f)
} else {
Debug::fmt(&self.0, f)
}
}
}
#[cfg(test)]
mod test {
use super::{PanicBuffer, Payload};
use alloc::format;
use core::convert::TryFrom;
fn panic_buf(bytes: &[u8]) -> PanicBuffer {
Payload::try_from(bytes).map(PanicBuffer).unwrap()
}
#[test]
fn panic_buffer_debug() {
let buf = panic_buf(b"Hello, world!");
assert_eq!(format!("{buf:?}"), r#""Hello, world!""#);
let buf = panic_buf(b"\xE0\x80\x80");
assert_eq!(format!("{buf:?}"), "0xe08080");
}
#[test]
fn panic_buffer_display() {
let buf = panic_buf(b"Hello, world!");
assert_eq!(format!("{buf}"), "Hello, world!");
let buf = panic_buf(b"\xE0\x80\x80");
assert_eq!(format!("{buf}"), "0xe08080");
}
}