use crate::account::{FixedLayout, Pod};
use hopper_runtime::{error::ProgramError, AccountView, Address, Ref, RefMut};
#[derive(Clone, Copy)]
pub struct VirtualSlot {
pub account_index: u8,
pub require_owned: bool,
pub require_writable: bool,
}
impl VirtualSlot {
#[inline(always)]
pub const fn read_only(account_index: u8) -> Self {
Self {
account_index,
require_owned: true,
require_writable: false,
}
}
#[inline(always)]
pub const fn writable(account_index: u8) -> Self {
Self {
account_index,
require_owned: true,
require_writable: true,
}
}
#[inline(always)]
pub const fn foreign(account_index: u8) -> Self {
Self {
account_index,
require_owned: false,
require_writable: false,
}
}
}
pub struct VirtualState<const N: usize> {
slots: [VirtualSlot; N],
count: usize,
}
impl<const N: usize> VirtualState<N> {
#[inline(always)]
pub const fn new() -> Self {
Self {
slots: [VirtualSlot {
account_index: 0,
require_owned: false,
require_writable: false,
}; N],
count: 0,
}
}
#[inline(always)]
pub const fn map(mut self, slot: usize, account_index: u8) -> Self {
assert!(slot < N, "slot index out of bounds");
self.slots[slot] = VirtualSlot::read_only(account_index);
if slot >= self.count {
self.count = slot + 1;
}
self
}
#[inline(always)]
pub const fn map_mut(mut self, slot: usize, account_index: u8) -> Self {
assert!(slot < N, "slot index out of bounds");
self.slots[slot] = VirtualSlot::writable(account_index);
if slot >= self.count {
self.count = slot + 1;
}
self
}
#[inline(always)]
pub const fn map_foreign(mut self, slot: usize, account_index: u8) -> Self {
assert!(slot < N, "slot index out of bounds");
self.slots[slot] = VirtualSlot::foreign(account_index);
if slot >= self.count {
self.count = slot + 1;
}
self
}
#[inline(always)]
pub const fn set_slot(mut self, slot: usize, vs: VirtualSlot) -> Self {
assert!(slot < N, "slot index out of bounds");
self.slots[slot] = vs;
if slot >= self.count {
self.count = slot + 1;
}
self
}
#[inline(always)]
pub const fn slot_count(&self) -> usize {
self.count
}
#[inline]
pub fn validate(
&self,
accounts: &[AccountView],
program_id: &Address,
) -> Result<(), ProgramError> {
let mut i = 0;
while i < self.count {
let slot = &self.slots[i];
let idx = slot.account_index as usize;
if idx >= accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
let acc = &accounts[idx];
if slot.require_owned {
crate::check::check_owner(acc, program_id)?;
}
if slot.require_writable {
crate::check::check_writable(acc)?;
}
i += 1;
}
Ok(())
}
#[inline]
pub fn overlay<'a, T: Pod + FixedLayout>(
&self,
accounts: &'a [AccountView],
slot: usize,
) -> Result<Ref<'a, T>, ProgramError> {
if slot >= self.count {
return Err(ProgramError::InvalidArgument);
}
let idx = self.slots[slot].account_index as usize;
if idx >= accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
let acc = &accounts[idx];
unsafe { acc.raw_ref::<T>() }
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn overlay_mut<'a, T: Pod + FixedLayout>(
&self,
accounts: &'a [AccountView],
slot: usize,
) -> Result<RefMut<'a, T>, ProgramError> {
if slot >= self.count {
return Err(ProgramError::InvalidArgument);
}
let vs = &self.slots[slot];
if !vs.require_writable {
return Err(ProgramError::InvalidArgument);
}
let idx = vs.account_index as usize;
if idx >= accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
let acc = &accounts[idx];
unsafe { acc.raw_mut::<T>() }
}
#[inline]
pub fn data<'a>(
&self,
accounts: &'a [AccountView],
slot: usize,
) -> Result<Ref<'a, [u8]>, ProgramError> {
if slot >= self.count {
return Err(ProgramError::InvalidArgument);
}
let idx = self.slots[slot].account_index as usize;
if idx >= accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
accounts[idx].try_borrow()
}
#[inline]
pub fn account<'a>(
&self,
accounts: &'a [AccountView],
slot: usize,
) -> Result<&'a AccountView, ProgramError> {
if slot >= self.count {
return Err(ProgramError::InvalidArgument);
}
let idx = self.slots[slot].account_index as usize;
accounts.get(idx).ok_or(ProgramError::NotEnoughAccountKeys)
}
}
impl<const N: usize> Default for VirtualState<N> {
fn default() -> Self {
Self::new()
}
}
pub struct ShardedAccess<'a, const SHARDS: usize> {
accounts: &'a [AccountView],
shard_indices: [u8; SHARDS],
shard_count: usize,
}
impl<'a, const SHARDS: usize> ShardedAccess<'a, SHARDS> {
#[inline]
pub fn new(accounts: &'a [AccountView], shard_indices: &[u8]) -> Result<Self, ProgramError> {
if shard_indices.len() > SHARDS {
return Err(ProgramError::InvalidArgument);
}
let mut indices = [0u8; SHARDS];
let mut i = 0;
while i < shard_indices.len() {
if shard_indices[i] as usize >= accounts.len() {
return Err(ProgramError::NotEnoughAccountKeys);
}
indices[i] = shard_indices[i];
i += 1;
}
Ok(Self {
accounts,
shard_indices: indices,
shard_count: shard_indices.len(),
})
}
#[inline(always)]
pub fn shard_for_key(&self, key: &[u8]) -> usize {
let mut hash: u32 = 0x811c_9dc5;
let mut i = 0;
while i < key.len() {
hash ^= key[i] as u32;
hash = hash.wrapping_mul(0x0100_0193);
i += 1;
}
(hash as usize) % self.shard_count
}
#[inline]
pub fn shard_account(&self, shard: usize) -> Result<&'a AccountView, ProgramError> {
if shard >= self.shard_count {
return Err(ProgramError::InvalidArgument);
}
let idx = self.shard_indices[shard] as usize;
self.accounts
.get(idx)
.ok_or(ProgramError::NotEnoughAccountKeys)
}
#[inline]
pub fn data_for_key(&self, key: &[u8]) -> Result<Ref<'a, [u8]>, ProgramError> {
let shard = self.shard_for_key(key);
let acc = self.shard_account(shard)?;
acc.try_borrow()
}
#[inline(always)]
pub fn shard_count(&self) -> usize {
self.shard_count
}
}