1use {
4 crate::{
5 bytemuck::{
6 pod_from_bytes, pod_from_bytes_mut, pod_slice_from_bytes, pod_slice_from_bytes_mut,
7 },
8 error::PodSliceError,
9 list::{list_view_mut::ListViewMut, list_view_read_only::ListViewReadOnly},
10 pod_length::PodLength,
11 primitives::PodU32,
12 },
13 bytemuck::Pod,
14 solana_program_error::ProgramError,
15 std::{
16 marker::PhantomData,
17 mem::{align_of, size_of},
18 ops::Range,
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 #[inline]
103 fn build_mut_view(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
104 let layout = Self::calculate_layout(buf.len())?;
105
106 let (header_bytes, data_bytes) = buf.split_at_mut(layout.data_range.start);
110 let len_bytes = &mut header_bytes[layout.length_range];
114
115 let length = pod_from_bytes_mut::<L>(len_bytes)?;
117 let data = pod_slice_from_bytes_mut::<T>(data_bytes)?;
118 let capacity = data.len();
119
120 Ok(ListViewMut {
121 length,
122 data,
123 capacity,
124 })
125 }
126
127 #[inline]
129 fn calculate_layout(buf_len: usize) -> Result<Layout, ProgramError> {
130 let len_field_end = size_of::<L>();
131 let header_padding = Self::header_padding()?;
132 let data_start = len_field_end.saturating_add(header_padding);
133
134 if buf_len < data_start {
135 return Err(PodSliceError::BufferTooSmall.into());
136 }
137
138 Ok(Layout {
139 length_range: 0..len_field_end,
140 data_range: data_start..buf_len,
141 })
142 }
143
144 #[inline]
149 fn header_padding() -> Result<usize, ProgramError> {
150 if align_of::<L>() != 1 {
152 return Err(ProgramError::InvalidArgument);
153 }
154
155 let length_size = size_of::<L>();
156 let data_align = align_of::<T>();
157
158 if data_align == 0 || data_align == 1 {
160 return Ok(0);
161 }
162
163 #[allow(clippy::arithmetic_side_effects)]
165 let remainder = length_size.wrapping_rem(data_align);
166
167 if remainder == 0 {
170 Ok(0)
171 } else {
172 Ok(data_align.wrapping_sub(remainder))
173 }
174 }
175}
176
177impl<T: Pod, L> ListView<T, L>
178where
179 L: PodLength,
180 PodSliceError: From<<L as TryFrom<usize>>::Error>,
181{
182 pub fn init(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
184 let view = Self::build_mut_view(buf)?;
185 *view.length = L::try_from(0).map_err(PodSliceError::from)?;
186 Ok(view)
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 #[cfg(not(target_arch = "bpf"))]
193 use crate::primitives::PodU128;
194 use {
195 super::*,
196 crate::{
197 list::List,
198 primitives::{PodU16, PodU32, PodU64},
199 },
200 bytemuck_derive::{Pod as DerivePod, Zeroable},
201 };
202
203 #[test]
204 fn test_size_of_no_padding() {
205 assert_eq!(ListView::<u8, PodU32>::size_of(10).unwrap(), 14);
208
209 assert_eq!(ListView::<u32>::size_of(10).unwrap(), 44);
213
214 assert_eq!(ListView::<u32>::size_of(0).unwrap(), 4);
218 }
219
220 #[test]
221 fn test_size_of_with_padding() {
222 assert_eq!(ListView::<u64, PodU32>::size_of(10).unwrap(), 88);
227
228 #[repr(C, align(16))]
229 #[derive(DerivePod, Zeroable, Copy, Clone)]
230 struct Align16(u128);
231
232 assert_eq!(ListView::<Align16>::size_of(10).unwrap(), 176);
238
239 assert_eq!(ListView::<u64, PodU32>::size_of(0).unwrap(), 8);
244 }
245
246 #[test]
247 fn test_size_of_overflow() {
248 let err = ListView::<u16, PodU32>::size_of(usize::MAX).unwrap_err();
251 assert_eq!(err, PodSliceError::CalculationFailure.into());
252
253 let err = ListView::<u8, PodU32>::size_of(usize::MAX).unwrap_err();
256 assert_eq!(err, PodSliceError::CalculationFailure.into());
257 }
258
259 #[test]
260 fn test_fails_with_non_aligned_length_type() {
261 #[repr(C, align(4))]
263 #[derive(Debug, Copy, Clone, Zeroable, DerivePod)]
264 struct TestPodU32(u32);
265
266 impl From<TestPodU32> for usize {
268 fn from(val: TestPodU32) -> Self {
269 val.0 as usize
270 }
271 }
272 impl TryFrom<usize> for TestPodU32 {
273 type Error = PodSliceError;
274 fn try_from(val: usize) -> Result<Self, Self::Error> {
275 Ok(Self(u32::try_from(val)?))
276 }
277 }
278
279 let mut buf = [0u8; 100];
280
281 let err_size_of = ListView::<u8, TestPodU32>::size_of(10).unwrap_err();
282 assert_eq!(err_size_of, ProgramError::InvalidArgument);
283
284 let err_unpack = ListView::<u8, TestPodU32>::unpack(&buf).unwrap_err();
285 assert_eq!(err_unpack, ProgramError::InvalidArgument);
286
287 let err_init = ListView::<u8, TestPodU32>::init(&mut buf).unwrap_err();
288 assert_eq!(err_init, ProgramError::InvalidArgument);
289 }
290
291 #[test]
292 fn test_padding_calculation() {
293 assert_eq!(ListView::<u8, PodU32>::header_padding().unwrap(), 0);
295
296 assert_eq!(ListView::<(), PodU64>::header_padding().unwrap(), 0);
298
299 assert_eq!(ListView::<u16, PodU16>::header_padding().unwrap(), 0);
301 assert_eq!(ListView::<u32, PodU32>::header_padding().unwrap(), 0);
302 assert_eq!(ListView::<u64, PodU64>::header_padding().unwrap(), 0);
303
304 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))]
315 #[derive(DerivePod, Zeroable, Copy, Clone)]
316 struct Align8(u64);
317
318 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))]
324 #[derive(DerivePod, Zeroable, Copy, Clone)]
325 struct Align16(u128);
326
327 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); }
331
332 #[test]
333 fn test_unpack_success_no_padding() {
334 let length: u32 = 2;
336 let capacity: usize = 3;
337 let item_size = size_of::<u32>();
338 let len_size = size_of::<PodU32>();
339 let buf_size = len_size + capacity * item_size;
340 let mut buf = vec![0u8; buf_size];
341
342 let pod_len: PodU32 = length.into();
343 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
344
345 let data_start = len_size;
346 let items = [100u32, 200u32];
347 let items_bytes = bytemuck::cast_slice(&items);
348 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
349
350 let view_ro = ListView::<u32, PodU32>::unpack(&buf).unwrap();
351 assert_eq!(view_ro.len(), length as usize);
352 assert_eq!(view_ro.capacity(), capacity);
353 assert_eq!(*view_ro, items[..]);
354
355 let view_mut = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap();
356 assert_eq!(view_mut.len(), length as usize);
357 assert_eq!(view_mut.capacity(), capacity);
358 assert_eq!(*view_mut, items[..]);
359 }
360
361 #[test]
362 fn test_unpack_success_with_padding() {
363 let padding = ListView::<u64, PodU32>::header_padding().unwrap();
365 assert_eq!(padding, 4);
366
367 let length: u32 = 2;
368 let capacity: usize = 2;
369 let item_size = size_of::<u64>();
370 let len_size = size_of::<PodU32>();
371 let buf_size = len_size + padding + capacity * item_size;
372 let mut buf = vec![0u8; buf_size];
373
374 let pod_len: PodU32 = length.into();
375 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
376
377 let data_start = len_size + padding;
379 let items = [100u64, 200u64];
380 let items_bytes = bytemuck::cast_slice(&items);
381 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
382
383 let view_ro = ListView::<u64, PodU32>::unpack(&buf).unwrap();
384 assert_eq!(view_ro.len(), length as usize);
385 assert_eq!(view_ro.capacity(), capacity);
386 assert_eq!(*view_ro, items[..]);
387
388 let view_mut = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap();
389 assert_eq!(view_mut.len(), length as usize);
390 assert_eq!(view_mut.capacity(), capacity);
391 assert_eq!(*view_mut, items[..]);
392 }
393
394 #[test]
395 fn test_unpack_success_zero_length() {
396 let capacity: usize = 5;
397 let item_size = size_of::<u32>();
398 let len_size = size_of::<PodU32>();
399 let buf_size = len_size + capacity * item_size;
400 let mut buf = vec![0u8; buf_size];
401
402 let pod_len: PodU32 = 0u32.into();
403 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
404
405 let view_ro = ListView::<u32, PodU32>::unpack(&buf).unwrap();
406 assert_eq!(view_ro.len(), 0);
407 assert_eq!(view_ro.capacity(), capacity);
408 assert!(view_ro.is_empty());
409 assert_eq!(&*view_ro, &[] as &[u32]);
410
411 let view_mut = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap();
412 assert_eq!(view_mut.len(), 0);
413 assert_eq!(view_mut.capacity(), capacity);
414 assert!(view_mut.is_empty());
415 assert_eq!(&*view_mut, &[] as &[u32]);
416 }
417
418 #[test]
419 fn test_unpack_success_full_capacity() {
420 let length: u64 = 3;
421 let capacity: usize = 3;
422 let item_size = size_of::<u64>();
423 let len_size = size_of::<PodU64>();
424 let buf_size = len_size + capacity * item_size;
425 let mut buf = vec![0u8; buf_size];
426
427 let pod_len: PodU64 = length.into();
428 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
429
430 let data_start = len_size;
431 let items = [1u64, 2u64, 3u64];
432 let items_bytes = bytemuck::cast_slice(&items);
433 buf[data_start..].copy_from_slice(items_bytes);
434
435 let view_ro = ListView::<u64>::unpack(&buf).unwrap();
436 assert_eq!(view_ro.len(), length as usize);
437 assert_eq!(view_ro.capacity(), capacity);
438 assert_eq!(*view_ro, items[..]);
439
440 let view_mut = ListView::<u64>::unpack_mut(&mut buf).unwrap();
441 assert_eq!(view_mut.len(), length as usize);
442 assert_eq!(view_mut.capacity(), capacity);
443 assert_eq!(*view_mut, items[..]);
444 }
445
446 #[test]
447 fn test_unpack_fail_buffer_too_small_for_header() {
448 let header_size = ListView::<u64, PodU32>::size_of(0).unwrap();
450 assert_eq!(header_size, 8);
451
452 let mut buf = vec![0u8; header_size - 1]; let err = ListView::<u64, PodU32>::unpack(&buf).unwrap_err();
456 assert_eq!(err, PodSliceError::BufferTooSmall.into());
457
458 let err = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap_err();
459 assert_eq!(err, PodSliceError::BufferTooSmall.into());
460 }
461
462 #[test]
463 fn test_unpack_fail_declared_length_exceeds_capacity() {
464 let declared_length: u32 = 4;
465 let capacity: usize = 3; let item_size = size_of::<u32>();
467 let len_size = size_of::<PodU32>();
468 let buf_size = len_size + capacity * item_size;
469 let mut buf = vec![0u8; buf_size];
470
471 let pod_len: PodU32 = declared_length.into();
473 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
474
475 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
476 assert_eq!(err, PodSliceError::BufferTooSmall.into());
477
478 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
479 assert_eq!(err, PodSliceError::BufferTooSmall.into());
480 }
481
482 #[test]
483 fn test_unpack_fail_data_part_not_multiple_of_item_size() {
484 let len_size = size_of::<PodU32>();
485
486 let buf_size = len_size + 5;
488 let mut buf = vec![0u8; buf_size];
489
490 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
493 assert_eq!(err, ProgramError::InvalidArgument);
494
495 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
496 assert_eq!(err, ProgramError::InvalidArgument);
497 }
498
499 #[test]
500 fn test_unpack_empty_buffer() {
501 let mut buf = [];
502 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
503 assert_eq!(err, PodSliceError::BufferTooSmall.into());
504
505 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
506 assert_eq!(err, PodSliceError::BufferTooSmall.into());
507 }
508
509 #[test]
510 fn test_init_success_no_padding() {
511 let capacity: usize = 5;
513 let len_size = size_of::<PodU32>();
514 let buf_size = ListView::<u32, PodU32>::size_of(capacity).unwrap();
515 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u32, PodU32>::init(&mut buf).unwrap();
518
519 assert_eq!(view.len(), 0);
520 assert_eq!(view.capacity(), capacity);
521 assert!(view.is_empty());
522
523 let length_bytes = &buf[0..len_size];
525 assert_eq!(length_bytes, &[0u8; 4]);
526 }
527
528 #[test]
529 fn test_init_success_with_padding() {
530 let capacity: usize = 3;
532 let len_size = size_of::<PodU32>();
533 let buf_size = ListView::<u64, PodU32>::size_of(capacity).unwrap();
534 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u64, PodU32>::init(&mut buf).unwrap();
537
538 assert_eq!(view.len(), 0);
539 assert_eq!(view.capacity(), capacity);
540 assert!(view.is_empty());
541
542 let length_bytes = &buf[0..len_size];
544 assert_eq!(length_bytes, &[0u8; 4]);
545 }
547
548 #[test]
549 fn test_init_success_zero_capacity() {
550 let buf_size = ListView::<u64, PodU32>::size_of(0).unwrap();
553 assert_eq!(buf_size, 8);
554 let mut buf = vec![0xFFu8; buf_size];
555
556 let view = ListView::<u64, PodU32>::init(&mut buf).unwrap();
557
558 assert_eq!(view.len(), 0);
559 assert_eq!(view.capacity(), 0);
560 assert!(view.is_empty());
561
562 let len_size = size_of::<PodU32>();
564 let length_bytes = &buf[0..len_size];
565 assert_eq!(length_bytes, &[0u8; 4]);
566 }
567
568 #[test]
569 fn test_init_fail_buffer_too_small() {
570 let mut buf = vec![0u8; 3];
572 let err = ListView::<u32, PodU32>::init(&mut buf).unwrap_err();
573 assert_eq!(err, PodSliceError::BufferTooSmall.into());
574
575 let mut buf_padded = vec![0u8; 7];
577 let err_padded = ListView::<u64, PodU32>::init(&mut buf_padded).unwrap_err();
578 assert_eq!(err_padded, PodSliceError::BufferTooSmall.into());
579 }
580
581 #[test]
582 fn test_init_success_default_length_type() {
583 let capacity = 5;
586 let len_size = size_of::<PodU32>(); let buf_size = ListView::<u32>::size_of(capacity).unwrap();
588 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u32>::init(&mut buf).unwrap();
591
592 assert_eq!(view.len(), 0);
593 assert_eq!(view.capacity(), capacity);
594 assert!(view.is_empty());
595
596 let length_bytes = &buf[0..len_size];
598 assert_eq!(length_bytes, &[0u8; 4]);
599 }
600
601 macro_rules! test_list_view_for_length_type {
602 ($test_name:ident, $LengthType:ty) => {
603 #[test]
604 fn $test_name() {
605 type T = u64;
606
607 let padding = ListView::<T, $LengthType>::header_padding().unwrap();
608 let length_usize = 2usize;
609 let capacity = 3;
610
611 let item_size = size_of::<T>();
612 let len_size = size_of::<$LengthType>();
613 let buf_size = len_size + padding + capacity * item_size;
614 let mut buf = vec![0u8; buf_size];
615
616 let pod_len = <$LengthType>::try_from(length_usize).unwrap();
618 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
619
620 let data_start = len_size + padding;
622 let items = [1000 as T, 2000 as T];
623 let items_bytes = bytemuck::cast_slice(&items);
624 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
625
626 let view_ro = ListView::<T, $LengthType>::unpack(&buf).unwrap();
628 assert_eq!(view_ro.len(), length_usize);
629 assert_eq!(view_ro.capacity(), capacity);
630 assert_eq!(*view_ro, items[..]);
631
632 let mut buf_mut = buf.clone();
634 let view_mut = ListView::<T, $LengthType>::unpack_mut(&mut buf_mut).unwrap();
635 assert_eq!(view_mut.len(), length_usize);
636 assert_eq!(view_mut.capacity(), capacity);
637 assert_eq!(*view_mut, items[..]);
638
639 let mut init_buf = vec![0xFFu8; buf_size];
641 let init_view = ListView::<T, $LengthType>::init(&mut init_buf).unwrap();
642 assert_eq!(init_view.len(), 0);
643 assert_eq!(init_view.capacity(), capacity);
644 assert_eq!(<$LengthType>::try_from(0usize).unwrap(), *init_view.length);
645 }
646 };
647 }
648
649 test_list_view_for_length_type!(list_view_with_pod_u16, PodU16);
650 test_list_view_for_length_type!(list_view_with_pod_u32, PodU32);
651 test_list_view_for_length_type!(list_view_with_pod_u64, PodU64);
652 #[cfg(not(target_arch = "bpf"))]
653 test_list_view_for_length_type!(list_view_with_pod_u128, PodU128);
654}