Enforce the retained history node limit
This commit is contained in:
@@ -40,6 +40,86 @@ func (c *retainedNodeCounter) add(root *document.Node) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *retainedNodeCounter) canAddClones(roots []*document.Node, copies int) bool {
|
||||
if c == nil || c.limit < 0 {
|
||||
return true
|
||||
}
|
||||
remaining := c.limit - c.count
|
||||
if remaining < 0 {
|
||||
return false
|
||||
}
|
||||
for copyIndex := 0; copyIndex < copies; copyIndex++ {
|
||||
for _, root := range roots {
|
||||
count, within := documentCloneNodeCount(root, remaining)
|
||||
if !within {
|
||||
return false
|
||||
}
|
||||
remaining -= count
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func documentCloneNodeCount(root *document.Node, limit int) (int, bool) {
|
||||
if root == nil {
|
||||
return 0, true
|
||||
}
|
||||
count := 0
|
||||
stack := []*document.Node{root}
|
||||
for len(stack) != 0 {
|
||||
node := stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
if node == nil {
|
||||
continue
|
||||
}
|
||||
count++
|
||||
if count > limit {
|
||||
return 0, false
|
||||
}
|
||||
for _, entry := range node.Mapping {
|
||||
stack = append(stack, entry.Value)
|
||||
}
|
||||
stack = append(stack, node.Sequence...)
|
||||
}
|
||||
return count, true
|
||||
}
|
||||
|
||||
func addInspectionHistory(counter *retainedNodeCounter, inspection *Inspection) bool {
|
||||
if inspection == nil {
|
||||
return false
|
||||
}
|
||||
for _, root := range []*document.Node{inspection.baseline, inspection.input, inspection.final, inspection.preCLI} {
|
||||
if !counter.add(root) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, application := range inspection.applications {
|
||||
if !counter.add(application.value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, application := range inspection.postDecodeApps {
|
||||
if !counter.add(application.value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, snapshots := range [][]resolutionSnapshot{inspection.resolution, inspection.resolutionReplay} {
|
||||
for _, snapshot := range snapshots {
|
||||
if !counter.add(snapshot.before) || !counter.add(snapshot.after) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, operations := range [][]inheritanceOperation{inspection.inheritance, inspection.metadata.inheritance} {
|
||||
for _, operation := range operations {
|
||||
if !counter.add(operation.parent) || !counter.add(operation.effective) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// History applications borrow immutable policy-validated input trees. Metadata
|
||||
// records answer presence and origin queries; replay reconstructs value changes.
|
||||
// Replay reconciles with canonical decoding, applies supplied CLI contributions,
|
||||
@@ -499,66 +579,3 @@ func replaceHistoryPath(root **document.Node, path Path, replacement *document.N
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func historyNodesWithinLimit(baseline, input, final *document.Node, applications []historyApplication, resolutions []resolutionSnapshot, inherited ...*inheritanceScope) bool {
|
||||
return historyNodesWithin(historyNodeLimit, baseline, input, final, applications, resolutions, inherited...)
|
||||
}
|
||||
|
||||
func historyNodesWithin(limit int, baseline, input, final *document.Node, applications []historyApplication, resolutions []resolutionSnapshot, inherited ...*inheritanceScope) bool {
|
||||
seen := make(map[*document.Node]bool)
|
||||
count := 0
|
||||
var visit func(*document.Node) bool
|
||||
visit = func(node *document.Node) bool {
|
||||
if node == nil || seen[node] {
|
||||
return true
|
||||
}
|
||||
seen[node] = true
|
||||
count++
|
||||
if count > limit {
|
||||
return false
|
||||
}
|
||||
for _, entry := range node.Mapping {
|
||||
if !visit(entry.Value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, child := range node.Sequence {
|
||||
if !visit(child) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if !visit(baseline) || !visit(input) || !visit(final) {
|
||||
return false
|
||||
}
|
||||
for _, application := range applications {
|
||||
if !visit(application.value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, resolution := range resolutions {
|
||||
if !visit(resolution.before) || !visit(resolution.after) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, expansion := range inherited {
|
||||
if expansion == nil {
|
||||
continue
|
||||
}
|
||||
if !visit(expansion.root) {
|
||||
return false
|
||||
}
|
||||
for _, operation := range expansion.operations {
|
||||
if !visit(operation.parent) || !visit(operation.effective) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for _, replacement := range expansion.replacements {
|
||||
if !visit(replacement.before) || !visit(replacement.after) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user