Skip to content

Instantly share code, notes, and snippets.

@FrankC01
Last active January 24, 2024 18:39
Show Gist options
  • Save FrankC01/abae44f481c67988820fbb8c2c836c27 to your computer and use it in GitHub Desktop.
Save FrankC01/abae44f481c67988820fbb8c2c836c27 to your computer and use it in GitHub Desktop.
Faux example of serializing BTreeMap to Solana State
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