Skip to main content

pfx_mod/
ctx.rs

1use wasmtime::{ResourceLimiter, Result, StoreLimits, StoreLimitsBuilder};
2
3use crate::error::Limit;
4use crate::limits::{Limits, LogLimits};
5
6pub struct Limiter {
7    inner: StoreLimits,
8    pub(crate) hit: Option<Limit>,
9}
10
11impl Limiter {
12    fn new(limits: &Limits) -> Self {
13        Self {
14            inner: StoreLimitsBuilder::new()
15                .memory_size(limits.memory_bytes)
16                .table_elements(limits.table_elements)
17                .instances(limits.instances)
18                .tables(limits.tables)
19                .memories(limits.memories)
20                .trap_on_grow_failure(true)
21                .build(),
22            hit: None,
23        }
24    }
25}
26
27impl ResourceLimiter for Limiter {
28    fn memory_growing(
29        &mut self,
30        current: usize,
31        desired: usize,
32        maximum: Option<usize>,
33    ) -> Result<bool> {
34        let grown = self.inner.memory_growing(current, desired, maximum);
35        if !matches!(grown, Ok(true)) {
36            self.hit = Some(Limit::Memory { bytes: desired });
37        }
38        grown
39    }
40
41    fn memory_grow_failed(&mut self, error: wasmtime::Error) -> Result<()> {
42        self.inner.memory_grow_failed(error)
43    }
44
45    fn table_growing(
46        &mut self,
47        current: usize,
48        desired: usize,
49        maximum: Option<usize>,
50    ) -> Result<bool> {
51        let grown = self.inner.table_growing(current, desired, maximum);
52        if !matches!(grown, Ok(true)) {
53            self.hit = Some(Limit::Table { elements: desired });
54        }
55        grown
56    }
57
58    fn table_grow_failed(&mut self, error: wasmtime::Error) -> Result<()> {
59        self.inner.table_grow_failed(error)
60    }
61
62    fn instances(&self) -> usize {
63        self.inner.instances()
64    }
65
66    fn tables(&self) -> usize {
67        self.inner.tables()
68    }
69
70    fn memories(&self) -> usize {
71        self.inner.memories()
72    }
73}
74
75#[derive(Default)]
76pub struct Log {
77    limits: Option<LogLimits>,
78    lines: Vec<String>,
79    this_tick: u32,
80    dropped: u64,
81}
82
83impl Log {
84    pub(crate) fn push(&mut self, text: &str) {
85        let Some(limits) = self.limits else {
86            return;
87        };
88        if self.this_tick >= limits.lines_per_tick {
89            self.dropped += 1;
90            return;
91        }
92        self.this_tick += 1;
93        let mut end = text.len().min(limits.line_bytes);
94        while !text.is_char_boundary(end) {
95            end -= 1;
96        }
97        self.lines.push(text[..end].to_string());
98    }
99
100    pub(crate) fn tick(&mut self) {
101        self.this_tick = 0;
102    }
103
104    pub(crate) fn take(&mut self) -> Vec<String> {
105        std::mem::take(&mut self.lines)
106    }
107
108    pub(crate) fn dropped(&self) -> u64 {
109        self.dropped
110    }
111}
112
113pub struct ModCtx<T> {
114    pub game: T,
115    pub(crate) limiter: Limiter,
116    pub(crate) log: Log,
117}
118
119impl<T> ModCtx<T> {
120    pub(crate) fn new(game: T, limits: &Limits) -> Self {
121        Self {
122            game,
123            limiter: Limiter::new(limits),
124            log: Log {
125                limits: limits.log,
126                ..Log::default()
127            },
128        }
129    }
130}