Last active
January 24, 2024 18:39
-
-
Save FrankC01/abae44f481c67988820fbb8c2c836c27 to your computer and use it in GitHub Desktop.
Faux example of serializing BTreeMap to Solana State
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
use std::{collections::BTreeMap, iter::repeat}; | |
use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; | |
use borsh::{BorshDeserialize, BorshSerialize}; | |
const INITIALIZED_BYTES: usize = 1; | |
const TRACKING_CHUNK_LENGTH: usize = 4; | |
const TRACKING_CHUNK_BYTES: usize = 5116; | |
const TRACKING_ACCOUNT_STATE_SPACE: usize = | |
INITIALIZED_BYTES + TRACKING_CHUNK_LENGTH + TRACKING_CHUNK_BYTES; | |
/// Get the Borsh container count (le) from buffer | |
fn count_from_le(array: &[u8]) -> usize { | |
(array[0] as usize) | |
| (array[1] as usize) << 8 | |
| (array[2] as usize) << 16 | |
| (array[3] as usize) << 24 | |
} | |
/// Convert a u32 to an array | |
fn transform_u32_to_array_of_u8(x: u32) -> [u8; 4] { | |
let b1: u8 = ((x >> 24) & 0xff) as u8; | |
let b2: u8 = ((x >> 16) & 0xff) as u8; | |
let b3: u8 = ((x >> 8) & 0xff) as u8; | |
let b4: u8 = (x & 0xff) as u8; | |
[b4, b3, b2, b1] | |
} | |
#[derive(Debug, Default)] | |
struct FauxProgram { | |
initialized: bool, | |
data_map: BTreeMap<String, String>, | |
} | |
impl FauxProgram { | |
fn add_keypair(&mut self, key: &str, value: &str) { | |
self.data_map.insert(key.to_string(), value.to_string()); | |
} | |
fn drop_key(&mut self, key: &str) { | |
self.data_map.remove(key); | |
} | |
/// Emulate Pack | |
#[allow(clippy::ptr_offset_with_cast)] | |
fn pack_into_slice(&self, dst: &mut [u8]) { | |
let dst = array_mut_ref![dst, 0, TRACKING_ACCOUNT_STATE_SPACE]; | |
let (is_initialized_dst, hmap_len, hmap_dst) = mut_array_refs![ | |
dst, | |
INITIALIZED_BYTES, | |
TRACKING_CHUNK_LENGTH, | |
TRACKING_CHUNK_BYTES | |
]; | |
is_initialized_dst[0] = self.initialized as u8; | |
let data_ser = self.data_map.try_to_vec().unwrap(); | |
hmap_len[..].copy_from_slice(&transform_u32_to_array_of_u8(data_ser.len() as u32)); | |
hmap_dst[..data_ser.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, TRACKING_ACCOUNT_STATE_SPACE]; | |
let (is_initialized_src, hmap_len, hmap_src) = array_refs![ | |
src, | |
INITIALIZED_BYTES, | |
TRACKING_CHUNK_LENGTH, | |
TRACKING_CHUNK_BYTES | |
]; | |
let is_initialized = match is_initialized_src { | |
[0] => false, | |
[1] => true, | |
_ => panic!(), | |
}; | |
let mut map_dser = BTreeMap::<String, String>::new(); | |
let hmap_length = count_from_le(hmap_len); | |
if hmap_length > 0 { | |
map_dser = BTreeMap::<String, String>::try_from_slice(&hmap_src[0..hmap_length]).unwrap() | |
} | |
Self { | |
initialized: is_initialized, | |
data_map: map_dser, | |
} | |
} | |
} | |
fn main() { | |
// Fake data assumes account was setup for enough data | |
let key1 = String::from("Key1"); | |
let key2 = String::from("Key2"); | |
let value1 = String::from("Value1"); | |
let value2 = String::from("Value2"); | |
let mut faux_data: Vec<u8> = repeat(0u8).take(TRACKING_ACCOUNT_STATE_SPACE).collect(); | |
let mut account_state = FauxProgram::unpack_from_slice(&faux_data); | |
println!("Account state T0 = {:?}", account_state); | |
account_state.initialized = true; | |
account_state.add_keypair(&key1, &value1); | |
account_state.add_keypair(&key2, &value2); | |
account_state.pack_into_slice(&mut faux_data); | |
let mut account_state_next = FauxProgram::unpack_from_slice(&faux_data); | |
println!("Account state T1 = {:?}", account_state_next); | |
account_state_next.drop_key(&key1); | |
account_state_next.pack_into_slice(&mut faux_data); | |
let account_state_last = FauxProgram::unpack_from_slice(&faux_data); | |
println!("Account state T2 = {:?}", account_state_last); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment