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