gear_core/
buffer.rs

1// This file is part of Gear.
2
3// Copyright (C) 2021-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Vector with limited len realization.
20
21use 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
38/// Max memory size, which runtime can allocate at once.
39/// Substrate allocator restrict allocations bigger then 512 wasm pages at once.
40/// See more information about:
41/// https://github.com/paritytech/substrate/blob/cc4d5cc8654d280f03a13421669ba03632e14aa7/client/allocator/src/freeing_bump.rs#L136-L149
42/// https://github.com/paritytech/substrate/blob/cc4d5cc8654d280f03a13421669ba03632e14aa7/primitives/core/src/lib.rs#L385-L388
43const RUNTIME_MAX_ALLOC_SIZE: usize = 512 * 0x10000;
44
45/// Take half from [RUNTIME_MAX_ALLOC_SIZE] in order to avoid problems with capacity overflow.
46const RUNTIME_MAX_BUFF_SIZE: usize = RUNTIME_MAX_ALLOC_SIZE / 2;
47
48/// Wrapper for payload slice.
49pub struct PayloadSlice {
50    /// Start of the slice.
51    start: usize,
52    /// End of the slice.
53    end: usize,
54    /// Payload
55    payload: Arc<Payload>,
56}
57
58impl PayloadSlice {
59    /// Try to create a new PayloadSlice.
60    pub fn try_new(start: u32, end: u32, payload: Arc<Payload>) -> Option<Self> {
61        // Check if start and end are within the bounds of the payload
62        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    /// Get slice of the payload.
74    pub fn slice(&self) -> &[u8] {
75        &self.payload[self.start..self.end]
76    }
77}
78
79/// Buffer which size cannot be bigger then max allowed allocation size in runtime.
80pub type RuntimeBuffer = LimitedVec<u8, RUNTIME_MAX_BUFF_SIZE>;
81
82/// Max payload size which one message can have (8 MiB).
83pub const MAX_PAYLOAD_SIZE: usize = 8 * 1024 * 1024;
84
85// **WARNING**: do not remove this check
86const _: () = assert!(MAX_PAYLOAD_SIZE <= u32::MAX as usize);
87
88/// Payload type for message.
89pub type Payload = LimitedVec<u8, MAX_PAYLOAD_SIZE>;
90
91impl Payload {
92    /// Get payload length as u32.
93    pub fn len_u32(&self) -> u32 {
94        // Safe, cause it's guarantied: `MAX_PAYLOAD_SIZE` <= u32::MAX
95        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/// Panic buffer which size cannot be bigger then max allowed payload size.
106#[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    /// Returns ref to the internal data.
127    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}