jiminy_core/account/
collection.rs1use pinocchio::error::ProgramError;
31
32use super::pod::{FixedLayout, Pod};
33
34const LEN_PREFIX: usize = 4;
36
37pub struct ZeroCopySlice<'a, T: Pod + FixedLayout> {
41 len: u32,
42 data: &'a [u8],
43 _marker: core::marker::PhantomData<T>,
44}
45
46impl<'a, T: Pod + FixedLayout> ZeroCopySlice<'a, T> {
47 #[inline(always)]
52 pub fn from_bytes(data: &'a [u8]) -> Result<Self, ProgramError> {
53 if data.len() < LEN_PREFIX {
54 return Err(ProgramError::AccountDataTooSmall);
55 }
56 let len = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
57 let required = LEN_PREFIX + (len as usize) * T::SIZE;
58 if data.len() < required {
59 return Err(ProgramError::AccountDataTooSmall);
60 }
61 Ok(Self {
62 len,
63 data,
64 _marker: core::marker::PhantomData,
65 })
66 }
67
68 #[inline(always)]
70 pub fn len(&self) -> u32 {
71 self.len
72 }
73
74 #[inline(always)]
76 pub fn is_empty(&self) -> bool {
77 self.len == 0
78 }
79
80 #[inline(always)]
82 pub fn byte_len(&self) -> usize {
83 LEN_PREFIX + (self.len as usize) * T::SIZE
84 }
85
86 #[inline(always)]
90 pub fn get(&self, index: u32) -> Result<&T, ProgramError> {
91 if index >= self.len {
92 return Err(ProgramError::InvalidArgument);
93 }
94 let offset = LEN_PREFIX + (index as usize) * T::SIZE;
95 #[cfg(target_os = "solana")]
98 {
99 Ok(unsafe { &*(self.data.as_ptr().add(offset) as *const T) })
100 }
101 #[cfg(not(target_os = "solana"))]
102 {
103 let ptr = self.data.as_ptr();
104 if (unsafe { ptr.add(offset) } as usize) % core::mem::align_of::<T>() != 0 {
105 return Err(ProgramError::InvalidAccountData);
106 }
107 Ok(unsafe { &*(ptr.add(offset) as *const T) })
108 }
109 }
110
111 #[inline(always)]
113 pub fn read(&self, index: u32) -> Result<T, ProgramError> {
114 if index >= self.len {
115 return Err(ProgramError::InvalidArgument);
116 }
117 let offset = LEN_PREFIX + (index as usize) * T::SIZE;
118 Ok(unsafe {
119 core::ptr::read_unaligned(self.data.as_ptr().add(offset) as *const T)
120 })
121 }
122
123 #[inline(always)]
125 pub fn iter(&self) -> ZeroCopyIter<'a, T> {
126 ZeroCopyIter {
127 data: self.data,
128 index: 0,
129 len: self.len,
130 _marker: core::marker::PhantomData,
131 }
132 }
133
134 #[inline(always)]
138 pub fn contains_bytes(&self, needle: &[u8]) -> bool {
139 if needle.len() != T::SIZE {
140 return false;
141 }
142 let mut i = 0u32;
143 while i < self.len {
144 let offset = LEN_PREFIX + (i as usize) * T::SIZE;
145 if &self.data[offset..offset + T::SIZE] == needle {
146 return true;
147 }
148 i += 1;
149 }
150 false
151 }
152}
153
154pub struct ZeroCopySliceMut<'a, T: Pod + FixedLayout> {
156 len: u32,
157 data: &'a mut [u8],
158 _marker: core::marker::PhantomData<T>,
159}
160
161impl<'a, T: Pod + FixedLayout> ZeroCopySliceMut<'a, T> {
162 #[inline(always)]
164 pub fn from_bytes(data: &'a mut [u8]) -> Result<Self, ProgramError> {
165 if data.len() < LEN_PREFIX {
166 return Err(ProgramError::AccountDataTooSmall);
167 }
168 let len = u32::from_le_bytes([data[0], data[1], data[2], data[3]]);
169 let required = LEN_PREFIX + (len as usize) * T::SIZE;
170 if data.len() < required {
171 return Err(ProgramError::AccountDataTooSmall);
172 }
173 Ok(Self {
174 len,
175 data,
176 _marker: core::marker::PhantomData,
177 })
178 }
179
180 #[inline(always)]
183 pub fn init(data: &'a mut [u8], len: u32) -> Result<Self, ProgramError> {
184 let required = LEN_PREFIX + (len as usize) * T::SIZE;
185 if data.len() < required {
186 return Err(ProgramError::AccountDataTooSmall);
187 }
188 data[0..4].copy_from_slice(&len.to_le_bytes());
189 let item_region = &mut data[LEN_PREFIX..required];
191 item_region.fill(0);
192 Ok(Self {
193 len,
194 data,
195 _marker: core::marker::PhantomData,
196 })
197 }
198
199 #[inline(always)]
201 pub fn len(&self) -> u32 {
202 self.len
203 }
204
205 #[inline(always)]
207 pub fn is_empty(&self) -> bool {
208 self.len == 0
209 }
210
211 #[inline(always)]
213 pub fn get_mut(&mut self, index: u32) -> Result<&mut T, ProgramError> {
214 if index >= self.len {
215 return Err(ProgramError::InvalidArgument);
216 }
217 let offset = LEN_PREFIX + (index as usize) * T::SIZE;
218 #[cfg(target_os = "solana")]
219 {
220 Ok(unsafe { &mut *(self.data.as_mut_ptr().add(offset) as *mut T) })
221 }
222 #[cfg(not(target_os = "solana"))]
223 {
224 let ptr = self.data.as_mut_ptr();
225 if (unsafe { ptr.add(offset) } as usize) % core::mem::align_of::<T>() != 0 {
226 return Err(ProgramError::InvalidAccountData);
227 }
228 Ok(unsafe { &mut *(ptr.add(offset) as *mut T) })
229 }
230 }
231
232 #[inline(always)]
234 pub fn get(&self, index: u32) -> Result<&T, ProgramError> {
235 if index >= self.len {
236 return Err(ProgramError::InvalidArgument);
237 }
238 let offset = LEN_PREFIX + (index as usize) * T::SIZE;
239 #[cfg(target_os = "solana")]
240 {
241 Ok(unsafe { &*(self.data.as_ptr().add(offset) as *const T) })
242 }
243 #[cfg(not(target_os = "solana"))]
244 {
245 let ptr = self.data.as_ptr();
246 if (unsafe { ptr.add(offset) } as usize) % core::mem::align_of::<T>() != 0 {
247 return Err(ProgramError::InvalidAccountData);
248 }
249 Ok(unsafe { &*(ptr.add(offset) as *const T) })
250 }
251 }
252
253 #[inline(always)]
255 pub fn set(&mut self, index: u32, value: &T) -> Result<(), ProgramError> {
256 if index >= self.len {
257 return Err(ProgramError::InvalidArgument);
258 }
259 let offset = LEN_PREFIX + (index as usize) * T::SIZE;
260 let src = value as *const T as *const u8;
261 unsafe {
262 core::ptr::copy_nonoverlapping(
263 src,
264 self.data.as_mut_ptr().add(offset),
265 T::SIZE,
266 );
267 }
268 Ok(())
269 }
270}
271
272pub struct ZeroCopyIter<'a, T: Pod + FixedLayout> {
274 data: &'a [u8],
275 index: u32,
276 len: u32,
277 _marker: core::marker::PhantomData<T>,
278}
279
280impl<'a, T: Pod + FixedLayout> Iterator for ZeroCopyIter<'a, T> {
281 type Item = T;
282
283 #[inline(always)]
284 fn next(&mut self) -> Option<Self::Item> {
285 if self.index >= self.len {
286 return None;
287 }
288 let offset = LEN_PREFIX + (self.index as usize) * T::SIZE;
289 self.index += 1;
290 Some(unsafe {
291 core::ptr::read_unaligned(self.data.as_ptr().add(offset) as *const T)
292 })
293 }
294
295 #[inline(always)]
296 fn size_hint(&self) -> (usize, Option<usize>) {
297 let remaining = (self.len - self.index) as usize;
298 (remaining, Some(remaining))
299 }
300}
301
302impl<'a, T: Pod + FixedLayout> ExactSizeIterator for ZeroCopyIter<'a, T> {}