github.com/solo-io/cue@v0.4.7/internal/core/export/toposort.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package export 16 17 import ( 18 "sort" 19 20 "github.com/solo-io/cue/internal/core/adt" 21 ) 22 23 // TODO: topological sort should go arguably in a more fundamental place as it 24 // may be needed to sort inputs for comprehensions. 25 26 // VertexFeatures returns the feature list of v. The list may include more 27 // features than for which there are arcs and also includes features for 28 // optional fields. It assumes the Structs fields is properly initialized. 29 func VertexFeatures(v *adt.Vertex) []adt.Feature { 30 sets := extractFeatures(v.Structs) 31 m := sortArcs(sets) // TODO: use for convenience. 32 33 // Add features that are not in m. This may happen when fields were 34 // dynamically created. 35 var a []adt.Feature 36 for _, arc := range v.Arcs { 37 if _, ok := m[arc.Label]; !ok { 38 a = append(a, arc.Label) 39 } 40 } 41 42 sets = extractFeatures(v.Structs) 43 if len(a) > 0 { 44 sets = append(sets, a) 45 } 46 47 return sortedArcs(sets) 48 } 49 50 // func structFeatures(a []*adt.StructLit) []adt.Feature { 51 // sets := extractFeatures(a) 52 // return sortedArcs(sets) 53 // } 54 55 func (e *exporter) sortedArcs(v *adt.Vertex) (sorted []*adt.Vertex) { 56 a := extractFeatures(v.Structs) 57 if len(a) == 0 { 58 return v.Arcs 59 } 60 61 sorted = make([]*adt.Vertex, len(v.Arcs)) 62 copy(sorted, v.Arcs) 63 64 m := sortArcs(a) 65 sort.SliceStable(sorted, func(i, j int) bool { 66 if m[sorted[i].Label] == 0 { 67 return m[sorted[j].Label] != 0 68 } 69 return m[sorted[i].Label] > m[sorted[j].Label] 70 }) 71 72 return sorted 73 } 74 75 // TODO: remove 76 func (e *exporter) extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) { 77 return extractFeatures(in) 78 } 79 80 func extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) { 81 for _, s := range in { 82 sorted := []adt.Feature{} 83 for _, e := range s.StructLit.Decls { 84 switch x := e.(type) { 85 case *adt.Field: 86 sorted = append(sorted, x.Label) 87 88 case *adt.OptionalField: 89 sorted = append(sorted, x.Label) 90 } 91 } 92 93 // Lists with a single element may still be useful to distinguish 94 // between known and unknown fields: unknown fields are sorted last. 95 if len(sorted) > 0 { 96 a = append(a, sorted) 97 } 98 } 99 return a 100 } 101 102 // sortedArcs is like sortArcs, but returns a the features of optional and 103 // required fields in an sorted slice. Ultimately, the implementation should 104 // use merge sort everywhere, and this will be the preferred method. Also, 105 // when querying optional fields as well, this helps identifying the optional 106 // fields. 107 func sortedArcs(fronts [][]adt.Feature) []adt.Feature { 108 m := sortArcs(fronts) 109 return sortedArcsFromMap(m) 110 } 111 112 func sortedArcsFromMap(m map[adt.Feature]int) []adt.Feature { 113 a := make([]adt.Feature, 0, len(m)) 114 115 for k := range m { 116 a = append(a, k) 117 } 118 119 sort.Slice(a, func(i, j int) bool { return m[a[i]] > m[a[j]] }) 120 121 return a 122 } 123 124 // sortArcs does a topological sort of arcs based on a variant of Kahn's 125 // algorithm. See 126 // https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/ 127 // 128 // It returns a map from feature to int where the feature with the highest 129 // number should be sorted first. 130 func sortArcs(fronts [][]adt.Feature) map[adt.Feature]int { 131 counts := map[adt.Feature]int{} 132 for _, a := range fronts { 133 if len(a) <= 1 { 134 continue // no dependencies 135 } 136 for _, f := range a[1:] { 137 counts[f]++ 138 } 139 } 140 141 // We could use a Heap instead of simple linear search here if we are 142 // concerned about the time complexity. 143 144 index := -1 145 outer: 146 for { 147 lists: 148 for i, a := range fronts { 149 for len(a) > 0 { 150 f := a[0] 151 n := counts[f] 152 if n > 0 { 153 continue lists 154 } 155 156 // advance list and decrease dependency. 157 a = a[1:] 158 fronts[i] = a 159 if len(a) > 1 && counts[a[0]] > 0 { 160 counts[a[0]]-- 161 } 162 163 if n == 0 { // may be head of other lists as well 164 counts[f] = index 165 index-- 166 } 167 continue outer // progress 168 } 169 } 170 171 for _, a := range fronts { 172 if len(a) > 0 { 173 // Detected a cycle. Fire at will to make progress. 174 counts[a[0]] = 0 175 continue outer 176 } 177 } 178 break 179 } 180 181 return counts 182 }