Skip to main content

gear_core/
buffer.rs

1// Copyright (C) Gear Technologies Inc.
2// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
3
4//! Vector with limited len realization.
5
6use 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
22/// Max memory size, which runtime can allocate at once.
23/// Substrate allocator restrict allocations bigger then 512 wasm pages at once.
24/// See more information about:
25/// https://github.com/paritytech/substrate/blob/cc4d5cc8654d280f03a13421669ba03632e14aa7/client/allocator/src/freeing_bump.rs#L136-L149
26/// https://github.com/paritytech/substrate/blob/cc4d5cc8654d280f03a13421669ba03632e14aa7/primitives/core/src/lib.rs#L385-L388
27const RUNTIME_MAX_ALLOC_SIZE: usize = 512 * 0x10000;
28
29/// Take half from [RUNTIME_MAX_ALLOC_SIZE] in order to avoid problems with capacity overflow.
30const RUNTIME_MAX_BUFF_SIZE: usize = RUNTIME_MAX_ALLOC_SIZE / 2;
31
32/// Wrapper for payload slice.
33pub struct PayloadSlice {
34    /// Start of the slice.
35    start: usize,
36    /// End of the slice.
37    end: usize,
38    /// Payload
39    payload: Arc<Payload>,
40}
41
42impl PayloadSlice {
43    /// Try to create a new PayloadSlice.
44    pub fn try_new(start: u32, end: u32, payload: Arc<Payload>) -> Option<Self> {
45        // Check if start and end are within the bounds of the payload
46        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    /// Get slice of the payload.
58    pub fn slice(&self) -> &[u8] {
59        &self.payload[self.start..self.end]
60    }
61}
62
63/// Buffer which size cannot be bigger then max allowed allocation size in runtime.
64pub type RuntimeBuffer = LimitedVec<u8, RUNTIME_MAX_BUFF_SIZE>;
65
66/// Max payload size which one message can have (8 MiB).
67pub const MAX_PAYLOAD_SIZE: usize = 8 * 1024 * 1024;
68
69// **WARNING**: do not remove this check
70const _: () = assert!(MAX_PAYLOAD_SIZE <= u32::MAX as usize);
71
72/// Payload type for message.
73pub type Payload = LimitedVec<u8, MAX_PAYLOAD_SIZE>;
74
75impl Payload {
76    /// Get payload length as u32.
77    pub fn len_u32(&self) -> u32 {
78        // Safe, cause it's guarantied: `MAX_PAYLOAD_SIZE` <= u32::MAX
79        self.len() as u32
80    }
81}
82
83/// Panic buffer which size cannot be bigger then max allowed payload size.
84#[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    /// Returns ref to the internal data.
105    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}