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
7 changes: 7 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions pod/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ bytemuck_derive = { version = "1.10.1" }
num-derive = "0.4"
num_enum = "0.7"
num-traits = "0.2"
pinocchio = "0.9.2"
serde = { version = "1.0.228", optional = true }
solana-program-error = "3.0.0"
solana-program-option = "3.0.0"
Expand Down
24 changes: 11 additions & 13 deletions pod/src/bytemuck.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! wrappers for `bytemuck` functions

use {bytemuck::Pod, solana_program_error::ProgramError};
use {crate::error::SplPodError, bytemuck::Pod};

/// On-chain size of a `Pod` type
pub const fn pod_get_packed_len<T: Pod>() -> usize {
Expand All @@ -13,38 +13,36 @@ pub fn pod_bytes_of<T: Pod>(t: &T) -> &[u8] {
}

/// Convert a slice of bytes into a `Pod` (zero copy)
pub fn pod_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&T, ProgramError> {
bytemuck::try_from_bytes(bytes).map_err(|_| ProgramError::InvalidArgument)
pub fn pod_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&T, SplPodError> {
Ok(bytemuck::try_from_bytes(bytes)?)
}

/// Maybe convert a slice of bytes into a `Pod` (zero copy)
///
/// Returns `None` if the slice is empty, or else `Err` if input length is not
/// equal to `pod_get_packed_len::<T>()`.
/// This function exists primarily because `Option<T>` is not a `Pod`.
pub fn pod_maybe_from_bytes<T: Pod>(bytes: &[u8]) -> Result<Option<&T>, ProgramError> {
pub fn pod_maybe_from_bytes<T: Pod>(bytes: &[u8]) -> Result<Option<&T>, SplPodError> {
if bytes.is_empty() {
Ok(None)
} else {
bytemuck::try_from_bytes(bytes)
.map(Some)
.map_err(|_| ProgramError::InvalidArgument)
Ok(bytemuck::try_from_bytes(bytes).map(Some)?)
}
}

/// Convert a slice of bytes into a mutable `Pod` (zero copy)
pub fn pod_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut T, ProgramError> {
bytemuck::try_from_bytes_mut(bytes).map_err(|_| ProgramError::InvalidArgument)
pub fn pod_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut T, SplPodError> {
Ok(bytemuck::try_from_bytes_mut(bytes)?)
}

/// Convert a slice of bytes into a `Pod` slice (zero copy)
pub fn pod_slice_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&[T], ProgramError> {
bytemuck::try_cast_slice(bytes).map_err(|_| ProgramError::InvalidArgument)
pub fn pod_slice_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&[T], SplPodError> {
Ok(bytemuck::try_cast_slice(bytes)?)
}

/// Convert a slice of bytes into a mutable `Pod` slice (zero copy)
pub fn pod_slice_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut [T], ProgramError> {
bytemuck::try_cast_slice_mut(bytes).map_err(|_| ProgramError::InvalidArgument)
pub fn pod_slice_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut [T], SplPodError> {
Ok(bytemuck::try_cast_slice_mut(bytes)?)
}

/// Convert a `Pod` slice into a single slice of bytes
Expand Down
82 changes: 72 additions & 10 deletions pod/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Error types
use pinocchio::program_error::ProgramError as PinocchioProgramError;
use {
bytemuck::PodCastError,
solana_program_error::{ProgramError, ToStr},
std::num::TryFromIntError,
};
Expand All @@ -15,7 +17,7 @@ use {
num_enum::TryFromPrimitive,
num_derive::FromPrimitive,
)]
pub enum PodSliceError {
pub enum SplPodError {
/// Error in checked math operation
#[error("Error in checked math operation")]
CalculationFailure,
Expand All @@ -25,30 +27,90 @@ pub enum PodSliceError {
/// Provided byte buffer too large for expected type
#[error("Provided byte buffer too large for expected type")]
BufferTooLarge,
/// Index out of range for list operation
#[error("Index out of range for list operation")]
IndexOutOfRange,
/// Type used as a length prefix has invalid alignment
#[error("Type used as a length prefix has invalid alignment")]
InvalidLengthTypeAlignment,
/// A `PodCast` operation from `bytemuck` failed
#[error("A `PodCast` operation from `bytemuck` failed")]
PodCast,
/// 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<SplPodError> for ProgramError {
fn from(e: SplPodError) -> Self {
ProgramError::Custom(e as u32)
}
}

impl ToStr for PodSliceError {
impl ToStr for SplPodError {
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"
SplPodError::CalculationFailure => "Error in checked math operation",
SplPodError::BufferTooSmall => "Provided byte buffer too small for expected type",
SplPodError::BufferTooLarge => "Provided byte buffer too large for expected type",
SplPodError::IndexOutOfRange => "Index out of range for list operation",
SplPodError::InvalidLengthTypeAlignment => "Type used as a length prefix has invalid alignment",
SplPodError::PodCast => "A `PodCast` operation from `bytemuck` failed",
SplPodError::ValueOutOfRange => "An integer conversion failed because the value was out of range for the target type",
}
}
}

impl From<TryFromIntError> for PodSliceError {
impl From<PodCastError> for SplPodError {
fn from(_: PodCastError) -> Self {
SplPodError::PodCast
}
}

impl From<TryFromIntError> for SplPodError {
fn from(_: TryFromIntError) -> Self {
PodSliceError::ValueOutOfRange
SplPodError::ValueOutOfRange
}
}

impl From<SplPodError> for PinocchioProgramError {
fn from(e: SplPodError) -> Self {
PinocchioProgramError::Custom(e as u32)
}
}

#[cfg(test)]
mod test {
use super::*;
use crate::list::ListView;
use pinocchio::program_error::ProgramError as PinocchioProgramError;

fn raises_solana_err() -> Result<(), ProgramError> {
ListView::<u8>::size_of(usize::MAX)?; // raises err
Ok(())
}

fn raises_pino_err() -> Result<(), PinocchioProgramError> {
ListView::<u8>::size_of(usize::MAX)?; // raises err
Ok(())
}

#[test]
fn test_from_pod_slice_error_for_solana_program_error() {
let result = raises_solana_err();
assert!(result.is_err());
let solana_err = result.unwrap_err();
let expected_err: ProgramError = SplPodError::CalculationFailure.into();
assert_eq!(solana_err, expected_err);
}

#[test]
fn test_from_pod_slice_error_for_pinocchio_program_error() {
let result = raises_pino_err();
assert!(result.is_err());
let pinocchio_err = result.unwrap_err();
let expected_solana_err: ProgramError = SplPodError::CalculationFailure.into();
let expected_pinocchio_err: PinocchioProgramError = u64::from(expected_solana_err).into();
assert_eq!(pinocchio_err, expected_pinocchio_err);
}
}
7 changes: 3 additions & 4 deletions pod/src/list/list_trait.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use {
crate::{list::ListView, pod_length::PodLength},
crate::{error::SplPodError, list::ListView, pod_length::PodLength},
bytemuck::Pod,
solana_program_error::ProgramError,
std::ops::Deref,
};

Expand All @@ -17,12 +16,12 @@ pub trait List: Deref<Target = [Self::Item]> {
fn capacity(&self) -> usize;

/// Returns the number of **bytes currently occupied** by the live elements
fn bytes_used(&self) -> Result<usize, ProgramError> {
fn bytes_used(&self) -> Result<usize, SplPodError> {
ListView::<Self::Item, Self::Length>::size_of(self.len())
}

/// Returns the number of **bytes reserved** by the entire backing buffer.
fn bytes_allocated(&self) -> Result<usize, ProgramError> {
fn bytes_allocated(&self) -> Result<usize, SplPodError> {
ListView::<Self::Item, Self::Length>::size_of(self.capacity())
}
}
Loading