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  }