mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-09 22:28:21 +00:00
All IO operates are now zero copy when on non-browsers while browsers still copy bytes
This commit is contained in:
223
lib/api/src/access.rs
Normal file
223
lib/api/src/access.rs
Normal file
@@ -0,0 +1,223 @@
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use crate::{WasmRef, WasmSlice};
|
||||
|
||||
pub(super) enum SliceCow<'a, T> {
|
||||
#[allow(dead_code)]
|
||||
Borrowed(&'a mut [T]),
|
||||
#[allow(dead_code)]
|
||||
Owned(Vec<T>, bool),
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<[T]> for SliceCow<'a, T> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
match self {
|
||||
Self::Borrowed(buf) => buf.as_ref(),
|
||||
Self::Owned(buf, _) => buf.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsMut<[T]> for SliceCow<'a, T> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
// Note: Zero padding is not required here as its a typed copy which does
|
||||
// not leak the bytes into the memory
|
||||
// https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes
|
||||
match self {
|
||||
Self::Borrowed(buf) => buf,
|
||||
Self::Owned(buf, modified) => {
|
||||
*modified = true;
|
||||
buf.as_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides direct memory access to a piece of memory that
|
||||
/// is owned by WASM
|
||||
pub struct WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
pub(super) slice: WasmSlice<'a, T>,
|
||||
pub(super) buf: SliceCow<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<[T]> for WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.buf.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsMut<[T]> for WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.buf.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
/// Returns an iterator of all the elements in the slice
|
||||
pub fn iter(&'a self) -> std::slice::Iter<'a, T> {
|
||||
self.as_ref().iter()
|
||||
}
|
||||
|
||||
/// Returns an iterator of all the elements in the slice
|
||||
pub fn iter_mut(&'a mut self) -> std::slice::IterMut<'a, T> {
|
||||
self.buf.as_mut().iter_mut()
|
||||
}
|
||||
|
||||
/// Number of elements in this slice
|
||||
pub fn len(&self) -> usize {
|
||||
self.buf.as_ref().len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WasmSliceAccess<'a, u8> {
|
||||
/// Writes to the address pointed to by this `WasmPtr` in a memory.
|
||||
#[inline]
|
||||
pub fn copy_from_slice(&mut self, src: &[u8]) {
|
||||
let dst = self.buf.as_mut();
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if let SliceCow::Owned(buf, modified) = &self.buf {
|
||||
if *modified == true {
|
||||
self.slice.write_slice(buf.as_ref()).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum RefCow<'a, T> {
|
||||
#[allow(dead_code)]
|
||||
Borrowed(&'a mut T),
|
||||
#[allow(dead_code)]
|
||||
Owned(T, bool),
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<T> for RefCow<'a, T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
match self {
|
||||
Self::Borrowed(val) => *val,
|
||||
Self::Owned(val, _) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsMut<T> for RefCow<'a, T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
// Note: Zero padding is not required here as its a typed copy which does
|
||||
// not leak the bytes into the memory
|
||||
// https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes
|
||||
match self {
|
||||
Self::Borrowed(val) => *val,
|
||||
Self::Owned(val, modified) => {
|
||||
*modified = true;
|
||||
val
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides direct memory access to a piece of memory that
|
||||
/// is owned by WASM
|
||||
pub struct WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
pub(super) ptr: WasmRef<'a, T>,
|
||||
pub(super) buf: RefCow<'a, T>,
|
||||
}
|
||||
|
||||
impl<'a, T> AsRef<T> for WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn as_ref(&self) -> &T {
|
||||
self.buf.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsMut<T> for WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
self.buf.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
/// Reads the address pointed to by this `WasmPtr` in a memory.
|
||||
#[inline]
|
||||
pub fn read(&self) -> T
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
self.as_ref().clone()
|
||||
}
|
||||
|
||||
/// Writes to the address pointed to by this `WasmPtr` in a memory.
|
||||
#[inline]
|
||||
pub fn write(&mut self, val: T) {
|
||||
// Note: Zero padding is not required here as its a typed copy which does
|
||||
// not leak the bytes into the memory
|
||||
// https://stackoverflow.com/questions/61114026/does-stdptrwrite-transfer-the-uninitialized-ness-of-the-bytes-it-writes
|
||||
*(self.as_mut()) = val;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
if let RefCow::Owned(val, modified) = &self.buf {
|
||||
if *modified == true {
|
||||
self.ptr.write(*val).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
/// Returns a mutable slice that is not yet initialized
|
||||
pub fn as_mut_uninit(&mut self) -> &mut [MaybeUninit<T>] {
|
||||
let ret: &mut [T] = self.buf.as_mut();
|
||||
let ret: &mut [MaybeUninit<T>] = unsafe { std::mem::transmute(ret) };
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
/// Returns a reference to an unitialized reference to this value
|
||||
pub fn as_mut_uninit(&mut self) -> &mut MaybeUninit<T> {
|
||||
let ret: &mut T = self.buf.as_mut();
|
||||
let ret: &mut MaybeUninit<T> = unsafe { std::mem::transmute(ret) };
|
||||
ret
|
||||
}
|
||||
}
|
||||
@@ -405,3 +405,29 @@ impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {}
|
||||
|
||||
impl<'a, T> WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn new(slice: WasmSlice<'a, T>) -> Result<Self, MemoryAccessError> {
|
||||
let buf = slice.read_to_vec()?;
|
||||
Ok(Self {
|
||||
slice,
|
||||
buf: SliceCow::Owned(buf),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn new(ptr: WasmRef<'a, T>) -> Result<Self, MemoryAccessError> {
|
||||
let val = slice.read()?;
|
||||
Ok(Self {
|
||||
ptr,
|
||||
buf: RefCow::Owned(val),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,5 +441,7 @@ mod js;
|
||||
#[cfg(feature = "js")]
|
||||
pub use js::*;
|
||||
|
||||
mod access;
|
||||
mod into_bytes;
|
||||
pub use access::WasmSliceAccess;
|
||||
pub use into_bytes::IntoBytes;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::RuntimeError;
|
||||
use crate::{
|
||||
access::{RefCow, SliceCow, WasmRefAccess},
|
||||
RuntimeError, WasmSliceAccess,
|
||||
};
|
||||
#[allow(unused_imports)]
|
||||
use crate::{Memory, Memory32, Memory64, MemorySize, MemoryView, WasmPtr};
|
||||
use std::{
|
||||
@@ -102,26 +105,19 @@ impl<'a, T: ValueType> WasmRef<'a, T> {
|
||||
/// Reads the location pointed to by this `WasmRef`.
|
||||
#[inline]
|
||||
pub fn read(self) -> Result<T, MemoryAccessError> {
|
||||
let mut out = MaybeUninit::uninit();
|
||||
let buf =
|
||||
unsafe { slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, mem::size_of::<T>()) };
|
||||
self.buffer.read(self.offset, buf)?;
|
||||
Ok(unsafe { out.assume_init() })
|
||||
Ok(self.access()?.read())
|
||||
}
|
||||
|
||||
/// Writes to the location pointed to by this `WasmRef`.
|
||||
#[inline]
|
||||
pub fn write(self, val: T) -> Result<(), MemoryAccessError> {
|
||||
let mut data = MaybeUninit::new(val);
|
||||
let data = unsafe {
|
||||
slice::from_raw_parts_mut(
|
||||
data.as_mut_ptr() as *mut MaybeUninit<u8>,
|
||||
mem::size_of::<T>(),
|
||||
)
|
||||
};
|
||||
val.zero_padding_bytes(data);
|
||||
let data = unsafe { slice::from_raw_parts(data.as_ptr() as *const _, data.len()) };
|
||||
self.buffer.write(self.offset, data)
|
||||
Ok(self.access()?.write(val))
|
||||
}
|
||||
|
||||
/// Gains direct access to the memory of this slice
|
||||
#[inline]
|
||||
pub fn access(self) -> Result<WasmRefAccess<'a, T>, MemoryAccessError> {
|
||||
WasmRefAccess::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,6 +236,12 @@ impl<'a, T: ValueType> WasmSlice<'a, T> {
|
||||
WasmSliceIter { slice: self }
|
||||
}
|
||||
|
||||
/// Gains direct access to the memory of this slice
|
||||
#[inline]
|
||||
pub fn access(self) -> Result<WasmSliceAccess<'a, T>, MemoryAccessError> {
|
||||
WasmSliceAccess::new(self)
|
||||
}
|
||||
|
||||
/// Reads an element of this slice.
|
||||
#[inline]
|
||||
pub fn read(self, idx: u64) -> Result<T, MemoryAccessError> {
|
||||
@@ -404,3 +406,66 @@ impl<'a, T: ValueType> DoubleEndedIterator for WasmSliceIter<'a, T> {
|
||||
}
|
||||
|
||||
impl<'a, T: ValueType> ExactSizeIterator for WasmSliceIter<'a, T> {}
|
||||
|
||||
impl<'a, T> WasmSliceAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn new(slice: WasmSlice<'a, T>) -> Result<Self, MemoryAccessError> {
|
||||
let total_len = slice
|
||||
.len
|
||||
.checked_mul(mem::size_of::<T>() as u64)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
let end = slice
|
||||
.offset
|
||||
.checked_add(total_len)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > slice.buffer.len as u64 {
|
||||
#[cfg(feature = "tracing")]
|
||||
warn!(
|
||||
"attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
|
||||
total_len, end, slice.buffer.len
|
||||
);
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
let buf = unsafe {
|
||||
let buf_ptr: *mut u8 = slice.buffer.base.add(slice.offset as usize);
|
||||
let buf_ptr: *mut T = std::mem::transmute(buf_ptr);
|
||||
std::slice::from_raw_parts_mut(buf_ptr, slice.len as usize)
|
||||
};
|
||||
Ok(Self {
|
||||
slice,
|
||||
buf: SliceCow::Borrowed(buf),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> WasmRefAccess<'a, T>
|
||||
where
|
||||
T: wasmer_types::ValueType,
|
||||
{
|
||||
fn new(ptr: WasmRef<'a, T>) -> Result<Self, MemoryAccessError> {
|
||||
let total_len = mem::size_of::<T>() as u64;
|
||||
let end = ptr
|
||||
.offset
|
||||
.checked_add(total_len)
|
||||
.ok_or(MemoryAccessError::Overflow)?;
|
||||
if end > ptr.buffer.len as u64 {
|
||||
#[cfg(feature = "tracing")]
|
||||
warn!(
|
||||
"attempted to read ({} bytes) beyond the bounds of the memory view ({} > {})",
|
||||
total_len, end, ptr.buffer.len
|
||||
);
|
||||
return Err(MemoryAccessError::HeapOutOfBounds);
|
||||
}
|
||||
let val = unsafe {
|
||||
let val_ptr: *mut u8 = ptr.buffer.base.add(ptr.offset as usize);
|
||||
let val_ptr: *mut T = std::mem::transmute(val_ptr);
|
||||
&mut *val_ptr
|
||||
};
|
||||
Ok(Self {
|
||||
ptr,
|
||||
buf: RefCow::Borrowed(val),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::access::WasmRefAccess;
|
||||
use crate::sys::{externals::MemoryView, FromToNativeWasmType};
|
||||
use crate::NativeWasmTypeInto;
|
||||
use crate::{MemoryAccessError, WasmRef, WasmSlice};
|
||||
@@ -193,6 +194,15 @@ impl<T: ValueType, M: MemorySize> WasmPtr<T, M> {
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
/// Creates a `WasmAccess`
|
||||
#[inline]
|
||||
pub fn access<'a>(
|
||||
&self,
|
||||
view: &'a MemoryView,
|
||||
) -> Result<WasmRefAccess<'a, T>, MemoryAccessError> {
|
||||
self.deref(view).access()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M: MemorySize> WasmPtr<u8, M> {
|
||||
|
||||
Reference in New Issue
Block a user