Skip to content

Commit 5dfa32c

Browse files
committed
feat: configure per-VM regex cache capacity
1 parent 1303d79 commit 5dfa32c

3 files changed

Lines changed: 77 additions & 0 deletions

File tree

src/builtins/runtime/regex.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,24 @@ impl RegexCache {
6464
self.recency.push_back(pattern.to_string());
6565
}
6666

67+
pub(crate) fn capacity(&self) -> usize {
68+
self.capacity
69+
}
70+
71+
pub(crate) fn set_capacity(&mut self, capacity: usize) {
72+
self.capacity = capacity;
73+
while self.entries.len() > capacity {
74+
let Some(oldest) = self.recency.pop_front() else {
75+
self.entries.clear();
76+
break;
77+
};
78+
self.entries.remove(&oldest);
79+
}
80+
if capacity == 0 {
81+
self.recency.clear();
82+
}
83+
}
84+
6785
pub(crate) fn len(&self) -> usize {
6886
self.entries.len()
6987
}
@@ -167,4 +185,35 @@ mod tests {
167185
assert_eq!(cache.len(), 2);
168186
assert_eq!(cache.compile_count(), 3);
169187
}
188+
189+
#[test]
190+
fn vm_regex_cache_capacity_can_be_changed_and_shrinks_immediately() {
191+
let mut vm = Vm::new(Program::new(Vec::new(), vec![OpCode::Ret as u8]));
192+
assert_eq!(vm.regex_cache_capacity(), DEFAULT_REGEX_CACHE_CAPACITY);
193+
194+
builtin_re_match_impl(&mut vm, "a", "a").expect("pattern should compile");
195+
builtin_re_match_impl(&mut vm, "b", "b").expect("pattern should compile");
196+
builtin_re_match_impl(&mut vm, "c", "c").expect("pattern should compile");
197+
vm.set_regex_cache_capacity(1);
198+
199+
assert_eq!(vm.regex_cache_capacity(), 1);
200+
assert_eq!(vm.regex_cache_entry_count(), 1);
201+
builtin_re_match_impl(&mut vm, "c", "c").expect("most recent pattern should remain");
202+
assert_eq!(vm.regex_cache_compile_count(), 3);
203+
assert_eq!(vm.regex_cache_hit_count(), 1);
204+
}
205+
206+
#[test]
207+
fn zero_vm_regex_cache_capacity_disables_caching() {
208+
let mut vm = Vm::new(Program::new(Vec::new(), vec![OpCode::Ret as u8]));
209+
vm.set_regex_cache_capacity(0);
210+
211+
builtin_re_match_impl(&mut vm, "same", "same").expect("pattern should compile");
212+
builtin_re_match_impl(&mut vm, "same", "same").expect("pattern should compile again");
213+
214+
assert_eq!(vm.regex_cache_capacity(), 0);
215+
assert_eq!(vm.regex_cache_entry_count(), 0);
216+
assert_eq!(vm.regex_cache_compile_count(), 2);
217+
assert_eq!(vm.regex_cache_hit_count(), 0);
218+
}
170219
}

src/vm/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,21 @@ impl Vm {
554554
self.interrupt_mode != InterruptMode::None
555555
}
556556

557+
/// Returns the maximum number of compiled regular expressions retained by this VM.
558+
///
559+
/// New VMs default to 512 entries. A capacity of zero disables caching.
560+
pub fn regex_cache_capacity(&self) -> usize {
561+
self.regex_cache.capacity()
562+
}
563+
564+
/// Changes this VM's compiled regular-expression cache capacity.
565+
///
566+
/// Shrinking evicts least-recently-used entries immediately. Setting zero clears
567+
/// all entries and disables caching until a positive capacity is configured.
568+
pub fn set_regex_cache_capacity(&mut self, capacity: usize) {
569+
self.regex_cache.set_capacity(capacity);
570+
}
571+
557572
pub fn regex_cache_entry_count(&self) -> usize {
558573
self.regex_cache.len()
559574
}

tests/vm/vm_runtime_tests.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ fn builtin_call_index_with_arity(source: &str, argc: u8) -> u16 {
2323
matched.unwrap_or_else(|| panic!("expected a call with arity {argc} in `{source}`"))
2424
}
2525

26+
#[test]
27+
fn regex_cache_capacity_is_configurable_through_public_vm_api() {
28+
let program = Program::new(Vec::new(), vec![OpCode::Ret as u8]);
29+
let mut vm = Vm::new(program);
30+
31+
assert_eq!(vm.regex_cache_capacity(), 512);
32+
vm.set_regex_cache_capacity(64);
33+
assert_eq!(vm.regex_cache_capacity(), 64);
34+
vm.set_regex_cache_capacity(0);
35+
assert_eq!(vm.regex_cache_capacity(), 0);
36+
assert_eq!(vm.regex_cache_entry_count(), 0);
37+
}
38+
2639
#[test]
2740
fn arithmetic_works() {
2841
let constants = vec![Value::Int(2), Value::Int(3)];

0 commit comments

Comments
 (0)