github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/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/joomcode/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(c *adt.OpContext, 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 a = sortedArcs(sets) 48 if adt.DebugSort > 0 { 49 adt.DebugSortFields(c, a) 50 } 51 return a 52 } 53 54 // func structFeatures(a []*adt.StructLit) []adt.Feature { 55 // sets := extractFeatures(a) 56 // return sortedArcs(sets) 57 // } 58 59 func (e *exporter) sortedArcs(v *adt.Vertex) (sorted []*adt.Vertex) { 60 if adt.DebugSort > 0 { 61 return v.Arcs 62 } 63 64 a := extractFeatures(v.Structs) 65 if len(a) == 0 { 66 return v.Arcs 67 } 68 69 sorted = make([]*adt.Vertex, len(v.Arcs)) 70 copy(sorted, v.Arcs) 71 72 m := sortArcs(a) 73 sort.SliceStable(sorted, func(i, j int) bool { 74 if m[sorted[i].Label] == 0 { 75 return m[sorted[j].Label] != 0 76 } 77 return m[sorted[i].Label] > m[sorted[j].Label] 78 }) 79 80 return sorted 81 } 82 83 // TODO: remove 84 func (e *exporter) extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) { 85 return extractFeatures(in) 86 } 87 88 func extractFeatures(in []*adt.StructInfo) (a [][]adt.Feature) { 89 for _, s := range in { 90 sorted := []adt.Feature{} 91 for _, e := range s.StructLit.Decls { 92 switch x := e.(type) { 93 case *adt.Field: 94 sorted = append(sorted, x.Label) 95 96 case *adt.OptionalField: 97 sorted = append(sorted, x.Label) 98 } 99 } 100 101 // Lists with a single element may still be useful to distinguish 102 // between known and unknown fields: unknown fields are sorted last. 103 if len(sorted) > 0 { 104 a = append(a, sorted) 105 } 106 } 107 return a 108 } 109 110 // sortedArcs is like sortArcs, but returns a the features of optional and 111 // required fields in an sorted slice. Ultimately, the implementation should 112 // use merge sort everywhere, and this will be the preferred method. Also, 113 // when querying optional fields as well, this helps identifying the optional 114 // fields. 115 func sortedArcs(fronts [][]adt.Feature) []adt.Feature { 116 m := sortArcs(fronts) 117 return sortedArcsFromMap(m) 118 } 119 120 func sortedArcsFromMap(m map[adt.Feature]int) []adt.Feature { 121 a := make([]adt.Feature, 0, len(m)) 122 123 for k := range m { 124 a = append(a, k) 125 } 126 127 sort.Slice(a, func(i, j int) bool { return m[a[i]] > m[a[j]] }) 128 129 return a 130 } 131 132 // sortArcs does a topological sort of arcs based on a variant of Kahn's 133 // algorithm. See 134 // https://www.geeksforgeeks.org/topological-sorting-indegree-based-solution/ 135 // 136 // It returns a map from feature to int where the feature with the highest 137 // number should be sorted first. 138 func sortArcs(fronts [][]adt.Feature) map[adt.Feature]int { 139 counts := map[adt.Feature]int{} 140 for _, a := range fronts { 141 if len(a) <= 1 { 142 continue // no dependencies 143 } 144 for _, f := range a[1:] { 145 counts[f]++ 146 } 147 } 148 149 // We could use a Heap instead of simple linear search here if we are 150 // concerned about the time complexity. 151 152 index := -1 153 outer: 154 for { 155 lists: 156 for i, a := range fronts { 157 for len(a) > 0 { 158 f := a[0] 159 n := counts[f] 160 if n > 0 { 161 continue lists 162 } 163 164 // advance list and decrease dependency. 165 a = a[1:] 166 fronts[i] = a 167 if len(a) > 1 && counts[a[0]] > 0 { 168 counts[a[0]]-- 169 } 170 171 if n == 0 { // may be head of other lists as well 172 counts[f] = index 173 index-- 174 } 175 continue outer // progress 176 } 177 } 178 179 for _, a := range fronts { 180 if len(a) > 0 { 181 // Detected a cycle. Fire at will to make progress. 182 counts[a[0]] = 0 183 continue outer 184 } 185 } 186 break 187 } 188 189 return counts 190 }