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 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::{
189 list::List,
190 primitives::{PodU128, PodU16, PodU32, PodU64},
191 },
192 bytemuck_derive::{Pod as DerivePod, Zeroable},
193 };
194
195 #[test]
196 fn test_size_of_no_padding() {
197 assert_eq!(ListView::<u8, PodU32>::size_of(10).unwrap(), 14);
200
201 assert_eq!(ListView::<u32>::size_of(10).unwrap(), 44);
205
206 assert_eq!(ListView::<u32>::size_of(0).unwrap(), 4);
210 }
211
212 #[test]
213 fn test_size_of_with_padding() {
214 assert_eq!(ListView::<u64, PodU32>::size_of(10).unwrap(), 88);
219
220 #[repr(C, align(16))]
221 #[derive(DerivePod, Zeroable, Copy, Clone)]
222 struct Align16(u128);
223
224 assert_eq!(ListView::<Align16>::size_of(10).unwrap(), 176);
230
231 assert_eq!(ListView::<u64, PodU32>::size_of(0).unwrap(), 8);
236 }
237
238 #[test]
239 fn test_size_of_overflow() {
240 let err = ListView::<u16, PodU32>::size_of(usize::MAX).unwrap_err();
243 assert_eq!(err, PodSliceError::CalculationFailure.into());
244
245 let err = ListView::<u8, PodU32>::size_of(usize::MAX).unwrap_err();
248 assert_eq!(err, PodSliceError::CalculationFailure.into());
249 }
250
251 #[test]
252 fn test_fails_with_non_aligned_length_type() {
253 #[repr(C, align(4))]
255 #[derive(Debug, Copy, Clone, Zeroable, DerivePod)]
256 struct TestPodU32(u32);
257
258 impl From<TestPodU32> for usize {
260 fn from(val: TestPodU32) -> Self {
261 val.0 as usize
262 }
263 }
264 impl TryFrom<usize> for TestPodU32 {
265 type Error = PodSliceError;
266 fn try_from(val: usize) -> Result<Self, Self::Error> {
267 Ok(Self(u32::try_from(val)?))
268 }
269 }
270
271 let mut buf = [0u8; 100];
272
273 let err_size_of = ListView::<u8, TestPodU32>::size_of(10).unwrap_err();
274 assert_eq!(err_size_of, ProgramError::InvalidArgument);
275
276 let err_unpack = ListView::<u8, TestPodU32>::unpack(&buf).unwrap_err();
277 assert_eq!(err_unpack, ProgramError::InvalidArgument);
278
279 let err_init = ListView::<u8, TestPodU32>::init(&mut buf).unwrap_err();
280 assert_eq!(err_init, ProgramError::InvalidArgument);
281 }
282
283 #[test]
284 fn test_padding_calculation() {
285 assert_eq!(ListView::<u8, PodU32>::header_padding().unwrap(), 0);
287
288 assert_eq!(ListView::<(), PodU64>::header_padding().unwrap(), 0);
290
291 assert_eq!(ListView::<u16, PodU16>::header_padding().unwrap(), 0);
293 assert_eq!(ListView::<u32, PodU32>::header_padding().unwrap(), 0);
294 assert_eq!(ListView::<u64, PodU64>::header_padding().unwrap(), 0);
295
296 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))]
307 #[derive(DerivePod, Zeroable, Copy, Clone)]
308 struct Align8(u64);
309
310 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))]
316 #[derive(DerivePod, Zeroable, Copy, Clone)]
317 struct Align16(u128);
318
319 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); }
323
324 #[test]
325 fn test_unpack_success_no_padding() {
326 let length: u32 = 2;
328 let capacity: usize = 3;
329 let item_size = size_of::<u32>();
330 let len_size = size_of::<PodU32>();
331 let buf_size = len_size + capacity * item_size;
332 let mut buf = vec![0u8; buf_size];
333
334 let pod_len: PodU32 = length.into();
335 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
336
337 let data_start = len_size;
338 let items = [100u32, 200u32];
339 let items_bytes = bytemuck::cast_slice(&items);
340 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
341
342 let view_ro = ListView::<u32, PodU32>::unpack(&buf).unwrap();
343 assert_eq!(view_ro.len(), length as usize);
344 assert_eq!(view_ro.capacity(), capacity);
345 assert_eq!(*view_ro, items[..]);
346
347 let view_mut = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap();
348 assert_eq!(view_mut.len(), length as usize);
349 assert_eq!(view_mut.capacity(), capacity);
350 assert_eq!(*view_mut, items[..]);
351 }
352
353 #[test]
354 fn test_unpack_success_with_padding() {
355 let padding = ListView::<u64, PodU32>::header_padding().unwrap();
357 assert_eq!(padding, 4);
358
359 let length: u32 = 2;
360 let capacity: usize = 2;
361 let item_size = size_of::<u64>();
362 let len_size = size_of::<PodU32>();
363 let buf_size = len_size + padding + capacity * item_size;
364 let mut buf = vec![0u8; buf_size];
365
366 let pod_len: PodU32 = length.into();
367 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
368
369 let data_start = len_size + padding;
371 let items = [100u64, 200u64];
372 let items_bytes = bytemuck::cast_slice(&items);
373 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
374
375 let view_ro = ListView::<u64, PodU32>::unpack(&buf).unwrap();
376 assert_eq!(view_ro.len(), length as usize);
377 assert_eq!(view_ro.capacity(), capacity);
378 assert_eq!(*view_ro, items[..]);
379
380 let view_mut = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap();
381 assert_eq!(view_mut.len(), length as usize);
382 assert_eq!(view_mut.capacity(), capacity);
383 assert_eq!(*view_mut, items[..]);
384 }
385
386 #[test]
387 fn test_unpack_success_zero_length() {
388 let capacity: usize = 5;
389 let item_size = size_of::<u32>();
390 let len_size = size_of::<PodU32>();
391 let buf_size = len_size + capacity * item_size;
392 let mut buf = vec![0u8; buf_size];
393
394 let pod_len: PodU32 = 0u32.into();
395 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
396
397 let view_ro = ListView::<u32, PodU32>::unpack(&buf).unwrap();
398 assert_eq!(view_ro.len(), 0);
399 assert_eq!(view_ro.capacity(), capacity);
400 assert!(view_ro.is_empty());
401 assert_eq!(&*view_ro, &[] as &[u32]);
402
403 let view_mut = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap();
404 assert_eq!(view_mut.len(), 0);
405 assert_eq!(view_mut.capacity(), capacity);
406 assert!(view_mut.is_empty());
407 assert_eq!(&*view_mut, &[] as &[u32]);
408 }
409
410 #[test]
411 fn test_unpack_success_full_capacity() {
412 let length: u64 = 3;
413 let capacity: usize = 3;
414 let item_size = size_of::<u64>();
415 let len_size = size_of::<PodU64>();
416 let buf_size = len_size + capacity * item_size;
417 let mut buf = vec![0u8; buf_size];
418
419 let pod_len: PodU64 = length.into();
420 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
421
422 let data_start = len_size;
423 let items = [1u64, 2u64, 3u64];
424 let items_bytes = bytemuck::cast_slice(&items);
425 buf[data_start..].copy_from_slice(items_bytes);
426
427 let view_ro = ListView::<u64>::unpack(&buf).unwrap();
428 assert_eq!(view_ro.len(), length as usize);
429 assert_eq!(view_ro.capacity(), capacity);
430 assert_eq!(*view_ro, items[..]);
431
432 let view_mut = ListView::<u64>::unpack_mut(&mut buf).unwrap();
433 assert_eq!(view_mut.len(), length as usize);
434 assert_eq!(view_mut.capacity(), capacity);
435 assert_eq!(*view_mut, items[..]);
436 }
437
438 #[test]
439 fn test_unpack_fail_buffer_too_small_for_header() {
440 let header_size = ListView::<u64, PodU32>::size_of(0).unwrap();
442 assert_eq!(header_size, 8);
443
444 let mut buf = vec![0u8; header_size - 1]; let err = ListView::<u64, PodU32>::unpack(&buf).unwrap_err();
448 assert_eq!(err, PodSliceError::BufferTooSmall.into());
449
450 let err = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap_err();
451 assert_eq!(err, PodSliceError::BufferTooSmall.into());
452 }
453
454 #[test]
455 fn test_unpack_fail_declared_length_exceeds_capacity() {
456 let declared_length: u32 = 4;
457 let capacity: usize = 3; let item_size = size_of::<u32>();
459 let len_size = size_of::<PodU32>();
460 let buf_size = len_size + capacity * item_size;
461 let mut buf = vec![0u8; buf_size];
462
463 let pod_len: PodU32 = declared_length.into();
465 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
466
467 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
468 assert_eq!(err, PodSliceError::BufferTooSmall.into());
469
470 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
471 assert_eq!(err, PodSliceError::BufferTooSmall.into());
472 }
473
474 #[test]
475 fn test_unpack_fail_data_part_not_multiple_of_item_size() {
476 let len_size = size_of::<PodU32>();
477
478 let buf_size = len_size + 5;
480 let mut buf = vec![0u8; buf_size];
481
482 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
485 assert_eq!(err, ProgramError::InvalidArgument);
486
487 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
488 assert_eq!(err, ProgramError::InvalidArgument);
489 }
490
491 #[test]
492 fn test_unpack_empty_buffer() {
493 let mut buf = [];
494 let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
495 assert_eq!(err, PodSliceError::BufferTooSmall.into());
496
497 let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
498 assert_eq!(err, PodSliceError::BufferTooSmall.into());
499 }
500
501 #[test]
502 fn test_init_success_no_padding() {
503 let capacity: usize = 5;
505 let len_size = size_of::<PodU32>();
506 let buf_size = ListView::<u32, PodU32>::size_of(capacity).unwrap();
507 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u32, PodU32>::init(&mut buf).unwrap();
510
511 assert_eq!(view.len(), 0);
512 assert_eq!(view.capacity(), capacity);
513 assert!(view.is_empty());
514
515 let length_bytes = &buf[0..len_size];
517 assert_eq!(length_bytes, &[0u8; 4]);
518 }
519
520 #[test]
521 fn test_init_success_with_padding() {
522 let capacity: usize = 3;
524 let len_size = size_of::<PodU32>();
525 let buf_size = ListView::<u64, PodU32>::size_of(capacity).unwrap();
526 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u64, PodU32>::init(&mut buf).unwrap();
529
530 assert_eq!(view.len(), 0);
531 assert_eq!(view.capacity(), capacity);
532 assert!(view.is_empty());
533
534 let length_bytes = &buf[0..len_size];
536 assert_eq!(length_bytes, &[0u8; 4]);
537 }
539
540 #[test]
541 fn test_init_success_zero_capacity() {
542 let buf_size = ListView::<u64, PodU32>::size_of(0).unwrap();
545 assert_eq!(buf_size, 8);
546 let mut buf = vec![0xFFu8; buf_size];
547
548 let view = ListView::<u64, PodU32>::init(&mut buf).unwrap();
549
550 assert_eq!(view.len(), 0);
551 assert_eq!(view.capacity(), 0);
552 assert!(view.is_empty());
553
554 let len_size = size_of::<PodU32>();
556 let length_bytes = &buf[0..len_size];
557 assert_eq!(length_bytes, &[0u8; 4]);
558 }
559
560 #[test]
561 fn test_init_fail_buffer_too_small() {
562 let mut buf = vec![0u8; 3];
564 let err = ListView::<u32, PodU32>::init(&mut buf).unwrap_err();
565 assert_eq!(err, PodSliceError::BufferTooSmall.into());
566
567 let mut buf_padded = vec![0u8; 7];
569 let err_padded = ListView::<u64, PodU32>::init(&mut buf_padded).unwrap_err();
570 assert_eq!(err_padded, PodSliceError::BufferTooSmall.into());
571 }
572
573 #[test]
574 fn test_init_success_default_length_type() {
575 let capacity = 5;
578 let len_size = size_of::<PodU32>(); let buf_size = ListView::<u32>::size_of(capacity).unwrap();
580 let mut buf = vec![0xFFu8; buf_size]; let view = ListView::<u32>::init(&mut buf).unwrap();
583
584 assert_eq!(view.len(), 0);
585 assert_eq!(view.capacity(), capacity);
586 assert!(view.is_empty());
587
588 let length_bytes = &buf[0..len_size];
590 assert_eq!(length_bytes, &[0u8; 4]);
591 }
592
593 macro_rules! test_list_view_for_length_type {
594 ($test_name:ident, $LengthType:ty) => {
595 #[test]
596 fn $test_name() {
597 type T = u64;
598
599 let padding = ListView::<T, $LengthType>::header_padding().unwrap();
600 let length_usize = 2usize;
601 let capacity = 3;
602
603 let item_size = size_of::<T>();
604 let len_size = size_of::<$LengthType>();
605 let buf_size = len_size + padding + capacity * item_size;
606 let mut buf = vec![0u8; buf_size];
607
608 let pod_len = <$LengthType>::try_from(length_usize).unwrap();
610 buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));
611
612 let data_start = len_size + padding;
614 let items = [1000 as T, 2000 as T];
615 let items_bytes = bytemuck::cast_slice(&items);
616 buf[data_start..(data_start + items_bytes.len())].copy_from_slice(items_bytes);
617
618 let view_ro = ListView::<T, $LengthType>::unpack(&buf).unwrap();
620 assert_eq!(view_ro.len(), length_usize);
621 assert_eq!(view_ro.capacity(), capacity);
622 assert_eq!(*view_ro, items[..]);
623
624 let mut buf_mut = buf.clone();
626 let view_mut = ListView::<T, $LengthType>::unpack_mut(&mut buf_mut).unwrap();
627 assert_eq!(view_mut.len(), length_usize);
628 assert_eq!(view_mut.capacity(), capacity);
629 assert_eq!(*view_mut, items[..]);
630
631 let mut init_buf = vec![0xFFu8; buf_size];
633 let init_view = ListView::<T, $LengthType>::init(&mut init_buf).unwrap();
634 assert_eq!(init_view.len(), 0);
635 assert_eq!(init_view.capacity(), capacity);
636 assert_eq!(<$LengthType>::try_from(0usize).unwrap(), *init_view.length);
637 }
638 };
639 }
640
641 test_list_view_for_length_type!(list_view_with_pod_u16, PodU16);
642 test_list_view_for_length_type!(list_view_with_pod_u32, PodU32);
643 test_list_view_for_length_type!(list_view_with_pod_u64, PodU64);
644 test_list_view_for_length_type!(list_view_with_pod_u128, PodU128);
645}