use std::cell::Cell;
use std::fmt;
use std::marker;
use std::rc::Rc;
use std::slice;
use std::str;
use std::sync::Arc;
pub use wiggle_macro::from_witx;
#[cfg(feature = "wiggle_metadata")]
pub use witx;
mod error;
mod guest_type;
mod region;
pub extern crate tracing;
pub use error::GuestError;
pub use guest_type::{GuestErrorType, GuestType, GuestTypeTransparent};
pub use region::Region;
pub unsafe trait GuestMemory {
fn base(&self) -> (*mut u8, u32);
fn validate_size_align(
&self,
offset: u32,
align: usize,
len: u32,
) -> Result<*mut u8, GuestError> {
let (base_ptr, base_len) = self.base();
let region = Region { start: offset, len };
let start = match (base_ptr as usize).checked_add(offset as usize) {
Some(ptr) => ptr,
None => return Err(GuestError::PtrOverflow),
};
let end = match start.checked_add(len as usize) {
Some(ptr) => ptr,
None => return Err(GuestError::PtrOverflow),
};
if end > (base_ptr as usize) + (base_len as usize) {
return Err(GuestError::PtrOutOfBounds(region));
}
if start % align != 0 {
return Err(GuestError::PtrNotAligned(region, align as u32));
}
Ok(start as *mut u8)
}
fn ptr<'a, T>(&'a self, offset: T::Pointer) -> GuestPtr<'a, T>
where
Self: Sized,
T: ?Sized + Pointee,
{
GuestPtr::new(self, offset)
}
fn has_outstanding_borrows(&self) -> bool;
fn is_borrowed(&self, r: Region) -> bool;
fn borrow(&self, r: Region) -> Result<BorrowHandle, GuestError>;
fn unborrow(&self, h: BorrowHandle);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct BorrowHandle(pub usize);
unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a T {
fn base(&self) -> (*mut u8, u32) {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_borrowed(&self, r: Region) -> bool {
T::is_borrowed(self, r)
}
fn borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::borrow(self, r)
}
fn unborrow(&self, h: BorrowHandle) {
T::unborrow(self, h)
}
}
unsafe impl<'a, T: ?Sized + GuestMemory> GuestMemory for &'a mut T {
fn base(&self) -> (*mut u8, u32) {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_borrowed(&self, r: Region) -> bool {
T::is_borrowed(self, r)
}
fn borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::borrow(self, r)
}
fn unborrow(&self, h: BorrowHandle) {
T::unborrow(self, h)
}
}
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Box<T> {
fn base(&self) -> (*mut u8, u32) {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_borrowed(&self, r: Region) -> bool {
T::is_borrowed(self, r)
}
fn borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::borrow(self, r)
}
fn unborrow(&self, h: BorrowHandle) {
T::unborrow(self, h)
}
}
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Rc<T> {
fn base(&self) -> (*mut u8, u32) {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_borrowed(&self, r: Region) -> bool {
T::is_borrowed(self, r)
}
fn borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::borrow(self, r)
}
fn unborrow(&self, h: BorrowHandle) {
T::unborrow(self, h)
}
}
unsafe impl<T: ?Sized + GuestMemory> GuestMemory for Arc<T> {
fn base(&self) -> (*mut u8, u32) {
T::base(self)
}
fn has_outstanding_borrows(&self) -> bool {
T::has_outstanding_borrows(self)
}
fn is_borrowed(&self, r: Region) -> bool {
T::is_borrowed(self, r)
}
fn borrow(&self, r: Region) -> Result<BorrowHandle, GuestError> {
T::borrow(self, r)
}
fn unborrow(&self, h: BorrowHandle) {
T::unborrow(self, h)
}
}
pub struct GuestPtr<'a, T: ?Sized + Pointee> {
mem: &'a (dyn GuestMemory + 'a),
pointer: T::Pointer,
_marker: marker::PhantomData<&'a Cell<T>>,
}
impl<'a, T: ?Sized + Pointee> GuestPtr<'a, T> {
pub fn new(mem: &'a (dyn GuestMemory + 'a), pointer: T::Pointer) -> GuestPtr<'a, T> {
GuestPtr {
mem,
pointer,
_marker: marker::PhantomData,
}
}
pub fn offset(&self) -> T::Pointer {
self.pointer
}
pub fn mem(&self) -> &'a (dyn GuestMemory + 'a) {
self.mem
}
pub fn cast<U>(&self) -> GuestPtr<'a, U>
where
T: Pointee<Pointer = u32>,
{
GuestPtr::new(self.mem, self.pointer)
}
pub fn read(&self) -> Result<T, GuestError>
where
T: GuestType<'a>,
{
T::read(self)
}
pub fn write(&self, val: T) -> Result<(), GuestError>
where
T: GuestType<'a>,
{
T::write(self, val)
}
pub fn add(&self, amt: u32) -> Result<GuestPtr<'a, T>, GuestError>
where
T: GuestType<'a> + Pointee<Pointer = u32>,
{
let offset = amt
.checked_mul(T::guest_size())
.and_then(|o| self.pointer.checked_add(o));
let offset = match offset {
Some(o) => o,
None => return Err(GuestError::PtrOverflow),
};
Ok(GuestPtr::new(self.mem, offset))
}
pub fn as_array(&self, elems: u32) -> GuestPtr<'a, [T]>
where
T: GuestType<'a> + Pointee<Pointer = u32>,
{
GuestPtr::new(self.mem, (self.pointer, elems))
}
}
impl<'a, T> GuestPtr<'a, [T]> {
pub fn offset_base(&self) -> u32 {
self.pointer.0
}
pub fn len(&self) -> u32 {
self.pointer.1
}
pub fn iter<'b>(
&'b self,
) -> impl ExactSizeIterator<Item = Result<GuestPtr<'a, T>, GuestError>> + 'b
where
T: GuestType<'a>,
{
let base = self.as_ptr();
(0..self.len()).map(move |i| base.add(i))
}
pub fn as_slice(&self) -> Result<GuestSlice<'a, T>, GuestError>
where
T: GuestTypeTransparent<'a>,
{
let len = match self.pointer.1.checked_mul(T::guest_size()) {
Some(l) => l,
None => return Err(GuestError::PtrOverflow),
};
let ptr =
self.mem
.validate_size_align(self.pointer.0, T::guest_align(), len)? as *mut T;
let borrow = self.mem.borrow(Region {
start: self.pointer.0,
len,
})?;
for offs in 0..self.pointer.1 {
T::validate(unsafe { ptr.add(offs as usize) })?;
}
let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
Ok(GuestSlice {
ptr,
mem: self.mem,
borrow,
})
}
pub fn copy_from_slice(&self, slice: &[T]) -> Result<(), GuestError>
where
T: GuestTypeTransparent<'a> + Copy,
{
let mut self_slice = self.as_slice()?;
if self_slice.len() != slice.len() {
return Err(GuestError::SliceLengthsDiffer);
}
self_slice.copy_from_slice(slice);
Ok(())
}
pub fn as_ptr(&self) -> GuestPtr<'a, T> {
GuestPtr::new(self.mem, self.offset_base())
}
pub fn get(&self, index: u32) -> Option<GuestPtr<'a, T>>
where
T: GuestType<'a>,
{
if index < self.len() {
Some(
self.as_ptr()
.add(index)
.expect("just performed bounds check"),
)
} else {
None
}
}
pub fn get_range(&self, r: std::ops::Range<u32>) -> Option<GuestPtr<'a, [T]>>
where
T: GuestType<'a>,
{
if r.end < r.start {
return None;
}
let range_length = r.end - r.start;
if r.start <= self.len() && r.end <= self.len() {
Some(
self.as_ptr()
.add(r.start)
.expect("just performed bounds check")
.as_array(range_length),
)
} else {
None
}
}
}
impl<'a> GuestPtr<'a, str> {
pub fn offset_base(&self) -> u32 {
self.pointer.0
}
pub fn len(&self) -> u32 {
self.pointer.1
}
pub fn as_bytes(&self) -> GuestPtr<'a, [u8]> {
GuestPtr::new(self.mem, self.pointer)
}
pub fn as_byte_ptr(&self) -> GuestPtr<'a, [u8]> {
GuestPtr::new(self.mem, self.pointer)
}
pub fn as_str(&self) -> Result<GuestStr<'a>, GuestError> {
let ptr = self
.mem
.validate_size_align(self.pointer.0, 1, self.pointer.1)?;
let borrow = self.mem.borrow(Region {
start: self.pointer.0,
len: self.pointer.1,
})?;
let ptr = unsafe { slice::from_raw_parts_mut(ptr, self.pointer.1 as usize) };
match str::from_utf8_mut(ptr) {
Ok(ptr) => Ok(GuestStr {
ptr,
mem: self.mem,
borrow,
}),
Err(e) => Err(GuestError::InvalidUtf8(e)),
}
}
}
impl<'a> GuestPtr<'a, [u8]> {
pub fn as_str_ptr(&self) -> GuestPtr<'a, str> {
GuestPtr::new(self.mem, self.pointer)
}
}
impl<T: ?Sized + Pointee> Clone for GuestPtr<'_, T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + Pointee> Copy for GuestPtr<'_, T> {}
impl<T: ?Sized + Pointee> fmt::Debug for GuestPtr<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
T::debug(self.pointer, f)
}
}
pub struct GuestSlice<'a, T> {
ptr: &'a mut [T],
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
impl<'a, T> std::ops::Deref for GuestSlice<'a, T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<'a, T> std::ops::DerefMut for GuestSlice<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
impl<'a, T> Drop for GuestSlice<'a, T> {
fn drop(&mut self) {
self.mem.unborrow(self.borrow)
}
}
pub struct GuestStr<'a> {
ptr: &'a mut str,
mem: &'a dyn GuestMemory,
borrow: BorrowHandle,
}
impl<'a> std::ops::Deref for GuestStr<'a> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.ptr
}
}
impl<'a> std::ops::DerefMut for GuestStr<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.ptr
}
}
impl<'a> Drop for GuestStr<'a> {
fn drop(&mut self) {
self.mem.unborrow(self.borrow)
}
}
mod private {
pub trait Sealed {}
impl<T> Sealed for T {}
impl<T> Sealed for [T] {}
impl Sealed for str {}
}
pub trait Pointee: private::Sealed {
#[doc(hidden)]
type Pointer: Copy;
#[doc(hidden)]
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result;
}
impl<T> Pointee for T {
type Pointer = u32;
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "*guest {:#x}", pointer)
}
}
impl<T> Pointee for [T] {
type Pointer = (u32, u32);
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "*guest {:#x}/{}", pointer.0, pointer.1)
}
}
impl Pointee for str {
type Pointer = (u32, u32);
fn debug(pointer: Self::Pointer, f: &mut fmt::Formatter) -> fmt::Result {
<[u8]>::debug(pointer, f)
}
}