internal/core/adt: use thread-safe WeakMap for regexp caching
Replace the lazy field-based regexp caching on String and Bytes structs
with a thread-safe WeakMap cache. This fixes a data race where multiple
goroutines could concurrently write to the RE field during evaluation.
The old approach cached compiled regexps directly in the String.RE and
Bytes.RE fields, which was fast for single-threaded access but caused
races in concurrent scenarios (see TODO comment at context.go:1240).
Changes:
- Add WeakMap[K,V] generic type using Go 1.24 weak pointers
- Add regexpCache using WeakMap[string, regexp.Regexp]
- Remove RE field from String and Bytes structs
- Add benchmarks for regexp caching
Performance comparison (Apple M2 Max):
Before (field cache, NOT thread-safe):
BenchmarkRegexpFieldCache 0.3 ns/op 0 allocs
After (WeakMap cache, thread-safe):
BenchmarkRegexpWeakMapCache 17 ns/op 0 allocs
BenchmarkRegexpWeakMapConcurrent 2 ns/op 0 allocs
Baseline:
BenchmarkRegexpCompile 1800 ns/op 38 allocs
The ~17ns cache lookup overhead is negligible compared to the 1800ns
regexp compilation cost, and the new approach provides thread-safety
with excellent parallel scaling (2ns/op under concurrent load).
The WeakMap infrastructure can be reused for future string interning.
Updates #2733
Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: I9235b1b2cab2c0ca88b7330590fb1db60f2d9acb
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1230711
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>