use {
crate::{
decode::{Decode, DecodeError, Decoder},
downcast::{DowncastMut, DowncastRef},
elem::{Elem, ElemEntity, ElemEntityT},
extern_ref::UnguardedExternRef,
func_ref::UnguardedFuncRef,
limits::Limits,
ref_::{Ref, RefType, UnguardedRef},
store::{Handle, HandlePair, Store, StoreId, UnguardedHandle},
trap::Trap,
},
std::{error::Error, fmt},
};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(transparent)]
pub struct Table(pub(crate) Handle<TableEntity>);
impl Table {
pub fn new(store: &mut Store, type_: TableType, val: Ref) -> Result<Self, TableError> {
assert!(type_.is_valid(), "invalid table type");
unsafe { Self::new_unguarded(store, type_, val.to_unguarded(store.id())) }
}
unsafe fn new_unguarded(
store: &mut Store,
type_: TableType,
val: UnguardedRef,
) -> Result<Self, TableError> {
match (type_.elem, val) {
(RefType::FuncRef, UnguardedRef::FuncRef(val)) => Ok(Self(
store.insert_table(TableEntity::FuncRef(TableEntityT::new(type_.limits, val))),
)),
(RefType::ExternRef, UnguardedRef::ExternRef(val)) => Ok(Self(
store.insert_table(TableEntity::ExternRef(TableEntityT::new(type_.limits, val))),
)),
_ => Err(TableError::ElemTypeMismatch),
}
}
pub fn type_(self, store: &Store) -> TableType {
match self.0.as_ref(store) {
TableEntity::FuncRef(table) => TableType {
limits: table.limits(),
elem: RefType::FuncRef,
},
TableEntity::ExternRef(table) => TableType {
limits: table.limits(),
elem: RefType::ExternRef,
},
}
}
pub fn get(self, store: &Store, idx: u32) -> Option<Ref> {
self.get_unguarded(store, idx)
.map(|val| unsafe { Ref::from_unguarded(val, store.id()) })
}
fn get_unguarded(self, store: &Store, idx: u32) -> Option<UnguardedRef> {
match self.0.as_ref(store) {
TableEntity::FuncRef(table) => table.get(idx).map(UnguardedRef::FuncRef),
TableEntity::ExternRef(table) => table.get(idx).map(UnguardedRef::ExternRef),
}
}
pub fn set(self, store: &mut Store, idx: u32, val: Ref) -> Result<(), TableError> {
unsafe { self.set_unguarded(store, idx, val.to_unguarded(store.id())) }
}
unsafe fn set_unguarded(
self,
store: &mut Store,
idx: u32,
val: UnguardedRef,
) -> Result<(), TableError> {
match (self.0.as_mut(store), val) {
(TableEntity::FuncRef(table), UnguardedRef::FuncRef(val)) => table.set(idx, val),
(TableEntity::ExternRef(table), UnguardedRef::ExternRef(val)) => table.set(idx, val),
_ => Err(TableError::ElemTypeMismatch),
}
}
pub fn size(&self, store: &Store) -> u32 {
match self.0.as_ref(store) {
TableEntity::FuncRef(table) => table.size(),
TableEntity::ExternRef(table) => table.size(),
}
}
pub fn grow(self, store: &mut Store, val: Ref, count: u32) -> Result<(), TableError> {
unsafe { self.grow_unguarded(store, val.to_unguarded(store.id()), count) }
}
unsafe fn grow_unguarded(
self,
store: &mut Store,
val: UnguardedRef,
count: u32,
) -> Result<(), TableError> {
match (self.0.as_mut(store), val) {
(TableEntity::FuncRef(table), UnguardedRef::FuncRef(val)) => table.grow(val, count),
(TableEntity::ExternRef(table), UnguardedRef::ExternRef(val)) => table.grow(val, count),
_ => Err(TableError::ElemTypeMismatch),
}
.map(|_| ())
}
pub(crate) fn init(
self,
store: &mut Store,
dst_idx: u32,
src_elem: Elem,
src_idx: u32,
count: u32,
) -> Result<(), Trap> {
let (dst_table, src_elem) = HandlePair(self.0, src_elem.0).as_mut_pair(store);
match (dst_table, src_elem) {
(TableEntity::FuncRef(table), ElemEntity::FuncRef(src_elem)) => {
table.init(dst_idx, src_elem, src_idx, count)
}
(TableEntity::ExternRef(table), ElemEntity::ExternRef(src_elem)) => {
table.init(dst_idx, src_elem, src_idx, count)
}
_ => panic!(),
}
}
pub(crate) unsafe fn from_unguarded(table: UnguardedTable, store_id: StoreId) -> Self {
Self(Handle::from_unguarded(table, store_id))
}
pub(crate) fn to_unguarded(self, store_id: StoreId) -> UnguardedTable {
self.0.to_unguarded(store_id)
}
}
pub(crate) type UnguardedTable = UnguardedHandle<TableEntity>;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct TableType {
pub limits: Limits,
pub elem: RefType,
}
impl TableType {
pub fn is_valid(self) -> bool {
if !self.limits.is_valid(u32::MAX) {
return false;
}
true
}
pub fn is_subtype_of(self, other: Self) -> bool {
if !self.limits.is_sublimit_of(other.limits) {
return false;
}
if self.elem != other.elem {
return false;
}
true
}
}
impl Decode for TableType {
fn decode(decoder: &mut Decoder<'_>) -> Result<Self, DecodeError> {
let elem = decoder.decode()?;
let limits = decoder.decode()?;
Ok(Self { limits, elem })
}
}
#[derive(Clone, Copy, Debug)]
#[non_exhaustive]
pub enum TableError {
AccessOutOfBounds,
ElemTypeMismatch,
FailedToGrow,
}
impl fmt::Display for TableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AccessOutOfBounds => write!(f, "table access out of bounds"),
Self::ElemTypeMismatch => write!(f, "table element type mismatch"),
Self::FailedToGrow => write!(f, "table failed to grow"),
}
}
}
impl Error for TableError {}
#[derive(Debug)]
pub(crate) enum TableEntity {
FuncRef(TableEntityT<UnguardedFuncRef>),
ExternRef(TableEntityT<UnguardedExternRef>),
}
impl TableEntity {
pub(crate) fn downcast_ref<T>(&self) -> Option<&TableEntityT<T>>
where
TableEntityT<T>: DowncastRef<Self>,
{
TableEntityT::downcast_ref(self)
}
pub(crate) fn downcast_mut<T>(&mut self) -> Option<&mut TableEntityT<T>>
where
TableEntityT<T>: DowncastMut<Self>,
{
TableEntityT::downcast_mut(self)
}
}
#[derive(Debug)]
pub(crate) struct TableEntityT<T> {
max: Option<u32>,
elems: Vec<T>,
}
impl<T> TableEntityT<T>
where
T: Copy,
{
fn new(limits: Limits, val: T) -> Self {
let min = limits.min as usize;
Self {
max: limits.max,
elems: vec![val; min],
}
}
fn limits(&self) -> Limits {
Limits {
min: u32::try_from(self.elems.len()).unwrap(),
max: self.max,
}
}
pub(crate) fn get(&self, idx: u32) -> Option<T> {
let idx = idx as usize;
let elem = self.elems.get(idx)?;
Some(*elem)
}
pub(crate) fn set(&mut self, idx: u32, val: T) -> Result<(), TableError> {
let idx = idx as usize;
let elem = self
.elems
.get_mut(idx)
.ok_or(TableError::AccessOutOfBounds)?;
*elem = val;
Ok(())
}
pub(crate) fn size(&self) -> u32 {
self.elems.len() as u32
}
pub(crate) fn grow(&mut self, val: T, count: u32) -> Result<u32, TableError> {
if count > self.max.unwrap_or(u32::MAX) - self.size() {
return Err(TableError::FailedToGrow)?;
}
let count = count as usize;
let size = self.size();
self.elems.resize(self.elems.len() + count, val);
Ok(size)
}
pub(crate) fn fill(&mut self, idx: u32, val: T, count: u32) -> Result<(), Trap> {
let idx = idx as usize;
let count = count as usize;
let elems = self
.elems
.get_mut(idx..)
.and_then(|elems| elems.get_mut(..count))
.ok_or(Trap::TableAccessOutOfBounds)?;
elems.fill(val);
Ok(())
}
pub(crate) fn copy(
&mut self,
dst_idx: u32,
src_table: &TableEntityT<T>,
src_idx: u32,
count: u32,
) -> Result<(), Trap> {
let dst_idx = dst_idx as usize;
let src_idx = src_idx as usize;
let count = count as usize;
let dst_elems = self
.elems
.get_mut(dst_idx..)
.and_then(|elems| elems.get_mut(..count))
.ok_or(Trap::TableAccessOutOfBounds)?;
let src_elems = src_table
.elems
.get(src_idx..)
.and_then(|elems| elems.get(..count))
.ok_or(Trap::TableAccessOutOfBounds)?;
dst_elems.copy_from_slice(src_elems);
Ok(())
}
pub(crate) fn copy_within(
&mut self,
dst_idx: u32,
src_idx: u32,
count: u32,
) -> Result<(), Trap> {
let dst_idx = dst_idx as usize;
let src_idx = src_idx as usize;
let count = count as usize;
if count > self.elems.len()
|| dst_idx > self.elems.len() - count
|| src_idx > self.elems.len() - count
{
return Err(Trap::TableAccessOutOfBounds)?;
}
self.elems.copy_within(src_idx..src_idx + count, dst_idx);
Ok(())
}
pub(crate) fn init(
&mut self,
dst_idx: u32,
src_elem: &ElemEntityT<T>,
src_idx: u32,
count: u32,
) -> Result<(), Trap> {
let dst_idx = dst_idx as usize;
let src_idx = src_idx as usize;
let count = count as usize;
let dst_elems = self
.elems
.get_mut(dst_idx..)
.and_then(|elems| elems.get_mut(..count))
.ok_or(Trap::TableAccessOutOfBounds)?;
let src_elems = src_elem
.elems()
.get(src_idx..)
.and_then(|elems| elems.get(..count))
.ok_or(Trap::TableAccessOutOfBounds)?;
dst_elems.copy_from_slice(src_elems);
Ok(())
}
}
impl DowncastRef<TableEntity> for TableEntityT<UnguardedFuncRef> {
fn downcast_ref(table: &TableEntity) -> Option<&Self> {
match table {
TableEntity::FuncRef(table) => Some(table),
_ => None,
}
}
}
impl DowncastMut<TableEntity> for TableEntityT<UnguardedFuncRef> {
fn downcast_mut(table: &mut TableEntity) -> Option<&mut Self> {
match table {
TableEntity::FuncRef(table) => Some(table),
_ => None,
}
}
}
impl DowncastRef<TableEntity> for TableEntityT<UnguardedExternRef> {
fn downcast_ref(table: &TableEntity) -> Option<&Self> {
match table {
TableEntity::ExternRef(table) => Some(table),
_ => None,
}
}
}
impl DowncastMut<TableEntity> for TableEntityT<UnguardedExternRef> {
fn downcast_mut(table: &mut TableEntity) -> Option<&mut Self> {
match table {
TableEntity::ExternRef(table) => Some(table),
_ => None,
}
}
}