* perf(model): rebuild DTD-default application iteratively, not recursively applyingDefaults rebuilt the tree with a per-node recursive walk, so applying DTD attribute defaults to a deeply-nested document overflowed the stack (around 2000 deep on a Swift Task stack). It now rebuilds bottom-up through a new shared iterative spine, Node.rebuildingBottomUp, which transforms each node once its children are rebuilt via an explicit work stack. The same primitive will carry the other tree-to-tree transforms (canonical prefix rewrite, XSLT namespace fixup) in follow-ups. Defaults are applied identically: 1788 tests pass. Part of #350. * perf(canonical): serialize C14N iteratively, not recursively Canonicalizer.emit recursed on element children, so canonicalizing a deeply-nested document overflowed the stack (crashed around 5000 deep). The element serializer now drives a deferred-close work stack (the same shape as the main serializer): each step carries the in-scope and already-rendered namespace context, children push reversed to emit in document order, and a close step emits the end tag. The shallow document case and all namespace-rendering logic are unchanged. The canonical bytes are byte-identical: the C14N 1.0, 1.1, and exclusive conformance corpora pass, full suite 1789. A 50000-deep document now canonicalizes without crashing. The node-subset helpers moved to their own file to keep within the file-length limit. Part of #350.
48 lines
2.2 KiB
Swift
48 lines
2.2 KiB
Swift
public extension PureXML.Validation.DTDSchema {
|
|
/// Returns `node` with DTD attribute defaults applied: every element gains any
|
|
/// attribute its `<!ATTLIST>` declares with a default or `#FIXED` value and
|
|
/// that the element omits. `#REQUIRED` and `#IMPLIED` add nothing. The injected
|
|
/// values are the normalized defaults from the declaration, so an editor reads
|
|
/// the same effective attributes a validating processor would see.
|
|
func applyingDefaults(to node: PureXML.Model.Node) -> PureXML.Model.Node {
|
|
// Rebuilt bottom-up (iterative) so a deeply-nested document does not
|
|
// overflow the stack; each element gains its DTD-declared defaults once its
|
|
// children are rebuilt.
|
|
node.rebuildingBottomUp { original, rebuiltChildren in
|
|
switch original {
|
|
case let .element(element):
|
|
var result = element
|
|
result.children = rebuiltChildren
|
|
return .element(withDefaults(result))
|
|
case .document:
|
|
return .document(rebuiltChildren)
|
|
default:
|
|
return original
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Adds the attributes `element`'s `<!ATTLIST>` declares with a default or
|
|
/// `#FIXED` value and that the element omits. `result.children` is assumed
|
|
/// already set by the caller.
|
|
private func withDefaults(_ element: PureXML.Model.Element) -> PureXML.Model.Element {
|
|
var result = element
|
|
guard let declarations = attributes[element.name.description] else { return result }
|
|
for declaration in declarations {
|
|
guard let value = Self.defaultValue(declaration) else { continue }
|
|
let present = result.attributes.contains {
|
|
$0.name.description == declaration.name || $0.name.localName == declaration.name
|
|
}
|
|
if !present { result.attributes.append(PureXML.Model.Attribute(declaration.name, value)) }
|
|
}
|
|
return result
|
|
}
|
|
|
|
private static func defaultValue(_ declaration: PureXML.Validation.AttributeDeclaration) -> String? {
|
|
switch declaration.defaultDecl {
|
|
case let .value(value), let .fixed(value): value
|
|
case .required, .implied: nil
|
|
}
|
|
}
|
|
}
|