|
|
|
|
Quelle depset.go
Sprache: unbekannt
|
|
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
// Copyright 2020 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2. 0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2. 0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package depset
import (
"bytes"
"errors"
"fmt"
"iter"
"slices"
"unique"
"github.com/google/blueprint/gobtools"
"github.com/google/blueprint/proptools"
"github.com/google/blueprint/uniquelist"
)
// DepSet is designed to be conceptually compatible with Bazel's depsets:
// https://docs.bazel.build/versions/master/skylark/depsets.html
type Order int
const (
PREORDER Order = iota
POSTORDER
TOPOLOGICAL
)
func (o Order) String() string {
switch o {
case PREORDER:
return "PREORDER"
case POSTORDER:
return "POSTORDER"
case TOPOLOGICAL:
return "TOPOLOGICAL"
default:
panic(fmt.Errorf("Invalid Order %d", o))
}
}
type depSettableType comparable
// A DepSet efficiently stores a slice of an arbitrary type from transitive dependencies witho ut
// copying. It is stored as a DAG of DepSet nodes, each of which has some direct contents and a list
// of dependency DepSet nodes.
//
// A DepSet has an order that will be used to walk the DAG when ToList() is called. The order
// can be POSTORDER, PREORDER, or TOPOLOGICAL. POSTORDER and PREORDER orders return a postordered
// or preordered left to right flattened list. TOPOLOGICAL returns a list that guarantees that
// elements of children are listed after all of their parents (unless there are duplicate direct
// elements in the DepSet or any of its transitive dependencies, in which case the ordering of the
// duplicated element is not guaranteed).
//
// A DepSet is created by New or NewBuilder.Build from the slice for direct contents
// and the DepSets of dependencies. A DepSet is immutable once created.
//
// DepSets are stored using UniqueList which uses the unique package to intern them, which ensures
// that the graph semantics of the DepSet are maintained even after serializing/deserializing or
// when mixing newly created and deserialized DepSets.
type DepSet[T depSettableType] struct {
// handle is a unique.Handle to an internal depSet object, which makes DepSets effectively a
// single pointer.
handle unique.Handle[depSet[T]]
}
type depSet[T depSettableType] struct {
preorder bool
reverse bool
order Order
direct uniquelist.UniqueList[T]
transitive uniquelist.UniqueList[DepSet[T]]
}
// impl returns a copy of the uniquified depSet for a DepSet.
func (d DepSet[T]) impl() depSet[T] {
return d.handle.Value()
}
func (d DepSet[T]) order() Order {
impl := d.impl()
return impl.order
}
// This method is required for DepSet to implement CustomEnc
func (d DepSet[T]) GetTypeId() int16 {
return -1
}
func (d DepSet[T]) Encode(c gobtools.EncContext, buf *bytes.Buffer) error {
return gobtools.EncodeReference(c, d, buf, func(value DepSet[T], buf *bytes.Buffer) error {
return value.encodeInternal(c, buf, func(buffer *bytes.Buffer, data T) error {
return gobtools.EncodeStruct(c, buffer, data)
})
})
}
func (d DepSet[T]) EncodeInterface(c gobtools.EncContext, buf *bytes.Buffer) error {
return gobtools.EncodeReference(c, d, buf, func(value DepSet[T], buf *bytes.Buffer) error {
return value.encodeInternal(c, buf, func(buffer *bytes.Buffer, data T) error {
return gobtools.EncodeInterface(c, buffer, data)
})
})
}
func (d DepSet[T]) EncodeString(c gobtools.EncContext, buf *bytes.Buffer) error {
return gobtools.EncodeReference(c, d, buf, func(value DepSet[T], buf *bytes.Buffer) error {
return value.encodeInternal(c, buf, func(buffer *bytes.Buffer, data T) error {
return gobtools.EncodeString(buffer, any(data).(string))
})
})
}
// The Gob encoding and decoding logic below only works in a single thread environment,
// which is currently the case. When parallel Gob cache processing is necessary the logic
// needs to be revisited.
func (d DepSet[T]) encodeInternal(c gobtools.EncContext, buf *bytes.Buffer, encode func(buffer *bytes.Buffer, data T) error) error {
var err error
var zeroDepSet DepSet[T]
if d == zeroDepSet {
return gobtools.EncodeBool(buf, false)
} else {
if err = gobtools.EncodeBool(buf, true); err != nil {
return err
}
}
impl := d.impl()
if err = errors.Join(
gobtools.EncodeBool(buf, impl.preorder),
gobtools.EncodeBool(buf, impl.reverse),
gobtools.EncodeInt16(buf, int16(impl.order))); err != nil {
return err
}
dlist := impl.direct.ToSlice()
if err = gobtools.EncodeInt32(buf, int32(len(dlist))); err != nil {
return err
}
for i := 0; i < len(dlist); i++ {
if err = encode(buf, dlist[i]); err != nil {
return err
}
}
tlist := impl.transitive.ToSlice()
if err = gobtools.EncodeInt32(buf, int32(len(tlist))); err != nil {
return err
}
for i := 0; i < len(tlist); i++ {
if err = gobtools.EncodeReference(c, tlist[i], buf, func(value DepSet[T], buf *bytes.Buffer) error {
return value.encodeInternal(c, buf, encode)
}); err != nil {
return err
}
}
return nil
}
func (d *DepSet[T]) Decode(c gobtools.EncContext, buf *bytes.Reader) error {
tmp, err := gobtools.DecodeReference(c, d, buf, func(value *DepSet[T], buf *bytes.Reader) error {
return value.decodeInternal(c, buf, func(reader *bytes.Reader, data *T) error {
return gobtools.DecodeStruct(c, reader, data)
})
})
if err == nil {
*d = *tmp
}
return err
}
func (d *DepSet[T]) DecodeInterface(c gobtools.EncContext, buf *bytes.Reader) error {
tmp, err := gobtools.DecodeReference(c, d, buf, func(value *DepSet[T], buf *bytes.Reader) error {
return value.decodeInternal(c, buf, func(reader *bytes.Reader, data *T) error {
var err error
if tmpVal, err := gobtools.DecodeInterface(c, reader); err == nil && tmpVal != nil {
*data = tmpVal.(T)
}
return err
})
})
if err == nil {
*d = *tmp
}
return err
}
func (d *DepSet[T]) DecodeString(c gobtools.EncContext, buf *bytes.Reader) error {
tmp, err := gobtools.DecodeReference(c, d, buf, func(value *DepSet[T], buf *bytes.Reader) error {
return value.decodeInternal(c, buf, func(reader *bytes.Reader, data *T) error {
var sValue string
if err := gobtools.DecodeString(reader, &sValue); err != nil {
return err
}
*data = any(sValue).(T)
return nil
})
})
if err == nil {
*d = *tmp
}
return err
}
func (d *DepSet[T]) decodeInternal(c gobtools.EncContext, buf *bytes.Reader, decode func(reader *bytes.Reader, value *T) error) error {
var err error
var valueSet bool
if err = gobtools.DecodeBool(buf, &valueSet); err != nil || !valueSet {
return err
}
var fromGob depSet[T]
var order int16
if err = errors.Join(
gobtools.DecodeBool(buf, &fromGob.preorder),
gobtools.DecodeBool(buf, &fromGob.reverse),
gobtools.DecodeInt16(buf, &order)); err != nil {
return err
}
fromGob.order = Order(order)
var dlist []T
var dlen int32
err = gobtools.DecodeInt32(buf, &dlen)
if err != nil {
return err
}
if dlen > 0 {
dlist = make([]T, dlen)
for i := 0; i < int(dlen); i++ {
if err = decode(buf, &dlist[i]); err != nil {
return err
}
}
}
fromGob.direct = uniquelist.Make(dlist)
var tlist []DepSet[T]
var tlen int32
err = gobtools.DecodeInt32(buf, &tlen)
if err != nil {
return err
}
if tlen > 0 {
tlist = make([]DepSet[T], tlen)
for i := 0; i < int(tlen); i++ {
tmp, err := gobtools.DecodeReference(c, &tlist[i], buf, func(value *DepSet[T], buf *bytes.Reader) error {
return value.decodeInternal(c, buf, decode)
})
if err == nil {
tlist[i] = *tmp
} else {
return err
}
}
}
fromGob.transitive = uniquelist.Make(tlist)
d.handle = unique.Make(fromGob)
return err
}
func boolToByte(b bool) byte {
if b {
return 1
}
return 0
}
func (d DepSet[T]) Hash(hasher *proptools.Hasher, typeName string, hashT func(*proptools.Hasher, T) error) error {
var err error
var zeroDepSet DepSet[T]
hasher.WriteString(fmt.Sprintf(":.depset.DepSet[%s]", typeName))
if d == zeroDepSet {
hasher.WriteByte(0)
return nil
}
hash := func(hasher1 *proptools.Hasher) error {
impl := d.impl()
hasher1.WriteString(":.bool")
hasher1.WriteByte(boolToByte(impl.preorder))
hasher1.WriteString(":.bool")
hasher1.WriteByte(boolToByte(impl.reverse))
hasher1.WriteString(":depset.Order")
hasher1.WriteInt(int(impl.order))
if err = impl.direct.Hash(hasher1, typeName, hashT); err != nil {
return err
}
hashD := func(hasher2 *proptools.Hasher, v DepSet[T]) error {
return v.Hash(hasher2, typeName, hashT)
}
return impl.transitive.Hash(hasher1, typeName, hashD)
}
return proptools.HashReference(hasher, d, hash)
}
// New returns an immutable DepSet with the given order, direct and transitive contents.
func New[T depSettableType](order Order, direct []T, transitive []DepSet[T]) DepSet[T] {
var directCopy []T
var transitiveCopy []DepSet[T]
// Create a zero value of DepSet, which will be used to check if the unique.Handle is the zero value.
var zeroDepSet DepSet[T]
nonEmptyTransitiveCount := 0
for _, t := range transitive {
// A zero valued DepSet has no associated unique.Handle for a depSet. It has no contents, so it can
// be skipped.
if t != zeroDepSet {
if t.handle.Value().order != order {
panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
order, t.handle.Value().order))
}
nonEmptyTransitiveCount++
}
}
directCopy = slices.Clone(direct)
if nonEmptyTransitiveCount > 0 {
transitiveCopy = make([]DepSet[T], 0, nonEmptyTransitiveCount)
}
var transitiveIter iter.Seq2[int, DepSet[T]]
if order == TOPOLOGICAL {
// TOPOLOGICAL is implemented as a postorder traversal followed by reversing the output.
// Pre-reverse the inputs here so their order is maintained in the output.
slices.Reverse(directCopy)
transitiveIter = slices.Backward(transitive)
} else {
transitiveIter = slices.All(transitive)
}
// Copy only the non-zero-valued elements in the transitive list. transitiveIter may be a forwards
// or backards iterator.
for _, t := range transitiveIter {
if t != zeroDepSet {
transitiveCopy = append(transitiveCopy, t)
}
}
// Optimization: If both the direct and transitive lists are empty then this DepSet is semantically
// equivalent to the zero valued DepSet (effectively a nil pointer). Returning the zero value will
// allow this DepSet to be skipped in DepSets that reference this one as a transitive input, saving
// memory.
if len(directCopy) == 0 && len(transitive) == 0 {
return DepSet[T]{}
}
// Create a depSet to hold the contents.
depSet := depSet[T]{
preorder: order == PREORDER,
reverse: order == TOPOLOGICAL,
order: order,
direct: uniquelist.Make(directCopy),
transitive: uniquelist.Make(transitiveCopy),
}
// Uniquify the depSet and store it in a DepSet.
return DepSet[T]{unique.Make(depSet)}
}
// Builder is used to create an immutable DepSet.
type Builder[T depSettableType] struct {
order Order
direct []T
transitive []DepSet[T]
}
// NewBuilder returns a Builder to create an immutable DepSet with the given order and
// type, represented by a slice of type that will be in the DepSet.
func NewBuilder[T depSettableType](order Order) *Builder[T] {
return &Builder[T]{
order: order,
}
}
// DirectSlice adds direct contents to the DepSet being built by a Builder. Newly added direct
// contents are to the right of any existing direct contents.
func (b *Builder[T]) DirectSlice(direct []T) *Builder[T] {
b.direct = append(b.direct, direct...)
return b
}
// Direct adds direct contents to the DepSet being built by a Builder. Newly added direct
// contents are to the right of any existing direct contents.
func (b *Builder[T]) Direct(direct ...T) *Builder[T] {
b.direct = append(b.direct, direct...)
return b
}
// Transitive adds transitive contents to the DepSet being built by a Builder. Newly added
// transitive contents are to the right of any existing transitive contents.
func (b *Builder[T]) Transitive(transitive ...DepSet[T]) *Builder[T] {
var zeroDepSet DepSet[T]
for _, t := range transitive {
if t != zeroDepSet && t.order() != b.order {
panic(fmt.Errorf("incompatible order, new DepSet is %s but transitive DepSet is %s",
b.order, t.order()))
}
}
b.transitive = append(b.transitive, transitive...)
return b
}
// Build returns the DepSet being built by this Builder. The Builder retains its contents
// for creating more depSets.
func (b *Builder[T]) Build() DepSet[T] {
return New(b.order, b.direct, b.transitive)
}
// collect collects the contents of the DepSet in depth-first order, preordered if d.preorder is set,
// otherwise postordered.
func (d DepSet[T]) collect() []T {
visited := make(map[DepSet[T]]bool)
var list []T
var dfs func(d DepSet[T])
dfs = func(d DepSet[T]) {
impl := d.impl()
visited[d] = true
if impl.preorder {
list = impl.direct.AppendTo(list)
}
for dep := range impl.transitive.Iter() {
if !visited[dep] {
dfs(dep)
}
}
if !impl.preorder {
list = impl.direct.AppendTo(list)
}
}
dfs(d)
return list
}
// ToList returns the DepSet flattened to a list. The order in the list is based on the order
// of the DepSet. POSTORDER and PREORDER orders return a postordered or preordered left to right
// flattened list. TOPOLOGICAL returns a list that guarantees that elements of children are listed
// after all of their parents (unless there are duplicate direct elements in the DepSet or any of
// its transitive dependencies, in which case the ordering of the duplicated element is not
// guaranteed).
func (d DepSet[T]) ToList() []T {
var zeroDepSet unique.Handle[depSet[T]]
if d.handle == zeroDepSet {
return nil
}
impl := d.impl()
list := d.collect()
list = firstUniqueInPlace(list)
if impl.reverse {
slices.Reverse(list)
}
return list
}
// SetMinus returns a new DepSet containing elements in the receiver DepSet 'd'
// that are not present in the 'other' DepSet. The resulting DepSet is "flat"
// (it has no transitive members) and inherits the traversal order from the receiver 'd'.
func (d DepSet[T]) SetMinus(other DepSet[T]) DepSet[T] {
listA := d.ToList()
if len(listA) == 0 {
// If the receiver is empty, the result is always an empty DepSet.
return DepSet[T]{}
}
// Flatten the 'other' DepSet to a list. If it's empty, we don't need to do any
// subtraction, so we can return a new flat DepSet with the receiver's elements.
listB := other.ToList()
if len(listB) == 0 {
return New(d.order(), listA, nil)
}
// Create a map from the 'other' list for efficient lookups.
otherElements := make(map[T]bool, len(listB))
for _, item := range listB {
otherElements[item] = true
}
// Build the result slice by including only the elements from listA that do not
// exist in the otherElements map.
result := make([]T, 0, len(listA))
for _, item := range listA {
if _, exists := otherElements[item]; !exists {
result = append(result, item)
}
}
// Create a new, flat DepSet from the resulting slice with the same order as 'd'.
return New(d.order(), result, nil)
}
// firstUniqueInPlace returns all unique elements of a slice, keeping the first copy of
// each. It modifies the slice contents in place, and returns a subslice of the original
// slice.
func firstUniqueInPlace[T comparable](slice []T) []T {
// 128 was chosen based on BenchmarkFirstUniqueStrings results.
if len(slice) > 128 {
return firstUniqueMap(slice)
}
return firstUniqueList(slice)
}
// firstUniqueList is an implementation of firstUnique using an O(N^2) list comparison to look for
// duplicates.
func firstUniqueList[T any](in []T) []T {
writeIndex := 0
outer:
for readIndex := 0; readIndex < len(in); readIndex++ {
for compareIndex := 0; compareIndex < writeIndex; compareIndex++ {
if interface{}(in[readIndex]) == interface{}(in[compareIndex]) {
// The value at readIndex already exists somewhere in the output region
// of the slice before writeIndex, skip it.
continue outer
}
}
if readIndex != writeIndex {
in[writeIndex] = in[readIndex]
}
writeIndex++
}
return in[0:writeIndex]
}
// firstUniqueMap is an implementation of firstUnique using an O(N) hash set lookup to look for
// duplicates.
func firstUniqueMap[T comparable](in []T) []T {
writeIndex := 0
seen := make(map[T]bool, len(in))
for readIndex := 0; readIndex < len(in); readIndex++ {
if _, exists := seen[in[readIndex]]; exists {
continue
}
seen[in[readIndex]] = true
if readIndex != writeIndex {
in[writeIndex] = in[readIndex]
}
writeIndex++
}
return in[0:writeIndex]
}
[Dauer der Verarbeitung: 0.25 Sekunden, vorverarbeitet 2026-06-28]
|
2026-07-09
|
|
|
|
|