Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions list-view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ edition = "2021"

[dependencies]
bytemuck = "1.25.0"
num-derive = "0.4.2"
num_enum = "0.7.5"
num-traits = "0.2.19"
solana-program-error = "3.0.0"
spl-pod = { version = "0.7.2", path = "../pod" }
thiserror = "2.0.18"

[dev-dependencies]
bytemuck_derive = "1.10.2"
Expand Down
24 changes: 10 additions & 14 deletions pod/src/error.rs → list-view/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use {
solana_program_error::{ProgramError, ToStr},
};

/// Errors that may be returned by the spl-pod library.
/// Errors that may be returned by the spl-list-view library.
#[repr(u32)]
#[derive(
Debug,
Expand All @@ -15,40 +15,36 @@ use {
num_enum::TryFromPrimitive,
num_derive::FromPrimitive,
)]
pub enum PodSliceError {
pub enum ListViewError {
/// Error in checked math operation
#[error("Error in checked math operation")]
CalculationFailure,
/// Provided byte buffer too small for expected type
#[error("Provided byte buffer too small for expected type")]
BufferTooSmall,
/// Provided byte buffer too large for expected type
#[error("Provided byte buffer too large for expected type")]
BufferTooLarge,
/// An integer conversion failed because the value was out of range for the target type
#[error("An integer conversion failed because the value was out of range for the target type")]
ValueOutOfRange,
}

impl From<PodSliceError> for ProgramError {
fn from(e: PodSliceError) -> Self {
impl From<ListViewError> for ProgramError {
fn from(e: ListViewError) -> Self {
ProgramError::Custom(e as u32)
}
}

impl ToStr for PodSliceError {
impl ToStr for ListViewError {
fn to_str(&self) -> &'static str {
match self {
PodSliceError::CalculationFailure => "Error in checked math operation",
PodSliceError::BufferTooSmall => "Provided byte buffer too small for expected type",
PodSliceError::BufferTooLarge => "Provided byte buffer too large for expected type",
PodSliceError::ValueOutOfRange => "An integer conversion failed because the value was out of range for the target type"
ListViewError::CalculationFailure => "Error in checked math operation",
ListViewError::BufferTooSmall => "Provided byte buffer too small for expected type",
ListViewError::ValueOutOfRange => "An integer conversion failed because the value was out of range for the target type"
}
}
}

impl From<TryFromIntError> for PodSliceError {
impl From<TryFromIntError> for ListViewError {
fn from(_: TryFromIntError) -> Self {
PodSliceError::ValueOutOfRange
ListViewError::ValueOutOfRange
}
}
6 changes: 4 additions & 2 deletions list-view/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
mod error;
mod list_trait;
mod list_view;
mod list_view_mut;
mod list_view_read_only;
mod pod_length;

pub use {
list_trait::List, list_view::ListView, list_view_mut::ListViewMut,
list_view_read_only::ListViewReadOnly,
error::ListViewError, list_trait::List, list_view::ListView, list_view_mut::ListViewMut,
list_view_read_only::ListViewReadOnly, pod_length::PodLength,
};
6 changes: 4 additions & 2 deletions list-view/src/list_trait.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use {
crate::ListView, bytemuck::Pod, core::ops::Deref, solana_program_error::ProgramError,
spl_pod::pod_length::PodLength,
crate::{pod_length::PodLength, ListView},
bytemuck::Pod,
core::ops::Deref,
solana_program_error::ProgramError,
};

/// A trait to abstract the shared, read-only behavior
Expand Down
39 changes: 20 additions & 19 deletions list-view/src/list_view.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
//! `ListView`, a compact, zero-copy array wrapper.

use {
crate::{list_view_mut::ListViewMut, list_view_read_only::ListViewReadOnly},
crate::{
error::ListViewError, list_view_mut::ListViewMut, list_view_read_only::ListViewReadOnly,
pod_length::PodLength,
},
bytemuck::Pod,
core::{
marker::PhantomData,
Expand All @@ -13,8 +16,6 @@ use {
bytemuck::{
pod_from_bytes, pod_from_bytes_mut, pod_slice_from_bytes, pod_slice_from_bytes_mut,
},
error::PodSliceError,
pod_length::PodLength,
primitives::PodU32,
},
};
Expand Down Expand Up @@ -57,7 +58,7 @@ impl<T: Pod, L: PodLength> ListView<T, L> {
.checked_mul(num_items)
.and_then(|curr| curr.checked_add(size_of::<L>()))
.and_then(|curr| curr.checked_add(header_padding))
.ok_or_else(|| PodSliceError::CalculationFailure.into())
.ok_or_else(|| ListViewError::CalculationFailure.into())
}

/// Unpack a read-only buffer into a `ListViewReadOnly`
Expand All @@ -79,7 +80,7 @@ impl<T: Pod, L: PodLength> ListView<T, L> {
let capacity = data.len();

if (*length).into() > capacity {
return Err(PodSliceError::BufferTooSmall.into());
return Err(ListViewError::BufferTooSmall.into());
}

Ok(ListViewReadOnly {
Expand All @@ -93,15 +94,15 @@ impl<T: Pod, L: PodLength> ListView<T, L> {
pub fn unpack_mut(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
let view = Self::build_mut_view(buf)?;
if (*view.length).into() > view.capacity {
return Err(PodSliceError::BufferTooSmall.into());
return Err(ListViewError::BufferTooSmall.into());
}
Ok(view)
}

/// Initialize a buffer: sets `length = 0` and returns a mutable `ListViewMut`.
pub fn init(buf: &mut [u8]) -> Result<ListViewMut<T, L>, ProgramError> {
let view = Self::build_mut_view(buf)?;
*view.length = L::try_from(0)?;
*view.length = L::try_from(0usize).map_err(ListViewError::from)?;
Ok(view)
}

Expand Down Expand Up @@ -139,7 +140,7 @@ impl<T: Pod, L: PodLength> ListView<T, L> {
let data_start = len_field_end.saturating_add(header_padding);

if buf_len < data_start {
return Err(PodSliceError::BufferTooSmall.into());
return Err(ListViewError::BufferTooSmall.into());
}

Ok(Layout {
Expand Down Expand Up @@ -238,12 +239,12 @@ mod tests {
// Case 1: Multiplication overflows.
// `size_of::<u16>() * usize::MAX` will overflow.
let err = ListView::<u16, PodU32>::size_of(usize::MAX).unwrap_err();
assert_eq!(err, PodSliceError::CalculationFailure.into());
assert_eq!(err, ListViewError::CalculationFailure.into());

// Case 2: Multiplication does not overflow, but subsequent addition does.
// `size_of::<u8>() * usize::MAX` does not overflow, but adding `size_of<L>` will.
let err = ListView::<u8, PodU32>::size_of(usize::MAX).unwrap_err();
assert_eq!(err, PodSliceError::CalculationFailure.into());
assert_eq!(err, ListViewError::CalculationFailure.into());
}

#[test]
Expand All @@ -260,7 +261,7 @@ mod tests {
}
}
impl TryFrom<usize> for TestPodU32 {
type Error = PodSliceError;
type Error = core::num::TryFromIntError;
fn try_from(val: usize) -> Result<Self, Self::Error> {
Ok(Self(u32::try_from(val)?))
}
Expand Down Expand Up @@ -443,10 +444,10 @@ mod tests {
let mut buf = vec![0u8; header_size - 1]; // 7 bytes

let err = ListView::<u64, PodU32>::unpack(&buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());

let err = ListView::<u64, PodU32>::unpack_mut(&mut buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());
}

#[test]
Expand All @@ -463,10 +464,10 @@ mod tests {
buf[0..len_size].copy_from_slice(bytemuck::bytes_of(&pod_len));

let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());

let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());
}

#[test]
Expand All @@ -490,10 +491,10 @@ mod tests {
fn test_unpack_empty_buffer() {
let mut buf = [];
let err = ListView::<u32, PodU32>::unpack(&buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());

let err = ListView::<u32, PodU32>::unpack_mut(&mut buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());
}

#[test]
Expand Down Expand Up @@ -560,12 +561,12 @@ mod tests {
// Header requires 4 bytes (size_of<PodU32>)
let mut buf = vec![0u8; 3];
let err = ListView::<u32, PodU32>::init(&mut buf).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());

// With padding, header requires 8 bytes (4 for len, 4 for pad)
let mut buf_padded = vec![0u8; 7];
let err_padded = ListView::<u64, PodU32>::init(&mut buf_padded).unwrap_err();
assert_eq!(err_padded, PodSliceError::BufferTooSmall.into());
assert_eq!(err_padded, ListViewError::BufferTooSmall.into());
}

#[test]
Expand Down
14 changes: 7 additions & 7 deletions list-view/src/list_view_mut.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! `ListViewMut`, a mutable, compact, zero-copy array wrapper.

use {
crate::list_trait::List,
crate::{error::ListViewError, list_trait::List, pod_length::PodLength},
bytemuck::Pod,
core::ops::{Deref, DerefMut},
solana_program_error::ProgramError,
spl_pod::{error::PodSliceError, pod_length::PodLength, primitives::PodU32},
spl_pod::primitives::PodU32,
};

#[derive(Debug)]
Expand All @@ -20,10 +20,10 @@ impl<T: Pod, L: PodLength> ListViewMut<'_, T, L> {
pub fn push(&mut self, item: T) -> Result<(), ProgramError> {
let length = (*self.length).into();
if length >= self.capacity {
Err(PodSliceError::BufferTooSmall.into())
Err(ListViewError::BufferTooSmall.into())
} else {
self.data[length] = item;
*self.length = L::try_from(length.saturating_add(1))?;
*self.length = L::try_from(length.saturating_add(1)).map_err(ListViewError::from)?;
Ok(())
}
}
Expand All @@ -46,7 +46,7 @@ impl<T: Pod, L: PodLength> ListViewMut<'_, T, L> {

// Store the new length (len - 1)
let new_len = len.checked_sub(1).unwrap();
*self.length = L::try_from(new_len)?;
*self.length = L::try_from(new_len).map_err(ListViewError::from)?;

Ok(removed_item)
}
Expand Down Expand Up @@ -144,7 +144,7 @@ mod tests {
// Try to push beyond capacity
let item4 = TestStruct::new(4, 40);
let err = view.push(item4).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());

// Ensure state is unchanged
assert_eq!(view.len(), 3);
Expand Down Expand Up @@ -277,7 +277,7 @@ mod tests {
assert!(view.is_empty());

let err = view.push(TestStruct::new(1, 1)).unwrap_err();
assert_eq!(err, PodSliceError::BufferTooSmall.into());
assert_eq!(err, ListViewError::BufferTooSmall.into());

let err = view.remove(0).unwrap_err();
assert_eq!(err, ProgramError::InvalidArgument);
Expand Down
9 changes: 3 additions & 6 deletions list-view/src/list_view_read_only.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! `ListViewReadOnly`, a read-only, compact, zero-copy array wrapper.

use {
crate::list_trait::List,
crate::{list_trait::List, pod_length::PodLength},
bytemuck::Pod,
core::ops::Deref,
spl_pod::{pod_length::PodLength, primitives::PodU32},
spl_pod::primitives::PodU32,
};

#[derive(Debug)]
Expand Down Expand Up @@ -39,10 +39,7 @@ mod tests {
crate::ListView,
bytemuck_derive::{Pod as DerivePod, Zeroable},
core::mem::size_of,
spl_pod::{
pod_length::PodLength,
primitives::{PodU32, PodU64},
},
spl_pod::primitives::{PodU32, PodU64},
};

#[repr(C, align(16))]
Expand Down
8 changes: 8 additions & 0 deletions list-view/src/pod_length.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use {bytemuck::Pod, core::num::TryFromIntError};

/// Marker trait for converting to/from Pod `uint`'s and `usize`
pub trait PodLength: Pod + TryFrom<usize, Error = TryFromIntError> + Into<usize> {}

/// Blanket implementation to automatically implement `PodLength` for any type
/// that satisfies the required bounds.
impl<T> PodLength for T where T: Pod + TryFrom<usize, Error = TryFromIntError> + Into<usize> {}
Comment on lines +3 to +8
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Migrated from pod/src/pod_length.rs

4 changes: 0 additions & 4 deletions pod/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,10 @@ wincode = ["dep:wincode"]
borsh = { version = "1.5.7", features = ["derive", "unstable__schema"], optional = true }
bytemuck = { version = "1.23.2" }
bytemuck_derive = { version = "1.10.1" }
num-derive = "0.4"
num_enum = "0.7"
num-traits = "0.2"
serde = { version = "1.0.228", optional = true, features = ["derive"] }
solana-address = { version = "2.2.0", features = ["bytemuck"] }
solana-program-error = "3.0.0"
solana-program-option = "3.0.0"
thiserror = "2.0"
wincode = { version = "0.4.4", features = ["derive"], optional = true }

[dev-dependencies]
Expand Down
Loading
Loading