use {
super::*,
crate::account::{AccountView, RuntimeAccount},
alloc::vec::Vec,
core::ptr,
};
#[repr(C)]
#[cfg_attr(feature = "copy", derive(Copy))]
#[derive(Clone)]
pub struct AccountLayout {
pub borrow_state: u8,
pub is_signer: u8,
pub is_writable: u8,
pub executable: u8,
pub resize_delta: i32,
pub key: Address,
pub owner: Address,
pub lamports: u64,
pub data_len: u64,
}
#[allow(dead_code)]
#[derive(Clone, Copy, Debug)]
pub enum DecrementStrategy {
Strictly1,
Average1_05,
Average2,
}
#[inline]
pub fn simple_prng(seed: u64) -> u64 {
const A: u64 = 16_807;
const M: u64 = 2_147_483_647; let s = if seed == 0 { 1 } else { seed };
(A.wrapping_mul(s)) % M
}
pub fn generate_mock_entries(
num_entries: usize,
start_slot: u64,
strategy: DecrementStrategy,
) -> Vec<(u64, Hash)> {
let mut entries = Vec::with_capacity(num_entries);
let mut current_slot = start_slot;
for i in 0..num_entries {
let hash_byte = (i % 256) as u8;
let hash = [hash_byte; HASH_BYTES];
entries.push((current_slot, hash));
let random_val = simple_prng(i as u64);
let dec = match strategy {
DecrementStrategy::Strictly1 => 1,
DecrementStrategy::Average1_05 => {
if random_val.is_multiple_of(20) {
2
} else {
1
}
}
DecrementStrategy::Average2 => {
if random_val.is_multiple_of(2) {
1
} else {
3
}
}
};
current_slot = current_slot.saturating_sub(dec);
}
entries
}
pub fn build_slot_hashes_bytes(declared_len: u64, entries: &[(u64, Hash)]) -> Vec<u8> {
let mut data = alloc::vec![0u8; MAX_SIZE];
data[..NUM_ENTRIES_SIZE].copy_from_slice(&declared_len.to_le_bytes());
let mut offset = NUM_ENTRIES_SIZE;
for (slot, hash) in entries {
data[offset..offset + SLOT_SIZE].copy_from_slice(&slot.to_le_bytes());
data[offset + SLOT_SIZE..offset + ENTRY_SIZE].copy_from_slice(hash);
offset += ENTRY_SIZE;
}
data
}
#[inline]
pub fn create_mock_data(entries: &[(u64, Hash)]) -> Vec<u8> {
build_slot_hashes_bytes(entries.len() as u64, entries)
}
pub unsafe fn make_account_view(
key: Address,
data: &[u8],
borrow_state: u8,
) -> (AccountView, Vec<u64>) {
let hdr_size = mem::size_of::<AccountLayout>();
let total = hdr_size + data.len();
let words = total.div_ceil(8);
let mut backing: Vec<u64> = alloc::vec![0u64; words];
assert!(
mem::align_of::<u64>() >= mem::align_of::<AccountLayout>(),
"`backing` should be properly aligned to store an `AccountLayout` instance"
);
let hdr_ptr = backing.as_mut_ptr() as *mut AccountLayout;
ptr::write(
hdr_ptr,
AccountLayout {
borrow_state,
is_signer: 0,
is_writable: 0,
executable: 0,
resize_delta: 0,
key,
owner: Address::new_from_array([0u8; 32]),
lamports: 0,
data_len: data.len() as u64,
},
);
ptr::copy_nonoverlapping(
data.as_ptr(),
(hdr_ptr as *mut u8).add(hdr_size),
data.len(),
);
(
AccountView::new_unchecked(hdr_ptr as *mut RuntimeAccount),
backing,
)
}
#[test]
fn test_account_layout_compatibility() {
assert_eq!(
mem::size_of::<AccountLayout>(),
mem::size_of::<RuntimeAccount>(),
"Header size must match Account size"
);
assert_eq!(
mem::align_of::<AccountLayout>(),
mem::align_of::<RuntimeAccount>(),
"Header alignment must match Account alignment"
);
unsafe {
let test_header = AccountLayout {
borrow_state: 42,
is_signer: 1,
is_writable: 1,
executable: 0,
resize_delta: 100,
key: Address::new_from_array([1u8; 32]),
owner: Address::new_from_array([2u8; 32]),
lamports: 1000,
data_len: 256,
};
let account_ptr = &test_header as *const AccountLayout as *const RuntimeAccount;
let account_ref = &*account_ptr;
assert_eq!(
account_ref.borrow_state, 42,
"borrow_state field should be accessible and match"
);
assert_eq!(
account_ref.data_len, 256,
"data_len field should be accessible and match"
);
}
}