use core::marker::PhantomData;
use crate::borrow_registry::BorrowToken;
use crate::compat::{BackendRef, BackendRefMut};
use crate::error::ProgramError;
#[cfg(target_os = "solana")]
pub struct Ref<'a, T: ?Sized> {
ptr: *const T,
state: *mut u8,
_marker: PhantomData<&'a T>,
}
#[cfg(not(target_os = "solana"))]
pub struct Ref<'a, T: ?Sized> {
ptr: *const T,
guard: BackendRef<'a, [u8]>,
token: BorrowToken,
_marker: PhantomData<&'a T>,
}
impl<'a> Ref<'a, [u8]> {
#[inline(always)]
pub(crate) fn from_backend(inner: BackendRef<'a, [u8]>, token: BorrowToken) -> Self {
#[cfg(target_os = "solana")]
{
let _ = token; let (bytes, state) = inner.into_raw_parts();
Self {
ptr: bytes as *const [u8],
state,
_marker: PhantomData,
}
}
#[cfg(not(target_os = "solana"))]
{
let ptr = (&*inner) as *const [u8];
Self {
ptr,
guard: inner,
token,
_marker: PhantomData,
}
}
}
#[inline(always)]
pub unsafe fn project<U: ?Sized>(self, ptr: *const U) -> Ref<'a, U> {
#[cfg(target_os = "solana")]
{
let state = self.state;
core::mem::forget(self);
Ref {
ptr,
state,
_marker: PhantomData,
}
}
#[cfg(not(target_os = "solana"))]
{
let Self { guard, token, .. } = self;
Ref {
ptr,
guard,
token,
_marker: PhantomData,
}
}
}
#[inline(always)]
pub fn slice_from(self, offset: usize) -> Ref<'a, [u8]> {
let bytes = unsafe { &*self.ptr };
let new_ptr = &bytes[offset..] as *const [u8];
unsafe { self.project(new_ptr) }
}
#[inline(always)]
pub fn slice(self, offset: usize, len: usize) -> Result<Ref<'a, [u8]>, ProgramError> {
let bytes = unsafe { &*self.ptr };
let end = offset
.checked_add(len)
.ok_or(ProgramError::ArithmeticOverflow)?;
if end > bytes.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let new_ptr = &bytes[offset..end] as *const [u8];
Ok(unsafe { self.project(new_ptr) })
}
#[inline(always)]
pub fn as_bytes_ptr(&self) -> *const u8 {
let bytes: &[u8] = self;
bytes.as_ptr()
}
}
impl<T: ?Sized> Ref<'_, T> {
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
self.ptr
}
}
impl<'a, T> Ref<'a, T> {
#[cfg(target_os = "solana")]
#[inline(always)]
pub(crate) fn from_segment(ptr: *const T, state: *mut u8) -> Self {
Self {
ptr,
state,
_marker: PhantomData,
}
}
}
impl<T: ?Sized> core::ops::Deref for Ref<'_, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
#[cfg(target_os = "solana")]
impl<T: ?Sized> Drop for Ref<'_, T> {
#[inline(always)]
fn drop(&mut self) {
unsafe {
let current = *self.state;
if current == 1 {
*self.state = hopper_native::NOT_BORROWED;
} else {
*self.state = current - 1;
}
}
}
}
#[cfg(target_os = "solana")]
pub struct RefMut<'a, T: ?Sized> {
ptr: *mut T,
state: *mut u8,
_marker: PhantomData<&'a mut T>,
}
#[cfg(not(target_os = "solana"))]
pub struct RefMut<'a, T: ?Sized> {
ptr: *mut T,
guard: BackendRefMut<'a, [u8]>,
token: BorrowToken,
_marker: PhantomData<&'a mut T>,
}
impl<'a> RefMut<'a, [u8]> {
#[inline(always)]
pub(crate) fn from_backend(inner: BackendRefMut<'a, [u8]>, token: BorrowToken) -> Self {
#[cfg(target_os = "solana")]
{
let _ = token;
let (bytes, state) = inner.into_raw_parts();
Self {
ptr: bytes as *mut [u8],
state,
_marker: PhantomData,
}
}
#[cfg(not(target_os = "solana"))]
{
let ptr = (&*inner as *const [u8]).cast_mut();
Self {
ptr,
guard: inner,
token,
_marker: PhantomData,
}
}
}
#[inline(always)]
pub unsafe fn project<U: ?Sized>(self, ptr: *mut U) -> RefMut<'a, U> {
#[cfg(target_os = "solana")]
{
let state = self.state;
core::mem::forget(self);
RefMut {
ptr,
state,
_marker: PhantomData,
}
}
#[cfg(not(target_os = "solana"))]
{
let Self { guard, token, .. } = self;
RefMut {
ptr,
guard,
token,
_marker: PhantomData,
}
}
}
#[inline(always)]
pub fn slice_from(self, offset: usize) -> RefMut<'a, [u8]> {
let bytes = unsafe { &mut *self.ptr };
let new_ptr = &mut bytes[offset..] as *mut [u8];
unsafe { self.project(new_ptr) }
}
#[inline(always)]
pub fn slice(self, offset: usize, len: usize) -> Result<RefMut<'a, [u8]>, ProgramError> {
let bytes = unsafe { &mut *self.ptr };
let end = offset
.checked_add(len)
.ok_or(ProgramError::ArithmeticOverflow)?;
if end > bytes.len() {
return Err(ProgramError::AccountDataTooSmall);
}
let new_ptr = &mut bytes[offset..end] as *mut [u8];
Ok(unsafe { self.project(new_ptr) })
}
#[inline(always)]
pub fn as_bytes_mut_ptr(&mut self) -> *mut u8 {
let bytes: &mut [u8] = self;
bytes.as_mut_ptr()
}
}
impl<'a, T> RefMut<'a, T> {
#[cfg(target_os = "solana")]
#[inline(always)]
pub(crate) fn from_segment(ptr: *mut T, state: *mut u8) -> Self {
Self {
ptr,
state,
_marker: PhantomData,
}
}
}
impl<T: ?Sized> RefMut<'_, T> {
#[inline(always)]
pub fn as_ptr(&self) -> *const T {
self.ptr
}
#[inline(always)]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr
}
}
impl<T: ?Sized> core::ops::Deref for RefMut<'_, T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T: ?Sized> core::ops::DerefMut for RefMut<'_, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
#[cfg(target_os = "solana")]
impl<T: ?Sized> Drop for RefMut<'_, T> {
#[inline(always)]
fn drop(&mut self) {
unsafe {
*self.state = hopper_native::NOT_BORROWED;
}
}
}
impl<T: ?Sized> core::fmt::Debug for Ref<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Ref")
.field("ptr", &self.ptr)
.finish_non_exhaustive()
}
}
impl<T: ?Sized> core::fmt::Debug for RefMut<'_, T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("RefMut")
.field("ptr", &self.ptr)
.finish_non_exhaustive()
}
}
#[cfg(target_os = "solana")]
const _: () = {
assert!(
core::mem::size_of::<Ref<'static, u64>>()
== core::mem::size_of::<usize>() * 2,
"Ref<T: Sized> on Solana must be exactly (ptr, state) = 2 words",
);
assert!(
core::mem::size_of::<RefMut<'static, u64>>()
== core::mem::size_of::<usize>() * 2,
"RefMut<T: Sized> on Solana must be exactly (ptr, state) = 2 words",
);
};