xpointer(string-range(...)) matched the search string at every position
of each node's value and re-allocated the whole needle window per
position, an O(text * needle) search and a denial-of-service vector on a
long node value with a near-matching needle. Replace it with a
linear-time Knuth-Morris-Pratt search (failure function built once, since
the needle is fixed across nodes). Matches are unchanged: same
non-overlapping, left-to-right occurrences, same offset/length.
A near-matching needle over a 16000-character node drops from about
0.18 s to about 0.002 s (~100x), now linear.
- Eval-time prefix bindings: Query.evaluate(over:namespaces:) and
value(at:...:namespaces:) supply prefix to URI bindings, so a name test
like q:item resolves q to a URI and matches by namespace regardless of
the document's own prefix. The match is namespace-aware only when
bindings are present, so existing unprefixed/prefix-string queries are
unchanged.
- XPointer schemes: xpath1() evaluates an XPath expression (like
xpointer()), and xmlns(p=uri) binds a prefix for the expression parts
that follow it, threaded into the XPath evaluation.
The range() scheme (and so XInclude range inclusion) remains.
Part of #88
Add the PureXML.XPointer namespace resolving the shorthand bare-name form
(id), the element() scheme (child-element positions from an id or the
document root), and the xpointer() scheme (a full XPath expression). Scheme
parts chain with the XPointer fallback rule: the first non-empty selection
wins. element() data is translated to the equivalent XPath and evaluated by
PureXML.XPath, so no separate evaluator is needed.
Closes#46Closes#47Closes#23