#![allow(clippy::missing_safety_doc)]
use solana_program::{
entrypoint::MAX_PERMITTED_DATA_INCREASE, program_error::ProgramError,
program_memory::sol_memset, pubkey::Pubkey,
};
use std::{ptr::NonNull, slice::from_raw_parts_mut};
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub(crate) struct Account {
pub(crate) borrow_state: u8,
is_signer: u8,
is_writable: u8,
executable: u8,
original_data_len: [u8; 4],
key: Pubkey,
owner: Pubkey,
lamports: u64,
pub(crate) data_len: u64,
}
macro_rules! get_original_data_len {
( $self:expr ) => {
unsafe { *(&(*$self).original_data_len as *const _ as *const u32) as usize }
};
}
macro_rules! set_original_data_len {
( $self:expr, $len:expr ) => {
unsafe {
*(&mut (*$self).original_data_len) = u32::to_le_bytes($len as u32);
}
};
}
#[repr(C)]
#[derive(Clone, PartialEq, Eq)]
pub struct AccountInfo {
pub(crate) raw: *mut Account,
}
impl AccountInfo {
#[inline(always)]
pub fn key(&self) -> &Pubkey {
unsafe { &(*self.raw).key }
}
#[inline(always)]
pub fn owner(&self) -> &Pubkey {
unsafe { &(*self.raw).owner }
}
#[inline(always)]
pub fn is_signer(&self) -> bool {
unsafe { (*self.raw).is_signer != 0 }
}
#[inline(always)]
pub fn is_writable(&self) -> bool {
unsafe { (*self.raw).is_writable != 0 }
}
#[inline(always)]
pub fn executable(&self) -> bool {
unsafe { (*self.raw).executable != 0 }
}
#[inline(always)]
pub fn data_len(&self) -> usize {
unsafe { (*self.raw).data_len as usize }
}
#[inline(always)]
pub fn data_is_empty(&self) -> bool {
self.data_len() == 0
}
#[rustversion::attr(since(1.72), allow(invalid_reference_casting))]
pub fn assign(&self, new_owner: &Pubkey) {
unsafe {
std::ptr::write_volatile(
&(*self.raw).owner as *const Pubkey as *mut [u8; 32],
new_owner.to_bytes(),
);
}
}
pub unsafe fn unchecked_borrow_lamports(&self) -> &u64 {
&(*self.raw).lamports
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn unchecked_borrow_mut_lamports(&self) -> &mut u64 {
&mut (*self.raw).lamports
}
pub unsafe fn unchecked_borrow_data(&self) -> &[u8] {
core::slice::from_raw_parts(self.data_ptr(), self.data_len())
}
#[allow(clippy::mut_from_ref)]
pub unsafe fn unchecked_borrow_mut_data(&self) -> &mut [u8] {
core::slice::from_raw_parts_mut(self.data_ptr(), self.data_len())
}
pub fn try_borrow_lamports(&self) -> Result<Ref<u64>, ProgramError> {
let borrow_state = unsafe { &mut (*self.raw).borrow_state };
if *borrow_state & 0b_1000_0000 != 0 {
return Err(ProgramError::AccountBorrowFailed);
}
if *borrow_state & 0b_0111_0000 == 0b_0111_0000 {
return Err(ProgramError::AccountBorrowFailed);
}
*borrow_state += 1 << LAMPORTS_SHIFT;
Ok(Ref {
value: unsafe { &(*self.raw).lamports },
state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
borrow_shift: LAMPORTS_SHIFT,
})
}
pub fn try_borrow_mut_lamports(&self) -> Result<RefMut<u64>, ProgramError> {
let borrow_state = unsafe { &mut (*self.raw).borrow_state };
if *borrow_state & 0b_1111_0000 != 0 {
return Err(ProgramError::AccountBorrowFailed);
}
*borrow_state |= 0b_1000_0000;
Ok(RefMut {
value: unsafe { &mut (*self.raw).lamports },
state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
borrow_mask: LAMPORTS_MASK,
})
}
pub fn try_borrow_data(&self) -> Result<Ref<[u8]>, ProgramError> {
let borrow_state = unsafe { &mut (*self.raw).borrow_state };
if *borrow_state & 0b_0000_1000 != 0 {
return Err(ProgramError::AccountBorrowFailed);
}
if *borrow_state & 0b0111 == 0b0111 {
return Err(ProgramError::AccountBorrowFailed);
}
*borrow_state += 1;
Ok(Ref {
value: unsafe { core::slice::from_raw_parts(self.data_ptr(), self.data_len()) },
state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
borrow_shift: DATA_SHIFT,
})
}
pub fn try_borrow_mut_data(&self) -> Result<RefMut<[u8]>, ProgramError> {
let borrow_state = unsafe { &mut (*self.raw).borrow_state };
if *borrow_state & 0b_0000_1111 != 0 {
return Err(ProgramError::AccountBorrowFailed);
}
*borrow_state |= 0b0000_1000;
Ok(RefMut {
value: unsafe { from_raw_parts_mut(self.data_ptr(), self.data_len()) },
state: unsafe { NonNull::new_unchecked(&mut (*self.raw).borrow_state) },
borrow_mask: DATA_MASK,
})
}
pub fn realloc(&self, new_len: usize, zero_init: bool) -> Result<(), ProgramError> {
let mut data = self.try_borrow_mut_data()?;
let current_len = data.len();
if new_len == current_len {
return Ok(());
}
let original_len = match get_original_data_len!(self.raw) {
len if len > 0 => len,
_ => {
set_original_data_len!(self.raw, current_len);
current_len
}
};
if new_len.saturating_sub(original_len) > MAX_PERMITTED_DATA_INCREASE {
return Err(ProgramError::InvalidRealloc);
}
unsafe {
let data_ptr = data.as_mut_ptr();
*(data_ptr.offset(-8) as *mut u64) = new_len as u64;
data.value = from_raw_parts_mut(data_ptr, new_len);
}
if zero_init {
let len_increase = new_len.saturating_sub(current_len);
if len_increase > 0 {
sol_memset(&mut data[original_len..], 0, len_increase);
}
}
Ok(())
}
fn data_ptr(&self) -> *mut u8 {
unsafe { (self.raw as *const _ as *mut u8).add(std::mem::size_of::<Account>()) }
}
}
const LAMPORTS_SHIFT: u8 = 4;
const DATA_SHIFT: u8 = 0;
pub struct Ref<'a, T: ?Sized> {
value: &'a T,
state: NonNull<u8>,
borrow_shift: u8,
}
impl<'a, T: ?Sized> core::ops::Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<'a, T: ?Sized> Drop for Ref<'a, T> {
fn drop(&mut self) {
unsafe { *self.state.as_mut() -= 1 << self.borrow_shift };
}
}
const LAMPORTS_MASK: u8 = 0b_0111_1111;
const DATA_MASK: u8 = 0b_1111_0111;
pub struct RefMut<'a, T: ?Sized> {
value: &'a mut T,
state: NonNull<u8>,
borrow_mask: u8,
}
impl<'a, T: ?Sized> core::ops::Deref for RefMut<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<'a, T: ?Sized> core::ops::DerefMut for RefMut<'a, T> {
fn deref_mut(&mut self) -> &mut <Self as core::ops::Deref>::Target {
self.value
}
}
impl<'a, T: ?Sized> Drop for RefMut<'a, T> {
fn drop(&mut self) {
unsafe { *self.state.as_mut() &= self.borrow_mask };
}
}