diff --git a/Cargo.lock b/Cargo.lock index b8424c21a..f9ee09dfe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3347,7 +3347,7 @@ dependencies = [ "num-traits", "rand 0.8.5", "solana-program", - "thiserror 1.0.64", + "thiserror 2.0.11", "zerocopy 0.8.14", ] diff --git a/program-libs/batched-merkle-tree/Cargo.toml b/program-libs/batched-merkle-tree/Cargo.toml index b59fe125f..9d3749db2 100644 --- a/program-libs/batched-merkle-tree/Cargo.toml +++ b/program-libs/batched-merkle-tree/Cargo.toml @@ -14,7 +14,7 @@ solana = [] [dependencies] aligned-sized = { workspace=true} solana-program = { workspace = true } -light-zero-copy = { workspace=true, features = ["solana"] } +light-zero-copy = { workspace=true, features = ["solana", "std"] } light-hasher = { workspace=true, features = ["solana"] } light-utils = { workspace=true } light-bloom-filter = { workspace=true, features = ["solana"] } diff --git a/program-libs/batched-merkle-tree/src/merkle_tree.rs b/program-libs/batched-merkle-tree/src/merkle_tree.rs index 99da3fb07..ef0a30cdd 100644 --- a/program-libs/batched-merkle-tree/src/merkle_tree.rs +++ b/program-libs/batched-merkle-tree/src/merkle_tree.rs @@ -196,7 +196,7 @@ impl<'a> BatchedMerkleTreeAccount<'a> { return Err(MerkleTreeMetadataError::InvalidTreeType.into()); } if account_data_len != metadata.get_account_size()? { - return Err(ZeroCopyError::InvalidAccountSize.into()); + return Err(ZeroCopyError::Size.into()); } let (root_history, account_data) = ZeroCopyCyclicVecU64::from_bytes_at(account_data)?; @@ -254,7 +254,7 @@ impl<'a> BatchedMerkleTreeAccount<'a> { "account.get_account_size(): {}", account_metadata.get_account_size()? ); - return Err(ZeroCopyError::InvalidAccountSize.into()); + return Err(ZeroCopyError::Size.into()); } let (mut root_history, account_data) = ZeroCopyCyclicVecU64::new_at( diff --git a/program-libs/batched-merkle-tree/src/queue.rs b/program-libs/batched-merkle-tree/src/queue.rs index fcaea2091..00e1b18fd 100644 --- a/program-libs/batched-merkle-tree/src/queue.rs +++ b/program-libs/batched-merkle-tree/src/queue.rs @@ -233,7 +233,7 @@ impl<'a> BatchedQueueAccount<'a> { .batch_metadata .queue_account_size(account_metadata.metadata.queue_type)? ); - return Err(ZeroCopyError::InvalidAccountSize.into()); + return Err(ZeroCopyError::Size.into()); } let (batches, value_vecs, bloom_filter_stores, hashchain_store) = init_queue( diff --git a/program-libs/batched-merkle-tree/tests/rollover_state_tree.rs b/program-libs/batched-merkle-tree/tests/rollover_state_tree.rs index e9143512b..d4f723285 100644 --- a/program-libs/batched-merkle-tree/tests/rollover_state_tree.rs +++ b/program-libs/batched-merkle-tree/tests/rollover_state_tree.rs @@ -188,7 +188,7 @@ fn test_rollover() { network_fee: params.network_fee, }; let result = rollover_batched_state_tree(params); - assert_eq!(result, Err(ZeroCopyError::InvalidAccountSize.into())); + assert_eq!(result, Err(ZeroCopyError::Size.into())); } // 4. Failing: invalid queue size { @@ -219,7 +219,7 @@ fn test_rollover() { network_fee: params.network_fee, }; let result = rollover_batched_state_tree(params); - assert_eq!(result, Err(ZeroCopyError::InvalidAccountSize.into())); + assert_eq!(result, Err(ZeroCopyError::Size.into())); } // 5. Functional: rollover address tree { diff --git a/program-libs/zero-copy/Cargo.toml b/program-libs/zero-copy/Cargo.toml index 0babdb85e..78d66d140 100644 --- a/program-libs/zero-copy/Cargo.toml +++ b/program-libs/zero-copy/Cargo.toml @@ -7,15 +7,17 @@ license = "Apache-2.0" edition = "2021" [features] -default = [] +default = ["std"] solana = ["solana-program"] +std = [] [dependencies] solana-program = { workspace = true, optional = true } -thiserror = "1.0" +thiserror = {version="2.0", default-features = false} num-traits = { version = "0.2" } -zerocopy = {version="0.8.14", features=["derive"]} +zerocopy = {version="0.8.14"} [dev-dependencies] rand = "0.8" num-traits.workspace = true +zerocopy = {version="0.8.14", features=["derive"]} diff --git a/program-libs/zero-copy/src/cyclic_vec.rs b/program-libs/zero-copy/src/cyclic_vec.rs index e57105ed0..6082f32e3 100644 --- a/program-libs/zero-copy/src/cyclic_vec.rs +++ b/program-libs/zero-copy/src/cyclic_vec.rs @@ -1,10 +1,11 @@ -use core::fmt; -use std::{ - fmt::Debug, +use core::{ + fmt::{self, Debug}, marker::PhantomData, mem::size_of, ops::{Index, IndexMut}, }; +#[cfg(feature = "std")] +use std::vec::Vec; use zerocopy::Ref; @@ -37,8 +38,7 @@ where pub fn new_at(capacity: L, bytes: &'a mut [u8]) -> Result<(Self, &'a mut [u8]), ZeroCopyError> { let (meta_data, bytes) = bytes.split_at_mut(Self::metadata_size()); - let (current_index, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + let (current_index, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data)?; if u64::from(*current_index) != 0 { return Err(ZeroCopyError::MemoryNotZeroed); } @@ -47,6 +47,7 @@ where Ok((Self { current_index, vec }, bytes)) } + #[cfg(feature = "std")] pub fn new_at_multiple( num: usize, capacity: L, @@ -67,12 +68,12 @@ where pub fn from_bytes_at(bytes: &'a mut [u8]) -> Result<(Self, &'a mut [u8]), ZeroCopyError> { let (meta_data, bytes) = bytes.split_at_mut(Self::metadata_size()); - let (current_index, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + let (current_index, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data)?; let (vec, bytes) = ZeroCopyVec::<'a, L, T, PAD>::from_bytes_at(bytes)?; Ok((Self { current_index, vec }, bytes)) } + #[cfg(feature = "std")] pub fn from_bytes_at_multiple( num: usize, mut bytes: &'a mut [u8], @@ -235,6 +236,7 @@ where self.vec.try_into_array() } + #[cfg(feature = "std")] #[inline] pub fn to_vec(&self) -> Vec { self.vec.to_vec() @@ -325,6 +327,6 @@ where { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.to_vec()) + write!(f, "{:?}", self.as_slice()) } } diff --git a/program-libs/zero-copy/src/errors.rs b/program-libs/zero-copy/src/errors.rs index b89136667..3d89fdc43 100644 --- a/program-libs/zero-copy/src/errors.rs +++ b/program-libs/zero-copy/src/errors.rs @@ -1,3 +1,5 @@ +use core::convert::Infallible; + use thiserror::Error; #[derive(Debug, Error, PartialEq)] @@ -10,16 +12,16 @@ pub enum ZeroCopyError { IterFromOutOfBounds, #[error("Memory allocated {0}, Memory required {0}")] InsufficientMemoryAllocated(usize, usize), - #[error("Invalid Account size.")] - InvalidAccountSize, #[error("Unaligned pointer.")] UnalignedPointer, #[error("Memory not zeroed.")] MemoryNotZeroed, #[error("InvalidConversion.")] InvalidConversion, - #[error("Zero copy cast error {0}")] - CastError(String), + #[error("Invalid data {0}.")] + InvalidData(Infallible), + #[error("Invalid size.")] + Size, } #[cfg(feature = "solana")] @@ -30,11 +32,11 @@ impl From for u32 { ZeroCopyError::ArraySize(_, _) => 15002, ZeroCopyError::IterFromOutOfBounds => 15003, ZeroCopyError::InsufficientMemoryAllocated(_, _) => 15004, - ZeroCopyError::InvalidAccountSize => 15005, ZeroCopyError::UnalignedPointer => 15006, ZeroCopyError::MemoryNotZeroed => 15007, ZeroCopyError::InvalidConversion => 15008, - ZeroCopyError::CastError(_) => 15009, + ZeroCopyError::InvalidData(_) => 15009, + ZeroCopyError::Size => 15010, } } } @@ -45,3 +47,27 @@ impl From for solana_program::program_error::ProgramError { solana_program::program_error::ProgramError::Custom(e.into()) } } + +impl + From< + zerocopy::ConvertError< + zerocopy::AlignmentError, + zerocopy::SizeError, + core::convert::Infallible, + >, + > for ZeroCopyError +{ + fn from( + err: zerocopy::ConvertError< + zerocopy::AlignmentError, + zerocopy::SizeError, + core::convert::Infallible, + >, + ) -> Self { + match err { + zerocopy::ConvertError::Alignment(_) => ZeroCopyError::UnalignedPointer, + zerocopy::ConvertError::Size(_) => ZeroCopyError::Size, + zerocopy::ConvertError::Validity(i) => ZeroCopyError::InvalidData(i), + } + } +} diff --git a/program-libs/zero-copy/src/lib.rs b/program-libs/zero-copy/src/lib.rs index dad69b3b8..8f6a39e6d 100644 --- a/program-libs/zero-copy/src/lib.rs +++ b/program-libs/zero-copy/src/lib.rs @@ -1,9 +1,13 @@ +#![no_std] + pub mod cyclic_vec; pub mod errors; pub mod slice_mut; pub mod vec; +use core::mem::{align_of, size_of}; -use std::mem::{align_of, size_of}; +#[cfg(feature = "std")] +extern crate std; pub fn add_padding(offset: &mut usize) { let padding = align_of::().saturating_sub(size_of::()); diff --git a/program-libs/zero-copy/src/slice_mut.rs b/program-libs/zero-copy/src/slice_mut.rs index 683882560..577e850e1 100644 --- a/program-libs/zero-copy/src/slice_mut.rs +++ b/program-libs/zero-copy/src/slice_mut.rs @@ -1,8 +1,11 @@ -use core::{fmt, slice}; -use std::{ +use core::{ + fmt, mem::size_of, ops::{Index, IndexMut}, + slice, }; +#[cfg(feature = "std")] +use std::vec::Vec; use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Ref}; @@ -43,24 +46,22 @@ where } // write new value then deserialize as immutable { - let (mut len, _) = Ref::<&mut [u8], L>::from_prefix(bytes) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + let (mut len, _) = Ref::<&mut [u8], L>::from_prefix(bytes)?; if u64::from(*len) != 0 { return Err(ZeroCopyError::MemoryNotZeroed); } Ref::<&mut [u8], L>::write(&mut len, length); } let (meta_data, bytes) = bytes.split_at_mut(Self::metadata_size()); - let (len, _padding) = Ref::<&[u8], L>::from_prefix(meta_data) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + let (len, _padding) = Ref::<&[u8], L>::from_prefix(meta_data)?; let len_usize: usize = u64::from(length) as usize; let (bytes, remaining_bytes) = - Ref::<&mut [u8], [T]>::from_prefix_with_elems(bytes, len_usize) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + Ref::<&mut [u8], [T]>::from_prefix_with_elems(bytes, len_usize)?; Ok((Self { length: len, bytes }, remaining_bytes)) } + #[cfg(feature = "std")] pub fn new_at_multiple( num_slices: usize, capacity: L, @@ -91,9 +92,7 @@ where } let (meta_data, bytes) = bytes.split_at_mut(meta_data_size); - let (length, _padding) = Ref::<&[u8], L>::from_prefix(meta_data).map_err(|e| { - ZeroCopyError::CastError(format!("Failed to cast metadata to length: {}", e)) - })?; + let (length, _padding) = Ref::<&[u8], L>::from_prefix(meta_data)?; let usize_len: usize = u64::from(*length) as usize; let full_vector_size = Self::data_size(*length); if bytes.len() < full_vector_size { @@ -103,11 +102,11 @@ where )); } let (bytes, remaining_bytes) = - Ref::<&mut [u8], [T]>::from_prefix_with_elems(bytes, usize_len) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + Ref::<&mut [u8], [T]>::from_prefix_with_elems(bytes, usize_len)?; Ok((ZeroCopySliceMut { length, bytes }, remaining_bytes)) } + #[cfg(feature = "std")] pub fn from_bytes_at_multiple( num_slices: usize, mut bytes: &'a mut [u8], @@ -125,7 +124,7 @@ where if self.len() != N { return Err(ZeroCopyError::ArraySize(N, self.len())); } - Ok(std::array::from_fn(|i| *self.get(i).unwrap())) + Ok(core::array::from_fn(|i| *self.get(i).unwrap())) } #[inline] @@ -209,6 +208,8 @@ where self.as_mut_slice().get_mut(index) } + #[cfg(feature = "std")] + #[inline] pub fn to_vec(&self) -> Vec { self.as_slice().to_vec() } @@ -240,14 +241,14 @@ where } } -impl<'b, L, T, const PAD: bool> IntoIterator for &'b ZeroCopySliceMut<'_, L, T, PAD> +impl<'a, L, T, const PAD: bool> IntoIterator for &'a ZeroCopySliceMut<'_, L, T, PAD> where L: ZeroCopyTraits, T: ZeroCopyTraits, u64: From, { - type Item = &'b T; - type IntoIter = slice::Iter<'b, T>; + type Item = &'a T; + type IntoIter = slice::Iter<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -255,14 +256,14 @@ where } } -impl<'b, L, T, const PAD: bool> IntoIterator for &'b mut ZeroCopySliceMut<'_, L, T, PAD> +impl<'a, L, T, const PAD: bool> IntoIterator for &'a mut ZeroCopySliceMut<'_, L, T, PAD> where L: ZeroCopyTraits, T: ZeroCopyTraits, u64: From, { - type Item = &'b mut T; - type IntoIter = slice::IterMut<'b, T>; + type Item = &'a mut T; + type IntoIter = slice::IterMut<'a, T>; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -270,19 +271,19 @@ where } } -impl<'b, L, T, const PAD: bool> ZeroCopySliceMut<'_, L, T, PAD> +impl<'a, L, T, const PAD: bool> ZeroCopySliceMut<'_, L, T, PAD> where L: ZeroCopyTraits, T: ZeroCopyTraits, u64: From, { #[inline] - pub fn iter(&'b self) -> slice::Iter<'b, T> { + pub fn iter(&'a self) -> slice::Iter<'a, T> { self.as_slice().iter() } #[inline] - pub fn iter_mut(&'b mut self) -> slice::IterMut<'b, T> { + pub fn iter_mut(&'a mut self) -> slice::IterMut<'a, T> { self.as_mut_slice().iter_mut() } } @@ -295,7 +296,7 @@ where { #[inline] fn eq(&self, other: &Self) -> bool { - self.as_slice() == other.as_slice() && self.len() == other.len() + self.as_slice() == other.as_slice() } } @@ -307,6 +308,6 @@ where { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.to_vec()) + write!(f, "{:?}", self.as_slice()) } } diff --git a/program-libs/zero-copy/src/vec.rs b/program-libs/zero-copy/src/vec.rs index 144a2ee9d..229286996 100644 --- a/program-libs/zero-copy/src/vec.rs +++ b/program-libs/zero-copy/src/vec.rs @@ -1,9 +1,11 @@ -use core::slice; -use std::{ +use core::{ fmt, mem::size_of, ops::{Index, IndexMut}, + slice, }; +#[cfg(feature = "std")] +use std::vec::Vec; use zerocopy::Ref; @@ -44,8 +46,7 @@ where pub fn new_at(capacity: L, bytes: &'a mut [u8]) -> Result<(Self, &'a mut [u8]), ZeroCopyError> { let (meta_data, bytes) = bytes.split_at_mut(Self::metadata_size()); - let (length, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + let (length, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data)?; if u64::from(*length) != 0 { return Err(ZeroCopyError::MemoryNotZeroed); } @@ -53,6 +54,7 @@ where Ok((Self { length, slice }, bytes)) } + #[cfg(feature = "std")] pub fn new_at_multiple( num: usize, capacity: L, @@ -73,12 +75,13 @@ where pub fn from_bytes_at(bytes: &'a mut [u8]) -> Result<(Self, &'a mut [u8]), ZeroCopyError> { let (meta_data, bytes) = bytes.split_at_mut(Self::metadata_size()); - let (length, _padding) = Ref::<&mut [u8], L>::from_prefix(meta_data) - .map_err(|e| ZeroCopyError::CastError(e.to_string()))?; + let (length, _padding) = + Ref::<&mut [u8], L>::from_prefix(meta_data).map_err(ZeroCopyError::from)?; let (slice, bytes) = ZeroCopySliceMut::<'a, L, T, PAD>::from_bytes_at(bytes)?; Ok((Self { length, slice }, bytes)) } + #[cfg(feature = "std")] pub fn from_bytes_at_multiple( num: usize, mut bytes: &'a mut [u8], @@ -201,7 +204,7 @@ where let len = self.len(); let new_len = len + slice.len(); if new_len > self.capacity() { - panic!("Capacity overflow. Cannot copy slice into ZeroCopyVec"); + panic!("Capacity overflow. Cannot copy slice into ZeroCopyVec."); } self.slice.as_mut_slice()[len..].copy_from_slice(slice); *self.length = (new_len as u64) @@ -210,6 +213,7 @@ where .unwrap(); } + #[cfg(feature = "std")] #[inline] pub fn to_vec(&self) -> Vec { self.as_slice().to_vec() @@ -248,7 +252,7 @@ where } } -impl<'a, L, T, const PAD: bool> IntoIterator for &'a ZeroCopyVec<'a, L, T, PAD> +impl<'a, L, T, const PAD: bool> IntoIterator for &'a ZeroCopyVec<'_, L, T, PAD> where L: ZeroCopyTraits, T: ZeroCopyTraits, @@ -263,7 +267,7 @@ where } } -impl<'a, L, T, const PAD: bool> IntoIterator for &'a mut ZeroCopyVec<'a, L, T, PAD> +impl<'a, L, T, const PAD: bool> IntoIterator for &'a mut ZeroCopyVec<'_, L, T, PAD> where L: ZeroCopyTraits, T: ZeroCopyTraits, @@ -278,19 +282,19 @@ where } } -impl<'b, L, T, const PAD: bool> ZeroCopyVec<'_, L, T, PAD> +impl<'a, L, T, const PAD: bool> ZeroCopyVec<'_, L, T, PAD> where L: ZeroCopyTraits, T: ZeroCopyTraits, u64: From + TryInto, { #[inline] - pub fn iter(&'b self) -> slice::Iter<'b, T> { + pub fn iter(&'a self) -> slice::Iter<'a, T> { self.as_slice().iter() } #[inline] - pub fn iter_mut(&'b mut self) -> slice::IterMut<'b, T> { + pub fn iter_mut(&'a mut self) -> slice::IterMut<'a, T> { self.as_mut_slice().iter_mut() } } @@ -315,6 +319,6 @@ where { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{:?}", self.to_vec()) + write!(f, "{:?}", self.as_slice()) } } diff --git a/program-libs/zero-copy/tests/cyclic_vec_tests.rs b/program-libs/zero-copy/tests/cyclic_vec_tests.rs index f3c03ad0b..2af872408 100644 --- a/program-libs/zero-copy/tests/cyclic_vec_tests.rs +++ b/program-libs/zero-copy/tests/cyclic_vec_tests.rs @@ -687,3 +687,49 @@ fn test_partial_eq() { } assert_eq!(vec, vec2); } + +#[test] +fn test_new_memory_not_zeroed() { + let capacity = 5; + let mut data = vec![1; ZeroCopyCyclicVecU64::::required_size_for_capacity(capacity)]; + let vec = ZeroCopyCyclicVecU64::::new(capacity, &mut data); + assert!(matches!(vec, Err(ZeroCopyError::MemoryNotZeroed))); +} + +#[test] +fn test_index() { + let length: u64 = 4; + + let mut buffer = vec![0u8; ZeroCopyCyclicVecU64::::required_size_for_capacity(length)]; + let values = [1u32, 2, 3, 4]; + let (mut slice, _) = ZeroCopyCyclicVecU64::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopyVeceMut"); + for value in &values { + slice.push(*value); + } + assert_eq!(slice[0], 1); + assert_eq!(slice[1], 2); + assert_eq!(slice[2], 3); + assert_eq!(slice[3], 4); + slice[0] = 10; + assert_eq!(slice[0], 10); + assert_eq!(slice[1], 2); + assert_eq!(slice[2], 3); + assert_eq!(slice[3], 4); + assert_eq!(slice.as_slice(), &[10, 2, 3, 4]); +} + +#[test] +fn test_debug_fmt() { + let length: u64 = 4; + let mut buffer = vec![0u8; ZeroCopyCyclicVecU64::::required_size_for_capacity(length)]; + let values = [1u32, 2, 3, 4]; + + let (mut slice, _) = ZeroCopyCyclicVecU64::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopyVeceMut"); + for value in values.iter() { + slice.push(*value); + } + + assert_eq!(format!("{:?}", slice), "[1, 2, 3, 4]"); +} diff --git a/program-libs/zero-copy/tests/slice_mut_test.rs b/program-libs/zero-copy/tests/slice_mut_test.rs index 431845722..17f492ec9 100644 --- a/program-libs/zero-copy/tests/slice_mut_test.rs +++ b/program-libs/zero-copy/tests/slice_mut_test.rs @@ -174,7 +174,7 @@ fn test_unaligned() { let mut data = vec![0; ZeroCopySliceMut::::required_size_for_capacity(1)]; let result = ZeroCopySliceMut::::new(1, &mut data); - assert!(matches!(result, Err(ZeroCopyError::CastError(_))),); + assert!(matches!(result, Err(ZeroCopyError::UnalignedPointer)),); } { let mut data = @@ -255,6 +255,7 @@ fn test_empty() { "Expected no elements" ); assert_eq!(zero_copy_slice.as_mut_slice(), &[]); + assert_eq!(zero_copy_slice.to_vec(), vec![]); } @@ -493,6 +494,10 @@ fn test_failing_new() { vec, Err(ZeroCopyError::InsufficientMemoryAllocated(_, _)) )); + + let mut data = vec![1; ZeroCopySliceMutU64::::required_size_for_capacity(capacity)]; + let vec = ZeroCopySliceMutU64::::new(capacity, &mut data); + assert!(matches!(vec, Err(ZeroCopyError::MemoryNotZeroed))); } #[test] @@ -511,3 +516,93 @@ fn test_failing_from_bytes_at() { Err(ZeroCopyError::InsufficientMemoryAllocated(_, _)) )); } + +#[test] +fn test_data_as_ptr_and_data_as_mut_ptr() { + let mut buffer = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + let required_size = ZeroCopySliceMut::::required_size_for_capacity(length); + assert!(buffer.len() >= required_size); + let (mut slice, _) = ZeroCopySliceMut::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopySliceMut"); + slice.as_mut_slice().copy_from_slice(&values); + let data_ptr = slice.data_as_ptr(); + unsafe { + for (i, value) in values.iter().enumerate() { + assert_eq!(*data_ptr.add(i), *value); + } + } + let data_mut_ptr = slice.data_as_mut_ptr(); + unsafe { + for i in 0..length as usize { + *data_mut_ptr.add(i) += 1; + } + } + let expected_values = [2u32, 3, 4, 5]; + assert_eq!(slice.as_slice(), &expected_values); +} + +#[test] +fn test_into_iter_immutable() { + let mut buffer = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + let (mut slice, _) = ZeroCopySliceMut::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopySliceMut"); + slice.as_mut_slice().copy_from_slice(&values); + let mut iter = slice.into_iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), Some(&4)); + assert_eq!(iter.next(), None); +} + +#[test] +fn test_into_iter_mutable() { + let mut buffer = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + let (mut slice, _) = ZeroCopySliceMut::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopySliceMut"); + slice.as_mut_slice().copy_from_slice(&values); + for x in &mut slice { + *x += 10; + } + assert_eq!(slice.as_slice(), &[11, 12, 13, 14]); +} + +#[test] +fn test_partial_eq() { + let mut buffer1 = vec![0u8; 32]; + let mut buffer2 = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + + let (mut slice1, _) = ZeroCopySliceMut::::new_at(length, &mut buffer1) + .expect("Failed to create ZeroCopySliceMut"); + let (mut slice2, _) = ZeroCopySliceMut::::new_at(length, &mut buffer2) + .expect("Failed to create ZeroCopySliceMut"); + + slice1.as_mut_slice().copy_from_slice(&values); + slice2.as_mut_slice().copy_from_slice(&values); + + assert_eq!(slice1, slice2); + + slice2.as_mut_slice()[0] = 10; + assert_ne!(slice1, slice2); +} + +#[test] +fn test_debug_fmt() { + let mut buffer = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + + let (mut slice, _) = ZeroCopySliceMut::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopySliceMut"); + slice.as_mut_slice().copy_from_slice(&values); + + assert_eq!(format!("{:?}", slice), "[1, 2, 3, 4]"); +} diff --git a/program-libs/zero-copy/tests/vec_tests.rs b/program-libs/zero-copy/tests/vec_tests.rs index 728a2407c..6b656ffa2 100644 --- a/program-libs/zero-copy/tests/vec_tests.rs +++ b/program-libs/zero-copy/tests/vec_tests.rs @@ -345,6 +345,7 @@ fn assert_empty_vec( assert_eq!(vec.as_slice(), reference_vec.as_slice()); // 11. vector as_mut_slice returns correct slice assert_eq!(vec.as_mut_slice(), reference_vec.as_mut_slice()); + // 12. vector to_vec returns correct vector assert_eq!(vec.to_vec(), reference_vec); // 13. (iter) iterating over vector returns correct elements @@ -533,3 +534,73 @@ fn test_partial_eq() { vec.clear(); assert_ne!(vec, vec2); } + +#[test] +fn test_new_memory_not_zeroed() { + let capacity = 5; + let mut data = vec![1; ZeroCopyVecU64::::required_size_for_capacity(capacity)]; + let vec = ZeroCopyVecU64::::new(capacity, &mut data); + assert!(matches!(vec, Err(ZeroCopyError::MemoryNotZeroed))); +} + +#[should_panic = "Capacity overflow. Cannot copy slice into ZeroCopyVec."] +#[test] +fn test_extend_from_slice_over_capacity() { + let capacity = 5; + let mut data = vec![0; ZeroCopyVecU64::::required_size_for_capacity(capacity)]; + let mut vec = ZeroCopyVecU64::::new(capacity, &mut data).unwrap(); + let slice = [1, 2, 3, 4, 5, 6]; + vec.extend_from_slice(&slice); +} + +#[should_panic = "Capacity overflow. Cannot copy slice into ZeroCopyVec."] +#[test] +fn test_extend_from_slice_over_capacity_2() { + let capacity = 5; + let mut data = vec![0; ZeroCopyVecU64::::required_size_for_capacity(capacity)]; + let mut vec = ZeroCopyVecU64::::new(capacity, &mut data).unwrap(); + let slice = [1, 2, 3, 4, 5]; + vec.extend_from_slice(&slice); + let slice = [1]; + vec.extend_from_slice(&slice); +} + +#[test] +fn test_extend_from_slice() { + let capacity = 5; + let mut data = vec![0; ZeroCopyVecU64::::required_size_for_capacity(capacity)]; + let mut vec = ZeroCopyVecU64::::new(capacity, &mut data).unwrap(); + let slice = [1, 2, 3, 4, 5]; + vec.extend_from_slice(&slice); +} + +#[test] +fn test_into_iter_mutable() { + let mut buffer = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + let (mut slice, _) = ZeroCopyVecU64::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopyVeceMut"); + for value in &values { + slice.push(*value).expect("Failed to push value"); + } + for x in &mut slice { + *x += 10; + } + assert_eq!(slice.as_slice(), &[11, 12, 13, 14]); +} + +#[test] +fn test_debug_fmt() { + let mut buffer = vec![0u8; 32]; + let length: u64 = 4; + let values = [1u32, 2, 3, 4]; + + let (mut slice, _) = ZeroCopyVecU64::::new_at(length, &mut buffer) + .expect("Failed to create ZeroCopyVeceMut"); + for value in &values { + slice.push(*value).expect("Failed to push value"); + } + + assert_eq!(format!("{:?}", slice), "[1, 2, 3, 4]"); +}