mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
Add util.HasherMap (#7363)
This is a simpler version of util.TypedHashMap where the keys implement a `.Hash()` method and as such won't need one to be passed in, and where the values are largely ignored by the map. These maps are smaller / more performant, but most importantly, they are nicer to work with. Perf wise, this saves about 600k+ allocs and 40 MB allocated memory in `regal lint bundle`: ``` 1207614875 ns/op 3293454016 B/op 64802095 allocs/op 1197978125 ns/op 3256960504 B/op 64164871 allocs/op ``` Also: - Use `strings.Builder` instead of `fmt.Sprintf` in one location - Remove `ValueMap.Copy` as it was only used in a test Signed-off-by: Anders Eknert <anders@styra.com>
This commit is contained in:
+4
-4
@@ -781,8 +781,8 @@ func (rc *refChecker) checkRef(curr *TypeEnv, node *typeTreeNode, ref Ref, idx i
|
||||
|
||||
case RootDocumentNames.Contains(ref[0]):
|
||||
if idx != 0 {
|
||||
node.Children().Iter(func(_, child util.T) bool {
|
||||
_ = rc.checkRef(curr, child.(*typeTreeNode), ref, idx+1) // ignore error
|
||||
node.Children().Iter(func(_ Value, child *typeTreeNode) bool {
|
||||
_ = rc.checkRef(curr, child, ref, idx+1) // ignore error
|
||||
return false
|
||||
})
|
||||
return nil
|
||||
@@ -1127,8 +1127,8 @@ func newArgError(loc *Location, builtinName Ref, msg string, have []types.Type,
|
||||
}
|
||||
|
||||
func getOneOfForNode(node *typeTreeNode) (result []Value) {
|
||||
node.Children().Iter(func(k, _ util.T) bool {
|
||||
result = append(result, k.(Value))
|
||||
node.Children().Iter(func(k Value, _ *typeTreeNode) bool {
|
||||
result = append(result, k)
|
||||
return false
|
||||
})
|
||||
|
||||
|
||||
@@ -402,6 +402,10 @@ func TermValueCompare(a, b *Term) int {
|
||||
return a.Value.Compare(b.Value)
|
||||
}
|
||||
|
||||
func TermValueEqual(a, b *Term) bool {
|
||||
return ValueEqual(a.Value, b.Value)
|
||||
}
|
||||
|
||||
func ValueEqual(a, b Value) bool {
|
||||
// TODO(ae): why doesn't this work the same?
|
||||
//
|
||||
|
||||
+25
-22
@@ -124,7 +124,7 @@ type Compiler struct {
|
||||
|
||||
localvargen *localVarGenerator
|
||||
moduleLoader ModuleLoader
|
||||
ruleIndices *util.HashMap
|
||||
ruleIndices *util.HasherMap[Ref, RuleIndex]
|
||||
stages []stage
|
||||
maxErrs int
|
||||
sorted []string // list of sorted module names
|
||||
@@ -303,15 +303,10 @@ type stage struct {
|
||||
func NewCompiler() *Compiler {
|
||||
|
||||
c := &Compiler{
|
||||
Modules: map[string]*Module{},
|
||||
RewrittenVars: map[Var]Var{},
|
||||
Required: &Capabilities{},
|
||||
ruleIndices: util.NewHashMap(func(a, b util.T) bool {
|
||||
r1, r2 := a.(Ref), b.(Ref)
|
||||
return r1.Equal(r2)
|
||||
}, func(x util.T) int {
|
||||
return x.(Ref).Hash()
|
||||
}),
|
||||
Modules: map[string]*Module{},
|
||||
RewrittenVars: map[Var]Var{},
|
||||
Required: &Capabilities{},
|
||||
ruleIndices: util.NewHasherMap[Ref, RuleIndex](RefEqual),
|
||||
maxErrs: CompileErrorLimitDefault,
|
||||
after: map[string][]CompilerStageDefinition{},
|
||||
unsafeBuiltinsMap: map[string]struct{}{},
|
||||
@@ -825,7 +820,7 @@ func (c *Compiler) RuleIndex(path Ref) RuleIndex {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return r.(RuleIndex)
|
||||
return r
|
||||
}
|
||||
|
||||
// PassesTypeCheck determines whether the given body passes type checking
|
||||
@@ -1738,13 +1733,9 @@ func (c *Compiler) err(err *Error) {
|
||||
c.Errors = append(c.Errors, err)
|
||||
}
|
||||
|
||||
func (c *Compiler) getExports() *util.HashMap {
|
||||
func (c *Compiler) getExports() *util.HasherMap[Ref, []Ref] {
|
||||
|
||||
rules := util.NewHashMap(func(a, b util.T) bool {
|
||||
return a.(Ref).Equal(b.(Ref))
|
||||
}, func(v util.T) int {
|
||||
return v.(Ref).Hash()
|
||||
})
|
||||
rules := util.NewHasherMap[Ref, []Ref](RefEqual)
|
||||
|
||||
for _, name := range c.sorted {
|
||||
mod := c.Modules[name]
|
||||
@@ -1757,18 +1748,30 @@ func (c *Compiler) getExports() *util.HashMap {
|
||||
return rules
|
||||
}
|
||||
|
||||
func hashMapAdd(rules *util.HashMap, pkg, rule Ref) {
|
||||
func refSliceEqual(a, b []Ref) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if !a[i].Equal(b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func hashMapAdd(rules *util.HasherMap[Ref, []Ref], pkg, rule Ref) {
|
||||
prev, ok := rules.Get(pkg)
|
||||
if !ok {
|
||||
rules.Put(pkg, []Ref{rule})
|
||||
return
|
||||
}
|
||||
for _, p := range prev.([]Ref) {
|
||||
for _, p := range prev {
|
||||
if p.Equal(rule) {
|
||||
return
|
||||
}
|
||||
}
|
||||
rules.Put(pkg, append(prev.([]Ref), rule))
|
||||
rules.Put(pkg, append(prev, rule))
|
||||
}
|
||||
|
||||
func (c *Compiler) GetAnnotationSet() *AnnotationSet {
|
||||
@@ -1867,7 +1870,7 @@ func (c *Compiler) resolveAllRefs() {
|
||||
|
||||
var ruleExports []Ref
|
||||
if x, ok := rules.Get(mod.Package.Path); ok {
|
||||
ruleExports = x.([]Ref)
|
||||
ruleExports = x
|
||||
}
|
||||
|
||||
globals := getGlobals(mod.Package, ruleExports, mod.Imports)
|
||||
@@ -3014,7 +3017,7 @@ func (qc *queryCompiler) resolveRefs(qctx *QueryContext, body Body) (Body, error
|
||||
var ruleExports []Ref
|
||||
rules := qc.compiler.getExports()
|
||||
if exist, ok := rules.Get(pkg.Path); ok {
|
||||
ruleExports = exist.([]Ref)
|
||||
ruleExports = exist
|
||||
}
|
||||
|
||||
globals = getGlobals(qctx.Package, ruleExports, qctx.Imports)
|
||||
|
||||
+19
-23
@@ -410,28 +410,8 @@ func TestCompilerGetExports(t *testing.T) {
|
||||
// TODO(sr): add multi-val rule, and ref-with-var single-value rule.
|
||||
}
|
||||
|
||||
hashMap := func(ms map[string][]string) *util.HashMap {
|
||||
rules := util.NewHashMap(func(a, b util.T) bool {
|
||||
switch a := a.(type) {
|
||||
case Ref:
|
||||
return a.Equal(b.(Ref))
|
||||
case []Ref:
|
||||
b := b.([]Ref)
|
||||
if len(b) != len(a) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if !a[i].Equal(b[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}, func(v util.T) int {
|
||||
return v.(Ref).Hash()
|
||||
})
|
||||
hashMap := func(ms map[string][]string) *util.HasherMap[Ref, []Ref] {
|
||||
rules := util.NewHasherMap[Ref, []Ref](RefEqual)
|
||||
for r, rs := range ms {
|
||||
refs := make([]Ref, len(rs))
|
||||
for i := range rs {
|
||||
@@ -449,13 +429,29 @@ func TestCompilerGetExports(t *testing.T) {
|
||||
c.Modules[strconv.Itoa(i)] = m
|
||||
c.sorted = append(c.sorted, strconv.Itoa(i))
|
||||
}
|
||||
if exp, act := hashMap(tc.exports), c.getExports(); !exp.Equal(act) {
|
||||
if exp, act := hashMap(tc.exports), c.getExports(); !refMapEqual(exp, act) {
|
||||
t.Errorf("expected %v, got %v", exp, act)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func refMapEqual(a, b *util.HasherMap[Ref, []Ref]) bool {
|
||||
if a.Len() != b.Len() {
|
||||
return false
|
||||
}
|
||||
return !a.Iter(func(k Ref, v []Ref) bool {
|
||||
v2, ok := b.Get(k)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
if !refSliceEqual(v, v2) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
func toRef(s string) Ref {
|
||||
switch t := MustParseTerm(s).Value.(type) {
|
||||
case Var:
|
||||
|
||||
+19
-24
@@ -200,10 +200,7 @@ func (env *TypeEnv) getRefRecExtent(node *typeTreeNode) types.Type {
|
||||
|
||||
children := []*types.StaticProperty{}
|
||||
|
||||
node.Children().Iter(func(k, v util.T) bool {
|
||||
key := k.(Value)
|
||||
child := v.(*typeTreeNode)
|
||||
|
||||
node.Children().Iter(func(key Value, child *typeTreeNode) bool {
|
||||
tpe := env.getRefRecExtent(child)
|
||||
|
||||
// NOTE(sr): Converting to Golang-native types here is an extension of what we did
|
||||
@@ -237,14 +234,14 @@ func (env *TypeEnv) wrap() *TypeEnv {
|
||||
type typeTreeNode struct {
|
||||
key Value
|
||||
value types.Type
|
||||
children *util.HashMap
|
||||
children *util.HasherMap[Value, *typeTreeNode]
|
||||
}
|
||||
|
||||
func newTypeTree() *typeTreeNode {
|
||||
return &typeTreeNode{
|
||||
key: nil,
|
||||
value: nil,
|
||||
children: util.NewHashMap(valueEq, valueHash),
|
||||
children: util.NewHasherMap[Value, *typeTreeNode](ValueEqual),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,10 +250,10 @@ func (n *typeTreeNode) Child(key Value) *typeTreeNode {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return value.(*typeTreeNode)
|
||||
return value
|
||||
}
|
||||
|
||||
func (n *typeTreeNode) Children() *util.HashMap {
|
||||
func (n *typeTreeNode) Children() *util.HasherMap[Value, *typeTreeNode] {
|
||||
return n.children
|
||||
}
|
||||
|
||||
@@ -267,7 +264,7 @@ func (n *typeTreeNode) Get(path Ref) types.Type {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
curr = child.(*typeTreeNode)
|
||||
curr = child
|
||||
}
|
||||
return curr.Value()
|
||||
}
|
||||
@@ -285,7 +282,7 @@ func (n *typeTreeNode) PutOne(key Value, tpe types.Type) {
|
||||
child.key = key
|
||||
n.children.Put(key, child)
|
||||
} else {
|
||||
child = c.(*typeTreeNode)
|
||||
child = c
|
||||
}
|
||||
|
||||
child.value = tpe
|
||||
@@ -302,7 +299,7 @@ func (n *typeTreeNode) Put(path Ref, tpe types.Type) {
|
||||
child.key = term.Value
|
||||
curr.children.Put(child.key, child)
|
||||
} else {
|
||||
child = c.(*typeTreeNode)
|
||||
child = c
|
||||
}
|
||||
|
||||
curr = child
|
||||
@@ -324,8 +321,7 @@ func (n *typeTreeNode) Insert(path Ref, tpe types.Type, env *TypeEnv) {
|
||||
child.key = term.Value
|
||||
curr.children.Put(child.key, child)
|
||||
} else {
|
||||
child = c.(*typeTreeNode)
|
||||
|
||||
child = c
|
||||
if child.value != nil && i+1 < len(path) {
|
||||
// If child has an object value, merge the new value into it.
|
||||
if o, ok := child.value.(*types.Object); ok {
|
||||
@@ -426,13 +422,12 @@ func (n *typeTreeNode) String() string {
|
||||
b.WriteString(v.String())
|
||||
}
|
||||
|
||||
n.children.Iter(func(_, v util.T) bool {
|
||||
if child, ok := v.(*typeTreeNode); ok {
|
||||
b.WriteString("\n\t+ ")
|
||||
s := child.String()
|
||||
s = strings.ReplaceAll(s, "\n", "\n\t")
|
||||
b.WriteString(s)
|
||||
}
|
||||
n.children.Iter(func(_ Value, child *typeTreeNode) bool {
|
||||
b.WriteString("\n\t+ ")
|
||||
s := child.String()
|
||||
s = strings.ReplaceAll(s, "\n", "\n\t")
|
||||
b.WriteString(s)
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -472,8 +467,8 @@ func insertIntoObject(o *types.Object, path Ref, tpe types.Type, env *TypeEnv) (
|
||||
|
||||
func (n *typeTreeNode) Leafs() map[*Ref]types.Type {
|
||||
leafs := map[*Ref]types.Type{}
|
||||
n.children.Iter(func(_, v util.T) bool {
|
||||
collectLeafs(v.(*typeTreeNode), nil, leafs)
|
||||
n.children.Iter(func(_ Value, v *typeTreeNode) bool {
|
||||
collectLeafs(v, nil, leafs)
|
||||
return false
|
||||
})
|
||||
return leafs
|
||||
@@ -485,8 +480,8 @@ func collectLeafs(n *typeTreeNode, path Ref, leafs map[*Ref]types.Type) {
|
||||
leafs[&nPath] = n.Value()
|
||||
return
|
||||
}
|
||||
n.children.Iter(func(_, v util.T) bool {
|
||||
collectLeafs(v.(*typeTreeNode), nPath, leafs)
|
||||
n.children.Iter(func(_ Value, v *typeTreeNode) bool {
|
||||
collectLeafs(v, nPath, leafs)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
+16
-28
@@ -235,7 +235,7 @@ type refindex struct {
|
||||
type refindices struct {
|
||||
isVirtual func(Ref) bool
|
||||
rules map[*Rule][]*refindex
|
||||
frequency *util.HashMap
|
||||
frequency *util.HasherMap[Ref, int]
|
||||
sorted []Ref
|
||||
}
|
||||
|
||||
@@ -243,12 +243,7 @@ func newrefindices(isVirtual func(Ref) bool) *refindices {
|
||||
return &refindices{
|
||||
isVirtual: isVirtual,
|
||||
rules: map[*Rule][]*refindex{},
|
||||
frequency: util.NewHashMap(func(a, b util.T) bool {
|
||||
r1, r2 := a.(Ref), b.(Ref)
|
||||
return r1.Equal(r2)
|
||||
}, func(x util.T) int {
|
||||
return x.(Ref).Hash()
|
||||
}),
|
||||
frequency: util.NewHasherMap[Ref, int](RefEqual),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,9 +291,9 @@ func (i *refindices) Sorted() []Ref {
|
||||
counts := make([]int, 0, i.frequency.Len())
|
||||
i.sorted = make([]Ref, 0, i.frequency.Len())
|
||||
|
||||
i.frequency.Iter(func(k, v util.T) bool {
|
||||
counts = append(counts, v.(int))
|
||||
i.sorted = append(i.sorted, k.(Ref))
|
||||
i.frequency.Iter(func(k Ref, v int) bool {
|
||||
counts = append(counts, v)
|
||||
i.sorted = append(i.sorted, k)
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -399,7 +394,7 @@ func (i *refindices) insert(rule *Rule, index *refindex) {
|
||||
count = 0
|
||||
}
|
||||
|
||||
i.frequency.Put(index.Ref, count.(int)+1)
|
||||
i.frequency.Put(index.Ref, count+1)
|
||||
|
||||
for pos, other := range i.rules[rule] {
|
||||
if other.Ref.Equal(index.Ref) {
|
||||
@@ -467,7 +462,7 @@ type trieNode struct {
|
||||
next *trieNode
|
||||
any *trieNode
|
||||
undefined *trieNode
|
||||
scalars *util.HashMap
|
||||
scalars *util.HasherMap[Value, *trieNode]
|
||||
array *trieNode
|
||||
rules []*ruleNode
|
||||
}
|
||||
@@ -492,9 +487,7 @@ func (node *trieNode) String() string {
|
||||
}
|
||||
if node.scalars.Len() > 0 {
|
||||
buf := make([]string, 0, node.scalars.Len())
|
||||
node.scalars.Iter(func(k, v util.T) bool {
|
||||
key := k.(Value)
|
||||
val := v.(*trieNode)
|
||||
node.scalars.Iter(func(key Value, val *trieNode) bool {
|
||||
buf = append(buf, fmt.Sprintf("scalar(%v):%p", key, val))
|
||||
return false
|
||||
})
|
||||
@@ -535,7 +528,7 @@ type ruleNode struct {
|
||||
|
||||
func newTrieNodeImpl() *trieNode {
|
||||
return &trieNode{
|
||||
scalars: util.NewHashMap(valueEq, valueHash),
|
||||
scalars: util.NewHasherMap[Value, *trieNode](ValueEqual),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -551,8 +544,7 @@ func (node *trieNode) Do(walker trieWalker) {
|
||||
node.undefined.Do(next)
|
||||
}
|
||||
|
||||
node.scalars.Iter(func(_, v util.T) bool {
|
||||
child := v.(*trieNode)
|
||||
node.scalars.Iter(func(_ Value, child *trieNode) bool {
|
||||
child.Do(next)
|
||||
return false
|
||||
})
|
||||
@@ -618,7 +610,7 @@ func (node *trieNode) insertValue(value Value) *trieNode {
|
||||
child = newTrieNodeImpl()
|
||||
node.scalars.Put(value, child)
|
||||
}
|
||||
return child.(*trieNode)
|
||||
return child
|
||||
case *Array:
|
||||
if node.array == nil {
|
||||
node.array = newTrieNodeImpl()
|
||||
@@ -647,7 +639,7 @@ func (node *trieNode) insertArray(arr *Array) *trieNode {
|
||||
child = newTrieNodeImpl()
|
||||
node.scalars.Put(head, child)
|
||||
}
|
||||
return child.(*trieNode).insertArray(arr.Slice(1, -1))
|
||||
return child.insertArray(arr.Slice(1, -1))
|
||||
}
|
||||
|
||||
panic("illegal value")
|
||||
@@ -712,7 +704,7 @@ func (node *trieNode) traverseValue(resolver ValueResolver, tr *trieTraversalRes
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return child.(*trieNode).Traverse(resolver, tr)
|
||||
return child.Traverse(resolver, tr)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -741,7 +733,7 @@ func (node *trieNode) traverseArray(resolver ValueResolver, tr *trieTraversalRes
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return child.(*trieNode).traverseArray(resolver, tr, arr.Slice(1, -1))
|
||||
return child.traverseArray(resolver, tr, arr.Slice(1, -1))
|
||||
}
|
||||
|
||||
func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalResult) error {
|
||||
@@ -767,12 +759,8 @@ func (node *trieNode) traverseUnknown(resolver ValueResolver, tr *trieTraversalR
|
||||
}
|
||||
|
||||
var iterErr error
|
||||
node.scalars.Iter(func(_, v util.T) bool {
|
||||
child := v.(*trieNode)
|
||||
if iterErr = child.traverseUnknown(resolver, tr); iterErr != nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
node.scalars.Iter(func(_ Value, child *trieNode) bool {
|
||||
return child.traverseUnknown(resolver, tr) != nil
|
||||
})
|
||||
|
||||
return iterErr
|
||||
|
||||
+5
-30
@@ -13,15 +13,14 @@ import (
|
||||
// ValueMap represents a key/value map between AST term values. Any type of term
|
||||
// can be used as a key in the map.
|
||||
type ValueMap struct {
|
||||
hashMap *util.HashMap
|
||||
hashMap *util.TypedHashMap[Value, Value]
|
||||
}
|
||||
|
||||
// NewValueMap returns a new ValueMap.
|
||||
func NewValueMap() *ValueMap {
|
||||
vs := &ValueMap{
|
||||
hashMap: util.NewHashMap(valueEq, valueHash),
|
||||
return &ValueMap{
|
||||
hashMap: util.NewTypedHashMap(ValueEqual, ValueEqual, Value.Hash, Value.Hash, nil),
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// MarshalJSON provides a custom marshaller for the ValueMap which
|
||||
@@ -39,16 +38,6 @@ func (vs *ValueMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tmp)
|
||||
}
|
||||
|
||||
// Copy returns a shallow copy of the ValueMap.
|
||||
func (vs *ValueMap) Copy() *ValueMap {
|
||||
if vs == nil {
|
||||
return nil
|
||||
}
|
||||
cpy := NewValueMap()
|
||||
cpy.hashMap = vs.hashMap.Copy()
|
||||
return cpy
|
||||
}
|
||||
|
||||
// Equal returns true if this ValueMap equals the other.
|
||||
func (vs *ValueMap) Equal(other *ValueMap) bool {
|
||||
if vs == nil {
|
||||
@@ -72,7 +61,7 @@ func (vs *ValueMap) Len() int {
|
||||
func (vs *ValueMap) Get(k Value) Value {
|
||||
if vs != nil {
|
||||
if v, ok := vs.hashMap.Get(k); ok {
|
||||
return v.(Value)
|
||||
return v
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -92,11 +81,7 @@ func (vs *ValueMap) Iter(iter func(Value, Value) bool) bool {
|
||||
if vs == nil {
|
||||
return false
|
||||
}
|
||||
return vs.hashMap.Iter(func(kt, vt util.T) bool {
|
||||
k := kt.(Value)
|
||||
v := vt.(Value)
|
||||
return iter(k, v)
|
||||
})
|
||||
return vs.hashMap.Iter(iter)
|
||||
}
|
||||
|
||||
// Put inserts a key k into the map with value v.
|
||||
@@ -121,13 +106,3 @@ func (vs *ValueMap) String() string {
|
||||
}
|
||||
return vs.hashMap.String()
|
||||
}
|
||||
|
||||
func valueHash(v util.T) int {
|
||||
return v.(Value).Hash()
|
||||
}
|
||||
|
||||
func valueEq(a, b util.T) bool {
|
||||
av := a.(Value)
|
||||
bv := b.(Value)
|
||||
return av.Compare(bv) == 0
|
||||
}
|
||||
|
||||
+5
-15
@@ -38,22 +38,15 @@ func TestValueMapIter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueMapCopy(t *testing.T) {
|
||||
a := NewValueMap()
|
||||
a.Put(String("x"), String("foo"))
|
||||
a.Put(String("y"), String("bar"))
|
||||
b := a.Copy()
|
||||
b.Delete(String("y"))
|
||||
if a.Get(String("y")) != String("bar") {
|
||||
t.Fatalf("Unexpected a['y'] value: %v", a.Get(String("y")))
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueMapEqual(t *testing.T) {
|
||||
a := NewValueMap()
|
||||
a.Put(String("x"), String("foo"))
|
||||
a.Put(String("y"), String("bar"))
|
||||
b := a.Copy()
|
||||
|
||||
b := NewValueMap()
|
||||
b.Put(String("x"), String("foo"))
|
||||
b.Put(String("y"), String("bar"))
|
||||
|
||||
if !a.Equal(b) {
|
||||
t.Fatalf("Expected a == b but not for: %v / %v", a, b)
|
||||
}
|
||||
@@ -89,9 +82,6 @@ func TestValueMapString(t *testing.T) {
|
||||
|
||||
func TestValueMapNil(t *testing.T) {
|
||||
var a *ValueMap
|
||||
if a.Copy() != nil {
|
||||
t.Fatalf("Expected nil map copy to be nil")
|
||||
}
|
||||
a.Delete(String("foo"))
|
||||
var b *ValueMap
|
||||
if !a.Equal(b) {
|
||||
|
||||
+10
-19
@@ -13,41 +13,32 @@ import (
|
||||
|
||||
// SchemaSet holds a map from a path to a schema.
|
||||
type SchemaSet struct {
|
||||
m *util.HashMap
|
||||
m *util.HasherMap[Ref, any]
|
||||
}
|
||||
|
||||
// NewSchemaSet returns an empty SchemaSet.
|
||||
func NewSchemaSet() *SchemaSet {
|
||||
|
||||
eqFunc := func(a, b util.T) bool {
|
||||
return a.(Ref).Equal(b.(Ref))
|
||||
}
|
||||
|
||||
hashFunc := func(x util.T) int { return x.(Ref).Hash() }
|
||||
|
||||
return &SchemaSet{
|
||||
m: util.NewHashMap(eqFunc, hashFunc),
|
||||
m: util.NewHasherMap[Ref, any](RefEqual),
|
||||
}
|
||||
}
|
||||
|
||||
// Put inserts a raw schema into the set.
|
||||
func (ss *SchemaSet) Put(path Ref, raw interface{}) {
|
||||
func (ss *SchemaSet) Put(path Ref, raw any) {
|
||||
ss.m.Put(path, raw)
|
||||
}
|
||||
|
||||
// Get returns the raw schema identified by the path.
|
||||
func (ss *SchemaSet) Get(path Ref) interface{} {
|
||||
if ss == nil {
|
||||
return nil
|
||||
func (ss *SchemaSet) Get(path Ref) any {
|
||||
if ss != nil {
|
||||
if x, ok := ss.m.Get(path); ok {
|
||||
return x
|
||||
}
|
||||
}
|
||||
x, ok := ss.m.Get(path)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return x
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadSchema(raw interface{}, allowNet []string) (types.Type, error) {
|
||||
func loadSchema(raw any, allowNet []string) (types.Type, error) {
|
||||
|
||||
jsonSchema, err := compileSchema(raw, allowNet)
|
||||
if err != nil {
|
||||
|
||||
+20
-22
@@ -6,7 +6,7 @@ package dependencies
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"slices"
|
||||
|
||||
"github.com/open-policy-agent/opa/v1/ast"
|
||||
"github.com/open-policy-agent/opa/v1/util"
|
||||
@@ -168,25 +168,27 @@ func virtual(compiler *ast.Compiler, x interface{}, virtualRefs *dependencies) e
|
||||
}
|
||||
|
||||
type dependencies struct {
|
||||
refs *util.HashMap
|
||||
visitedRules *util.HashMap
|
||||
refs *util.HasherMap[ast.Ref, ast.Ref]
|
||||
visitedRules *util.TypedHashMap[*ast.Rule, *ast.Rule]
|
||||
}
|
||||
|
||||
func newRefSet() *dependencies {
|
||||
return &dependencies{
|
||||
refs: util.NewHashMap(func(a, b util.T) bool {
|
||||
return a.(ast.Ref).Equal(b.(ast.Ref))
|
||||
}, func(a util.T) int {
|
||||
return a.(ast.Ref).Hash()
|
||||
}),
|
||||
visitedRules: util.NewHashMap(func(a, b util.T) bool {
|
||||
return a.(*ast.Rule).Equal(b.(*ast.Rule))
|
||||
}, func(a util.T) int {
|
||||
return a.(*ast.Rule).Ref().Hash()
|
||||
}),
|
||||
refs: util.NewHasherMap[ast.Ref, ast.Ref](ast.RefEqual),
|
||||
visitedRules: util.NewTypedHashMap[*ast.Rule, *ast.Rule](
|
||||
(*ast.Rule).Equal,
|
||||
nil,
|
||||
ruleHash,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func ruleHash(r *ast.Rule) int {
|
||||
return r.Ref().Hash()
|
||||
}
|
||||
|
||||
func (rs *dependencies) add(r ast.Ref) {
|
||||
rs.refs.Put(r, r)
|
||||
}
|
||||
@@ -201,22 +203,18 @@ func (rs *dependencies) visited(rule *ast.Rule) bool {
|
||||
}
|
||||
|
||||
func (rs *dependencies) toSlice() []ast.Ref {
|
||||
var result []ast.Ref
|
||||
rs.refs.Iter(func(k, _ util.T) bool {
|
||||
result = append(result, k.(ast.Ref))
|
||||
result := make([]ast.Ref, 0, rs.refs.Len())
|
||||
rs.refs.Iter(func(k, _ ast.Ref) bool {
|
||||
result = append(result, k)
|
||||
return false
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
func dedup(refs []ast.Ref) []ast.Ref {
|
||||
sort.Slice(refs, func(i, j int) bool {
|
||||
return refs[i].Compare(refs[j]) < 0
|
||||
})
|
||||
slices.SortFunc(refs, ast.RefCompare)
|
||||
|
||||
return filter(refs, func(a, b ast.Ref) bool {
|
||||
return a.Compare(b) == 0
|
||||
})
|
||||
return slices.CompactFunc(refs, ast.RefEqual)
|
||||
}
|
||||
|
||||
// filter removes all items from the list that cause pred to return true. It is
|
||||
|
||||
+11
-2
@@ -2855,17 +2855,26 @@ func parseStringsToRefs(s []string) ([]ast.Ref, error) {
|
||||
func finishFunction(name string, bctx topdown.BuiltinContext, result *ast.Term, err error, iter func(*ast.Term) error) error {
|
||||
if err != nil {
|
||||
var e *HaltError
|
||||
sb := strings.Builder{}
|
||||
if errors.As(err, &e) {
|
||||
sb.Grow(len(name) + len(e.Error()) + 2)
|
||||
sb.WriteString(name)
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(e.Error())
|
||||
tdErr := &topdown.Error{
|
||||
Code: topdown.BuiltinErr,
|
||||
Message: fmt.Sprintf("%v: %v", name, e.Error()),
|
||||
Message: sb.String(),
|
||||
Location: bctx.Location,
|
||||
}
|
||||
return topdown.Halt{Err: tdErr.Wrap(e)}
|
||||
}
|
||||
sb.Grow(len(name) + len(err.Error()) + 2)
|
||||
sb.WriteString(name)
|
||||
sb.WriteString(": ")
|
||||
sb.WriteString(err.Error())
|
||||
tdErr := &topdown.Error{
|
||||
Code: topdown.BuiltinErr,
|
||||
Message: fmt.Sprintf("%v: %v", name, err.Error()),
|
||||
Message: sb.String(),
|
||||
Location: bctx.Location,
|
||||
}
|
||||
return tdErr.Wrap(err)
|
||||
|
||||
+15
-23
@@ -44,7 +44,7 @@ type virtualCache struct {
|
||||
|
||||
type virtualCacheElem struct {
|
||||
value *ast.Term
|
||||
children *util.HashMap
|
||||
children *util.HasherMap[*ast.Term, *virtualCacheElem]
|
||||
undefined bool
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (c *virtualCache) Get(ref ast.Ref) (*ast.Term, bool) {
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
node = x.(*virtualCacheElem)
|
||||
node = x
|
||||
}
|
||||
if node.undefined {
|
||||
return nil, true
|
||||
@@ -92,7 +92,7 @@ func (c *virtualCache) Put(ref ast.Ref, value *ast.Term) {
|
||||
for i := range ref {
|
||||
x, ok := node.children.Get(ref[i])
|
||||
if ok {
|
||||
node = x.(*virtualCacheElem)
|
||||
node = x
|
||||
} else {
|
||||
next := newVirtualCacheElem()
|
||||
node.children.Put(ref[i], next)
|
||||
@@ -113,13 +113,13 @@ func (c *virtualCache) Keys() []ast.Ref {
|
||||
|
||||
func keysRecursive(root ast.Ref, node *virtualCacheElem) []ast.Ref {
|
||||
var keys []ast.Ref
|
||||
node.children.Iter(func(k, v util.T) bool {
|
||||
ref := root.Append(k.(*ast.Term))
|
||||
if v.(*virtualCacheElem).value != nil {
|
||||
node.children.Iter(func(k *ast.Term, v *virtualCacheElem) bool {
|
||||
ref := root.Append(k)
|
||||
if v.value != nil {
|
||||
keys = append(keys, ref)
|
||||
}
|
||||
if v.(*virtualCacheElem).children.Len() > 0 {
|
||||
keys = append(keys, keysRecursive(ref, v.(*virtualCacheElem))...)
|
||||
if v.children.Len() > 0 {
|
||||
keys = append(keys, keysRecursive(ref, v)...)
|
||||
}
|
||||
return false
|
||||
})
|
||||
@@ -130,12 +130,8 @@ func newVirtualCacheElem() *virtualCacheElem {
|
||||
return &virtualCacheElem{children: newVirtualCacheHashMap()}
|
||||
}
|
||||
|
||||
func newVirtualCacheHashMap() *util.HashMap {
|
||||
return util.NewHashMap(func(a, b util.T) bool {
|
||||
return a.(*ast.Term).Equal(b.(*ast.Term))
|
||||
}, func(x util.T) int {
|
||||
return x.(*ast.Term).Hash()
|
||||
})
|
||||
func newVirtualCacheHashMap() *util.HasherMap[*ast.Term, *virtualCacheElem] {
|
||||
return util.NewHasherMap[*ast.Term, *virtualCacheElem](ast.TermValueEqual)
|
||||
}
|
||||
|
||||
// baseCache implements a trie structure to cache base documents read out of
|
||||
@@ -244,7 +240,7 @@ type comprehensionCache struct {
|
||||
|
||||
type comprehensionCacheElem struct {
|
||||
value *ast.Term
|
||||
children *util.HashMap
|
||||
children *util.HasherMap[*ast.Term, *comprehensionCacheElem]
|
||||
}
|
||||
|
||||
func newComprehensionCache() *comprehensionCache {
|
||||
@@ -281,7 +277,7 @@ func (c *comprehensionCacheElem) Get(key []*ast.Term) *ast.Term {
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
node = x.(*comprehensionCacheElem)
|
||||
node = x
|
||||
}
|
||||
return node.value
|
||||
}
|
||||
@@ -291,7 +287,7 @@ func (c *comprehensionCacheElem) Put(key []*ast.Term, value *ast.Term) {
|
||||
for i := range key {
|
||||
x, ok := node.children.Get(key[i])
|
||||
if ok {
|
||||
node = x.(*comprehensionCacheElem)
|
||||
node = x
|
||||
} else {
|
||||
next := newComprehensionCacheElem()
|
||||
node.children.Put(key[i], next)
|
||||
@@ -301,12 +297,8 @@ func (c *comprehensionCacheElem) Put(key []*ast.Term, value *ast.Term) {
|
||||
node.value = value
|
||||
}
|
||||
|
||||
func newComprehensionCacheHashMap() *util.HashMap {
|
||||
return util.NewHashMap(func(a, b util.T) bool {
|
||||
return a.(*ast.Term).Equal(b.(*ast.Term))
|
||||
}, func(x util.T) int {
|
||||
return x.(*ast.Term).Hash()
|
||||
})
|
||||
func newComprehensionCacheHashMap() *util.HasherMap[*ast.Term, *comprehensionCacheElem] {
|
||||
return util.NewHasherMap[*ast.Term, *comprehensionCacheElem](ast.TermValueEqual)
|
||||
}
|
||||
|
||||
type functionMocksStack struct {
|
||||
|
||||
Vendored
+3
-9
@@ -372,19 +372,13 @@ type InterQueryValueCacheBucket interface {
|
||||
}
|
||||
|
||||
type interQueryValueCacheBucket struct {
|
||||
items util.TypedHashMap[ast.Value, any]
|
||||
items util.HasherMap[ast.Value, any]
|
||||
config *NamedValueCacheConfig
|
||||
mtx sync.RWMutex
|
||||
}
|
||||
|
||||
func newItemsMap() *util.TypedHashMap[ast.Value, any] {
|
||||
return util.NewTypedHashMap[ast.Value, any](
|
||||
func(a, b ast.Value) bool { return a.Compare(b) == 0 },
|
||||
func(any, any) bool { return false }, // map equality not supported
|
||||
func(a ast.Value) int { return a.Hash() },
|
||||
func(any) int { return 0 }, // map equality not supported
|
||||
nil,
|
||||
)
|
||||
func newItemsMap() *util.HasherMap[ast.Value, any] {
|
||||
return util.NewHasherMap[ast.Value, any](ast.ValueEqual)
|
||||
}
|
||||
|
||||
func (c *interQueryValueCacheBucket) Get(k ast.Value) (any, bool) {
|
||||
|
||||
@@ -14,18 +14,14 @@ import (
|
||||
type rankFunc func(*unionFindRoot, *unionFindRoot) (*unionFindRoot, *unionFindRoot)
|
||||
|
||||
type unionFind struct {
|
||||
roots *util.HashMap
|
||||
roots *util.HasherMap[ast.Value, *unionFindRoot]
|
||||
parents *ast.ValueMap
|
||||
rank rankFunc
|
||||
}
|
||||
|
||||
func newUnionFind(rank rankFunc) *unionFind {
|
||||
return &unionFind{
|
||||
roots: util.NewHashMap(func(a util.T, b util.T) bool {
|
||||
return a.(ast.Value).Compare(b.(ast.Value)) == 0
|
||||
}, func(v util.T) int {
|
||||
return v.(ast.Value).Hash()
|
||||
}),
|
||||
roots: util.NewHasherMap[ast.Value, *unionFindRoot](ast.ValueEqual),
|
||||
parents: ast.NewValueMap(),
|
||||
rank: rank,
|
||||
}
|
||||
@@ -53,7 +49,7 @@ func (uf *unionFind) Find(v ast.Value) (*unionFindRoot, bool) {
|
||||
|
||||
if parent.Compare(v) == 0 {
|
||||
r, ok := uf.roots.Get(v)
|
||||
return r.(*unionFindRoot), ok
|
||||
return r, ok
|
||||
}
|
||||
|
||||
return uf.Find(parent)
|
||||
@@ -93,13 +89,13 @@ func (uf *unionFind) String() string {
|
||||
map[string]ast.Value{},
|
||||
}
|
||||
|
||||
uf.roots.Iter(func(k util.T, v util.T) bool {
|
||||
o.Roots[k.(ast.Value).String()] = struct {
|
||||
uf.roots.Iter(func(k ast.Value, v *unionFindRoot) bool {
|
||||
o.Roots[k.String()] = struct {
|
||||
Constant *ast.Term
|
||||
Key ast.Value
|
||||
}{
|
||||
v.(*unionFindRoot).constant,
|
||||
v.(*unionFindRoot).key,
|
||||
v.constant,
|
||||
v.key,
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
+2
-13
@@ -781,28 +781,17 @@ type httpSendCacheEntry struct {
|
||||
|
||||
// The httpSendCache is used for intra-query caching of http.send results.
|
||||
type httpSendCache struct {
|
||||
entries *util.HashMap
|
||||
entries *util.HasherMap[ast.Value, httpSendCacheEntry]
|
||||
}
|
||||
|
||||
func newHTTPSendCache() *httpSendCache {
|
||||
return &httpSendCache{
|
||||
entries: util.NewHashMap(valueEq, valueHash),
|
||||
entries: util.NewHasherMap[ast.Value, httpSendCacheEntry](ast.ValueEqual),
|
||||
}
|
||||
}
|
||||
|
||||
func valueHash(v util.T) int {
|
||||
return ast.StringTerm(v.(ast.Value).String()).Hash()
|
||||
}
|
||||
|
||||
func valueEq(a, b util.T) bool {
|
||||
av := a.(ast.Value)
|
||||
bv := b.(ast.Value)
|
||||
return av.String() == bv.String()
|
||||
}
|
||||
|
||||
func (cache *httpSendCache) get(k ast.Value) *httpSendCacheEntry {
|
||||
if v, ok := cache.entries.Get(k); ok {
|
||||
v := v.(httpSendCacheEntry)
|
||||
return &v
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -12,6 +12,10 @@ import (
|
||||
// T is a concise way to refer to T.
|
||||
type T interface{}
|
||||
|
||||
type Hasher interface {
|
||||
Hash() int
|
||||
}
|
||||
|
||||
type hashEntry[K any, V any] struct {
|
||||
k K
|
||||
v V
|
||||
@@ -177,3 +181,91 @@ func (h *TypedHashMap[K, V]) Update(other *TypedHashMap[K, V]) *TypedHashMap[K,
|
||||
})
|
||||
return updated
|
||||
}
|
||||
|
||||
type hasherEntry[K Hasher, V any] struct {
|
||||
k K
|
||||
v V
|
||||
next *hasherEntry[K, V]
|
||||
}
|
||||
|
||||
// HasherMap represents a simpler version of TypedHashMap that uses Hasher's
|
||||
// for keys, and requires only an equality function for keys. Ideally we'd have
|
||||
// and Equal method for all key types too, and we could get rid of that requirement.
|
||||
type HasherMap[K Hasher, V any] struct {
|
||||
keq func(K, K) bool
|
||||
table map[int]*hasherEntry[K, V]
|
||||
size int
|
||||
}
|
||||
|
||||
// NewHasherMap returns a new empty HasherMap.
|
||||
func NewHasherMap[K Hasher, V any](keq func(K, K) bool) *HasherMap[K, V] {
|
||||
return &HasherMap[K, V]{
|
||||
keq: keq,
|
||||
table: make(map[int]*hasherEntry[K, V]),
|
||||
size: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value for k.
|
||||
func (h *HasherMap[K, V]) Get(k K) (V, bool) {
|
||||
for entry := h.table[k.Hash()]; entry != nil; entry = entry.next {
|
||||
if h.keq(entry.k, k) {
|
||||
return entry.v, true
|
||||
}
|
||||
}
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
|
||||
// Put inserts a key/value pair into this HashMap. If the key is already present, the existing
|
||||
// value is overwritten.
|
||||
func (h *HasherMap[K, V]) Put(k K, v V) {
|
||||
hash := k.Hash()
|
||||
head := h.table[hash]
|
||||
for entry := head; entry != nil; entry = entry.next {
|
||||
if h.keq(entry.k, k) {
|
||||
entry.v = v
|
||||
return
|
||||
}
|
||||
}
|
||||
h.table[hash] = &hasherEntry[K, V]{k: k, v: v, next: head}
|
||||
h.size++
|
||||
}
|
||||
|
||||
// Delete removes the key k.
|
||||
func (h *HasherMap[K, V]) Delete(k K) {
|
||||
hash := k.Hash()
|
||||
var prev *hasherEntry[K, V]
|
||||
for entry := h.table[hash]; entry != nil; entry = entry.next {
|
||||
if h.keq(entry.k, k) {
|
||||
if prev != nil {
|
||||
prev.next = entry.next
|
||||
} else {
|
||||
h.table[hash] = entry.next
|
||||
}
|
||||
h.size--
|
||||
return
|
||||
}
|
||||
prev = entry
|
||||
}
|
||||
}
|
||||
|
||||
// Iter invokes the iter function for each element in the HasherMap.
|
||||
// If the iter function returns true, iteration stops and the return value is true.
|
||||
// If the iter function never returns true, iteration proceeds through all elements
|
||||
// and the return value is false.
|
||||
func (h *HasherMap[K, V]) Iter(iter func(K, V) bool) bool {
|
||||
for _, entry := range h.table {
|
||||
for ; entry != nil; entry = entry.next {
|
||||
if iter(entry.k, entry.v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the current size of this HashMap.
|
||||
func (h *HasherMap[K, V]) Len() int {
|
||||
return h.size
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user