diff --git a/Cargo.lock b/Cargo.lock index cdec3eb0..89197310 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -834,9 +834,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.44" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] @@ -1538,8 +1538,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94f52dd8f733a13f6a18e55de83cf97c4c3f5fdf27ea3830bcff0b35313efcc2" dependencies = [ + "borsh", "bytemuck", "bytemuck_derive", + "serde", + "serde_derive", + "wincode", ] [[package]] @@ -1691,17 +1695,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "spl-list-view" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ff25a803cee203606fc29a5cac537b9b78b5c4bee107579efc0a678a53c4e9f" -dependencies = [ - "bytemuck", - "solana-program-error", - "spl-pod 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "spl-pod" version = "0.7.2" @@ -1718,6 +1711,7 @@ dependencies = [ "solana-program-error", "solana-program-option", "solana-pubkey", + "solana-zero-copy", "solana-zk-sdk", "spl-pod 0.7.2", "test-case", @@ -1757,22 +1751,7 @@ dependencies = [ "solana-program-error", "solana-sha256-hasher", "solana-sysvar", - "spl-program-error-derive 0.6.0", - "thiserror 2.0.18", -] - -[[package]] -name = "spl-program-error" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c4f6cf26cb6768110bf024bc7224326c720d711f7ad25d16f40f6cee40edb2d" -dependencies = [ - "num-derive", - "num-traits", - "num_enum", - "solana-msg", - "solana-program-error", - "spl-program-error-derive 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "spl-program-error-derive", "thiserror 2.0.18", ] @@ -1786,18 +1765,6 @@ dependencies = [ "syn", ] -[[package]] -name = "spl-program-error-derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec8965aa4dc6c74701cbb48b9cad5af35b9a394514934949edbb357b78f840d" -dependencies = [ - "proc-macro2", - "quote", - "sha2", - "syn", -] - [[package]] name = "spl-tlv-account-resolution" version = "0.11.1" @@ -1813,11 +1780,10 @@ dependencies = [ "solana-instruction", "solana-program-error", "solana-pubkey", - "spl-discriminator 0.5.1", - "spl-list-view 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-pod 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-program-error 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spl-type-length-value 0.9.0", + "spl-discriminator 0.5.2", + "spl-list-view", + "spl-pod 0.7.2", + "spl-type-length-value 0.9.1", "thiserror 2.0.18", "tokio", ] @@ -2258,9 +2224,9 @@ dependencies = [ [[package]] name = "wincode" -version = "0.4.4" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "466e67917609b2d40a838a5b972d1a6237c9749600cb8de8f65559b90d48485b" +checksum = "657690780ce23e6f66576a782ffd88eb353512381817029cc1d7a99154bb6d1f" dependencies = [ "pastey", "proc-macro2", @@ -2271,9 +2237,9 @@ dependencies = [ [[package]] name = "wincode-derive" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26a7a568eda854acc9945ed136a9d50b8c6d31911584624958808ae96eee3912" +checksum = "fca057fc9a13dd19cdb64ef558635d43c42667c0afa1ae7915ea1fa66993fd1a" dependencies = [ "darling", "proc-macro2", diff --git a/pod/Cargo.toml b/pod/Cargo.toml index cdf0c7ea..cbc87346 100644 --- a/pod/Cargo.toml +++ b/pod/Cargo.toml @@ -8,9 +8,9 @@ license = "Apache-2.0" edition = "2021" [features] -serde-traits = ["dep:serde"] -borsh = ["dep:borsh", "solana-pubkey/borsh"] -wincode = ["dep:wincode"] +serde-traits = ["dep:serde", "solana-zero-copy/serde"] +borsh = ["dep:borsh", "solana-pubkey/borsh", "solana-zero-copy/borsh"] +wincode = ["dep:wincode", "solana-zero-copy/wincode"] [dependencies] borsh = { version = "1.5.7", features = ["derive", "unstable__schema"], optional = true } @@ -24,6 +24,7 @@ wincode = { version = "0.4.4", features = ["derive"], optional = true } solana-program-error = "3.0.0" solana-program-option = "3.0.0" solana-pubkey = "3.0.0" +solana-zero-copy = { version = "1.0.0", features = ["bytemuck"] } solana-zk-sdk = "4.0.0" thiserror = "2.0" diff --git a/pod/src/lib.rs b/pod/src/lib.rs index b9a26da1..80ee01ad 100644 --- a/pod/src/lib.rs +++ b/pod/src/lib.rs @@ -9,6 +9,9 @@ pub mod pod_length; pub mod primitives; pub mod slice; +// Re-export the conversion macro (replaces the old #[macro_export] definition) +pub use solana_zero_copy::impl_int_conversion; + // Export current sdk types for downstream users building with a different sdk // version pub use {solana_program_error, solana_program_option, solana_pubkey}; diff --git a/pod/src/list/list_view.rs b/pod/src/list/list_view.rs index 23a64391..bbbbbcbb 100644 --- a/pod/src/list/list_view.rs +++ b/pod/src/list/list_view.rs @@ -98,13 +98,6 @@ impl ListView { Ok(view) } - /// Initialize a buffer: sets `length = 0` and returns a mutable `ListViewMut`. - pub fn init(buf: &mut [u8]) -> Result, ProgramError> { - let view = Self::build_mut_view(buf)?; - *view.length = L::try_from(0)?; - Ok(view) - } - /// Internal helper to build a mutable view without validation or initialization. #[inline] fn build_mut_view(buf: &mut [u8]) -> Result, ProgramError> { @@ -181,13 +174,28 @@ impl ListView { } } +impl ListView +where + L: PodLength, + PodSliceError: From<>::Error>, +{ + /// Initialize a buffer: sets `length = 0` and returns a mutable `ListViewMut`. + pub fn init(buf: &mut [u8]) -> Result, ProgramError> { + let view = Self::build_mut_view(buf)?; + *view.length = L::try_from(0).map_err(PodSliceError::from)?; + Ok(view) + } +} + #[cfg(test)] mod tests { + #[cfg(not(target_arch = "bpf"))] + use crate::primitives::PodU128; use { super::*, crate::{ list::List, - primitives::{PodU128, PodU16, PodU32, PodU64}, + primitives::{PodU16, PodU32, PodU64}, }, bytemuck_derive::{Pod as DerivePod, Zeroable}, }; @@ -641,5 +649,6 @@ mod tests { test_list_view_for_length_type!(list_view_with_pod_u16, PodU16); test_list_view_for_length_type!(list_view_with_pod_u32, PodU32); test_list_view_for_length_type!(list_view_with_pod_u64, PodU64); + #[cfg(not(target_arch = "bpf"))] test_list_view_for_length_type!(list_view_with_pod_u128, PodU128); } diff --git a/pod/src/list/list_view_mut.rs b/pod/src/list/list_view_mut.rs index 4f0ca49f..bb0993e1 100644 --- a/pod/src/list/list_view_mut.rs +++ b/pod/src/list/list_view_mut.rs @@ -16,7 +16,11 @@ pub struct ListViewMut<'data, T: Pod, L: PodLength = PodU32> { pub(crate) capacity: usize, } -impl ListViewMut<'_, T, L> { +impl ListViewMut<'_, T, L> +where + L: PodLength, + PodSliceError: From<>::Error>, +{ /// Add another item to the slice pub fn push(&mut self, item: T) -> Result<(), ProgramError> { let length = (*self.length).into(); @@ -24,7 +28,7 @@ impl ListViewMut<'_, T, L> { Err(PodSliceError::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(PodSliceError::from)?; Ok(()) } } @@ -47,7 +51,7 @@ impl 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(PodSliceError::from)?; Ok(removed_item) } @@ -110,7 +114,10 @@ mod tests { fn init_view_mut( buffer: &mut Vec, capacity: usize, - ) -> ListViewMut { + ) -> ListViewMut + where + PodSliceError: From<>::Error>, + { let size = ListView::::size_of(capacity).unwrap(); buffer.resize(size, 0); ListView::::init(buffer).unwrap() diff --git a/pod/src/list/list_view_read_only.rs b/pod/src/list/list_view_read_only.rs index 6d44379a..b4de478d 100644 --- a/pod/src/list/list_view_read_only.rs +++ b/pod/src/list/list_view_read_only.rs @@ -53,7 +53,10 @@ mod tests { length: usize, capacity: usize, items: &[T], - ) -> Vec { + ) -> Vec + where + >::Error: std::fmt::Debug, + { let size = ListView::::size_of(capacity).unwrap(); let mut buffer = vec![0u8; size]; diff --git a/pod/src/pod_length.rs b/pod/src/pod_length.rs index 0f46b574..6633863c 100644 --- a/pod/src/pod_length.rs +++ b/pod/src/pod_length.rs @@ -1,41 +1,13 @@ -use { - crate::{ - error::PodSliceError, - primitives::{PodU128, PodU16, PodU32, PodU64}, - }, - bytemuck::Pod, -}; +use {crate::error::PodSliceError, bytemuck::Pod}; /// Marker trait for converting to/from Pod `uint`'s and `usize` -pub trait PodLength: Pod + Into + TryFrom {} +pub trait PodLength: Pod + Into + TryFrom {} /// Blanket implementation to automatically implement `PodLength` for any type /// that satisfies the required bounds. -impl PodLength for T where T: Pod + Into + TryFrom {} - -/// Implements the `TryFrom` and `From for usize` conversions for a Pod integer type -macro_rules! impl_pod_length_for { - ($PodType:ty, $PrimitiveType:ty) => { - impl TryFrom for $PodType { - type Error = PodSliceError; - - fn try_from(val: usize) -> Result { - let primitive_val = <$PrimitiveType>::try_from(val)?; - Ok(primitive_val.into()) - } - } - - impl From<$PodType> for usize { - fn from(pod_val: $PodType) -> Self { - let primitive_val = <$PrimitiveType>::from(pod_val); - Self::try_from(primitive_val) - .expect("value out of range for usize on this platform") - } - } - }; +impl PodLength for T +where + T: Pod + Into + TryFrom, + PodSliceError: From<>::Error>, +{ } - -impl_pod_length_for!(PodU16, u16); -impl_pod_length_for!(PodU32, u32); -impl_pod_length_for!(PodU64, u64); -impl_pod_length_for!(PodU128, u128); diff --git a/pod/src/primitives.rs b/pod/src/primitives.rs index 23953b2f..aab208bc 100644 --- a/pod/src/primitives.rs +++ b/pod/src/primitives.rs @@ -1,147 +1,11 @@ -//! primitive types that can be used in `Pod`s -#[cfg(feature = "borsh")] -use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; -use bytemuck_derive::{Pod, Zeroable}; -#[cfg(feature = "serde-traits")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "wincode")] -use wincode::{SchemaRead, SchemaWrite}; - -/// The standard `bool` is not a `Pod`, define a replacement that is -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "bool", into = "bool"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodBool(pub u8); -impl PodBool { - pub const fn from_bool(b: bool) -> Self { - Self(if b { 1 } else { 0 }) - } -} - -impl From for PodBool { - fn from(b: bool) -> Self { - Self::from_bool(b) - } -} - -impl From<&bool> for PodBool { - fn from(b: &bool) -> Self { - Self(if *b { 1 } else { 0 }) - } -} - -impl From<&PodBool> for bool { - fn from(b: &PodBool) -> Self { - b.0 != 0 - } -} - -impl From for bool { - fn from(b: PodBool) -> Self { - b.0 != 0 - } -} - -/// Simple macro for implementing conversion functions between Pod* integers and -/// standard integers. -/// -/// The standard integer types can cause alignment issues when placed in a `Pod`, -/// so these replacements are usable in all `Pod`s. -#[macro_export] -macro_rules! impl_int_conversion { - ($P:ty, $I:ty) => { - impl $P { - pub const fn from_primitive(n: $I) -> Self { - Self(n.to_le_bytes()) - } - } - impl From<$I> for $P { - fn from(n: $I) -> Self { - Self::from_primitive(n) - } - } - impl From<$P> for $I { - fn from(pod: $P) -> Self { - Self::from_le_bytes(pod.0) - } - } - }; -} - -/// `u16` type that can be used in `Pod`s -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "u16", into = "u16"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU16(pub [u8; 2]); -impl_int_conversion!(PodU16, u16); - -/// `i16` type that can be used in Pods -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "i16", into = "i16"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodI16(pub [u8; 2]); -impl_int_conversion!(PodI16, i16); - -/// `u32` type that can be used in `Pod`s -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr( - feature = "borsh", - derive(BorshDeserialize, BorshSerialize, BorshSchema) -)] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "u32", into = "u32"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU32(pub [u8; 4]); -impl_int_conversion!(PodU32, u32); - -/// `u64` type that can be used in Pods -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr( - feature = "borsh", - derive(BorshDeserialize, BorshSerialize, BorshSchema) -)] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "u64", into = "u64"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU64(pub [u8; 8]); -impl_int_conversion!(PodU64, u64); - -/// `i64` type that can be used in Pods -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "i64", into = "i64"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodI64([u8; 8]); -impl_int_conversion!(PodI64, i64); - -/// `u128` type that can be used in Pods -#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))] -#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))] -#[cfg_attr( - feature = "borsh", - derive(BorshDeserialize, BorshSerialize, BorshSchema) -)] -#[cfg_attr(feature = "serde-traits", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "serde-traits", serde(from = "u128", into = "u128"))] -#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)] -#[repr(transparent)] -pub struct PodU128(pub [u8; 16]); -impl_int_conversion!(PodU128, u128); +//! Primitive types that can be used in `Pod`s. +//! +//! These are re-exported from [`solana_zero_copy::unaligned`]. +#[cfg(not(target_arch = "bpf"))] +pub use solana_zero_copy::unaligned::U128 as PodU128; +pub use solana_zero_copy::unaligned::{ + Bool as PodBool, I16 as PodI16, I64 as PodI64, U16 as PodU16, U32 as PodU32, U64 as PodU64, +}; #[cfg(test)] mod tests { @@ -259,6 +123,7 @@ mod tests { assert_eq!(pod_i64, deserialized); } + #[cfg(not(target_arch = "bpf"))] #[test] fn test_pod_u128() { assert!(pod_from_bytes::(&[]).is_err()); @@ -271,7 +136,7 @@ mod tests { ); } - #[cfg(feature = "serde-traits")] + #[cfg(all(feature = "serde-traits", not(target_arch = "bpf")))] #[test] fn test_pod_u128_serde() { let pod_u128: PodU128 = u128::MAX.into(); @@ -294,6 +159,7 @@ mod tests { #[test_case(PodU32::from_primitive(u32::MAX))] #[test_case(PodU64::from_primitive(u64::MAX))] #[test_case(PodI64::from_primitive(i64::MIN))] + #[cfg(not(target_arch = "bpf"))] #[test_case(PodU128::from_primitive(u128::MAX))] fn wincode_roundtrip< T: PartialEq diff --git a/tlv-account-resolution/Cargo.toml b/tlv-account-resolution/Cargo.toml index 1f3699b9..1cd0981c 100644 --- a/tlv-account-resolution/Cargo.toml +++ b/tlv-account-resolution/Cargo.toml @@ -20,11 +20,10 @@ solana-account-info = "3.0.0" solana-instruction = { version = "3.0.0", features = ["std"] } solana-program-error = "3.0.0" solana-pubkey = { version = "3.0.0", features = ["curve25519"] } -spl-discriminator = "0.5.1" -spl-list-view = "0.1.0" -spl-program-error = "0.8.0" -spl-pod = "0.7.1" -spl-type-length-value = "0.9.0" +spl-discriminator = { version = "0.5.1", path = "../discriminator" } +spl-list-view = { version = "0.1.0", path = "../list-view" } +spl-pod = { version = "0.7.2", path = "../pod" } +spl-type-length-value = { version = "0.9.0", path = "../type-length-value" } thiserror = "2.0" [dev-dependencies]