123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- /*
- 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
- }
|