use crate::machine::NetworkConfig;
pub trait MemoryLimiter: Sized {
fn memory_used(&self) -> usize;
fn grow_memory(&mut self, delta: usize) -> bool;
fn with_stack_frame<T, G, F, R>(t: &mut T, g: G, f: F) -> R
where
G: Fn(&mut T) -> &mut Self,
F: FnOnce(&mut T) -> R;
fn grow_instance_memory(&mut self, from: usize, to: usize) -> bool {
self.grow_memory(to.saturating_sub(from))
}
fn grow_instance_table(&mut self, from: usize, to: usize) -> bool {
self.grow_memory(to.saturating_sub(from).saturating_mul(8))
}
}
pub struct DefaultMemoryLimiter {
max_memory_bytes: usize,
curr_memory_bytes: usize,
}
impl DefaultMemoryLimiter {
pub fn new(max_memory_bytes: usize) -> Self {
Self {
max_memory_bytes,
curr_memory_bytes: 0,
}
}
pub fn for_network(config: &NetworkConfig) -> Self {
Self::new(config.max_memory_bytes as usize)
}
}
impl MemoryLimiter for DefaultMemoryLimiter {
fn memory_used(&self) -> usize {
self.curr_memory_bytes
}
fn grow_memory(&mut self, bytes: usize) -> bool {
let total_desired = self.curr_memory_bytes.saturating_add(bytes);
if total_desired > self.max_memory_bytes {
return false;
}
self.curr_memory_bytes = total_desired;
true
}
fn with_stack_frame<T, G, F, R>(t: &mut T, g: G, f: F) -> R
where
G: Fn(&mut T) -> &mut Self,
F: FnOnce(&mut T) -> R,
{
let memory_bytes = g(t).curr_memory_bytes;
let ret = f(t);
g(t).curr_memory_bytes = memory_bytes;
ret
}
}
#[cfg(test)]
mod tests {
use super::DefaultMemoryLimiter;
use crate::machine::limiter::MemoryLimiter;
#[test]
fn basics() {
let mut limits = DefaultMemoryLimiter::new(4);
assert!(limits.grow_memory(3));
assert!(limits.grow_memory(1)); assert!(!limits.grow_memory(1));
let mut limits = DefaultMemoryLimiter::new(6);
assert!(limits.grow_memory(1));
DefaultMemoryLimiter::with_stack_frame(
&mut limits,
|x| x,
|limits| {
assert!(limits.grow_memory(3)); DefaultMemoryLimiter::with_stack_frame(
limits,
|x| x,
|limits| {
assert!(!limits.grow_memory(3)); assert!(limits.grow_memory(2)); assert_eq!(limits.memory_used(), 1 + 3 + 2);
},
);
assert_eq!(limits.memory_used(), 4);
},
);
assert_eq!(limits.memory_used(), 1);
}
#[test]
fn table() {
let mut limits = DefaultMemoryLimiter::new(10);
assert!(limits.grow_instance_table(0, 1)); assert!(limits.grow_memory(2)); assert!(!limits.grow_memory(1));
}
}