Skip to content

Commit

Permalink
chore: make light-zero-copy no-std, increase code coverage, improve z…
Browse files Browse the repository at this point in the history
…erocopy -> light-zero-copy error conversion
  • Loading branch information
ananas-block committed Jan 20, 2025
1 parent 896a8f4 commit 85ce817
Show file tree
Hide file tree
Showing 14 changed files with 315 additions and 64 deletions.
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 @@ -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)?;
Expand Down Expand Up @@ -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(
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 @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions program-libs/batched-merkle-tree/tests/rollover_state_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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
{
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
51 changes: 26 additions & 25 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 @@ -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 {
Expand All @@ -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],
Expand All @@ -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]
Expand Down Expand Up @@ -209,6 +208,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 @@ -240,49 +241,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 @@ -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()
}
}

Expand All @@ -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())
}
}
Loading

0 comments on commit 85ce817

Please sign in to comment.