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}