mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			506 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			506 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2018 The Kubernetes Authors.
 | 
						|
 | 
						|
Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
you may not use this file except in compliance with the License.
 | 
						|
You may obtain a copy of the License at
 | 
						|
 | 
						|
    http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
 | 
						|
Unless required by applicable law or agreed to in writing, software
 | 
						|
distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
See the License for the specific language governing permissions and
 | 
						|
limitations under the License.
 | 
						|
*/
 | 
						|
 | 
						|
package fieldpath
 | 
						|
 | 
						|
import (
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"sigs.k8s.io/structured-merge-diff/v4/schema"
 | 
						|
)
 | 
						|
 | 
						|
// Set identifies a set of fields.
 | 
						|
type Set struct {
 | 
						|
	// Members lists fields that are part of the set.
 | 
						|
	// TODO: will be serialized as a list of path elements.
 | 
						|
	Members PathElementSet
 | 
						|
 | 
						|
	// Children lists child fields which themselves have children that are
 | 
						|
	// members of the set. Appearance in this list does not imply membership.
 | 
						|
	// Note: this is a tree, not an arbitrary graph.
 | 
						|
	Children SetNodeMap
 | 
						|
}
 | 
						|
 | 
						|
// NewSet makes a set from a list of paths.
 | 
						|
func NewSet(paths ...Path) *Set {
 | 
						|
	s := &Set{}
 | 
						|
	for _, p := range paths {
 | 
						|
		s.Insert(p)
 | 
						|
	}
 | 
						|
	return s
 | 
						|
}
 | 
						|
 | 
						|
// Insert adds the field identified by `p` to the set. Important: parent fields
 | 
						|
// are NOT added to the set; if that is desired, they must be added separately.
 | 
						|
func (s *Set) Insert(p Path) {
 | 
						|
	if len(p) == 0 {
 | 
						|
		// Zero-length path identifies the entire object; we don't
 | 
						|
		// track top-level ownership.
 | 
						|
		return
 | 
						|
	}
 | 
						|
	for {
 | 
						|
		if len(p) == 1 {
 | 
						|
			s.Members.Insert(p[0])
 | 
						|
			return
 | 
						|
		}
 | 
						|
		s = s.Children.Descend(p[0])
 | 
						|
		p = p[1:]
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Union returns a Set containing elements which appear in either s or s2.
 | 
						|
func (s *Set) Union(s2 *Set) *Set {
 | 
						|
	return &Set{
 | 
						|
		Members:  *s.Members.Union(&s2.Members),
 | 
						|
		Children: *s.Children.Union(&s2.Children),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Intersection returns a Set containing leaf elements which appear in both s
 | 
						|
// and s2. Intersection can be constructed from Union and Difference operations
 | 
						|
// (example in the tests) but it's much faster to do it in one pass.
 | 
						|
func (s *Set) Intersection(s2 *Set) *Set {
 | 
						|
	return &Set{
 | 
						|
		Members:  *s.Members.Intersection(&s2.Members),
 | 
						|
		Children: *s.Children.Intersection(&s2.Children),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Difference returns a Set containing elements which:
 | 
						|
// * appear in s
 | 
						|
// * do not appear in s2
 | 
						|
//
 | 
						|
// In other words, for leaf fields, this acts like a regular set difference
 | 
						|
// operation. When non leaf fields are compared with leaf fields ("parents"
 | 
						|
// which contain "children"), the effect is:
 | 
						|
// * parent - child = parent
 | 
						|
// * child - parent = {empty set}
 | 
						|
func (s *Set) Difference(s2 *Set) *Set {
 | 
						|
	return &Set{
 | 
						|
		Members:  *s.Members.Difference(&s2.Members),
 | 
						|
		Children: *s.Children.Difference(s2),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// RecursiveDifference returns a Set containing elements which:
 | 
						|
// * appear in s
 | 
						|
// * do not appear in s2
 | 
						|
//
 | 
						|
// Compared to a regular difference,
 | 
						|
// this removes every field **and its children** from s that is contained in s2.
 | 
						|
//
 | 
						|
// For example, with s containing `a.b.c` and s2 containing `a.b`,
 | 
						|
// a RecursiveDifference will result in `a`, as the entire node `a.b` gets removed.
 | 
						|
func (s *Set) RecursiveDifference(s2 *Set) *Set {
 | 
						|
	return &Set{
 | 
						|
		Members:  *s.Members.Difference(&s2.Members),
 | 
						|
		Children: *s.Children.RecursiveDifference(s2),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// EnsureNamedFieldsAreMembers returns a Set that contains all the
 | 
						|
// fields in s, as well as all the named fields that are typically not
 | 
						|
// included. For example, a set made of "a.b.c" will end-up also owning
 | 
						|
// "a" if it's a named fields but not "a.b" if it's a map.
 | 
						|
func (s *Set) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) *Set {
 | 
						|
	members := PathElementSet{
 | 
						|
		members: make(sortedPathElements, 0, s.Members.Size()+len(s.Children.members)),
 | 
						|
	}
 | 
						|
	atom, _ := sc.Resolve(tr)
 | 
						|
	members.members = append(members.members, s.Members.members...)
 | 
						|
	for _, node := range s.Children.members {
 | 
						|
		// Only insert named fields.
 | 
						|
		if node.pathElement.FieldName != nil && atom.Map != nil {
 | 
						|
			if _, has := atom.Map.FindField(*node.pathElement.FieldName); has {
 | 
						|
				members.Insert(node.pathElement)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return &Set{
 | 
						|
		Members:  members,
 | 
						|
		Children: *s.Children.EnsureNamedFieldsAreMembers(sc, tr),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Size returns the number of members of the set.
 | 
						|
func (s *Set) Size() int {
 | 
						|
	return s.Members.Size() + s.Children.Size()
 | 
						|
}
 | 
						|
 | 
						|
// Empty returns true if there are no members of the set. It is a separate
 | 
						|
// function from Size since it's common to check whether size > 0, and
 | 
						|
// potentially much faster to return as soon as a single element is found.
 | 
						|
func (s *Set) Empty() bool {
 | 
						|
	if s.Members.Size() > 0 {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	return s.Children.Empty()
 | 
						|
}
 | 
						|
 | 
						|
// Has returns true if the field referenced by `p` is a member of the set.
 | 
						|
func (s *Set) Has(p Path) bool {
 | 
						|
	if len(p) == 0 {
 | 
						|
		// No one owns "the entire object"
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	for {
 | 
						|
		if len(p) == 1 {
 | 
						|
			return s.Members.Has(p[0])
 | 
						|
		}
 | 
						|
		var ok bool
 | 
						|
		s, ok = s.Children.Get(p[0])
 | 
						|
		if !ok {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
		p = p[1:]
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Equals returns true if s and s2 have exactly the same members.
 | 
						|
func (s *Set) Equals(s2 *Set) bool {
 | 
						|
	return s.Members.Equals(&s2.Members) && s.Children.Equals(&s2.Children)
 | 
						|
}
 | 
						|
 | 
						|
// String returns the set one element per line.
 | 
						|
func (s *Set) String() string {
 | 
						|
	elements := []string{}
 | 
						|
	s.Iterate(func(p Path) {
 | 
						|
		elements = append(elements, p.String())
 | 
						|
	})
 | 
						|
	return strings.Join(elements, "\n")
 | 
						|
}
 | 
						|
 | 
						|
// Iterate calls f once for each field that is a member of the set (preorder
 | 
						|
// DFS). The path passed to f will be reused so make a copy if you wish to keep
 | 
						|
// it.
 | 
						|
func (s *Set) Iterate(f func(Path)) {
 | 
						|
	s.iteratePrefix(Path{}, f)
 | 
						|
}
 | 
						|
 | 
						|
func (s *Set) iteratePrefix(prefix Path, f func(Path)) {
 | 
						|
	s.Members.Iterate(func(pe PathElement) { f(append(prefix, pe)) })
 | 
						|
	s.Children.iteratePrefix(prefix, f)
 | 
						|
}
 | 
						|
 | 
						|
// WithPrefix returns the subset of paths which begin with the given prefix,
 | 
						|
// with the prefix not included.
 | 
						|
func (s *Set) WithPrefix(pe PathElement) *Set {
 | 
						|
	subset, ok := s.Children.Get(pe)
 | 
						|
	if !ok {
 | 
						|
		return NewSet()
 | 
						|
	}
 | 
						|
	return subset
 | 
						|
}
 | 
						|
 | 
						|
// Leaves returns a set containing only the leaf paths
 | 
						|
// of a set.
 | 
						|
func (s *Set) Leaves() *Set {
 | 
						|
	leaves := PathElementSet{}
 | 
						|
	im := 0
 | 
						|
	ic := 0
 | 
						|
 | 
						|
	// any members that are not also children are leaves
 | 
						|
outer:
 | 
						|
	for im < len(s.Members.members) {
 | 
						|
		member := s.Members.members[im]
 | 
						|
 | 
						|
		for ic < len(s.Children.members) {
 | 
						|
			d := member.Compare(s.Children.members[ic].pathElement)
 | 
						|
			if d == 0 {
 | 
						|
				ic++
 | 
						|
				im++
 | 
						|
				continue outer
 | 
						|
			} else if d < 0 {
 | 
						|
				break
 | 
						|
			} else /* if d > 0 */ {
 | 
						|
				ic++
 | 
						|
			}
 | 
						|
		}
 | 
						|
		leaves.members = append(leaves.members, member)
 | 
						|
		im++
 | 
						|
	}
 | 
						|
 | 
						|
	return &Set{
 | 
						|
		Members:  leaves,
 | 
						|
		Children: *s.Children.Leaves(),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// setNode is a pair of PathElement / Set, for the purpose of expressing
 | 
						|
// nested set membership.
 | 
						|
type setNode struct {
 | 
						|
	pathElement PathElement
 | 
						|
	set         *Set
 | 
						|
}
 | 
						|
 | 
						|
// SetNodeMap is a map of PathElement to subset.
 | 
						|
type SetNodeMap struct {
 | 
						|
	members sortedSetNode
 | 
						|
}
 | 
						|
 | 
						|
type sortedSetNode []setNode
 | 
						|
 | 
						|
// Implement the sort interface; this would permit bulk creation, which would
 | 
						|
// be faster than doing it one at a time via Insert.
 | 
						|
func (s sortedSetNode) Len() int           { return len(s) }
 | 
						|
func (s sortedSetNode) Less(i, j int) bool { return s[i].pathElement.Less(s[j].pathElement) }
 | 
						|
func (s sortedSetNode) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
 | 
						|
 | 
						|
// Descend adds pe to the set if necessary, returning the associated subset.
 | 
						|
func (s *SetNodeMap) Descend(pe PathElement) *Set {
 | 
						|
	loc := sort.Search(len(s.members), func(i int) bool {
 | 
						|
		return !s.members[i].pathElement.Less(pe)
 | 
						|
	})
 | 
						|
	if loc == len(s.members) {
 | 
						|
		s.members = append(s.members, setNode{pathElement: pe, set: &Set{}})
 | 
						|
		return s.members[loc].set
 | 
						|
	}
 | 
						|
	if s.members[loc].pathElement.Equals(pe) {
 | 
						|
		return s.members[loc].set
 | 
						|
	}
 | 
						|
	s.members = append(s.members, setNode{})
 | 
						|
	copy(s.members[loc+1:], s.members[loc:])
 | 
						|
	s.members[loc] = setNode{pathElement: pe, set: &Set{}}
 | 
						|
	return s.members[loc].set
 | 
						|
}
 | 
						|
 | 
						|
// Size returns the sum of the number of members of all subsets.
 | 
						|
func (s *SetNodeMap) Size() int {
 | 
						|
	count := 0
 | 
						|
	for _, v := range s.members {
 | 
						|
		count += v.set.Size()
 | 
						|
	}
 | 
						|
	return count
 | 
						|
}
 | 
						|
 | 
						|
// Empty returns false if there's at least one member in some child set.
 | 
						|
func (s *SetNodeMap) Empty() bool {
 | 
						|
	for _, n := range s.members {
 | 
						|
		if !n.set.Empty() {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// Get returns (the associated set, true) or (nil, false) if there is none.
 | 
						|
func (s *SetNodeMap) Get(pe PathElement) (*Set, bool) {
 | 
						|
	loc := sort.Search(len(s.members), func(i int) bool {
 | 
						|
		return !s.members[i].pathElement.Less(pe)
 | 
						|
	})
 | 
						|
	if loc == len(s.members) {
 | 
						|
		return nil, false
 | 
						|
	}
 | 
						|
	if s.members[loc].pathElement.Equals(pe) {
 | 
						|
		return s.members[loc].set, true
 | 
						|
	}
 | 
						|
	return nil, false
 | 
						|
}
 | 
						|
 | 
						|
// Equals returns true if s and s2 have the same structure (same nested
 | 
						|
// child sets).
 | 
						|
func (s *SetNodeMap) Equals(s2 *SetNodeMap) bool {
 | 
						|
	if len(s.members) != len(s2.members) {
 | 
						|
		return false
 | 
						|
	}
 | 
						|
	for i := range s.members {
 | 
						|
		if !s.members[i].pathElement.Equals(s2.members[i].pathElement) {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
		if !s.members[i].set.Equals(s2.members[i].set) {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return true
 | 
						|
}
 | 
						|
 | 
						|
// Union returns a SetNodeMap with members that appear in either s or s2.
 | 
						|
func (s *SetNodeMap) Union(s2 *SetNodeMap) *SetNodeMap {
 | 
						|
	out := &SetNodeMap{}
 | 
						|
 | 
						|
	i, j := 0, 0
 | 
						|
	for i < len(s.members) && j < len(s2.members) {
 | 
						|
		if s.members[i].pathElement.Less(s2.members[j].pathElement) {
 | 
						|
			out.members = append(out.members, s.members[i])
 | 
						|
			i++
 | 
						|
		} else {
 | 
						|
			if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
 | 
						|
				out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set.Union(s2.members[j].set)})
 | 
						|
				i++
 | 
						|
			} else {
 | 
						|
				out.members = append(out.members, s2.members[j])
 | 
						|
			}
 | 
						|
			j++
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if i < len(s.members) {
 | 
						|
		out.members = append(out.members, s.members[i:]...)
 | 
						|
	}
 | 
						|
	if j < len(s2.members) {
 | 
						|
		out.members = append(out.members, s2.members[j:]...)
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// Intersection returns a SetNodeMap with members that appear in both s and s2.
 | 
						|
func (s *SetNodeMap) Intersection(s2 *SetNodeMap) *SetNodeMap {
 | 
						|
	out := &SetNodeMap{}
 | 
						|
 | 
						|
	i, j := 0, 0
 | 
						|
	for i < len(s.members) && j < len(s2.members) {
 | 
						|
		if s.members[i].pathElement.Less(s2.members[j].pathElement) {
 | 
						|
			i++
 | 
						|
		} else {
 | 
						|
			if !s2.members[j].pathElement.Less(s.members[i].pathElement) {
 | 
						|
				res := s.members[i].set.Intersection(s2.members[j].set)
 | 
						|
				if !res.Empty() {
 | 
						|
					out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: res})
 | 
						|
				}
 | 
						|
				i++
 | 
						|
			}
 | 
						|
			j++
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// Difference returns a SetNodeMap with members that appear in s but not in s2.
 | 
						|
func (s *SetNodeMap) Difference(s2 *Set) *SetNodeMap {
 | 
						|
	out := &SetNodeMap{}
 | 
						|
 | 
						|
	i, j := 0, 0
 | 
						|
	for i < len(s.members) && j < len(s2.Children.members) {
 | 
						|
		if s.members[i].pathElement.Less(s2.Children.members[j].pathElement) {
 | 
						|
			out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set})
 | 
						|
			i++
 | 
						|
		} else {
 | 
						|
			if !s2.Children.members[j].pathElement.Less(s.members[i].pathElement) {
 | 
						|
 | 
						|
				diff := s.members[i].set.Difference(s2.Children.members[j].set)
 | 
						|
				// We aren't permitted to add nodes with no elements.
 | 
						|
				if !diff.Empty() {
 | 
						|
					out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: diff})
 | 
						|
				}
 | 
						|
 | 
						|
				i++
 | 
						|
			}
 | 
						|
			j++
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if i < len(s.members) {
 | 
						|
		out.members = append(out.members, s.members[i:]...)
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// RecursiveDifference returns a SetNodeMap with members that appear in s but not in s2.
 | 
						|
//
 | 
						|
// Compared to a regular difference,
 | 
						|
// this removes every field **and its children** from s that is contained in s2.
 | 
						|
//
 | 
						|
// For example, with s containing `a.b.c` and s2 containing `a.b`,
 | 
						|
// a RecursiveDifference will result in `a`, as the entire node `a.b` gets removed.
 | 
						|
func (s *SetNodeMap) RecursiveDifference(s2 *Set) *SetNodeMap {
 | 
						|
	out := &SetNodeMap{}
 | 
						|
 | 
						|
	i, j := 0, 0
 | 
						|
	for i < len(s.members) && j < len(s2.Children.members) {
 | 
						|
		if s.members[i].pathElement.Less(s2.Children.members[j].pathElement) {
 | 
						|
			if !s2.Members.Has(s.members[i].pathElement) {
 | 
						|
				out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: s.members[i].set})
 | 
						|
			}
 | 
						|
			i++
 | 
						|
		} else {
 | 
						|
			if !s2.Children.members[j].pathElement.Less(s.members[i].pathElement) {
 | 
						|
				if !s2.Members.Has(s.members[i].pathElement) {
 | 
						|
					diff := s.members[i].set.RecursiveDifference(s2.Children.members[j].set)
 | 
						|
					if !diff.Empty() {
 | 
						|
						out.members = append(out.members, setNode{pathElement: s.members[i].pathElement, set: diff})
 | 
						|
					}
 | 
						|
				}
 | 
						|
				i++
 | 
						|
			}
 | 
						|
			j++
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if i < len(s.members) {
 | 
						|
		for _, c := range s.members[i:] {
 | 
						|
			if !s2.Members.Has(c.pathElement) {
 | 
						|
				out.members = append(out.members, c)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return out
 | 
						|
}
 | 
						|
 | 
						|
// EnsureNamedFieldsAreMembers returns a set that contains all the named fields along with the leaves.
 | 
						|
func (s *SetNodeMap) EnsureNamedFieldsAreMembers(sc *schema.Schema, tr schema.TypeRef) *SetNodeMap {
 | 
						|
	out := make(sortedSetNode, 0, s.Size())
 | 
						|
	atom, _ := sc.Resolve(tr)
 | 
						|
	for _, member := range s.members {
 | 
						|
		tr := schema.TypeRef{}
 | 
						|
		if member.pathElement.FieldName != nil && atom.Map != nil {
 | 
						|
			tr = atom.Map.ElementType
 | 
						|
			if sf, ok := atom.Map.FindField(*member.pathElement.FieldName); ok {
 | 
						|
				tr = sf.Type
 | 
						|
			}
 | 
						|
		} else if member.pathElement.Key != nil && atom.List != nil {
 | 
						|
			tr = atom.List.ElementType
 | 
						|
		}
 | 
						|
		out = append(out, setNode{
 | 
						|
			pathElement: member.pathElement,
 | 
						|
			set:         member.set.EnsureNamedFieldsAreMembers(sc, tr),
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return &SetNodeMap{
 | 
						|
		members: out,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Iterate calls f for each PathElement in the set.
 | 
						|
func (s *SetNodeMap) Iterate(f func(PathElement)) {
 | 
						|
	for _, n := range s.members {
 | 
						|
		f(n.pathElement)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (s *SetNodeMap) iteratePrefix(prefix Path, f func(Path)) {
 | 
						|
	for _, n := range s.members {
 | 
						|
		pe := n.pathElement
 | 
						|
		n.set.iteratePrefix(append(prefix, pe), f)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// Leaves returns a SetNodeMap containing
 | 
						|
// only setNodes with leaf PathElements.
 | 
						|
func (s *SetNodeMap) Leaves() *SetNodeMap {
 | 
						|
	out := &SetNodeMap{}
 | 
						|
	out.members = make(sortedSetNode, len(s.members))
 | 
						|
	for i, n := range s.members {
 | 
						|
		out.members[i] = setNode{
 | 
						|
			pathElement: n.pathElement,
 | 
						|
			set:         n.set.Leaves(),
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return out
 | 
						|
}
 |