1// Copyright 2020 Google Inc. All rights reserved. 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 15package depset 16 17import ( 18 "fmt" 19 "iter" 20 "slices" 21 22 "github.com/google/blueprint/gobtools" 23) 24 25// DepSet is designed to be conceptually compatible with Bazel's depsets: 26// https://docs.bazel.build/versions/master/skylark/depsets.html 27 28type Order int 29 30const ( 31 PREORDER Order = iota 32 POSTORDER 33 TOPOLOGICAL 34) 35 36func (o Order) String() string { 37 switch o { 38 case PREORDER: 39 return "PREORDER" 40 case POSTORDER: 41 return "POSTORDER" 42 case TOPOLOGICAL: 43 return "TOPOLOGICAL" 44 default: 45 panic(fmt.Errorf("Invalid Order %d", o)) 46 } 47} 48 49type depSettableType comparable 50 51// A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies without 52// copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list 53// of dependency DepSet nodes. 54// 55// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order 56// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered 57// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that 58// elements of children are listed after all of their parents (unless there are duplicate direct 59// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the 60// duplicated element is not guaranteed). 61// 62// A DepSet is created by New or NewBuilder.Build from the slice for direct contents 63// and the *DepSets of dependencies. A DepSet is immutable once created. 64type DepSet[T depSettableType] struct { 65 handle *depSet[T] 66} 67 68type depSet[T depSettableType] struct { 69 preorder bool 70 reverse bool 71 order Order 72 direct []T 73 transitive []DepSet[T] 74} 75 76func (d DepSet[T]) impl() *depSet[T] { 77 return d.handle 78} 79 80func (d DepSet[T]) order() Order { 81 impl := d.impl() 82 return impl.order 83} 84 85type depSetGob[T depSettableType] struct { 86 Preorder bool 87 Reverse bool 88 Order Order 89 Direct []T 90 Transitive []DepSet[T] 91} 92 93func (d *DepSet[T]) ToGob() *depSetGob[T] { 94 impl := d.impl() 95 return &depSetGob[T]{ 96 Preorder: impl.preorder, 97 Reverse: impl.reverse, 98 Order: impl.order, 99 Direct: impl.direct, 100 Transitive: impl.transitive, 101 } 102} 103 104func (d *DepSet[T]) FromGob(data *depSetGob[T]) { 105 d.handle = &depSet[T]{ 106 preorder: data.Preorder, 107 reverse: data.Reverse, 108 order: data.Order, 109 direct: data.Direct, 110 transitive: data.Transitive, 111 } 112} 113 114func (d DepSet[T]) GobEncode() ([]byte, error) { 115 return gobtools.CustomGobEncode[depSetGob[T]](&d) 116} 117 118func (d *DepSet[T]) GobDecode(data []byte) error { 119 return gobtools.CustomGobDecode[depSetGob[T]](data, d) 120} 121 122// New returns an immutable DepSet with the given order, direct and transitive contents. 123func New[T depSettableType](order Order, direct []T, transitive []DepSet[T]) DepSet[T] { 124 var directCopy []T 125 var transitiveCopy []DepSet[T] 126 nonEmptyTransitiveCount := 0 127 for _, t := range transitive { 128 if t.handle != nil { 129 if t.order() != order { 130 panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s", 131 order, t.order())) 132 } 133 nonEmptyTransitiveCount++ 134 } 135 } 136 137 directCopy = slices.Clone(direct) 138 if nonEmptyTransitiveCount > 0 { 139 transitiveCopy = make([]DepSet[T], 0, nonEmptyTransitiveCount) 140 } 141 var transitiveIter iter.Seq2[int, DepSet[T]] 142 if order == TOPOLOGICAL { 143 // TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output. 144 // Pre-reverse the inputs here so their order is maintained in the output. 145 slices.Reverse(directCopy) 146 transitiveIter = slices.Backward(transitive) 147 } else { 148 transitiveIter = slices.All(transitive) 149 } 150 for _, t := range transitiveIter { 151 if t.handle != nil { 152 transitiveCopy = append(transitiveCopy, t) 153 } 154 } 155 156 if len(directCopy) == 0 && len(transitive) == 0 { 157 return DepSet[T]{nil} 158 } 159 160 depSet := &depSet[T]{ 161 preorder: order == PREORDER, 162 reverse: order == TOPOLOGICAL, 163 order: order, 164 direct: directCopy, 165 transitive: transitiveCopy, 166 } 167 168 return DepSet[T]{depSet} 169} 170 171// Builder is used to create an immutable DepSet. 172type Builder[T depSettableType] struct { 173 order Order 174 direct []T 175 transitive []DepSet[T] 176} 177 178// NewBuilder returns a Builder to create an immutable DepSet with the given order and 179// type, represented by a slice of type that will be in the DepSet. 180func NewBuilder[T depSettableType](order Order) *Builder[T] { 181 return &Builder[T]{ 182 order: order, 183 } 184} 185 186// DirectSlice adds direct contents to the DepSet being built by a Builder. Newly added direct 187// contents are to the right of any existing direct contents. 188func (b *Builder[T]) DirectSlice(direct []T) *Builder[T] { 189 b.direct = append(b.direct, direct...) 190 return b 191} 192 193// Direct adds direct contents to the DepSet being built by a Builder. Newly added direct 194// contents are to the right of any existing direct contents. 195func (b *Builder[T]) Direct(direct ...T) *Builder[T] { 196 b.direct = append(b.direct, direct...) 197 return b 198} 199 200// Transitive adds transitive contents to the DepSet being built by a Builder. Newly added 201// transitive contents are to the right of any existing transitive contents. 202func (b *Builder[T]) Transitive(transitive ...DepSet[T]) *Builder[T] { 203 for _, t := range transitive { 204 if t.handle != nil && t.order() != b.order { 205 panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s", 206 b.order, t.order())) 207 } 208 } 209 b.transitive = append(b.transitive, transitive...) 210 return b 211} 212 213// Build returns the DepSet being built by this Builder. The Builder retains its contents 214// for creating more depSets. 215func (b *Builder[T]) Build() DepSet[T] { 216 return New(b.order, b.direct, b.transitive) 217} 218 219// walk calls the visit method in depth-first order on a DepSet, preordered if d.preorder is set, 220// otherwise postordered. 221func (d DepSet[T]) walk(visit func([]T)) { 222 visited := make(map[DepSet[T]]bool) 223 224 var dfs func(d DepSet[T]) 225 dfs = func(d DepSet[T]) { 226 impl := d.impl() 227 visited[d] = true 228 if impl.preorder { 229 visit(impl.direct) 230 } 231 for _, dep := range impl.transitive { 232 if !visited[dep] { 233 dfs(dep) 234 } 235 } 236 237 if !impl.preorder { 238 visit(impl.direct) 239 } 240 } 241 242 dfs(d) 243} 244 245// ToList returns the DepSet flattened to a list. The order in the list is based on the order 246// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right 247// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed 248// after all of their parents (unless there are duplicate direct elements in the DepSet or any of 249// its transitive dependencies, in which case the ordering of the duplicated element is not 250// guaranteed). 251func (d DepSet[T]) ToList() []T { 252 if d.handle == nil { 253 return nil 254 } 255 impl := d.impl() 256 var list []T 257 d.walk(func(paths []T) { 258 list = append(list, paths...) 259 }) 260 list = firstUniqueInPlace(list) 261 if impl.reverse { 262 slices.Reverse(list) 263 } 264 return list 265} 266 267// firstUniqueInPlace returns all unique elements of a slice, keeping the first copy of 268// each. It modifies the slice contents in place, and returns a subslice of the original 269// slice. 270func firstUniqueInPlace[T comparable](slice []T) []T { 271 // 128 was chosen based on BenchmarkFirstUniqueStrings results. 272 if len(slice) > 128 { 273 return firstUniqueMap(slice) 274 } 275 return firstUniqueList(slice) 276} 277 278// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for 279// duplicates. 280func firstUniqueList[T any](in []T) []T { 281 writeIndex := 0 282outer: 283 for readIndex := 0; readIndex < len(in); readIndex++ { 284 for compareIndex := 0; compareIndex < writeIndex; compareIndex++ { 285 if interface{}(in[readIndex]) == interface{}(in[compareIndex]) { 286 // The value at readIndex already exists somewhere in the output region 287 // of the slice before writeIndex, skip it. 288 continue outer 289 } 290 } 291 if readIndex != writeIndex { 292 in[writeIndex] = in[readIndex] 293 } 294 writeIndex++ 295 } 296 return in[0:writeIndex] 297} 298 299// firstUniqueMap is an implementation of firstUnique using an O(N) hash set lookup to look for 300// duplicates. 301func firstUniqueMap[T comparable](in []T) []T { 302 writeIndex := 0 303 seen := make(map[T]bool, len(in)) 304 for readIndex := 0; readIndex < len(in); readIndex++ { 305 if _, exists := seen[in[readIndex]]; exists { 306 continue 307 } 308 seen[in[readIndex]] = true 309 if readIndex != writeIndex { 310 in[writeIndex] = in[readIndex] 311 } 312 writeIndex++ 313 } 314 return in[0:writeIndex] 315} 316