Skip to content

Instantly share code, notes, and snippets.

@FrankC01
Created October 20, 2021 10:38
Show Gist options
  • Save FrankC01/b03937c5e8c74753eb552ca1e15ba8f8 to your computer and use it in GitHub Desktop.
Save FrankC01/b03937c5e8c74753eb552ca1e15ba8f8 to your computer and use it in GitHub Desktop.
Emulating Vector of Structures into Solana Account `data`
// 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