spl_pod/
slice.rs

1//! Special types for working with slices of `Pod`s
2
3use {
4    crate::{
5        list::{ListView, ListViewMut, ListViewReadOnly},
6        primitives::PodU32,
7    },
8    bytemuck::Pod,
9    solana_program_error::ProgramError,
10};
11
12#[deprecated(
13    since = "0.6.0",
14    note = "This struct will be removed in the next major release (1.0.0). Please use `ListView` instead."
15)]
16/// Special type for using a slice of `Pod`s in a zero-copy way
17#[allow(deprecated)]
18pub struct PodSlice<'data, T: Pod> {
19    inner: ListViewReadOnly<'data, T, PodU32>,
20}
21
22#[allow(deprecated)]
23impl<'data, T: Pod> PodSlice<'data, T> {
24    /// Unpack the buffer into a slice
25    pub fn unpack<'a>(data: &'a [u8]) -> Result<Self, ProgramError>
26    where
27        'a: 'data,
28    {
29        let inner = ListView::<T, PodU32>::unpack(data)?;
30        Ok(Self { inner })
31    }
32
33    /// Get the slice data
34    pub fn data(&self) -> &[T] {
35        let len = self.inner.len();
36        &self.inner.data[..len]
37    }
38
39    /// Get the amount of bytes used by `num_items`
40    pub fn size_of(num_items: usize) -> Result<usize, ProgramError> {
41        ListView::<T, PodU32>::size_of(num_items)
42    }
43}
44
45#[deprecated(
46    since = "0.6.0",
47    note = "This struct will be removed in the next major release (1.0.0). Please use `ListView` instead."
48)]
49/// Special type for using a slice of mutable `Pod`s in a zero-copy way.
50/// Uses `ListView` under the hood.
51pub struct PodSliceMut<'data, T: Pod> {
52    inner: ListViewMut<'data, T, PodU32>,
53}
54
55#[allow(deprecated)]
56impl<'data, T: Pod> PodSliceMut<'data, T> {
57    /// Unpack the mutable buffer into a mutable slice
58    pub fn unpack<'a>(data: &'a mut [u8]) -> Result<Self, ProgramError>
59    where
60        'a: 'data,
61    {
62        let inner = ListView::<T, PodU32>::unpack_mut(data)?;
63        Ok(Self { inner })
64    }
65
66    /// Unpack the mutable buffer into a mutable slice, and initialize the
67    /// slice to 0-length
68    pub fn init<'a>(data: &'a mut [u8]) -> Result<Self, ProgramError>
69    where
70        'a: 'data,
71    {
72        let inner = ListView::<T, PodU32>::init(data)?;
73        Ok(Self { inner })
74    }
75
76    /// Add another item to the slice
77    pub fn push(&mut self, t: T) -> Result<(), ProgramError> {
78        self.inner.push(t)
79    }
80}
81
82#[cfg(test)]
83#[allow(deprecated)]
84mod tests {
85    use {
86        super::*,
87        crate::{bytemuck::pod_slice_to_bytes, error::PodSliceError},
88        bytemuck_derive::{Pod, Zeroable},
89    };
90
91    #[repr(C)]
92    #[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
93    struct TestStruct {
94        test_field: u8,
95        test_pubkey: [u8; 32],
96    }
97
98    const LENGTH_SIZE: usize = std::mem::size_of::<PodU32>();
99
100    #[test]
101    fn test_pod_slice() {
102        let test_field_bytes = [0];
103        let test_pubkey_bytes = [1; 32];
104        let len_bytes = [2, 0, 0, 0];
105
106        // Slice will contain 2 `TestStruct`
107        let mut data_bytes = [0; 66];
108        data_bytes[0..1].copy_from_slice(&test_field_bytes);
109        data_bytes[1..33].copy_from_slice(&test_pubkey_bytes);
110        data_bytes[33..34].copy_from_slice(&test_field_bytes);
111        data_bytes[34..66].copy_from_slice(&test_pubkey_bytes);
112
113        let mut pod_slice_bytes = [0; 70];
114        pod_slice_bytes[0..4].copy_from_slice(&len_bytes);
115        pod_slice_bytes[4..70].copy_from_slice(&data_bytes);
116
117        let pod_slice = PodSlice::<TestStruct>::unpack(&pod_slice_bytes).unwrap();
118        let pod_slice_data = pod_slice.data();
119
120        assert_eq!(pod_slice.inner.len(), 2);
121        assert_eq!(pod_slice_to_bytes(pod_slice.data()), data_bytes);
122        assert_eq!(pod_slice_data[0].test_field, test_field_bytes[0]);
123        assert_eq!(pod_slice_data[0].test_pubkey, test_pubkey_bytes);
124        assert_eq!(PodSlice::<TestStruct>::size_of(1).unwrap(), 37);
125    }
126
127    #[test]
128    fn test_pod_slice_buffer_too_large() {
129        // Length is 1. We pass one test struct with 6 trailing bytes to
130        // trigger BufferTooLarge.
131        let data_len = LENGTH_SIZE + std::mem::size_of::<TestStruct>() + 6;
132        let mut pod_slice_bytes = vec![1; data_len];
133        pod_slice_bytes[0..4].copy_from_slice(&[1, 0, 0, 0]);
134        let err = PodSlice::<TestStruct>::unpack(&pod_slice_bytes)
135            .err()
136            .unwrap();
137        assert!(matches!(err, ProgramError::InvalidArgument));
138    }
139
140    #[test]
141    fn test_pod_slice_buffer_larger_than_length_value() {
142        // If the buffer is longer than the u32 length value declares, it
143        // should still unpack successfully, as long as the length of the rest
144        // of the buffer can be divided by `size_of::<T>`.
145        let length: u32 = 12;
146        let length_le = length.to_le_bytes();
147
148        // First set up the data to have room for extra items.
149        let data_len = PodSlice::<TestStruct>::size_of(length as usize + 2).unwrap();
150        let mut data = vec![0; data_len];
151
152        // Now write the bogus length - which is smaller - into the first 4
153        // bytes.
154        data[..LENGTH_SIZE].copy_from_slice(&length_le);
155
156        let pod_slice = PodSlice::<TestStruct>::unpack(&data).unwrap();
157        let pod_slice_len = pod_slice.inner.len() as u32;
158        let data = pod_slice.data();
159        let data_vec = data.to_vec();
160
161        assert_eq!(pod_slice_len, length);
162        assert_eq!(data.len(), length as usize);
163        assert_eq!(data_vec.len(), length as usize);
164    }
165
166    #[test]
167    fn test_pod_slice_buffer_too_small() {
168        // 1 `TestStruct` + length = 37 bytes
169        // we pass 36 to trigger BufferTooSmall
170        let pod_slice_bytes = [1; 36];
171        let err = PodSlice::<TestStruct>::unpack(&pod_slice_bytes)
172            .err()
173            .unwrap();
174        assert!(matches!(err, ProgramError::InvalidArgument));
175    }
176
177    #[test]
178    fn test_pod_slice_buffer_shorter_than_length_value() {
179        // If the buffer is shorter than the u32 length value declares, we
180        // should get a BufferTooSmall error.
181        let length: u32 = 12;
182        let length_le = length.to_le_bytes();
183        for num_items in 0..length {
184            // First set up the data to have `num_elements` items.
185            let data_len = PodSlice::<TestStruct>::size_of(num_items as usize).unwrap();
186            let mut data = vec![0; data_len];
187
188            // Now write the bogus length - which is larger - into the first 4
189            // bytes.
190            data[..LENGTH_SIZE].copy_from_slice(&length_le);
191
192            // Expect an error on unpacking.
193            let err = PodSlice::<TestStruct>::unpack(&data).err().unwrap();
194            assert_eq!(
195                err,
196                PodSliceError::BufferTooSmall.into(),
197                "Expected an `PodSliceError::BufferTooSmall` error"
198            );
199        }
200    }
201
202    #[test]
203    fn test_pod_slice_mut() {
204        // slice can fit 2 `TestStruct`
205        let mut pod_slice_bytes = [0; 70];
206        // set length to 1, so we have room to push 1 more item
207        let len_bytes = [1, 0, 0, 0];
208        pod_slice_bytes[0..4].copy_from_slice(&len_bytes);
209
210        let mut pod_slice = PodSliceMut::<TestStruct>::unpack(&mut pod_slice_bytes).unwrap();
211
212        assert_eq!(pod_slice.inner.len(), 1);
213        pod_slice.push(TestStruct::default()).unwrap();
214        assert_eq!(pod_slice.inner.len(), 2);
215
216        let err = pod_slice
217            .push(TestStruct::default())
218            .expect_err("Expected an `PodSliceError::BufferTooSmall` error");
219        assert_eq!(err, PodSliceError::BufferTooSmall.into());
220    }
221}