Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: make light-zero-copy no-std, increase test coverage #1501

Merged
merged 1 commit into from
Jan 21, 2025
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
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion program-libs/batched-merkle-tree/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
4 changes: 2 additions & 2 deletions program-libs/batched-merkle-tree/src/merkle_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,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(
Expand Down Expand Up @@ -332,7 +332,7 @@ impl<'a> BatchedMerkleTreeAccount<'a> {
/// 3.3. If all zkps are inserted, set batch state to inserted.
/// 4. Increment next full batch index if inserted.
/// 5. Return the batch append event.
///
///
/// Note: when proving inclusion by index in
/// value array we need to insert the value into a bloom_filter once it is
/// inserted into the tree. Check this with get_num_inserted_zkps
Expand Down
2 changes: 1 addition & 1 deletion program-libs/batched-merkle-tree/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,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 (value_vecs, _bloom_filter_stores, hashchain_store, _) = init_queue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,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
{
Expand Down Expand Up @@ -214,7 +214,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
{
Expand Down
8 changes: 5 additions & 3 deletions program-libs/zero-copy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}
18 changes: 10 additions & 8 deletions program-libs/zero-copy/src/cyclic_vec.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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);
}
Expand All @@ -47,6 +47,7 @@ where
Ok((Self { current_index, vec }, bytes))
}

#[cfg(feature = "std")]
pub fn new_at_multiple(
num: usize,
capacity: L,
Expand All @@ -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],
Expand Down Expand Up @@ -235,6 +236,7 @@ where
self.vec.try_into_array()
}

#[cfg(feature = "std")]
#[inline]
pub fn to_vec(&self) -> Vec<T> {
self.vec.to_vec()
Expand Down Expand Up @@ -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())
}
}
38 changes: 32 additions & 6 deletions program-libs/zero-copy/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::convert::Infallible;

use thiserror::Error;

#[derive(Debug, Error, PartialEq)]
Expand All @@ -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")]
Expand All @@ -30,11 +32,11 @@ impl From<ZeroCopyError> 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,
}
}
}
Expand All @@ -45,3 +47,27 @@ impl From<ZeroCopyError> for solana_program::program_error::ProgramError {
solana_program::program_error::ProgramError::Custom(e.into())
}
}

impl<Src, Dst: ?Sized>
From<
zerocopy::ConvertError<
zerocopy::AlignmentError<Src, Dst>,
zerocopy::SizeError<Src, Dst>,
core::convert::Infallible,
>,
> for ZeroCopyError
{
fn from(
err: zerocopy::ConvertError<
zerocopy::AlignmentError<Src, Dst>,
zerocopy::SizeError<Src, Dst>,
core::convert::Infallible,
>,
) -> Self {
match err {
zerocopy::ConvertError::Alignment(_) => ZeroCopyError::UnalignedPointer,
zerocopy::ConvertError::Size(_) => ZeroCopyError::Size,
zerocopy::ConvertError::Validity(i) => ZeroCopyError::InvalidData(i),
}
}
}
6 changes: 5 additions & 1 deletion program-libs/zero-copy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<LEN, T>(offset: &mut usize) {
let padding = align_of::<T>().saturating_sub(size_of::<LEN>());
Expand Down
52 changes: 26 additions & 26 deletions program-libs/zero-copy/src/slice_mut.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -93,9 +94,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 {
Expand All @@ -105,12 +104,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))
}

#[inline]
#[cfg(feature = "std")]
pub fn from_bytes_at_multiple(
num_slices: usize,
mut bytes: &'a mut [u8],
Expand All @@ -128,7 +126,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]
Expand Down Expand Up @@ -212,6 +210,8 @@ where
self.as_mut_slice().get_mut(index)
}

#[cfg(feature = "std")]
#[inline]
pub fn to_vec(&self) -> Vec<T> {
self.as_slice().to_vec()
}
Expand Down Expand Up @@ -243,49 +243,49 @@ 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<L>,
{
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 {
self.iter()
}
}

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<L>,
{
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 {
self.iter_mut()
}
}

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<L>,
{
#[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()
}
}
Expand All @@ -298,7 +298,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()
}
}

Expand All @@ -310,6 +310,6 @@ where
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.to_vec())
write!(f, "{:?}", self.as_slice())
}
}
Loading
Loading