#![allow(unsafe_op_in_unsafe_fn)]
use std::{
ops::{Deref, DerefMut},
ptr::NonNull,
slice,
sync::atomic::{AtomicU32, Ordering::*},
};
use solana_pubkey::Pubkey;
use super::owned::OwnedAccount;
use super::{ALIGNMENT, AccountCore, STORAGE_UNIT, StorageUnit};
pub(super) const STATIC_SIZE: usize = size_of::<AccountCore>() + size_of::<DataHeader>();
pub(super) const IMAGE_OFFSET: usize =
(size_of::<AccountHeader>() + size_of::<Pubkey>()) / STORAGE_UNIT;
#[repr(C, align(8))]
pub(crate) struct AccountHeader {
pub(crate) sequence: AtomicU32,
pub(crate) space: u32,
}
impl AccountHeader {
pub(crate) fn new(space: u32) -> Self {
Self { sequence: 0.into(), space }
}
}
const _: () = assert!(size_of::<AccountHeader>() == ALIGNMENT);
const _: () = assert!(size_of::<AccountHeader>() == STORAGE_UNIT);
const _: () = assert!((size_of::<Pubkey>() + STORAGE_UNIT) / ALIGNMENT == IMAGE_OFFSET);
#[derive(Eq, PartialEq)]
pub struct BorrowedAccount {
pub(crate) header: NonNull<AccountHeader>,
pub(crate) core: NonNull<AccountCore>,
pub(crate) data: DataSlice,
pub(crate) version: u32,
}
#[inline]
fn offset(space: u32, sequence: u32, active: bool) -> usize {
let even = sequence.is_multiple_of(2);
let step = (active ^ even) as u32;
(step * space) as usize + IMAGE_OFFSET
}
impl BorrowedAccount {
pub(crate) fn sequence(&self) -> u32 {
unsafe { self.header.as_ref() }.sequence.load(Acquire)
}
pub unsafe fn span(ptr: NonNull<StorageUnit>) -> u32 {
let space = ptr.cast::<AccountHeader>().as_ref().space;
space * 2 + IMAGE_OFFSET as u32
}
pub unsafe fn pubkey(ptr: NonNull<StorageUnit>) -> Pubkey {
*ptr.add(1).cast().as_ref()
}
pub unsafe fn init(buffer: NonNull<StorageUnit>) -> Self {
let header = buffer.cast::<AccountHeader>();
let version = header.as_ref().sequence.load(Acquire);
let offset = offset(header.as_ref().space, version, true);
let core = header.add(offset).cast();
let data = DataSlice::init(core.add(1).cast());
Self { header, core, data, version }
}
pub unsafe fn translate(&mut self) {
let offset = offset(self.header.as_ref().space, self.version, false);
let dst = self.header.add(offset).cast();
let src = self.core.cast::<StorageUnit>();
if src == dst {
return;
}
let count = self.header.as_ref().space as usize;
dst.copy_from_nonoverlapping(src, count);
self.core = dst.cast();
self.data = DataSlice::init(self.core.add(1).cast());
}
pub fn commit(&self) {
let header = unsafe { self.header.as_ref() };
let shadow = unsafe {
self.header.add(offset(header.space, self.version, false)).cast::<AccountCore>()
};
if self.core != shadow {
return;
}
let next = self.version.wrapping_add(1);
let _ = header.sequence.compare_exchange(self.version, next, Release, Relaxed);
}
pub unsafe fn reset(&mut self) {
self.version = self.header.as_ref().sequence.load(Acquire);
let offset = offset(self.header.as_ref().space, self.version, true);
self.core = self.header.add(offset).cast();
self.data = DataSlice::init(self.core.add(1).cast());
}
pub unsafe fn rollback(&self) {
unsafe { self.header.as_ref().sequence.fetch_sub(1, Release) };
}
pub fn owner(&self) -> Pubkey {
unsafe { self.core.as_ref() }.owner
}
pub fn storage(&self) -> &[u8] {
let len = STATIC_SIZE + self.data.len();
unsafe { slice::from_raw_parts(self.core.as_ptr().cast(), len) }
}
}
impl From<&BorrowedAccount> for OwnedAccount {
fn from(value: &BorrowedAccount) -> Self {
Self {
core: *unsafe { value.core.as_ref() },
data: value.data.deref().to_vec().into(),
}
}
}
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct DataSlice {
header: NonNull<DataHeader>,
ptr: NonNull<u8>,
}
#[repr(C)]
pub(crate) struct DataHeader {
len: u32,
cap: u32,
}
impl DataHeader {
pub(crate) fn new(len: u32, allocation: u32) -> Self {
let cap = (allocation as usize * STORAGE_UNIT - STATIC_SIZE) as u32;
Self { len, cap }
}
}
impl DataSlice {
unsafe fn init(header: NonNull<DataHeader>) -> Self {
let ptr = header.add(1).cast();
Self { header, ptr }
}
pub(crate) fn len(&self) -> usize {
let header = unsafe { self.header.as_ref() };
header.len.min(header.cap) as usize
}
pub(crate) fn capacity(&self) -> usize {
let header = unsafe { self.header.as_ref() };
header.cap as usize
}
pub(crate) fn spare(&self) -> usize {
self.capacity() - self.len()
}
pub(crate) unsafe fn resize(&mut self, len: usize, val: u8) {
let prev = self.len();
debug_assert!(prev <= self.capacity());
debug_assert!(len <= self.capacity());
let delta = len.saturating_sub(prev);
if delta > 0 {
self.ptr.as_ptr().add(prev).write_bytes(val, delta);
}
self.header.as_mut().len = len as u32;
}
pub(crate) unsafe fn extend(&mut self, data: &[u8]) {
let len = self.len();
let dst = self.ptr.as_ptr().add(len);
dst.copy_from_nonoverlapping(data.as_ptr(), data.len());
self.header.as_mut().len += data.len() as u32;
}
pub(crate) unsafe fn set(&mut self, data: &[u8]) {
self.ptr.as_ptr().copy_from_nonoverlapping(data.as_ptr(), data.len());
self.header.as_mut().len = data.len() as u32;
}
}
impl Deref for DataSlice {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len()) }
}
}
impl DerefMut for DataSlice {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len()) }
}
}
unsafe impl Send for BorrowedAccount {}