Skip to content

Commit c4239ba

Browse files
authored
token-2022: Introduce PackedSizeOf and relax BaseState trait (solana-labs#6332)
Introduce `PackedSizeOf` and relax `BaseState` trait
1 parent f0b7823 commit c4239ba

File tree

2 files changed

+42
-22
lines changed

2 files changed

+42
-22
lines changed

token/program-2022/src/extension/mod.rs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use {
2424
transfer_fee::{TransferFeeAmount, TransferFeeConfig},
2525
transfer_hook::{TransferHook, TransferHookAccount},
2626
},
27-
state::{Account, Mint, Multisig},
27+
state::{Account, Mint, Multisig, PackedSizeOf},
2828
},
2929
bytemuck::{Pod, Zeroable},
3030
num_enum::{IntoPrimitive, TryFromPrimitive},
@@ -298,7 +298,7 @@ fn type_and_tlv_indices<S: BaseState>(
298298
if rest_input.is_empty() {
299299
Ok(None)
300300
} else {
301-
let account_type_index = BASE_ACCOUNT_LENGTH.saturating_sub(S::LEN);
301+
let account_type_index = BASE_ACCOUNT_LENGTH.saturating_sub(S::SIZE_OF);
302302
// check padding is all zeroes
303303
let tlv_start_index = account_type_index.saturating_add(size_of::<AccountType>());
304304
if rest_input.len() <= tlv_start_index {
@@ -427,7 +427,7 @@ pub trait BaseStateWithExtensions<S: BaseState> {
427427
fn try_get_account_len(&self) -> Result<usize, ProgramError> {
428428
let tlv_info = get_tlv_data_info(self.get_tlv_data())?;
429429
if tlv_info.extension_types.is_empty() {
430-
Ok(S::LEN)
430+
Ok(S::SIZE_OF)
431431
} else {
432432
let total_len = tlv_info
433433
.used_len
@@ -468,13 +468,13 @@ pub struct StateWithExtensionsOwned<S: BaseState> {
468468
/// Raw TLV data, deserialized on demand
469469
tlv_data: Vec<u8>,
470470
}
471-
impl<S: BaseState> StateWithExtensionsOwned<S> {
471+
impl<S: BaseState + Pack> StateWithExtensionsOwned<S> {
472472
/// Unpack base state, leaving the extension data as a slice
473473
///
474474
/// Fails if the base state is not initialized.
475475
pub fn unpack(mut input: Vec<u8>) -> Result<Self, ProgramError> {
476-
check_min_len_and_not_multisig(&input, S::LEN)?;
477-
let mut rest = input.split_off(S::LEN);
476+
check_min_len_and_not_multisig(&input, S::SIZE_OF)?;
477+
let mut rest = input.split_off(S::SIZE_OF);
478478
let base = S::unpack(&input)?;
479479
if let Some((account_type_index, tlv_start_index)) = type_and_tlv_indices::<S>(&rest)? {
480480
// type_and_tlv_indices() checks that returned indexes are within range
@@ -501,19 +501,19 @@ impl<S: BaseState> BaseStateWithExtensions<S> for StateWithExtensionsOwned<S> {
501501
/// Encapsulates immutable base state data (mint or account) with possible
502502
/// extensions
503503
#[derive(Debug, PartialEq)]
504-
pub struct StateWithExtensions<'data, S: BaseState> {
504+
pub struct StateWithExtensions<'data, S: BaseState + Pack> {
505505
/// Unpacked base data
506506
pub base: S,
507507
/// Slice of data containing all TLV data, deserialized on demand
508508
tlv_data: &'data [u8],
509509
}
510-
impl<'data, S: BaseState> StateWithExtensions<'data, S> {
510+
impl<'data, S: BaseState + Pack> StateWithExtensions<'data, S> {
511511
/// Unpack base state, leaving the extension data as a slice
512512
///
513513
/// Fails if the base state is not initialized.
514514
pub fn unpack(input: &'data [u8]) -> Result<Self, ProgramError> {
515-
check_min_len_and_not_multisig(input, S::LEN)?;
516-
let (base_data, rest) = input.split_at(S::LEN);
515+
check_min_len_and_not_multisig(input, S::SIZE_OF)?;
516+
let (base_data, rest) = input.split_at(S::SIZE_OF);
517517
let base = S::unpack(base_data)?;
518518
if let Some((account_type_index, tlv_start_index)) = type_and_tlv_indices::<S>(rest)? {
519519
// type_and_tlv_indices() checks that returned indexes are within range
@@ -532,7 +532,7 @@ impl<'data, S: BaseState> StateWithExtensions<'data, S> {
532532
}
533533
}
534534
}
535-
impl<'a, S: BaseState> BaseStateWithExtensions<S> for StateWithExtensions<'a, S> {
535+
impl<'a, S: BaseState + Pack> BaseStateWithExtensions<S> for StateWithExtensions<'a, S> {
536536
fn get_tlv_data(&self) -> &[u8] {
537537
self.tlv_data
538538
}
@@ -784,13 +784,13 @@ pub struct StateWithExtensionsMut<'data, S: BaseState> {
784784
/// Slice of data containing all TLV data, deserialized on demand
785785
tlv_data: &'data mut [u8],
786786
}
787-
impl<'data, S: BaseState> StateWithExtensionsMut<'data, S> {
787+
impl<'data, S: BaseState + Pack> StateWithExtensionsMut<'data, S> {
788788
/// Unpack base state, leaving the extension data as a mutable slice
789789
///
790790
/// Fails if the base state is not initialized.
791791
pub fn unpack(input: &'data mut [u8]) -> Result<Self, ProgramError> {
792-
check_min_len_and_not_multisig(input, S::LEN)?;
793-
let (base_data, rest) = input.split_at_mut(S::LEN);
792+
check_min_len_and_not_multisig(input, S::SIZE_OF)?;
793+
let (base_data, rest) = input.split_at_mut(S::SIZE_OF);
794794
let base = S::unpack(base_data)?;
795795
let (account_type, tlv_data) = unpack_type_and_tlv_data::<S>(rest)?;
796796
Ok(Self {
@@ -806,8 +806,8 @@ impl<'data, S: BaseState> StateWithExtensionsMut<'data, S> {
806806
///
807807
/// Fails if the base state has already been initialized.
808808
pub fn unpack_uninitialized(input: &'data mut [u8]) -> Result<Self, ProgramError> {
809-
check_min_len_and_not_multisig(input, S::LEN)?;
810-
let (base_data, rest) = input.split_at_mut(S::LEN);
809+
check_min_len_and_not_multisig(input, S::SIZE_OF)?;
810+
let (base_data, rest) = input.split_at_mut(S::SIZE_OF);
811811
let base = S::unpack_unchecked(base_data)?;
812812
if base.is_initialized() {
813813
return Err(TokenError::AlreadyInUse.into());
@@ -892,8 +892,8 @@ fn unpack_uninitialized_type_and_tlv_data<S: BaseState>(
892892
/// This method assumes that the `base_data` has already been packed with data
893893
/// of the desired type.
894894
pub fn set_account_type<S: BaseState>(input: &mut [u8]) -> Result<(), ProgramError> {
895-
check_min_len_and_not_multisig(input, S::LEN)?;
896-
let (base_data, rest) = input.split_at_mut(S::LEN);
895+
check_min_len_and_not_multisig(input, S::SIZE_OF)?;
896+
let (base_data, rest) = input.split_at_mut(S::SIZE_OF);
897897
if S::ACCOUNT_TYPE == AccountType::Account && !is_initialized_account(base_data)? {
898898
return Err(ProgramError::InvalidAccountData);
899899
}
@@ -1113,7 +1113,7 @@ impl ExtensionType {
11131113
extension_types: &[Self],
11141114
) -> Result<usize, ProgramError> {
11151115
if extension_types.is_empty() {
1116-
Ok(S::LEN)
1116+
Ok(S::SIZE_OF)
11171117
} else {
11181118
let extension_size = Self::try_get_total_tlv_len(extension_types)?;
11191119
let total_len = extension_size.saturating_add(BASE_ACCOUNT_AND_TYPE_LENGTH);
@@ -1216,7 +1216,7 @@ impl ExtensionType {
12161216
}
12171217

12181218
/// Trait for base states, specifying the associated enum
1219-
pub trait BaseState: Pack + IsInitialized {
1219+
pub trait BaseState: PackedSizeOf + IsInitialized {
12201220
/// Associated extension type enum, checked at the start of TLV entries
12211221
const ACCOUNT_TYPE: AccountType;
12221222
}
@@ -1287,7 +1287,7 @@ impl Extension for AccountPaddingTest {
12871287
/// NOTE: Since this function deals with fixed-size extensions, it does not
12881288
/// handle _decreasing_ the size of an account's data buffer, like the function
12891289
/// `alloc_and_serialize_variable_len_extension` does.
1290-
pub fn alloc_and_serialize<S: BaseState, V: Default + Extension + Pod>(
1290+
pub fn alloc_and_serialize<S: BaseState + Pack, V: Default + Extension + Pod>(
12911291
account_info: &AccountInfo,
12921292
new_extension: &V,
12931293
overwrite: bool,
@@ -1324,7 +1324,10 @@ pub fn alloc_and_serialize<S: BaseState, V: Default + Extension + Pod>(
13241324
///
13251325
/// NOTE: Unlike the `reallocate` instruction, this function will reduce the
13261326
/// size of an account if it has too many bytes allocated for the given value.
1327-
pub fn alloc_and_serialize_variable_len_extension<S: BaseState, V: Extension + VariableLenPack>(
1327+
pub fn alloc_and_serialize_variable_len_extension<
1328+
S: BaseState + Pack,
1329+
V: Extension + VariableLenPack,
1330+
>(
13281331
account_info: &AccountInfo,
13291332
new_extension: &V,
13301333
overwrite: bool,

token/program-2022/src/state.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ use {
1616
},
1717
};
1818

19+
/// Simplified version of the `Pack` trait which only gives the size of the
20+
/// packed struct. Useful when a function doesn't need a type to implement all
21+
/// of `Pack`, but a size is still needed.
22+
pub trait PackedSizeOf {
23+
/// The packed size of the struct
24+
const SIZE_OF: usize;
25+
}
26+
1927
/// Mint data.
2028
#[repr(C)]
2129
#[derive(Clone, Copy, Debug, Default, PartialEq)]
@@ -86,6 +94,9 @@ impl Pack for Mint {
8694
pack_coption_key(freeze_authority, freeze_authority_dst);
8795
}
8896
}
97+
impl PackedSizeOf for Mint {
98+
const SIZE_OF: usize = Self::LEN;
99+
}
89100

90101
/// Account data.
91102
#[repr(C)]
@@ -184,6 +195,9 @@ impl Pack for Account {
184195
pack_coption_key(close_authority, close_authority_dst);
185196
}
186197
}
198+
impl PackedSizeOf for Account {
199+
const SIZE_OF: usize = Self::LEN;
200+
}
187201

188202
/// Account state.
189203
#[repr(u8)]
@@ -254,6 +268,9 @@ impl Pack for Multisig {
254268
}
255269
}
256270
}
271+
impl PackedSizeOf for Multisig {
272+
const SIZE_OF: usize = Self::LEN;
273+
}
257274

258275
// Helpers
259276
pub(crate) fn pack_coption_key(src: &COption<Pubkey>, dst: &mut [u8; 36]) {

0 commit comments

Comments
 (0)