package engine // inheritancePolicyIndex borrows immutable policy entries. Semantic path // segments keep mapping keys distinct from sequence indexes. Record-owned // scopes have their own indexes, just as they have their own policy tables. type inheritancePolicyIndex struct { entry *collectionPolicyEntry children map[pathSegment]*inheritancePolicyIndex records map[int]*inheritancePolicyIndex } func newInheritancePolicyIndex(scope collectionPolicyScope) *inheritancePolicyIndex { index := &inheritancePolicyIndex{} for _, entry := range scope.collections { index.set(entry) } return index } func (index *inheritancePolicyIndex) set(entry collectionPolicyEntry) { node := index for _, segment := range entry.destination.segments { if node.children == nil { node.children = make(map[pathSegment]*inheritancePolicyIndex) } if node.children[segment] == nil { node.children[segment] = &inheritancePolicyIndex{} } node = node.children[segment] } node.entry = &entry node.records = nil for position, owner := range entry.policy.recordOrder { scope := entry.policy.recordPolicies[owner] if len(scope.collections) == 0 { continue } if node.records == nil { node.records = make(map[int]*inheritancePolicyIndex) } node.records[position] = newInheritancePolicyIndex(scope) } } // localize copies only policies in the requested scope. Earlier record-owned // policies are considered before direct entries added by destination expansion. func (index *inheritancePolicyIndex) localize(prefix Path) collectionPolicyScope { localized := collectionPolicyScope{} node := index for offset, segment := range prefix.segments { if node == nil { return localized } if segment.kind == pathSegmentIndex { if record := node.records[segment.index]; record != nil { remainder := Path{segments: prefix.segments[offset+1:]} for _, entry := range record.localize(remainder).collections { localized.set(entry.destination, entry.policy) } } } node = node.children[segment] } var collect func(*inheritancePolicyIndex) collect = func(node *inheritancePolicyIndex) { if node == nil { return } if node.entry != nil { relative, _ := relativeHistoryPath(prefix, node.entry.destination) localized.set(relative, cloneCollectionPolicyForInheritance(node.entry.policy)) } for _, child := range node.children { collect(child) } } collect(node) return localized } func (index *inheritancePolicyIndex) absoluteScope(prefix Path) collectionPolicyScope { scope := collectionPolicyScope{} for _, entry := range index.localize(prefix).collections { scope.set(appendDestination(prefix, entry.destination), entry.policy) } return scope } // replace updates the walker's owned table and its borrowed index together. // Retirement visits only direct entries below the changed destination. func (index *inheritancePolicyIndex) replace(scope *collectionPolicyScope, prefix Path, replacement collectionPolicyScope) { node := index var parent *inheritancePolicyIndex var last pathSegment for _, segment := range prefix.segments { if node == nil { break } parent, last, node = node, segment, node.children[segment] } var remove func(*inheritancePolicyIndex) remove = func(node *inheritancePolicyIndex) { if node == nil { return } if node.entry != nil { delete(scope.collections, node.entry.destination.String()) } for _, child := range node.children { remove(child) } } remove(node) if parent == nil { *index = inheritancePolicyIndex{} } else { delete(parent.children, last) } for _, entry := range replacement.collections { scope.set(entry.destination, entry.policy) index.set(scope.collections[entry.destination.String()]) } }