use crate::{
error::CreationError,
export::Export,
import::IsExport,
types::{ElementType, TableDescriptor},
vm,
};
use std::{
convert::TryFrom,
fmt, ptr,
sync::{Arc, Mutex},
};
mod anyfunc;
pub use self::anyfunc::Anyfunc;
pub(crate) use self::anyfunc::AnyfuncTable;
use crate::error::GrowError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TableAccessError {
IndexError,
#[allow(dead_code)]
TypeError,
}
pub trait StorableInTable: Sized {
fn unwrap_self(storage: &TableStorage, index: u32) -> Result<Self, TableAccessError>;
fn wrap_self(self, storage: &mut TableStorage, index: u32) -> Result<(), TableAccessError>;
}
impl<'a, F: Into<Anyfunc<'a>> + TryFrom<Anyfunc<'a>>> StorableInTable for F {
fn unwrap_self(storage: &TableStorage, index: u32) -> Result<Self, TableAccessError> {
match storage {
TableStorage::Anyfunc(ref anyfunc_table) => {
let anyfunc = anyfunc_table
.get(index)
.ok_or(TableAccessError::IndexError)?;
F::try_from(anyfunc).map_err(|_| TableAccessError::TypeError)
}
}
}
fn wrap_self(self, storage: &mut TableStorage, index: u32) -> Result<(), TableAccessError> {
let anyfunc: Anyfunc = self.into();
match storage {
TableStorage::Anyfunc(ref mut anyfunc_table) => anyfunc_table
.set(index, anyfunc)
.map_err(|_| TableAccessError::IndexError),
}
}
}
pub enum Element<'a> {
Anyfunc(Anyfunc<'a>),
}
impl<'a> StorableInTable for Element<'a> {
fn unwrap_self(storage: &TableStorage, index: u32) -> Result<Self, TableAccessError> {
match storage {
TableStorage::Anyfunc(ref anyfunc_table) => anyfunc_table
.get(index)
.map(Element::Anyfunc)
.ok_or(TableAccessError::IndexError),
}
}
fn wrap_self(self, storage: &mut TableStorage, index: u32) -> Result<(), TableAccessError> {
match self {
Element::Anyfunc(af) => af.wrap_self(storage, index),
}
}
}
pub enum TableStorage {
Anyfunc(Box<AnyfuncTable>),
}
pub struct Table {
desc: TableDescriptor,
storage: Arc<Mutex<(TableStorage, vm::LocalTable)>>,
}
impl Table {
pub fn new(desc: TableDescriptor) -> Result<Self, CreationError> {
if let Some(max) = desc.maximum {
if max < desc.minimum {
return Err(CreationError::InvalidDescriptor(
"Max table size is less than the minimum size".to_string(),
));
}
}
let mut local = vm::LocalTable {
base: ptr::null_mut(),
count: 0,
table: ptr::null_mut(),
};
let storage = match desc.element {
ElementType::Anyfunc => TableStorage::Anyfunc(AnyfuncTable::new(desc, &mut local)?),
};
Ok(Self {
desc,
storage: Arc::new(Mutex::new((storage, local))),
})
}
pub fn descriptor(&self) -> TableDescriptor {
self.desc
}
#[allow(dead_code)]
pub(crate) fn get<T: StorableInTable>(&self, index: u32) -> Result<T, TableAccessError> {
let guard = self.storage.lock().unwrap();
let (storage, _) = &*guard;
T::unwrap_self(storage, index)
}
pub fn set<T: StorableInTable>(&self, index: u32, element: T) -> Result<(), TableAccessError> {
let mut guard = self.storage.lock().unwrap();
let (storage, _) = &mut *guard;
T::wrap_self(element, storage, index)
}
pub(crate) fn anyfunc_direct_access_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut [vm::Anyfunc]) -> R,
{
let mut storage = self.storage.lock().unwrap();
match &mut *storage {
(TableStorage::Anyfunc(ref mut anyfunc_table), _) => f(anyfunc_table.internal_buffer()),
}
}
pub fn size(&self) -> u32 {
let storage = self.storage.lock().unwrap();
match &*storage {
(TableStorage::Anyfunc(ref anyfunc_table), _) => anyfunc_table.current_size(),
}
}
pub fn grow(&self, delta: u32) -> Result<u32, GrowError> {
if delta == 0 {
return Ok(self.size());
}
let mut storage = self.storage.lock().unwrap();
match &mut *storage {
(TableStorage::Anyfunc(ref mut anyfunc_table), ref mut local) => anyfunc_table
.grow(delta, local)
.ok_or(GrowError::TableGrowError),
}
}
pub fn vm_local_table(&mut self) -> *mut vm::LocalTable {
let mut storage = self.storage.lock().unwrap();
&mut storage.1
}
}
impl IsExport for Table {
fn to_export(&self) -> Export {
Export::Table(self.clone())
}
}
impl Clone for Table {
fn clone(&self) -> Self {
Self {
desc: self.desc,
storage: Arc::clone(&self.storage),
}
}
}
impl fmt::Debug for Table {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Table")
.field("desc", &self.desc)
.field("size", &self.size())
.finish()
}
}
#[cfg(test)]
mod table_tests {
use super::{ElementType, Table, TableDescriptor};
#[test]
fn test_initial_table_size() {
let table = Table::new(TableDescriptor {
element: ElementType::Anyfunc,
minimum: 10,
maximum: Some(20),
})
.unwrap();
assert_eq!(table.size(), 10);
}
}