use anyhow::{bail, Result};
pub const DEFAULT_INSTANCE_LIMIT: usize = 10000;
pub const DEFAULT_TABLE_LIMIT: usize = 10000;
pub const DEFAULT_MEMORY_LIMIT: usize = 10000;
pub trait ResourceLimiter {
fn memory_growing(
&mut self,
current: usize,
desired: usize,
maximum: Option<usize>,
) -> Result<bool>;
fn memory_grow_failed(&mut self, error: anyhow::Error) -> Result<()> {
log::debug!("ignoring memory growth failure error: {error:?}");
Ok(())
}
fn table_growing(&mut self, current: u32, desired: u32, maximum: Option<u32>) -> Result<bool>;
fn table_grow_failed(&mut self, error: anyhow::Error) -> Result<()> {
log::debug!("ignoring table growth failure error: {error:?}");
Ok(())
}
fn instances(&self) -> usize {
DEFAULT_INSTANCE_LIMIT
}
fn tables(&self) -> usize {
DEFAULT_TABLE_LIMIT
}
fn memories(&self) -> usize {
DEFAULT_MEMORY_LIMIT
}
}
#[cfg(feature = "async")]
#[async_trait::async_trait]
pub trait ResourceLimiterAsync {
async fn memory_growing(
&mut self,
current: usize,
desired: usize,
maximum: Option<usize>,
) -> Result<bool>;
fn memory_grow_failed(&mut self, error: anyhow::Error) -> Result<()> {
log::debug!("ignoring memory growth failure error: {error:?}");
Ok(())
}
async fn table_growing(
&mut self,
current: u32,
desired: u32,
maximum: Option<u32>,
) -> Result<bool>;
fn table_grow_failed(&mut self, error: anyhow::Error) -> Result<()> {
log::debug!("ignoring table growth failure error: {error:?}");
Ok(())
}
fn instances(&self) -> usize {
DEFAULT_INSTANCE_LIMIT
}
fn tables(&self) -> usize {
DEFAULT_TABLE_LIMIT
}
fn memories(&self) -> usize {
DEFAULT_MEMORY_LIMIT
}
}
pub struct StoreLimitsBuilder(StoreLimits);
impl StoreLimitsBuilder {
pub fn new() -> Self {
Self(StoreLimits::default())
}
pub fn memory_size(mut self, limit: usize) -> Self {
self.0.memory_size = Some(limit);
self
}
pub fn table_elements(mut self, limit: u32) -> Self {
self.0.table_elements = Some(limit);
self
}
pub fn instances(mut self, limit: usize) -> Self {
self.0.instances = limit;
self
}
pub fn tables(mut self, tables: usize) -> Self {
self.0.tables = tables;
self
}
pub fn memories(mut self, memories: usize) -> Self {
self.0.memories = memories;
self
}
pub fn trap_on_grow_failure(mut self, trap: bool) -> Self {
self.0.trap_on_grow_failure = trap;
self
}
pub fn build(self) -> StoreLimits {
self.0
}
}
#[derive(Clone, Debug)]
pub struct StoreLimits {
memory_size: Option<usize>,
table_elements: Option<u32>,
instances: usize,
tables: usize,
memories: usize,
trap_on_grow_failure: bool,
}
impl Default for StoreLimits {
fn default() -> Self {
Self {
memory_size: None,
table_elements: None,
instances: DEFAULT_INSTANCE_LIMIT,
tables: DEFAULT_TABLE_LIMIT,
memories: DEFAULT_MEMORY_LIMIT,
trap_on_grow_failure: false,
}
}
}
impl ResourceLimiter for StoreLimits {
fn memory_growing(
&mut self,
_current: usize,
desired: usize,
maximum: Option<usize>,
) -> Result<bool> {
let allow = match self.memory_size {
Some(limit) if desired > limit => false,
_ => match maximum {
Some(max) if desired > max => false,
_ => true,
},
};
if !allow && self.trap_on_grow_failure {
bail!("forcing trap when growing memory to {desired} bytes")
} else {
Ok(allow)
}
}
fn memory_grow_failed(&mut self, error: anyhow::Error) -> Result<()> {
if self.trap_on_grow_failure {
Err(error.context("forcing a memory growth failure to be a trap"))
} else {
log::debug!("ignoring memory growth failure error: {error:?}");
Ok(())
}
}
fn table_growing(&mut self, _current: u32, desired: u32, maximum: Option<u32>) -> Result<bool> {
let allow = match self.table_elements {
Some(limit) if desired > limit => false,
_ => match maximum {
Some(max) if desired > max => false,
_ => true,
},
};
if !allow && self.trap_on_grow_failure {
bail!("forcing trap when growing table to {desired} elements")
} else {
Ok(allow)
}
}
fn table_grow_failed(&mut self, error: anyhow::Error) -> Result<()> {
if self.trap_on_grow_failure {
Err(error.context("forcing a table growth failure to be a trap"))
} else {
log::debug!("ignoring table growth failure error: {error:?}");
Ok(())
}
}
fn instances(&self) -> usize {
self.instances
}
fn tables(&self) -> usize {
self.tables
}
fn memories(&self) -> usize {
self.memories
}
}