use crate::sys::{externals::Memory, FromToNativeWasmType};
use crate::NativeWasmTypeInto;
use crate::{MemoryAccessError, WasmRef, WasmSlice};
use std::convert::TryFrom;
use std::{fmt, marker::PhantomData, mem};
use wasmer_types::ValueType;
use super::store::AsStoreRef;
pub use wasmer_types::Memory32;
pub use wasmer_types::Memory64;
pub use wasmer_types::MemorySize;
pub type WasmPtr64<T> = WasmPtr<T, Memory64>;
#[repr(transparent)]
pub struct WasmPtr<T, M: MemorySize = Memory32> {
offset: M::Offset,
_phantom: PhantomData<*mut T>,
}
impl<T, M: MemorySize> WasmPtr<T, M> {
#[inline]
pub fn new(offset: M::Offset) -> Self {
Self {
offset,
_phantom: PhantomData,
}
}
#[inline]
pub fn offset(self) -> M::Offset {
self.offset
}
#[inline]
pub fn cast<U>(self) -> WasmPtr<U, M> {
WasmPtr {
offset: self.offset,
_phantom: PhantomData,
}
}
#[inline]
pub fn null() -> Self {
Self::new(M::ZERO)
}
#[inline]
pub fn is_null(self) -> bool {
self.offset.into() == 0
}
#[inline]
pub fn add_offset(self, offset: M::Offset) -> Result<Self, MemoryAccessError> {
let base = self.offset.into();
let index = offset.into();
let offset = index
.checked_mul(mem::size_of::<T>() as u64)
.ok_or(MemoryAccessError::Overflow)?;
let address = base
.checked_add(offset)
.ok_or(MemoryAccessError::Overflow)?;
let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?;
Ok(Self::new(address))
}
#[inline]
pub fn sub_offset(self, offset: M::Offset) -> Result<Self, MemoryAccessError> {
let base = self.offset.into();
let index = offset.into();
let offset = index
.checked_mul(mem::size_of::<T>() as u64)
.ok_or(MemoryAccessError::Overflow)?;
let address = base
.checked_sub(offset)
.ok_or(MemoryAccessError::Overflow)?;
let address = M::Offset::try_from(address).map_err(|_| MemoryAccessError::Overflow)?;
Ok(Self::new(address))
}
}
impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
#[inline]
pub fn deref<'a>(self, store: &'a impl AsStoreRef, memory: &'a Memory) -> WasmRef<'a, T> {
WasmRef::new(store, memory, self.offset.into())
}
#[inline]
pub fn read(self, store: &impl AsStoreRef, memory: &Memory) -> Result<T, MemoryAccessError> {
self.deref(&store, memory).read()
}
#[inline]
pub fn write(
self,
store: &impl AsStoreRef,
memory: &Memory,
val: T,
) -> Result<(), MemoryAccessError> {
self.deref(&store, memory).write(val)
}
#[inline]
pub fn slice<'a>(
self,
store: &'a impl AsStoreRef,
memory: &'a Memory,
len: M::Offset,
) -> Result<WasmSlice<'a, T>, MemoryAccessError> {
WasmSlice::new(store, memory, self.offset.into(), len.into())
}
#[inline]
pub fn read_until(
self,
store: &impl AsStoreRef,
memory: &Memory,
mut end: impl FnMut(&T) -> bool,
) -> Result<Vec<T>, MemoryAccessError> {
let mut vec = Vec::new();
for i in 0u64.. {
let i = M::Offset::try_from(i).map_err(|_| MemoryAccessError::Overflow)?;
let val = self.add_offset(i)?.deref(&store, memory).read()?;
if end(&val) {
break;
}
vec.push(val);
}
Ok(vec)
}
}
impl<M: MemorySize> WasmPtr<u8, M> {
#[inline]
pub fn read_utf8_string(
self,
store: &impl AsStoreRef,
memory: &Memory,
len: M::Offset,
) -> Result<String, MemoryAccessError> {
let vec = self.slice(&store, memory, len)?.read_to_vec()?;
Ok(String::from_utf8(vec)?)
}
#[inline]
pub fn read_utf8_string_with_nul(
self,
store: &impl AsStoreRef,
memory: &Memory,
) -> Result<String, MemoryAccessError> {
let vec = self.read_until(store, memory, |&byte| byte == 0)?;
Ok(String::from_utf8(vec)?)
}
}
unsafe impl<T: ValueType, M: MemorySize> FromToNativeWasmType for WasmPtr<T, M>
where
<M as wasmer_types::MemorySize>::Native: NativeWasmTypeInto,
{
type Native = M::Native;
fn to_native(self) -> Self::Native {
M::offset_to_native(self.offset)
}
fn from_native(n: Self::Native) -> Self {
Self {
offset: M::native_to_offset(n),
_phantom: PhantomData,
}
}
}
unsafe impl<T: ValueType, M: MemorySize> ValueType for WasmPtr<T, M> {
fn zero_padding_bytes(&self, _bytes: &mut [mem::MaybeUninit<u8>]) {}
}
impl<T: ValueType, M: MemorySize> Clone for WasmPtr<T, M> {
fn clone(&self) -> Self {
Self {
offset: self.offset,
_phantom: PhantomData,
}
}
}
impl<T: ValueType, M: MemorySize> Copy for WasmPtr<T, M> {}
impl<T: ValueType, M: MemorySize> PartialEq for WasmPtr<T, M> {
fn eq(&self, other: &Self) -> bool {
self.offset.into() == other.offset.into()
}
}
impl<T: ValueType, M: MemorySize> Eq for WasmPtr<T, M> {}
impl<T: ValueType, M: MemorySize> fmt::Debug for WasmPtr<T, M> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"WasmPtr(offset: {}, pointer: {:#x})",
self.offset.into(),
self.offset.into()
)
}
}