dharitri_chain_core/types/
boxed_bytes.rs

1use alloc::{
2    alloc::{alloc, realloc, Layout},
3    boxed::Box,
4    vec,
5    vec::Vec,
6};
7
8use crate::codec::*;
9
10/// Simple wrapper around a boxed byte slice,
11/// but with a lot of optimized methods for manipulating it.
12/// The focus is on reducing code size rather improving speed.
13///
14/// Used to be used extensively in smart contracts, before the introduction of ManagedBuffer,
15/// but was superseded by it.
16#[derive(Clone, PartialEq, Eq, Debug)]
17pub struct BoxedBytes(Box<[u8]>);
18
19impl BoxedBytes {
20    pub fn empty() -> Self {
21        BoxedBytes(Box::from([0u8; 0]))
22    }
23
24    pub fn zeros(len: usize) -> Self {
25        let bytes_box = Box::from(vec![0u8; len]);
26        BoxedBytes(bytes_box)
27    }
28
29    #[inline]
30    pub fn as_ptr(&self) -> *const u8 {
31        self.0.as_ptr()
32    }
33
34    #[inline]
35    pub fn as_mut_ptr(&mut self) -> *mut u8 {
36        self.0.as_mut_ptr()
37    }
38
39    #[inline]
40    pub fn len(&self) -> usize {
41        self.0.len()
42    }
43
44    #[inline]
45    pub fn is_empty(&self) -> bool {
46        self.0.is_empty()
47    }
48
49    #[inline]
50    pub fn into_box(self) -> Box<[u8]> {
51        self.0
52    }
53
54    #[inline]
55    pub fn into_vec(self) -> Vec<u8> {
56        self.0.into_vec()
57    }
58
59    #[inline]
60    pub fn as_slice(&self) -> &[u8] {
61        &self.0
62    }
63
64    #[inline]
65    pub fn as_mut_slice(&mut self) -> &mut [u8] {
66        &mut self.0
67    }
68
69    /// Create new instance by concatenating several byte slices.
70    pub fn from_concat(slices: &[&[u8]]) -> Self {
71        let mut result_len = 0usize;
72        let mut index = slices.len();
73        while index > 0 {
74            index -= 1;
75            result_len += slices[index].len();
76        }
77        unsafe {
78            let layout = Layout::from_size_align(result_len, core::mem::align_of::<u8>()).unwrap();
79            let result_ptr = alloc(layout);
80            let mut current_index = 0usize;
81            for slice in slices.iter() {
82                core::ptr::copy_nonoverlapping(
83                    slice.as_ptr(),
84                    result_ptr.add(current_index),
85                    slice.len(),
86                );
87                current_index += slice.len();
88            }
89            let bytes_box = Box::from_raw(core::slice::from_raw_parts_mut(result_ptr, result_len));
90            BoxedBytes(bytes_box)
91        }
92    }
93
94    /// Splits BoxedBytes into 2 others at designated position.
95    /// Returns the original and an empty BoxedBytes if position argument out of range.
96    pub fn split(self, at: usize) -> (BoxedBytes, BoxedBytes) {
97        if at >= self.len() {
98            (self, BoxedBytes::empty())
99        } else {
100            let other_len = self.len() - at;
101            unsafe {
102                // breaking down the input into its components
103                let self_layout =
104                    Layout::from_size_align(self.len(), core::mem::align_of::<u8>()).unwrap();
105                let self_ptr = Box::into_raw(self.0) as *mut u8;
106
107                // the data for the second result needs to be copied somewhere else
108                let other_layout =
109                    Layout::from_size_align(other_len, core::mem::align_of::<u8>()).unwrap();
110                let other_ptr = alloc(other_layout);
111                core::ptr::copy_nonoverlapping(self_ptr.add(at), other_ptr, other_len);
112
113                // truncating the memory for the first using a realloc
114                // got inspiration for this from the RawVec implementation
115                let realloc_ptr = realloc(self_ptr, self_layout, at);
116
117                // packaging the resulting parts nicely
118                let bytes_box_1 = Box::from_raw(core::slice::from_raw_parts_mut(realloc_ptr, at));
119                let bytes_box_2 =
120                    Box::from_raw(core::slice::from_raw_parts_mut(other_ptr, other_len));
121                (BoxedBytes(bytes_box_1), BoxedBytes(bytes_box_2))
122            }
123        }
124    }
125}
126
127impl AsRef<[u8]> for BoxedBytes {
128    #[inline]
129    fn as_ref(&self) -> &[u8] {
130        &self.0
131    }
132}
133
134impl<'a> From<&'a [u8]> for BoxedBytes {
135    #[inline]
136    fn from(byte_slice: &'a [u8]) -> Self {
137        BoxedBytes(Box::from(byte_slice))
138    }
139}
140
141impl From<Box<[u8]>> for BoxedBytes {
142    #[inline]
143    fn from(b: Box<[u8]>) -> Self {
144        BoxedBytes(b)
145    }
146}
147
148impl From<Vec<u8>> for BoxedBytes {
149    #[inline]
150    fn from(v: Vec<u8>) -> Self {
151        BoxedBytes(v.into_boxed_slice())
152    }
153}
154
155impl From<&Vec<u8>> for BoxedBytes {
156    #[inline]
157    fn from(v: &Vec<u8>) -> Self {
158        BoxedBytes::from(v.as_slice())
159    }
160}
161
162/// This allows us to use a mutable BoxedBytes as top encode output.
163impl TopEncodeOutput for &mut BoxedBytes {
164    type NestedBuffer = Vec<u8>;
165
166    fn set_slice_u8(self, bytes: &[u8]) {
167        *self = BoxedBytes::from(bytes);
168    }
169
170    fn start_nested_encode(&self) -> Self::NestedBuffer {
171        Vec::<u8>::new()
172    }
173
174    fn finalize_nested_encode(self, nb: Self::NestedBuffer) {
175        self.set_slice_u8(nb.as_slice());
176    }
177}
178
179impl NestedEncode for BoxedBytes {
180    #[inline]
181    fn dep_encode_or_handle_err<O, H>(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr>
182    where
183        O: NestedEncodeOutput,
184        H: EncodeErrorHandler,
185    {
186        self.len().dep_encode_or_handle_err(dest, h)?;
187        dest.write(self.as_ref());
188        Ok(())
189    }
190}
191
192impl TopEncode for BoxedBytes {
193    #[inline]
194    fn top_encode_or_handle_err<O, H>(&self, output: O, _h: H) -> Result<(), H::HandledErr>
195    where
196        O: TopEncodeOutput,
197        H: EncodeErrorHandler,
198    {
199        output.set_slice_u8(self.as_ref());
200        Ok(())
201    }
202}
203
204impl NestedDecode for BoxedBytes {
205    fn dep_decode_or_handle_err<I, H>(input: &mut I, h: H) -> Result<Self, H::HandledErr>
206    where
207        I: NestedDecodeInput,
208        H: DecodeErrorHandler,
209    {
210        let size = usize::dep_decode_or_handle_err(input, h)?;
211        let mut result = BoxedBytes::zeros(size);
212        input.read_into(result.as_mut_slice(), h)?;
213        Ok(result)
214    }
215}
216
217impl TopDecode for BoxedBytes {
218    fn top_decode_or_handle_err<I, H>(input: I, _h: H) -> Result<Self, H::HandledErr>
219    where
220        I: TopDecodeInput,
221        H: DecodeErrorHandler,
222    {
223        Ok(BoxedBytes(input.into_boxed_slice_u8()))
224    }
225}
226
227////////////////////////////////////////////////////////////////////////////////
228
229#[cfg(test)]
230mod tests {
231    use super::*;
232
233    #[test]
234    fn test_concat_1() {
235        let bb = BoxedBytes::from_concat(&[&b"abc"[..], &b"def"[..]]);
236        assert_eq!(bb, BoxedBytes::from(&b"abcdef"[..]));
237    }
238
239    #[test]
240    fn test_concat_2() {
241        let bb = BoxedBytes::from_concat(&[&b"abc"[..], &b""[..], &b"def"[..]]);
242        assert_eq!(bb, BoxedBytes::from(&b"abcdef"[..]));
243    }
244
245    #[test]
246    fn test_concat_empty_1() {
247        let bb = BoxedBytes::from_concat(&[&b""[..], &b""[..], &b""[..]]);
248        assert_eq!(bb, BoxedBytes::from(&b""[..]));
249    }
250
251    #[test]
252    fn test_concat_empty_2() {
253        let bb = BoxedBytes::from_concat(&[]);
254        assert_eq!(bb, BoxedBytes::from(&b""[..]));
255    }
256
257    #[test]
258    fn test_is_empty() {
259        assert!(BoxedBytes::empty().is_empty());
260    }
261
262    #[test]
263    fn test_size_of() {
264        use core::mem::size_of;
265        assert_eq!(size_of::<BoxedBytes>(), 2 * size_of::<usize>());
266        assert_eq!(size_of::<Option<BoxedBytes>>(), 2 * size_of::<usize>());
267    }
268
269    #[test]
270    fn test_split_1() {
271        let (bb1, bb2) = BoxedBytes::from(&b"abcdef"[..]).split(3);
272        assert_eq!(bb1, BoxedBytes::from(&b"abc"[..]));
273        assert_eq!(bb2, BoxedBytes::from(&b"def"[..]));
274    }
275
276    #[test]
277    fn test_split_2() {
278        let (bb1, bb2) = BoxedBytes::from(&b"abcdef"[..]).split(0);
279        assert_eq!(bb1, BoxedBytes::from(&b""[..]));
280        assert_eq!(bb2, BoxedBytes::from(&b"abcdef"[..]));
281    }
282
283    #[test]
284    fn test_split_over() {
285        let (bb1, bb2) = BoxedBytes::from(&b"abcdef"[..]).split(6);
286        assert_eq!(bb1, BoxedBytes::from(&b"abcdef"[..]));
287        assert_eq!(bb2, BoxedBytes::from(&b""[..]));
288
289        let (bb1, bb2) = BoxedBytes::from(&b"abcdef"[..]).split(7);
290        assert_eq!(bb1, BoxedBytes::from(&b"abcdef"[..]));
291        assert_eq!(bb2, BoxedBytes::from(&b""[..]));
292    }
293}