1use {
4 crate::{list_view_mut::ListViewMut, list_view_read_only::ListViewReadOnly},
5 bytemuck::Pod,
6 core::{
7 marker::PhantomData,
8 mem::{align_of, size_of},
9 ops::Range,
10 },
11 solana_program_error::ProgramError,
12 spl_pod::{
13 bytemuck::{
14 pod_from_bytes, pod_from_bytes_mut, pod_slice_from_bytes, pod_slice_from_bytes_mut,
15 },
16 error::PodSliceError,
17 pod_length::PodLength,
18 primitives::PodU32,
19 },
20};
21
22pub struct ListView<T: Pod, L: PodLength = PodU32>(PhantomData<(T, L)>);
45
46struct Layout {
47 length_range: Range<usize>,
48 data_range: Range<usize>,
49}
50
51impl<T: Pod, L: PodLength> ListView<T, L> {
52 pub fn size_of(num_items: usize) -> Result<usize, ProgramError> {
55 let header_padding = Self::header_padding()?;
56 size_of::<T>()
57 .checked_mul(num_items)
58 .and_then(|curr| curr.checked_add(size_of::<L>()))
59 .and_then(|curr| curr.checked_add(header_padding))
60 .ok_or_else(|| PodSliceError::CalculationFailure.into())
61 }
62
63 pub fn unpack(buf: &[u8]) -> Result<ListViewReadOnly<T, L>, ProgramError> {
65 let layout = Self::calculate_layout(buf.len())?;
66
67 let len_bytes = &buf[layout.length_range];
75 let data_bytes = &buf[layout.data_range];
76
77 let length = pod_from_bytes::<L>(len_bytes)?;
78 let data = pod_slice_from_bytes::<T>(data_bytes)?;
79 let capacity = data.len();
80
81 if (*length).into() > capacity {
82 return Err(PodSliceError::BufferTooSmall.into());
83 }
84
85 Ok(ListViewReadOnly {
86 length,
87 data,
88 capacity,
89 })
90 }
91
92 pub fn unpack_mut(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
94 let view = Self::build_mut_view(buf)?;
95 if (*view.length).into() > view.capacity {
96 return Err(PodSliceError::BufferTooSmall.into());
97 }
98 Ok(view)
99 }
100
101 pub fn init(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
103 let view = Self::build_mut_view(buf)?;
104 *view.length = L::try_from(0)?;
105 Ok(view)
106 }
107
108 #[inline]
110 fn build_mut_view(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
111 let layout = Self::calculate_layout(buf.len())?;
112
113 let (header_bytes, data_bytes) = buf.split_at_mut(layout.data_range.start);
117 let len_bytes = &mut header_bytes[layout.length_range];
121
122 let length = pod_from_bytes_mut::<L>(len_bytes)?;
124 let data = pod_slice_from_bytes_mut::<T>(data_bytes)?;
125 let capacity = data.len();
126
127 Ok(ListViewMut {
128 length,
129 data,
130 capacity,
131 })
132 }
133
134 #[inline]
136 fn calculate_layout(buf_len: usize) -> Result<Layout, ProgramError> {
137 let len_field_end = size_of::<L>();
138 let header_padding = Self::header_padding()?;
139 let data_start = len_field_end.saturating_add(header_padding);
140
141 if buf_len < data_start {
142 return Err(PodSliceError::BufferTooSmall.into());
143 }
144
145 Ok(Layout {
146 length_range: 0..len_field_end,
147 data_range: data_start..buf_len,
148 })
149 }
150
151 #[inline]
156 fn header_padding() -> Result<usize, ProgramError> {
157 if align_of::<L>() != 1 {
159 return Err(ProgramError::InvalidArgument);
160 }
161
162 let length_size = size_of::<L>();
163 let data_align = align_of::<T>();
164
165 if data_align == 0 || data_align == 1 {
167 return Ok(0);
168 }
169
170 #[allow(clippy::arithmetic_side_effects)]
172 let remainder = length_size.wrapping_rem(data_align);
173
174 if remainder == 0 {
177 Ok(0)
178 } else {
179 Ok(data_align.wrapping_sub(remainder))
180 }
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use {
187 super::*,
188 crate::List,
189 bytemuck_derive::{Pod as DerivePod, Zeroable},
190 spl_pod::primitives::{PodU128, PodU16, PodU32, PodU64},
191 };
192
193 #[test]
194 fn test_size_of_no_padding() {
195 assert_eq!(ListView::<u8, PodU32>::size_of(10).unwrap(), 14);
198
199 assert_eq!(ListView::<u32>::size_of(10).unwrap(), 44);
203
204 assert_eq!(ListView::<u32>::size_of(0).unwrap(), 4);
208 }
209
210 #[test]
211 fn test_size_of_with_padding() {
212 assert_eq!(ListView::<u64, PodU32>::size_of(10).unwrap(), 88);
217
218 #[repr(C, align(16))]
219 #[derive(DerivePod, Zeroable, Copy, Clone)]
220 struct Align16(u128);
221
222 assert_eq!(ListView::<Align16>::size_of(10).unwrap(), 176);
228
229 assert_eq!(ListView::<u64, PodU32>::size_of(0).unwrap(), 8);
234 }
235
236 #[test]
237 fn test_size_of_overflow() {
238 let err = ListView::<u16, PodU32>::size_of(usize::MAX).unwrap_err();
241 assert_eq!(err, PodSliceError::CalculationFailure.into());
242
243 let err = ListView::<u8, PodU32>::size_of(usize::MAX).unwrap_err();
246 assert_eq!(err, PodSliceError::CalculationFailure.into());
247 }
248
249 #[test]
250 fn test_fails_with_non_aligned_length_type() {
251 #[repr(C, align(4))]
253 #[derive(Debug, Copy, Clone, Zeroable, DerivePod)]
254 struct TestPodU32(u32);
255
256 impl From<TestPodU32> for usize {
258 fn from(val: TestPodU32) -> Self {
259 val.0 as usize
260 }
261 }
262 impl TryFrom<usize> for TestPodU32 {
263 type Error = PodSliceError;
264 fn try_from(val: usize) -> Result<Self, Self::Error> {
265 Ok(Self(u32::try_from(val)?))
266 }
267 }
268
269 let mut buf = [0u8; 100];
270
271 let err_size_of = ListView::<u8, TestPodU32>::size_of(10).unwrap_err();
272 assert_eq!(err_size_of, ProgramError::InvalidArgument);
273
274 let err_unpack = ListView::<u8, TestPodU32>::unpack(&buf).unwrap_err();
275 assert_eq!(err_unpack, ProgramError::InvalidArgument);
276
277 let err_init = ListView::<u8, TestPodU32>::init(&mut buf).unwrap_err();
278 assert_eq!(err_init, ProgramError::InvalidArgument);
279 }
280
281 #[test]
282 fn test_padding_calculation() {
283 assert_eq!(ListView::<u8, PodU32>::header_padding().unwrap(), 0);
285
286 assert_eq!(ListView::<(), PodU64>::header_padding().unwrap(), 0);
288
289 assert_eq!(ListView::<u16, PodU16>::header_padding().unwrap(), 0);
291 assert_eq!(ListView::<u32, PodU32>::header_padding().unwrap(), 0);
292 assert_eq!(ListView::<u64, PodU64>::header_padding().unwrap(), 0);
293
294 assert_eq!(ListView::<u16, PodU64>::header_padding().unwrap(), 0); assert_eq!(ListView::<u32, PodU64>::header_padding().unwrap(), 0); assert_eq!(ListView::<u32, PodU16>::header_padding().unwrap(), 2); assert_eq!(ListView::<u64, PodU16>::header_padding().unwrap(), 6); assert_eq!(ListView::<u64, PodU32>::header_padding().unwrap(), 4); #[repr(C, align(8))]
305 #[derive(DerivePod, Zeroable, Copy, Clone)]
306 struct Align8(u64);
307
308 assert_eq!(ListView::<Align8, PodU16>::header_padding().unwrap(), 6); assert_eq!(ListView::<Align8, PodU32>::header_padding().unwrap(), 4); assert_eq!(ListView::<Align8, PodU64>::header_padding().unwrap(), 0); #[repr(C, align(16))]
314 #[derive(DerivePod, Zeroable, Copy, Clone)]
315 struct Align16(u128);
316
317 assert_eq!(ListView::<Align16, PodU16>::header_padding().unwrap(), 14); assert_eq!(ListView::<Align16, PodU32>::header_padding().unwrap(), 12); assert_eq!(ListView::<Align16, PodU64>::header_padding().unwrap(), 8); }
321
322 #[test]
323 fn test_unpack_success_no_padding() {
324 let length: u32 = 2;
326 let capacity: usize = 3;
327 let item_size = size_of::<u32>();
328 let len_size = size_of::<PodU32>();
329 let buf_size = len_size + capacity * item_size;
330 let mut buf = vec![0u8; buf_size];
331
332 let pod_len: PodU32 = length.into();
333 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
334
335 let data_start = len_size;
336 let items = [100u32, 200u32];
337 let items_bytes = bytemuck::cast_slice(&items);
338 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
339
340 let view_ro = ListView::<u32, PodU32>::unpack(&buf).unwrap();
341 assert_eq!(view_ro.len(), length as usize);
342 assert_eq!(view_ro.capacity(), capacity);
343 assert_eq!(*view_ro, items[..]);
344
345 let view_mut = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap();
346 assert_eq!(view_mut.len(), length as usize);
347 assert_eq!(view_mut.capacity(), capacity);
348 assert_eq!(*view_mut, items[..]);
349 }
350
351 #[test]
352 fn test_unpack_success_with_padding() {
353 let padding = ListView::<u64, PodU32>::header_padding().unwrap();
355 assert_eq!(padding, 4);
356
357 let length: u32 = 2;
358 let capacity: usize = 2;
359 let item_size = size_of::<u64>();
360 let len_size = size_of::<PodU32>();
361 let buf_size = len_size + padding + capacity * item_size;
362 let mut buf = vec![0u8; buf_size];
363
364 let pod_len: PodU32 = length.into();
365 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
366
367 let data_start = len_size + padding;
369 let items = [100u64, 200u64];
370 let items_bytes = bytemuck::cast_slice(&items);
371 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
372
373 let view_ro = ListView::<u64, PodU32>::unpack(&buf).unwrap();
374 assert_eq!(view_ro.len(), length as usize);
375 assert_eq!(view_ro.capacity(), capacity);
376 assert_eq!(*view_ro, items[..]);
377
378 let view_mut = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap();
379 assert_eq!(view_mut.len(), length as usize);
380 assert_eq!(view_mut.capacity(), capacity);
381 assert_eq!(*view_mut, items[..]);
382 }
383
384 #[test]
385 fn test_unpack_success_zero_length() {
386 let capacity: usize = 5;
387 let item_size = size_of::<u32>();
388 let len_size = size_of::<PodU32>();
389 let buf_size = len_size + capacity * item_size;
390 let mut buf = vec![0u8; buf_size];
391
392 let pod_len: PodU32 = 0u32.into();
393 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
394
395 let view_ro = ListView::<u32, PodU32>::unpack(&buf).unwrap();
396 assert_eq!(view_ro.len(), 0);
397 assert_eq!(view_ro.capacity(), capacity);
398 assert!(view_ro.is_empty());
399 assert_eq!(&*view_ro, &[] as &[u32]);
400
401 let view_mut = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap();
402 assert_eq!(view_mut.len(), 0);
403 assert_eq!(view_mut.capacity(), capacity);
404 assert!(view_mut.is_empty());
405 assert_eq!(&*view_mut, &[] as &[u32]);
406 }
407
408 #[test]
409 fn test_unpack_success_full_capacity() {
410 let length: u64 = 3;
411 let capacity: usize = 3;
412 let item_size = size_of::<u64>();
413 let len_size = size_of::<PodU64>();
414 let buf_size = len_size + capacity * item_size;
415 let mut buf = vec![0u8; buf_size];
416
417 let pod_len: PodU64 = length.into();
418 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
419
420 let data_start = len_size;
421 let items = [1u64, 2u64, 3u64];
422 let items_bytes = bytemuck::cast_slice(&items);
423 buf[data_start..].copy_from_slice(items_bytes);
424
425 let view_ro = ListView::<u64>::unpack(&buf).unwrap();
426 assert_eq!(view_ro.len(), length as usize);
427 assert_eq!(view_ro.capacity(), capacity);
428 assert_eq!(*view_ro, items[..]);
429
430 let view_mut = ListView::<u64>::unpack_mut(&mut buf).unwrap();
431 assert_eq!(view_mut.len(), length as usize);
432 assert_eq!(view_mut.capacity(), capacity);
433 assert_eq!(*view_mut, items[..]);
434 }
435
436 #[test]
437 fn test_unpack_fail_buffer_too_small_for_header() {
438 let header_size = ListView::<u64, PodU32>::size_of(0).unwrap();
440 assert_eq!(header_size, 8);
441
442 let mut buf = vec![0u8; header_size - 1]; let err = ListView::<u64, PodU32>::unpack(&buf).unwrap_err();
446 assert_eq!(err, PodSliceError::BufferTooSmall.into());
447
448 let err = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap_err();
449 assert_eq!(err, PodSliceError::BufferTooSmall.into());
450 }
451
452 #[test]
453 fn test_unpack_fail_declared_length_exceeds_capacity() {
454 let declared_length: u32 = 4;
455 let capacity: usize = 3; let item_size = size_of::<u32>();
457 let len_size = size_of::<PodU32>();
458 let buf_size = len_size + capacity * item_size;
459 let mut buf = vec![0u8; buf_size];
460
461 let pod_len: PodU32 = declared_length.into();
463 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
464
465 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
466 assert_eq!(err, PodSliceError::BufferTooSmall.into());
467
468 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
469 assert_eq!(err, PodSliceError::BufferTooSmall.into());
470 }
471
472 #[test]
473 fn test_unpack_fail_data_part_not_multiple_of_item_size() {
474 let len_size = size_of::<PodU32>();
475
476 let buf_size = len_size + 5;
478 let mut buf = vec![0u8; buf_size];
479
480 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
483 assert_eq!(err, ProgramError::InvalidArgument);
484
485 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
486 assert_eq!(err, ProgramError::InvalidArgument);
487 }
488
489 #[test]
490 fn test_unpack_empty_buffer() {
491 let mut buf = [];
492 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
493 assert_eq!(err, PodSliceError::BufferTooSmall.into());
494
495 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
496 assert_eq!(err, PodSliceError::BufferTooSmall.into());
497 }
498
499 #[test]
500 fn test_init_success_no_padding() {
501 let capacity: usize = 5;
503 let len_size = size_of::<PodU32>();
504 let buf_size = ListView::<u32, PodU32>::size_of(capacity).unwrap();
505 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u32, PodU32>::init(&mut buf).unwrap();
508
509 assert_eq!(view.len(), 0);
510 assert_eq!(view.capacity(), capacity);
511 assert!(view.is_empty());
512
513 let length_bytes = &buf[0..len_size];
515 assert_eq!(length_bytes, &[0u8; 4]);
516 }
517
518 #[test]
519 fn test_init_success_with_padding() {
520 let capacity: usize = 3;
522 let len_size = size_of::<PodU32>();
523 let buf_size = ListView::<u64, PodU32>::size_of(capacity).unwrap();
524 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u64, PodU32>::init(&mut buf).unwrap();
527
528 assert_eq!(view.len(), 0);
529 assert_eq!(view.capacity(), capacity);
530 assert!(view.is_empty());
531
532 let length_bytes = &buf[0..len_size];
534 assert_eq!(length_bytes, &[0u8; 4]);
535 }
537
538 #[test]
539 fn test_init_success_zero_capacity() {
540 let buf_size = ListView::<u64, PodU32>::size_of(0).unwrap();
543 assert_eq!(buf_size, 8);
544 let mut buf = vec![0xFFu8; buf_size];
545
546 let view = ListView::<u64, PodU32>::init(&mut buf).unwrap();
547
548 assert_eq!(view.len(), 0);
549 assert_eq!(view.capacity(), 0);
550 assert!(view.is_empty());
551
552 let len_size = size_of::<PodU32>();
554 let length_bytes = &buf[0..len_size];
555 assert_eq!(length_bytes, &[0u8; 4]);
556 }
557
558 #[test]
559 fn test_init_fail_buffer_too_small() {
560 let mut buf = vec![0u8; 3];
562 let err = ListView::<u32, PodU32>::init(&mut buf).unwrap_err();
563 assert_eq!(err, PodSliceError::BufferTooSmall.into());
564
565 let mut buf_padded = vec![0u8; 7];
567 let err_padded = ListView::<u64, PodU32>::init(&mut buf_padded).unwrap_err();
568 assert_eq!(err_padded, PodSliceError::BufferTooSmall.into());
569 }
570
571 #[test]
572 fn test_init_success_default_length_type() {
573 let capacity = 5;
576 let len_size = size_of::<PodU32>(); let buf_size = ListView::<u32>::size_of(capacity).unwrap();
578 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u32>::init(&mut buf).unwrap();
581
582 assert_eq!(view.len(), 0);
583 assert_eq!(view.capacity(), capacity);
584 assert!(view.is_empty());
585
586 let length_bytes = &buf[0..len_size];
588 assert_eq!(length_bytes, &[0u8; 4]);
589 }
590
591 macro_rules! test_list_view_for_length_type {
592 ($test_name:ident, $LengthType:ty) => {
593 #[test]
594 fn $test_name() {
595 type T = u64;
596
597 let padding = ListView::<T, $LengthType>::header_padding().unwrap();
598 let length_usize = 2usize;
599 let capacity = 3;
600
601 let item_size = size_of::<T>();
602 let len_size = size_of::<$LengthType>();
603 let buf_size = len_size + padding + capacity * item_size;
604 let mut buf = vec![0u8; buf_size];
605
606 let pod_len = <$LengthType>::try_from(length_usize).unwrap();
608 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
609
610 let data_start = len_size + padding;
612 let items = [1000 as T, 2000 as T];
613 let items_bytes = bytemuck::cast_slice(&items);
614 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
615
616 let view_ro = ListView::<T, $LengthType>::unpack(&buf).unwrap();
618 assert_eq!(view_ro.len(), length_usize);
619 assert_eq!(view_ro.capacity(), capacity);
620 assert_eq!(*view_ro, items[..]);
621
622 let mut buf_mut = buf.clone();
624 let view_mut = ListView::<T, $LengthType>::unpack_mut(&mut buf_mut).unwrap();
625 assert_eq!(view_mut.len(), length_usize);
626 assert_eq!(view_mut.capacity(), capacity);
627 assert_eq!(*view_mut, items[..]);
628
629 let mut init_buf = vec![0xFFu8; buf_size];
631 let init_view = ListView::<T, $LengthType>::init(&mut init_buf).unwrap();
632 assert_eq!(init_view.len(), 0);
633 assert_eq!(init_view.capacity(), capacity);
634 assert_eq!(<$LengthType>::try_from(0usize).unwrap(), *init_view.length);
635 }
636 };
637 }
638
639 test_list_view_for_length_type!(list_view_with_pod_u16, PodU16);
640 test_list_view_for_length_type!(list_view_with_pod_u32, PodU32);
641 test_list_view_for_length_type!(list_view_with_pod_u64, PodU64);
642 test_list_view_for_length_type!(list_view_with_pod_u128, PodU128);
643}