Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 54ed051

Browse files
author
Joe C
authored
Expanding TLV lib (#4602)
* rebase * borsh feature for TM example
1 parent afc0f00 commit 54ed051

File tree

9 files changed

+164
-256
lines changed

9 files changed

+164
-256
lines changed

Cargo.lock

Lines changed: 2 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libraries/tlv-account-resolution/Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@ test-sbf = []
1212

1313
[dependencies]
1414
bytemuck = { version = "1.13.1", features = ["derive"] }
15-
num-derive = "0.3"
16-
num-traits = "0.2"
17-
num_enum = "0.6.1"
1815
solana-program = "1.16.1"
1916
spl-discriminator = { version = "0.1", path = "../discriminator" }
17+
spl-program-error = { version = "0.1.0", path = "../program-error" }
2018
spl-type-length-value = { version = "0.1", path = "../type-length-value" }
21-
thiserror = "1.0"
2219

2320
[dev-dependencies]
2421
solana-program-test = "1.16.1"
Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
11
//! Error types
22
3-
use {
4-
num_derive::FromPrimitive,
5-
solana_program::{
6-
decode_error::DecodeError,
7-
msg,
8-
program_error::{PrintProgramError, ProgramError},
9-
},
10-
thiserror::Error,
11-
};
3+
use spl_program_error::*;
124

135
/// Errors that may be returned by the Account Resolution library.
14-
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
6+
#[spl_program_error]
157
pub enum AccountResolutionError {
168
/// Incorrect account provided
179
#[error("Incorrect account provided")]
@@ -25,48 +17,7 @@ pub enum AccountResolutionError {
2517
/// Some value initialized in TLV data
2618
#[error("Some value initialized in TLV data")]
2719
TlvInitialized,
28-
/// Provided byte buffer too small for validation pubkeys
29-
#[error("Provided byte buffer too small for validation pubkeys")]
30-
BufferTooSmall,
31-
/// Error in checked math operation
32-
#[error("Error in checked math operation")]
33-
CalculationFailure,
3420
/// Too many pubkeys provided
3521
#[error("Too many pubkeys provided")]
3622
TooManyPubkeys,
37-
/// Provided byte buffer too large for expected type
38-
#[error("Provided byte buffer too large for expected type")]
39-
BufferTooLarge,
40-
}
41-
impl From<AccountResolutionError> for ProgramError {
42-
fn from(e: AccountResolutionError) -> Self {
43-
ProgramError::Custom(e as u32)
44-
}
45-
}
46-
impl<T> DecodeError<T> for AccountResolutionError {
47-
fn type_of() -> &'static str {
48-
"AccountResolutionError"
49-
}
50-
}
51-
52-
impl PrintProgramError for AccountResolutionError {
53-
fn print<E>(&self)
54-
where
55-
E: 'static
56-
+ std::error::Error
57-
+ DecodeError<E>
58-
+ PrintProgramError
59-
+ num_traits::FromPrimitive,
60-
{
61-
match self {
62-
Self::IncorrectAccount => msg!("Incorrect account provided"),
63-
Self::NotEnoughAccounts => msg!("Not enough accounts provided"),
64-
Self::TlvUninitialized => msg!("No value initialized in TLV data"),
65-
Self::TlvInitialized => msg!("Some value initialized in TLV data"),
66-
Self::BufferTooSmall => msg!("Provided byte buffer too small for validation pubkeys"),
67-
Self::CalculationFailure => msg!("Error in checked math operation"),
68-
Self::TooManyPubkeys => msg!("Too many pubkeys provided"),
69-
Self::BufferTooLarge => msg!("Provided byte buffer too large for expected type"),
70-
}
71-
}
7223
}
Lines changed: 2 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,11 @@
11
//! Pod types to be used with bytemuck for zero-copy serde
22
33
use {
4-
crate::error::AccountResolutionError,
54
bytemuck::{Pod, Zeroable},
6-
solana_program::{
7-
account_info::AccountInfo, instruction::AccountMeta, program_error::ProgramError,
8-
pubkey::Pubkey,
9-
},
10-
spl_type_length_value::pod::{pod_from_bytes, pod_from_bytes_mut, PodU32},
5+
solana_program::{account_info::AccountInfo, instruction::AccountMeta, pubkey::Pubkey},
6+
spl_type_length_value::pod::PodBool,
117
};
128

13-
/// Convert a slice into a mutable `Pod` slice (zero copy)
14-
pub fn pod_slice_from_bytes<T: Pod>(bytes: &[u8]) -> Result<&[T], ProgramError> {
15-
bytemuck::try_cast_slice(bytes).map_err(|_| ProgramError::InvalidArgument)
16-
}
17-
/// Convert a slice into a mutable `Pod` slice (zero copy)
18-
pub fn pod_slice_from_bytes_mut<T: Pod>(bytes: &mut [u8]) -> Result<&mut [T], ProgramError> {
19-
bytemuck::try_cast_slice_mut(bytes).map_err(|_| ProgramError::InvalidArgument)
20-
}
21-
22-
/// The standard `bool` is not a `Pod`, define a replacement that is
23-
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
24-
#[repr(transparent)]
25-
pub struct PodBool(u8);
26-
impl From<bool> for PodBool {
27-
fn from(b: bool) -> Self {
28-
Self(if b { 1 } else { 0 })
29-
}
30-
}
31-
impl From<&PodBool> for bool {
32-
fn from(b: &PodBool) -> Self {
33-
b.0 != 0
34-
}
35-
}
36-
impl From<PodBool> for bool {
37-
fn from(b: PodBool) -> Self {
38-
b.0 != 0
39-
}
40-
}
41-
429
/// The standard `AccountMeta` is not a `Pod`, define a replacement that is
4310
#[repr(C)]
4411
#[derive(Clone, Copy, Debug, Default, PartialEq, Pod, Zeroable)]
@@ -87,113 +54,3 @@ impl From<&PodAccountMeta> for AccountMeta {
8754
}
8855
}
8956
}
90-
91-
const LENGTH_SIZE: usize = std::mem::size_of::<PodU32>();
92-
/// Special type for using a slice of `Pod`s in a zero-copy way
93-
pub struct PodSlice<'data, T: Pod> {
94-
length: &'data PodU32,
95-
data: &'data [T],
96-
}
97-
impl<'data, T: Pod> PodSlice<'data, T> {
98-
/// Unpack the buffer into a slice
99-
pub fn unpack<'a>(data: &'a [u8]) -> Result<Self, ProgramError>
100-
where
101-
'a: 'data,
102-
{
103-
if data.len() < LENGTH_SIZE {
104-
return Err(AccountResolutionError::BufferTooSmall.into());
105-
}
106-
let (length, data) = data.split_at(LENGTH_SIZE);
107-
let length = pod_from_bytes::<PodU32>(length)?;
108-
let _max_length = max_len_for_type::<T>(data.len())?;
109-
let data = pod_slice_from_bytes(data)?;
110-
Ok(Self { length, data })
111-
}
112-
113-
/// Get the slice data
114-
pub fn data(&self) -> &[T] {
115-
let length = u32::from(*self.length) as usize;
116-
&self.data[..length]
117-
}
118-
119-
/// Get the amount of bytes used by `num_items`
120-
pub fn size_of(num_items: usize) -> Result<usize, ProgramError> {
121-
std::mem::size_of::<T>()
122-
.checked_mul(num_items)
123-
.and_then(|len| len.checked_add(LENGTH_SIZE))
124-
.ok_or_else(|| AccountResolutionError::CalculationFailure.into())
125-
}
126-
}
127-
128-
/// Special type for using a slice of mutable `Pod`s in a zero-copy way
129-
pub struct PodSliceMut<'data, T: Pod> {
130-
length: &'data mut PodU32,
131-
data: &'data mut [T],
132-
max_length: usize,
133-
}
134-
impl<'data, T: Pod> PodSliceMut<'data, T> {
135-
/// Unpack the mutable buffer into a mutable slice, with the option to
136-
/// initialize the data
137-
fn unpack_internal<'a>(data: &'a mut [u8], init: bool) -> Result<Self, ProgramError>
138-
where
139-
'a: 'data,
140-
{
141-
if data.len() < LENGTH_SIZE {
142-
return Err(AccountResolutionError::BufferTooSmall.into());
143-
}
144-
let (length, data) = data.split_at_mut(LENGTH_SIZE);
145-
let length = pod_from_bytes_mut::<PodU32>(length)?;
146-
if init {
147-
*length = 0.into();
148-
}
149-
let max_length = max_len_for_type::<T>(data.len())?;
150-
let data = pod_slice_from_bytes_mut(data)?;
151-
Ok(Self {
152-
length,
153-
data,
154-
max_length,
155-
})
156-
}
157-
158-
/// Unpack the mutable buffer into a mutable slice
159-
pub fn unpack<'a>(data: &'a mut [u8]) -> Result<Self, ProgramError>
160-
where
161-
'a: 'data,
162-
{
163-
Self::unpack_internal(data, /* init */ false)
164-
}
165-
166-
/// Unpack the mutable buffer into a mutable slice, and initialize the
167-
/// slice to 0-length
168-
pub fn init<'a>(data: &'a mut [u8]) -> Result<Self, ProgramError>
169-
where
170-
'a: 'data,
171-
{
172-
Self::unpack_internal(data, /* init */ true)
173-
}
174-
175-
/// Add another item to the slice
176-
pub fn push(&mut self, t: T) -> Result<(), ProgramError> {
177-
let length = u32::from(*self.length);
178-
if length as usize == self.max_length {
179-
Err(AccountResolutionError::BufferTooSmall.into())
180-
} else {
181-
self.data[length as usize] = t;
182-
*self.length = length.saturating_add(1).into();
183-
Ok(())
184-
}
185-
}
186-
}
187-
188-
fn max_len_for_type<T>(data_len: usize) -> Result<usize, ProgramError> {
189-
let size: usize = std::mem::size_of::<T>();
190-
let max_len = data_len
191-
.checked_div(size)
192-
.ok_or(AccountResolutionError::CalculationFailure)?;
193-
// check that it isn't overallocated
194-
if max_len.saturating_mul(size) != data_len {
195-
Err(AccountResolutionError::BufferTooLarge.into())
196-
} else {
197-
Ok(max_len)
198-
}
199-
}

libraries/tlv-account-resolution/src/state.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
//! State transition types
22
33
use {
4-
crate::{
5-
error::AccountResolutionError,
6-
pod::{PodAccountMeta, PodSlice, PodSliceMut},
7-
},
4+
crate::{error::AccountResolutionError, pod::PodAccountMeta},
85
solana_program::{
96
account_info::AccountInfo,
107
instruction::{AccountMeta, Instruction},
118
program_error::ProgramError,
129
},
1310
spl_discriminator::SplDiscriminate,
14-
spl_type_length_value::state::{TlvState, TlvStateBorrowed, TlvStateMut},
11+
spl_type_length_value::{
12+
pod::{PodSlice, PodSliceMut},
13+
state::{TlvState, TlvStateBorrowed, TlvStateMut},
14+
},
1515
};
1616

1717
/// Stateless helper for storing additional accounts required for an instruction.

libraries/type-length-value/Cargo.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,11 @@ exclude = ["js/**"]
1212
borsh = ["dep:borsh"]
1313

1414
[dependencies]
15-
arrayref = "0.3.7"
1615
borsh = { version = "0.10", optional = true }
1716
bytemuck = { version = "1.13.1", features = ["derive"] }
18-
num-derive = "0.3"
19-
num-traits = "0.2"
20-
num_enum = "0.6.1"
2117
solana-program = "1.16.1"
2218
spl-discriminator = { version = "0.1.0", path = "../discriminator" }
23-
thiserror = "1.0"
19+
spl-program-error = { version = "0.1.0", path = "../program-error" }
2420

2521
[lib]
2622
crate-type = ["cdylib", "lib"]
Lines changed: 11 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,23 @@
11
//! Error types
22
3-
use {
4-
num_derive::FromPrimitive,
5-
solana_program::{
6-
decode_error::DecodeError,
7-
msg,
8-
program_error::{PrintProgramError, ProgramError},
9-
},
10-
thiserror::Error,
11-
};
3+
use spl_program_error::*;
124

135
/// Errors that may be returned by the Token program.
14-
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
6+
#[spl_program_error]
157
pub enum TlvError {
16-
// 0
178
/// Type not found in TLV data
189
#[error("Type not found in TLV data")]
1910
TypeNotFound,
2011
/// Type already exists in TLV data
2112
#[error("Type already exists in TLV data")]
2213
TypeAlreadyExists,
23-
}
24-
impl From<TlvError> for ProgramError {
25-
fn from(e: TlvError) -> Self {
26-
ProgramError::Custom(e as u32)
27-
}
28-
}
29-
impl<T> DecodeError<T> for TlvError {
30-
fn type_of() -> &'static str {
31-
"TlvError"
32-
}
33-
}
34-
impl PrintProgramError for TlvError {
35-
fn print<E>(&self)
36-
where
37-
E: 'static
38-
+ std::error::Error
39-
+ DecodeError<E>
40-
+ PrintProgramError
41-
+ num_traits::FromPrimitive,
42-
{
43-
match self {
44-
Self::TypeNotFound => msg!("Type not found in TLV data"),
45-
Self::TypeAlreadyExists => msg!("Type already exists in TLV data"),
46-
}
47-
}
14+
/// Error in checked math operation
15+
#[error("Error in checked math operation")]
16+
CalculationFailure,
17+
/// Provided byte buffer too small for expected type
18+
#[error("Provided byte buffer too small for expected type")]
19+
BufferTooSmall,
20+
/// Provided byte buffer too large for expected type
21+
#[error("Provided byte buffer too large for expected type")]
22+
BufferTooLarge,
4823
}

0 commit comments

Comments
 (0)