PureXML/Sources/XSLT/XSLTMatchCache.swift
Mihaela Mihaljevic 7d3ef26558 perf(xpath): compile patterns once per run, not per node
XSLT: template selection resolves each match pattern once per transform via
a per-run MatchCache (the pattern's node set is computed once over the source
tree; selection becomes an identity lookup), replacing per-(node, template)
recompilation and full-tree re-walks. XSD identity validation: a per-run query
cache compiles each selector/field XPath once instead of at every visited
element. Schematron was verified to already store compiled queries. Behavior
unchanged (all suites green); a multi-template shape test over 300 nodes
guards the optimized path. MatchCache lives in its own file to keep the
transformer under the length cap.

Closes #112
2026-06-10 03:24:38 +02:00

27 lines
1.2 KiB
Swift

extension PureXML.XSLT {
/// Caches, per transform, the node set each match pattern selects over the
/// source tree, so a pattern is compiled and evaluated once per document
/// rather than once per (node, template) pair during template selection.
final class MatchCache {
private var matched: [String: Set<ObjectIdentifier>] = [:]
/// The identities of the nodes `pattern` selects over `root`, computed and
/// cached on first use. A branch that fails to compile selects nothing.
func nodes(matching pattern: String, over root: PureXML.Model.TreeNode) -> Set<ObjectIdentifier> {
if let cached = matched[pattern] { return cached }
var result: Set<ObjectIdentifier> = []
for branch in pattern.split(separator: "|") {
let trimmed = branch.trimmingXMLWhitespace()
let path = trimmed.hasPrefix("/") ? trimmed : "//" + trimmed
if let query = try? PureXML.XPath.Query(path) {
for node in query.nodes(over: root) {
result.insert(ObjectIdentifier(node))
}
}
}
matched[pattern] = result
return result
}
}
}