Created
October 20, 2021 10:38
-
-
Save FrankC01/b03937c5e8c74753eb552ca1e15ba8f8 to your computer and use it in GitHub Desktop.
Emulating Vector of Structures into Solana Account `data`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Used to map pointers into data array | |
use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; | |
use borsh::{BorshDeserialize, BorshSerialize}; | |
use std::iter::repeat; | |
const INITIALIZED_BYTES: usize = 1; | |
const VECTOR_CHUNK_LENGTH: usize = 4; | |
const VECTOR_CHUNK_BYTES: usize = 5116; | |
const DATA_ACCOUNT_STATE_SPACE: usize = | |
INITIALIZED_BYTES + VECTOR_CHUNK_LENGTH + VECTOR_CHUNK_BYTES; | |
#[derive(Debug, BorshDeserialize, BorshSerialize, Default)] | |
struct FauxStructure { | |
somestring: String, | |
somecount: u32, | |
} | |
#[derive(Debug, BorshDeserialize, BorshSerialize, Default)] | |
struct FauxProgram { | |
initialized: bool, | |
data_vec: Vec<FauxStructure>, | |
} | |
impl FauxProgram { | |
/// Add a new FauxStructure to the vector | |
fn add_struct(&mut self, mystruct: FauxStructure) { | |
self.data_vec.push(mystruct); | |
self.drop_first() | |
} | |
/// Drops the first element if there are 2 or more | |
fn drop_first(&mut self) { | |
// If I have more than one entry | |
// remove the 0th | |
if self.data_vec.len() > 2 { | |
self.data_vec.remove(0); | |
} | |
// Do nothing | |
else { | |
} | |
} | |
/// Emulate Pack | |
#[allow(clippy::ptr_offset_with_cast)] | |
fn pack_into_slice(&self, dst: &mut [u8]) { | |
// Setup a pointer to the overall data | |
let dst = array_mut_ref![dst, 0, DATA_ACCOUNT_STATE_SPACE]; | |
// Convert array to three (3) reference pointers | |
let (is_initialized_dst, vec_len, vec_dst) = mut_array_refs![ | |
dst, | |
INITIALIZED_BYTES, | |
VECTOR_CHUNK_LENGTH, | |
VECTOR_CHUNK_BYTES | |
]; | |
is_initialized_dst[0] = self.initialized as u8; | |
// Serialize the vector using Borsh | |
let data_ser = self.data_vec.try_to_vec().unwrap(); | |
// Get the resulting length in bytes | |
let data_len = data_ser.len(); | |
// Store the length (used in later unpacking/deserializing) | |
vec_len[..].copy_from_slice(&(data_len as u32).to_le_bytes()); | |
// Store the data | |
vec_dst[..data_len].copy_from_slice(&data_ser); | |
} | |
/// Emulate Unpack | |
#[allow(clippy::ptr_offset_with_cast)] | |
fn unpack_from_slice(src: &[u8]) -> Self { | |
let src = array_ref![src, 0, DATA_ACCOUNT_STATE_SPACE]; | |
let (is_initialized_src, vec_len, vec_src) = array_refs![ | |
src, | |
INITIALIZED_BYTES, | |
VECTOR_CHUNK_LENGTH, | |
VECTOR_CHUNK_BYTES | |
]; | |
let is_initialized = match is_initialized_src { | |
[0] => false, | |
[1] => true, | |
_ => panic!(), | |
}; | |
// Setup default empty vector if first use | |
let mut vec_dser = Vec::<FauxStructure>::new(); | |
let data_length = u32::from_le_bytes(*vec_len) as usize; | |
// Has data, deseriaalize to vector of structures | |
if data_length > 0 { | |
vec_dser = Vec::<FauxStructure>::try_from_slice(&vec_src[0..data_length]).unwrap() | |
} | |
// Return instance of encapsulating structure | |
Self { | |
initialized: is_initialized, | |
data_vec: vec_dser, | |
} | |
} | |
} | |
fn main() { | |
// Setup the 'emulated' account data space | |
let mut faux_data: Vec<u8> = repeat(0u8).take(DATA_ACCOUNT_STATE_SPACE).collect(); | |
// Emulate unpacking from an accounts data area | |
let mut account_state = FauxProgram::unpack_from_slice(&faux_data); | |
println!("Account initial state T0 = {:?}", account_state); | |
// Should always initialize the state before instruction data | |
account_state.initialized = true; | |
// Add 1st fake structure destined to reside in account's data space | |
account_state.add_struct(FauxStructure { | |
somestring: String::from("first"), | |
somecount: 1u32, | |
}); | |
println!("Account state T1 (added first) = {:?}", account_state); | |
// Add 2nd fake structure destined to reside in account's data space | |
account_state.add_struct(FauxStructure { | |
somestring: String::from("second"), | |
somecount: 2u32, | |
}); | |
println!( | |
"Account state T2 (added second and packing) = {:?}", | |
account_state | |
); | |
// Emulate packing into account data space | |
account_state.pack_into_slice(&mut faux_data); | |
// Emulate unpacking from an accounts data area | |
let mut account_state_next = FauxProgram::unpack_from_slice(&faux_data); | |
println!( | |
"Account state T3 (after unpacked) = {:?}", | |
account_state_next | |
); | |
// Add 3rd fake structure destined to resode in account's data space | |
account_state_next.add_struct(FauxStructure { | |
somestring: String::from("third"), | |
somecount: 3u32, | |
}); | |
println!( | |
"Account state T4 (added third and packing) = {:?}", | |
account_state | |
); | |
// Emulate packing into account data space | |
account_state_next.pack_into_slice(&mut faux_data); | |
let account_state_last = FauxProgram::unpack_from_slice(&faux_data); | |
println!("Account state end mock = {:?}", account_state_last); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment