mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 18:53:35 +00:00
rebase: bump the k8s-dependencies group in /e2e with 3 updates
Bumps the k8s-dependencies group in /e2e with 3 updates: [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery), [k8s.io/cloud-provider](https://github.com/kubernetes/cloud-provider) and [k8s.io/pod-security-admission](https://github.com/kubernetes/pod-security-admission). Updates `k8s.io/apimachinery` from 0.32.3 to 0.33.0 - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.32.3...v0.33.0) Updates `k8s.io/cloud-provider` from 0.32.3 to 0.33.0 - [Commits](https://github.com/kubernetes/cloud-provider/compare/v0.32.3...v0.33.0) Updates `k8s.io/pod-security-admission` from 0.32.3 to 0.33.0 - [Commits](https://github.com/kubernetes/pod-security-admission/compare/v0.32.3...v0.33.0) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: k8s-dependencies - dependency-name: k8s.io/cloud-provider dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: k8s-dependencies - dependency-name: k8s.io/pod-security-admission dependency-version: 0.33.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: k8s-dependencies ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
mergify[bot]
parent
d52dc2c4ba
commit
dd77e72800
654
e2e/vendor/github.com/google/cel-go/checker/cost.go
generated
vendored
654
e2e/vendor/github.com/google/cel-go/checker/cost.go
generated
vendored
@ -28,15 +28,20 @@ import (
|
||||
|
||||
// CostEstimator estimates the sizes of variable length input data and the costs of functions.
|
||||
type CostEstimator interface {
|
||||
// EstimateSize returns a SizeEstimate for the given AstNode, or nil if
|
||||
// the estimator has no estimate to provide. The size is equivalent to the result of the CEL `size()` function:
|
||||
// length of strings and bytes, number of map entries or number of list items.
|
||||
// EstimateSize is only called for AstNodes where
|
||||
// CEL does not know the size; EstimateSize is not called for values defined inline in CEL where the size
|
||||
// is already obvious to CEL.
|
||||
// EstimateSize returns a SizeEstimate for the given AstNode, or nil if the estimator has no
|
||||
// estimate to provide.
|
||||
//
|
||||
// The size is equivalent to the result of the CEL `size()` function:
|
||||
// * Number of unicode characters in a string
|
||||
// * Number of bytes in a sequence
|
||||
// * Number of map entries or number of list items.
|
||||
//
|
||||
// EstimateSize is only called for AstNodes where CEL does not know the size; EstimateSize is not
|
||||
// called for values defined inline in CEL where the size is already obvious to CEL.
|
||||
EstimateSize(element AstNode) *SizeEstimate
|
||||
// EstimateCallCost returns the estimated cost of an invocation, or nil if
|
||||
// the estimator has no estimate to provide.
|
||||
|
||||
// EstimateCallCost returns the estimated cost of an invocation, or nil if the estimator has no
|
||||
// estimate to provide.
|
||||
EstimateCallCost(function, overloadID string, target *AstNode, args []AstNode) *CallEstimate
|
||||
}
|
||||
|
||||
@ -44,6 +49,7 @@ type CostEstimator interface {
|
||||
// The ResultSize should only be provided if the call results in a map, list, string or bytes.
|
||||
type CallEstimate struct {
|
||||
CostEstimate
|
||||
|
||||
ResultSize *SizeEstimate
|
||||
}
|
||||
|
||||
@ -53,10 +59,13 @@ type AstNode interface {
|
||||
// represent type directly reachable from the provided type declarations.
|
||||
// The first path element is a variable. All subsequent path elements are one of: field name, '@items', '@keys', '@values'.
|
||||
Path() []string
|
||||
|
||||
// Type returns the deduced type of the AstNode.
|
||||
Type() *types.Type
|
||||
|
||||
// Expr returns the expression of the AstNode.
|
||||
Expr() ast.Expr
|
||||
|
||||
// ComputedSize returns a size estimate of the AstNode derived from information available in the CEL expression.
|
||||
// For constants and inline list and map declarations, the exact size is returned. For concatenated list, strings
|
||||
// and bytes, the size is derived from the size estimates of the operands. nil is returned if there is no
|
||||
@ -84,36 +93,7 @@ func (e astNode) Expr() ast.Expr {
|
||||
}
|
||||
|
||||
func (e astNode) ComputedSize() *SizeEstimate {
|
||||
if e.derivedSize != nil {
|
||||
return e.derivedSize
|
||||
}
|
||||
var v uint64
|
||||
switch e.expr.Kind() {
|
||||
case ast.LiteralKind:
|
||||
switch ck := e.expr.AsLiteral().(type) {
|
||||
case types.String:
|
||||
// converting to runes here is an O(n) operation, but
|
||||
// this is consistent with how size is computed at runtime,
|
||||
// and how the language definition defines string size
|
||||
v = uint64(len([]rune(ck)))
|
||||
case types.Bytes:
|
||||
v = uint64(len(ck))
|
||||
case types.Bool, types.Double, types.Duration,
|
||||
types.Int, types.Timestamp, types.Uint,
|
||||
types.Null:
|
||||
v = uint64(1)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case ast.ListKind:
|
||||
v = uint64(e.expr.AsList().Size())
|
||||
case ast.MapKind:
|
||||
v = uint64(e.expr.AsMap().Size())
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
return &SizeEstimate{Min: v, Max: v}
|
||||
return e.derivedSize
|
||||
}
|
||||
|
||||
// SizeEstimate represents an estimated size of a variable length string, bytes, map or list.
|
||||
@ -121,6 +101,16 @@ type SizeEstimate struct {
|
||||
Min, Max uint64
|
||||
}
|
||||
|
||||
// UnknownSizeEstimate returns a size between 0 and max uint
|
||||
func UnknownSizeEstimate() SizeEstimate {
|
||||
return unknownSizeEstimate
|
||||
}
|
||||
|
||||
// FixedSizeEstimate returns a size estimate with a fixed min and max range.
|
||||
func FixedSizeEstimate(size uint64) SizeEstimate {
|
||||
return SizeEstimate{Min: size, Max: size}
|
||||
}
|
||||
|
||||
// Add adds to another SizeEstimate and returns the sum.
|
||||
// If add would result in an uint64 overflow, the result is math.MaxUint64.
|
||||
func (se SizeEstimate) Add(sizeEstimate SizeEstimate) SizeEstimate {
|
||||
@ -175,12 +165,22 @@ type CostEstimate struct {
|
||||
Min, Max uint64
|
||||
}
|
||||
|
||||
// UnknownCostEstimate returns a cost with an unknown impact.
|
||||
func UnknownCostEstimate() CostEstimate {
|
||||
return unknownCostEstimate
|
||||
}
|
||||
|
||||
// FixedCostEstimate returns a cost with a fixed min and max range.
|
||||
func FixedCostEstimate(cost uint64) CostEstimate {
|
||||
return CostEstimate{Min: cost, Max: cost}
|
||||
}
|
||||
|
||||
// Add adds the costs and returns the sum.
|
||||
// If add would result in an uint64 overflow for the min or max, the value is set to math.MaxUint64.
|
||||
func (ce CostEstimate) Add(cost CostEstimate) CostEstimate {
|
||||
return CostEstimate{
|
||||
addUint64NoOverflow(ce.Min, cost.Min),
|
||||
addUint64NoOverflow(ce.Max, cost.Max),
|
||||
Min: addUint64NoOverflow(ce.Min, cost.Min),
|
||||
Max: addUint64NoOverflow(ce.Max, cost.Max),
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,8 +188,8 @@ func (ce CostEstimate) Add(cost CostEstimate) CostEstimate {
|
||||
// If multiply would result in an uint64 overflow, the result is math.MaxUint64.
|
||||
func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate {
|
||||
return CostEstimate{
|
||||
multiplyUint64NoOverflow(ce.Min, cost.Min),
|
||||
multiplyUint64NoOverflow(ce.Max, cost.Max),
|
||||
Min: multiplyUint64NoOverflow(ce.Min, cost.Min),
|
||||
Max: multiplyUint64NoOverflow(ce.Max, cost.Max),
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,8 +197,8 @@ func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate {
|
||||
// nearest integer of the result, rounded up.
|
||||
func (ce CostEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate {
|
||||
return CostEstimate{
|
||||
multiplyByCostFactor(ce.Min, costPerUnit),
|
||||
multiplyByCostFactor(ce.Max, costPerUnit),
|
||||
Min: multiplyByCostFactor(ce.Min, costPerUnit),
|
||||
Max: multiplyByCostFactor(ce.Max, costPerUnit),
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,49 +245,6 @@ func multiplyByCostFactor(x uint64, y float64) uint64 {
|
||||
return uint64(ceil)
|
||||
}
|
||||
|
||||
var (
|
||||
selectAndIdentCost = CostEstimate{Min: common.SelectAndIdentCost, Max: common.SelectAndIdentCost}
|
||||
constCost = CostEstimate{Min: common.ConstCost, Max: common.ConstCost}
|
||||
|
||||
createListBaseCost = CostEstimate{Min: common.ListCreateBaseCost, Max: common.ListCreateBaseCost}
|
||||
createMapBaseCost = CostEstimate{Min: common.MapCreateBaseCost, Max: common.MapCreateBaseCost}
|
||||
createMessageBaseCost = CostEstimate{Min: common.StructCreateBaseCost, Max: common.StructCreateBaseCost}
|
||||
)
|
||||
|
||||
type coster struct {
|
||||
// exprPath maps from Expr Id to field path.
|
||||
exprPath map[int64][]string
|
||||
// iterRanges tracks the iterRange of each iterVar.
|
||||
iterRanges iterRangeScopes
|
||||
// computedSizes tracks the computed sizes of call results.
|
||||
computedSizes map[int64]SizeEstimate
|
||||
checkedAST *ast.AST
|
||||
estimator CostEstimator
|
||||
overloadEstimators map[string]FunctionEstimator
|
||||
// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
|
||||
presenceTestCost CostEstimate
|
||||
}
|
||||
|
||||
// Use a stack of iterVar -> iterRange Expr Ids to handle shadowed variable names.
|
||||
type iterRangeScopes map[string][]int64
|
||||
|
||||
func (vs iterRangeScopes) push(varName string, expr ast.Expr) {
|
||||
vs[varName] = append(vs[varName], expr.ID())
|
||||
}
|
||||
|
||||
func (vs iterRangeScopes) pop(varName string) {
|
||||
varStack := vs[varName]
|
||||
vs[varName] = varStack[:len(varStack)-1]
|
||||
}
|
||||
|
||||
func (vs iterRangeScopes) peek(varName string) (int64, bool) {
|
||||
varStack := vs[varName]
|
||||
if len(varStack) > 0 {
|
||||
return varStack[len(varStack)-1], true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// CostOption configures flags which affect cost computations.
|
||||
type CostOption func(*coster) error
|
||||
|
||||
@ -300,7 +257,7 @@ func PresenceTestHasCost(hasCost bool) CostOption {
|
||||
c.presenceTestCost = selectAndIdentCost
|
||||
return nil
|
||||
}
|
||||
c.presenceTestCost = CostEstimate{Min: 0, Max: 0}
|
||||
c.presenceTestCost = FixedCostEstimate(0)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -325,10 +282,11 @@ func Cost(checked *ast.AST, estimator CostEstimator, opts ...CostOption) (CostEs
|
||||
checkedAST: checked,
|
||||
estimator: estimator,
|
||||
overloadEstimators: map[string]FunctionEstimator{},
|
||||
exprPath: map[int64][]string{},
|
||||
iterRanges: map[string][]int64{},
|
||||
exprPaths: map[int64][]string{},
|
||||
localVars: make(scopes),
|
||||
computedSizes: map[int64]SizeEstimate{},
|
||||
presenceTestCost: CostEstimate{Min: 1, Max: 1},
|
||||
computedEntrySizes: map[int64]entrySizeEstimate{},
|
||||
presenceTestCost: FixedCostEstimate(1),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
err := opt(c)
|
||||
@ -339,6 +297,165 @@ func Cost(checked *ast.AST, estimator CostEstimator, opts ...CostOption) (CostEs
|
||||
return c.cost(checked.Expr()), nil
|
||||
}
|
||||
|
||||
type coster struct {
|
||||
// exprPaths maps from Expr Id to field path.
|
||||
exprPaths map[int64][]string
|
||||
// localVars tracks the local and iteration variables assigned during evaluation.
|
||||
localVars scopes
|
||||
// computedSizes tracks the computed sizes of call results.
|
||||
computedSizes map[int64]SizeEstimate
|
||||
// computedEntrySizes tracks the size of list and map entries
|
||||
computedEntrySizes map[int64]entrySizeEstimate
|
||||
|
||||
checkedAST *ast.AST
|
||||
estimator CostEstimator
|
||||
overloadEstimators map[string]FunctionEstimator
|
||||
// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
|
||||
presenceTestCost CostEstimate
|
||||
}
|
||||
|
||||
// entrySizeEstimate captures the container kind and associated key/index and value SizeEstimate values.
|
||||
//
|
||||
// An entrySizeEstimate only exists if both the key/index and the value have SizeEstimate values, otherwise
|
||||
// a nil entrySizeEstimate should be used.
|
||||
type entrySizeEstimate struct {
|
||||
containerKind types.Kind
|
||||
key SizeEstimate
|
||||
val SizeEstimate
|
||||
}
|
||||
|
||||
// container returns the container kind (list or map) of the entry.
|
||||
func (s *entrySizeEstimate) container() types.Kind {
|
||||
if s == nil {
|
||||
return types.UnknownKind
|
||||
}
|
||||
return s.containerKind
|
||||
}
|
||||
|
||||
// keySize returns the SizeEstimate for the key if one exists.
|
||||
func (s *entrySizeEstimate) keySize() *SizeEstimate {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return &s.key
|
||||
}
|
||||
|
||||
// valSize returns the SizeEstimate for the value if one exists.
|
||||
func (s *entrySizeEstimate) valSize() *SizeEstimate {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return &s.val
|
||||
}
|
||||
|
||||
func (s *entrySizeEstimate) union(other *entrySizeEstimate) *entrySizeEstimate {
|
||||
if s == nil || other == nil {
|
||||
return nil
|
||||
}
|
||||
sk := s.key.Union(other.key)
|
||||
sv := s.val.Union(other.val)
|
||||
return &entrySizeEstimate{
|
||||
containerKind: s.containerKind,
|
||||
key: sk,
|
||||
val: sv,
|
||||
}
|
||||
}
|
||||
|
||||
// localVar captures the local variable size and entrySize estimates if they exist for variables
|
||||
type localVar struct {
|
||||
exprID int64
|
||||
path []string
|
||||
size *SizeEstimate
|
||||
entrySize *entrySizeEstimate
|
||||
}
|
||||
|
||||
// scopes is a stack of variable name to integer id stack to handle scopes created by cel.bind() like macros
|
||||
type scopes map[string][]*localVar
|
||||
|
||||
func (s scopes) push(varName string, expr ast.Expr, path []string, size *SizeEstimate, entrySize *entrySizeEstimate) {
|
||||
s[varName] = append(s[varName], &localVar{
|
||||
exprID: expr.ID(),
|
||||
path: path,
|
||||
size: size,
|
||||
entrySize: entrySize,
|
||||
})
|
||||
}
|
||||
|
||||
func (s scopes) pop(varName string) {
|
||||
varStack := s[varName]
|
||||
s[varName] = varStack[:len(varStack)-1]
|
||||
}
|
||||
|
||||
func (s scopes) peek(varName string) (*localVar, bool) {
|
||||
varStack := s[varName]
|
||||
if len(varStack) > 0 {
|
||||
return varStack[len(varStack)-1], true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (c *coster) pushIterKey(varName string, rangeExpr ast.Expr) {
|
||||
entrySize := c.computeEntrySize(rangeExpr)
|
||||
size := entrySize.keySize()
|
||||
path := c.getPath(rangeExpr)
|
||||
container := entrySize.container()
|
||||
if container == types.UnknownKind {
|
||||
container = c.getType(rangeExpr).Kind()
|
||||
}
|
||||
subpath := "@keys"
|
||||
if container == types.ListKind {
|
||||
subpath = "@indices"
|
||||
}
|
||||
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
|
||||
}
|
||||
|
||||
func (c *coster) pushIterValue(varName string, rangeExpr ast.Expr) {
|
||||
entrySize := c.computeEntrySize(rangeExpr)
|
||||
size := entrySize.valSize()
|
||||
path := c.getPath(rangeExpr)
|
||||
container := entrySize.container()
|
||||
if container == types.UnknownKind {
|
||||
container = c.getType(rangeExpr).Kind()
|
||||
}
|
||||
subpath := "@values"
|
||||
if container == types.ListKind {
|
||||
subpath = "@items"
|
||||
}
|
||||
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
|
||||
}
|
||||
|
||||
func (c *coster) pushIterSingle(varName string, rangeExpr ast.Expr) {
|
||||
entrySize := c.computeEntrySize(rangeExpr)
|
||||
size := entrySize.keySize()
|
||||
subpath := "@keys"
|
||||
container := entrySize.container()
|
||||
if container == types.UnknownKind {
|
||||
container = c.getType(rangeExpr).Kind()
|
||||
}
|
||||
if container == types.ListKind {
|
||||
size = entrySize.valSize()
|
||||
subpath = "@items"
|
||||
}
|
||||
path := c.getPath(rangeExpr)
|
||||
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
|
||||
}
|
||||
|
||||
func (c *coster) pushLocalVar(varName string, e ast.Expr) {
|
||||
path := c.getPath(e)
|
||||
// note: retrieve the entry size for the local variable based on the size of the binding expression
|
||||
// since the binding expression could be a list or map, the entry size should also be propagated
|
||||
entrySize := c.computeEntrySize(e)
|
||||
c.localVars.push(varName, e, path, c.computeSize(e), entrySize)
|
||||
}
|
||||
|
||||
func (c *coster) peekLocalVar(varName string) (*localVar, bool) {
|
||||
return c.localVars.peek(varName)
|
||||
}
|
||||
|
||||
func (c *coster) popLocalVar(varName string) {
|
||||
c.localVars.pop(varName)
|
||||
}
|
||||
|
||||
func (c *coster) cost(e ast.Expr) CostEstimate {
|
||||
if e == nil {
|
||||
return CostEstimate{}
|
||||
@ -360,7 +477,11 @@ func (c *coster) cost(e ast.Expr) CostEstimate {
|
||||
case ast.StructKind:
|
||||
cost = c.costCreateStruct(e)
|
||||
case ast.ComprehensionKind:
|
||||
cost = c.costComprehension(e)
|
||||
if c.isBind(e) {
|
||||
cost = c.costBind(e)
|
||||
} else {
|
||||
cost = c.costComprehension(e)
|
||||
}
|
||||
default:
|
||||
return CostEstimate{}
|
||||
}
|
||||
@ -370,17 +491,11 @@ func (c *coster) cost(e ast.Expr) CostEstimate {
|
||||
func (c *coster) costIdent(e ast.Expr) CostEstimate {
|
||||
identName := e.AsIdent()
|
||||
// build and track the field path
|
||||
if iterRange, ok := c.iterRanges.peek(identName); ok {
|
||||
switch c.checkedAST.GetType(iterRange).Kind() {
|
||||
case types.ListKind:
|
||||
c.addPath(e, append(c.exprPath[iterRange], "@items"))
|
||||
case types.MapKind:
|
||||
c.addPath(e, append(c.exprPath[iterRange], "@keys"))
|
||||
}
|
||||
if v, ok := c.peekLocalVar(identName); ok {
|
||||
c.addPath(e, v.path)
|
||||
} else {
|
||||
c.addPath(e, []string{identName})
|
||||
}
|
||||
|
||||
return selectAndIdentCost
|
||||
}
|
||||
|
||||
@ -405,14 +520,18 @@ func (c *coster) costSelect(e ast.Expr) CostEstimate {
|
||||
|
||||
// build and track the field path
|
||||
c.addPath(e, append(c.getPath(sel.Operand()), sel.FieldName()))
|
||||
|
||||
return sum
|
||||
}
|
||||
|
||||
func (c *coster) costCall(e ast.Expr) CostEstimate {
|
||||
// Dyn is just a way to disable type-checking, so return the cost of 1 with the cost of the argument
|
||||
if dynEstimate := c.maybeUnwrapDynCall(e); dynEstimate != nil {
|
||||
return *dynEstimate
|
||||
}
|
||||
|
||||
// Continue estimating the cost of all other calls.
|
||||
call := e.AsCall()
|
||||
args := call.Args()
|
||||
|
||||
var sum CostEstimate
|
||||
|
||||
argTypes := make([]AstNode, len(args))
|
||||
@ -435,7 +554,7 @@ func (c *coster) costCall(e ast.Expr) CostEstimate {
|
||||
fnCost := CostEstimate{Min: uint64(math.MaxUint64), Max: 0}
|
||||
var resultSize *SizeEstimate
|
||||
for _, overload := range overloadIDs {
|
||||
overloadCost := c.functionCost(call.FunctionName(), overload, &targetType, argTypes, argCosts)
|
||||
overloadCost := c.functionCost(e, call.FunctionName(), overload, &targetType, argTypes, argCosts)
|
||||
fnCost = fnCost.Union(overloadCost.CostEstimate)
|
||||
if overloadCost.ResultSize != nil {
|
||||
if resultSize == nil {
|
||||
@ -449,37 +568,73 @@ func (c *coster) costCall(e ast.Expr) CostEstimate {
|
||||
switch overload {
|
||||
case overloads.IndexList:
|
||||
if len(args) > 0 {
|
||||
// note: assigning resultSize here could be redundant with the path-based lookup later
|
||||
resultSize = c.computeEntrySize(args[0]).valSize()
|
||||
c.addPath(e, append(c.getPath(args[0]), "@items"))
|
||||
}
|
||||
case overloads.IndexMap:
|
||||
if len(args) > 0 {
|
||||
resultSize = c.computeEntrySize(args[0]).valSize()
|
||||
c.addPath(e, append(c.getPath(args[0]), "@values"))
|
||||
}
|
||||
}
|
||||
if resultSize == nil {
|
||||
resultSize = c.computeSize(e)
|
||||
}
|
||||
}
|
||||
if resultSize != nil {
|
||||
c.computedSizes[e.ID()] = *resultSize
|
||||
}
|
||||
c.setSize(e, resultSize)
|
||||
return sum.Add(fnCost)
|
||||
}
|
||||
|
||||
func (c *coster) maybeUnwrapDynCall(e ast.Expr) *CostEstimate {
|
||||
call := e.AsCall()
|
||||
if call.FunctionName() != "dyn" {
|
||||
return nil
|
||||
}
|
||||
arg := call.Args()[0]
|
||||
argCost := c.cost(arg)
|
||||
c.copySizeEstimates(e, arg)
|
||||
callCost := FixedCostEstimate(1).Add(argCost)
|
||||
return &callCost
|
||||
}
|
||||
|
||||
func (c *coster) costCreateList(e ast.Expr) CostEstimate {
|
||||
create := e.AsList()
|
||||
var sum CostEstimate
|
||||
itemSize := SizeEstimate{Min: math.MaxUint64, Max: 0}
|
||||
if create.Size() == 0 {
|
||||
itemSize.Min = 0
|
||||
}
|
||||
for _, e := range create.Elements() {
|
||||
sum = sum.Add(c.cost(e))
|
||||
is := c.sizeOrUnknown(e)
|
||||
itemSize = itemSize.Union(is)
|
||||
}
|
||||
c.setEntrySize(e, &entrySizeEstimate{containerKind: types.ListKind, key: FixedSizeEstimate(1), val: itemSize})
|
||||
return sum.Add(createListBaseCost)
|
||||
}
|
||||
|
||||
func (c *coster) costCreateMap(e ast.Expr) CostEstimate {
|
||||
mapVal := e.AsMap()
|
||||
var sum CostEstimate
|
||||
keySize := SizeEstimate{Min: math.MaxUint64, Max: 0}
|
||||
valSize := SizeEstimate{Min: math.MaxUint64, Max: 0}
|
||||
if mapVal.Size() == 0 {
|
||||
valSize.Min = 0
|
||||
keySize.Min = 0
|
||||
}
|
||||
for _, ent := range mapVal.Entries() {
|
||||
entry := ent.AsMapEntry()
|
||||
sum = sum.Add(c.cost(entry.Key()))
|
||||
sum = sum.Add(c.cost(entry.Value()))
|
||||
// Compute the key size range
|
||||
ks := c.sizeOrUnknown(entry.Key())
|
||||
keySize = keySize.Union(ks)
|
||||
// Compute the value size range
|
||||
vs := c.sizeOrUnknown(entry.Value())
|
||||
valSize = valSize.Union(vs)
|
||||
}
|
||||
c.setEntrySize(e, &entrySizeEstimate{containerKind: types.MapKind, key: keySize, val: valSize})
|
||||
return sum.Add(createMapBaseCost)
|
||||
}
|
||||
|
||||
@ -498,43 +653,76 @@ func (c *coster) costComprehension(e ast.Expr) CostEstimate {
|
||||
var sum CostEstimate
|
||||
sum = sum.Add(c.cost(comp.IterRange()))
|
||||
sum = sum.Add(c.cost(comp.AccuInit()))
|
||||
c.pushLocalVar(comp.AccuVar(), comp.AccuInit())
|
||||
|
||||
// Track the iterRange of each IterVar for field path construction
|
||||
c.iterRanges.push(comp.IterVar(), comp.IterRange())
|
||||
// Track the iterRange of each IterVar and AccuVar for field path construction
|
||||
if comp.HasIterVar2() {
|
||||
c.pushIterKey(comp.IterVar(), comp.IterRange())
|
||||
c.pushIterValue(comp.IterVar2(), comp.IterRange())
|
||||
} else {
|
||||
c.pushIterSingle(comp.IterVar(), comp.IterRange())
|
||||
}
|
||||
|
||||
// Determine the cost for each element in the loop
|
||||
loopCost := c.cost(comp.LoopCondition())
|
||||
stepCost := c.cost(comp.LoopStep())
|
||||
c.iterRanges.pop(comp.IterVar())
|
||||
|
||||
// Clear the intermediate variable tracking.
|
||||
c.popLocalVar(comp.IterVar())
|
||||
if comp.HasIterVar2() {
|
||||
c.popLocalVar(comp.IterVar2())
|
||||
}
|
||||
|
||||
// Determine the result cost.
|
||||
sum = sum.Add(c.cost(comp.Result()))
|
||||
rangeCnt := c.sizeEstimate(c.newAstNode(comp.IterRange()))
|
||||
|
||||
c.computedSizes[e.ID()] = rangeCnt
|
||||
c.localVars.pop(comp.AccuVar())
|
||||
|
||||
// Estimate the cost of the loop.
|
||||
rangeCnt := c.sizeOrUnknown(comp.IterRange())
|
||||
rangeCost := rangeCnt.MultiplyByCost(stepCost.Add(loopCost))
|
||||
sum = sum.Add(rangeCost)
|
||||
|
||||
switch k := comp.AccuInit().Kind(); k {
|
||||
case ast.LiteralKind:
|
||||
c.setSize(e, c.computeSize(comp.AccuInit()))
|
||||
case ast.ListKind, ast.MapKind:
|
||||
c.setSize(e, &rangeCnt)
|
||||
// For a step which produces a container value, it will have an entry size associated
|
||||
// with its expression id.
|
||||
if stepEntrySize := c.computeEntrySize(comp.LoopStep()); stepEntrySize != nil {
|
||||
c.setEntrySize(e, stepEntrySize)
|
||||
break
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
func (c *coster) sizeEstimate(t AstNode) SizeEstimate {
|
||||
if l := t.ComputedSize(); l != nil {
|
||||
return *l
|
||||
}
|
||||
if l := c.estimator.EstimateSize(t); l != nil {
|
||||
return *l
|
||||
}
|
||||
// return an estimate of 1 for return types of set
|
||||
// lengths, since strings/bytes/more complex objects could be of
|
||||
// variable length
|
||||
if isScalar(t.Type()) {
|
||||
// TODO: since the logic for size estimation is split between
|
||||
// ComputedSize and isScalar, changing one will likely require changing
|
||||
// the other, so they should be merged in the future if possible
|
||||
return SizeEstimate{Min: 1, Max: 1}
|
||||
}
|
||||
return SizeEstimate{Min: 0, Max: math.MaxUint64}
|
||||
func (c *coster) isBind(e ast.Expr) bool {
|
||||
comp := e.AsComprehension()
|
||||
iterRange := comp.IterRange()
|
||||
loopCond := comp.LoopCondition()
|
||||
return iterRange.Kind() == ast.ListKind && iterRange.AsList().Size() == 0 &&
|
||||
loopCond.Kind() == ast.LiteralKind && loopCond.AsLiteral() == types.False &&
|
||||
comp.AccuVar() != parser.AccumulatorName
|
||||
}
|
||||
|
||||
func (c *coster) functionCost(function, overloadID string, target *AstNode, args []AstNode, argCosts []CostEstimate) CallEstimate {
|
||||
func (c *coster) costBind(e ast.Expr) CostEstimate {
|
||||
comp := e.AsComprehension()
|
||||
var sum CostEstimate
|
||||
// Binds are lazily initialized, so we retain the cost of an empty iteration range.
|
||||
sum = sum.Add(c.cost(comp.IterRange()))
|
||||
sum = sum.Add(c.cost(comp.AccuInit()))
|
||||
|
||||
c.pushLocalVar(comp.AccuVar(), comp.AccuInit())
|
||||
sum = sum.Add(c.cost(comp.Result()))
|
||||
c.popLocalVar(comp.AccuVar())
|
||||
|
||||
// Associate the bind output size with the result size.
|
||||
c.copySizeEstimates(e, comp.Result())
|
||||
return sum
|
||||
}
|
||||
|
||||
func (c *coster) functionCost(e ast.Expr, function, overloadID string, target *AstNode, args []AstNode, argCosts []CostEstimate) CallEstimate {
|
||||
argCostSum := func() CostEstimate {
|
||||
var sum CostEstimate
|
||||
for _, a := range argCosts {
|
||||
@ -559,35 +747,42 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
|
||||
case overloads.ExtFormatString:
|
||||
if target != nil {
|
||||
// ResultSize not calculated because we can't bound the max size.
|
||||
return CallEstimate{CostEstimate: c.sizeEstimate(*target).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
|
||||
return CallEstimate{
|
||||
CostEstimate: c.sizeOrUnknown(*target).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
|
||||
}
|
||||
case overloads.StringToBytes:
|
||||
if len(args) == 1 {
|
||||
sz := c.sizeEstimate(args[0])
|
||||
sz := c.sizeOrUnknown(args[0])
|
||||
// ResultSize max is when each char converts to 4 bytes.
|
||||
return CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &SizeEstimate{Min: sz.Min, Max: sz.Max * 4}}
|
||||
return CallEstimate{
|
||||
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
|
||||
ResultSize: &SizeEstimate{Min: sz.Min, Max: sz.Max * 4}}
|
||||
}
|
||||
case overloads.BytesToString:
|
||||
if len(args) == 1 {
|
||||
sz := c.sizeEstimate(args[0])
|
||||
sz := c.sizeOrUnknown(args[0])
|
||||
// ResultSize min is when 4 bytes convert to 1 char.
|
||||
return CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &SizeEstimate{Min: sz.Min / 4, Max: sz.Max}}
|
||||
return CallEstimate{
|
||||
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
|
||||
ResultSize: &SizeEstimate{Min: sz.Min / 4, Max: sz.Max}}
|
||||
}
|
||||
case overloads.ExtQuoteString:
|
||||
if len(args) == 1 {
|
||||
sz := c.sizeEstimate(args[0])
|
||||
sz := c.sizeOrUnknown(args[0])
|
||||
// ResultSize max is when each char is escaped. 2 quote chars always added.
|
||||
return CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &SizeEstimate{Min: sz.Min + 2, Max: sz.Max*2 + 2}}
|
||||
return CallEstimate{
|
||||
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
|
||||
ResultSize: &SizeEstimate{Min: sz.Min + 2, Max: sz.Max*2 + 2}}
|
||||
}
|
||||
case overloads.StartsWithString, overloads.EndsWithString:
|
||||
if len(args) == 1 {
|
||||
return CallEstimate{CostEstimate: c.sizeEstimate(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
|
||||
return CallEstimate{CostEstimate: c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
|
||||
}
|
||||
case overloads.InList:
|
||||
// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
|
||||
// We just assume all list containment checks are O(n).
|
||||
if len(args) == 2 {
|
||||
return CallEstimate{CostEstimate: c.sizeEstimate(args[1]).MultiplyByCostFactor(1).Add(argCostSum())}
|
||||
return CallEstimate{CostEstimate: c.sizeOrUnknown(args[1]).MultiplyByCostFactor(1).Add(argCostSum())}
|
||||
}
|
||||
// O(nm) functions
|
||||
case overloads.MatchesString:
|
||||
@ -595,19 +790,19 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
|
||||
if target != nil && len(args) == 1 {
|
||||
// Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0
|
||||
// in case where string is empty but regex is still expensive.
|
||||
strCost := c.sizeEstimate(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor)
|
||||
strCost := c.sizeOrUnknown(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor)
|
||||
// We don't know how many expressions are in the regex, just the string length (a huge
|
||||
// improvement here would be to somehow get a count the number of expressions in the regex or
|
||||
// how many states are in the regex state machine and use that to measure regex cost).
|
||||
// For now, we're making a guess that each expression in a regex is typically at least 4 chars
|
||||
// in length.
|
||||
regexCost := c.sizeEstimate(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor)
|
||||
regexCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor)
|
||||
return CallEstimate{CostEstimate: strCost.Multiply(regexCost).Add(argCostSum())}
|
||||
}
|
||||
case overloads.ContainsString:
|
||||
if target != nil && len(args) == 1 {
|
||||
strCost := c.sizeEstimate(*target).MultiplyByCostFactor(common.StringTraversalCostFactor)
|
||||
substrCost := c.sizeEstimate(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor)
|
||||
strCost := c.sizeOrUnknown(*target).MultiplyByCostFactor(common.StringTraversalCostFactor)
|
||||
substrCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor)
|
||||
return CallEstimate{CostEstimate: strCost.Multiply(substrCost).Add(argCostSum())}
|
||||
}
|
||||
case overloads.LogicalOr, overloads.LogicalAnd:
|
||||
@ -617,7 +812,9 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
|
||||
argCost := CostEstimate{Min: lhs.Min, Max: lhs.Add(rhs).Max}
|
||||
return CallEstimate{CostEstimate: argCost}
|
||||
case overloads.Conditional:
|
||||
size := c.sizeEstimate(args[1]).Union(c.sizeEstimate(args[2]))
|
||||
size := c.sizeOrUnknown(args[1]).Union(c.sizeOrUnknown(args[2]))
|
||||
resultEntrySize := c.computeEntrySize(args[1].Expr()).union(c.computeEntrySize(args[2].Expr()))
|
||||
c.setEntrySize(e, resultEntrySize)
|
||||
conditionalCost := argCosts[0]
|
||||
ifTrueCost := argCosts[1]
|
||||
ifFalseCost := argCosts[2]
|
||||
@ -625,13 +822,19 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
|
||||
return CallEstimate{CostEstimate: argCost, ResultSize: &size}
|
||||
case overloads.AddString, overloads.AddBytes, overloads.AddList:
|
||||
if len(args) == 2 {
|
||||
lhsSize := c.sizeEstimate(args[0])
|
||||
rhsSize := c.sizeEstimate(args[1])
|
||||
lhsSize := c.sizeOrUnknown(args[0])
|
||||
rhsSize := c.sizeOrUnknown(args[1])
|
||||
resultSize := lhsSize.Add(rhsSize)
|
||||
rhsEntrySize := c.computeEntrySize(args[0].Expr())
|
||||
lhsEntrySize := c.computeEntrySize(args[1].Expr())
|
||||
resultEntrySize := rhsEntrySize.union(lhsEntrySize)
|
||||
if resultEntrySize != nil {
|
||||
c.setEntrySize(e, resultEntrySize)
|
||||
}
|
||||
switch overloadID {
|
||||
case overloads.AddList:
|
||||
// list concatenation is O(1), but we handle it here to track size
|
||||
return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum()), ResultSize: &resultSize}
|
||||
return CallEstimate{CostEstimate: FixedCostEstimate(1).Add(argCostSum()), ResultSize: &resultSize}
|
||||
default:
|
||||
return CallEstimate{CostEstimate: resultSize.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &resultSize}
|
||||
}
|
||||
@ -639,8 +842,8 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
|
||||
case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString,
|
||||
overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes,
|
||||
overloads.Equals, overloads.NotEquals:
|
||||
lhsCost := c.sizeEstimate(args[0])
|
||||
rhsCost := c.sizeEstimate(args[1])
|
||||
lhsCost := c.sizeOrUnknown(args[0])
|
||||
rhsCost := c.sizeOrUnknown(args[1])
|
||||
min := uint64(0)
|
||||
smallestMax := lhsCost.Max
|
||||
if rhsCost.Max < smallestMax {
|
||||
@ -650,14 +853,16 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
|
||||
min = 1
|
||||
}
|
||||
// equality of 2 scalar values results in a cost of 1
|
||||
return CallEstimate{CostEstimate: CostEstimate{Min: min, Max: smallestMax}.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
|
||||
return CallEstimate{
|
||||
CostEstimate: CostEstimate{Min: min, Max: smallestMax}.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
|
||||
}
|
||||
}
|
||||
// O(1) functions
|
||||
// See CostTracker.costCall for more details about O(1) cost calculations
|
||||
|
||||
// Benchmarks suggest that most of the other operations take +/- 50% of a base cost unit
|
||||
// which on an Intel xeon 2.20GHz CPU is 50ns.
|
||||
return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum())}
|
||||
return CallEstimate{CostEstimate: FixedCostEstimate(1).Add(argCostSum())}
|
||||
}
|
||||
|
||||
func (c *coster) getType(e ast.Expr) *types.Type {
|
||||
@ -665,28 +870,145 @@ func (c *coster) getType(e ast.Expr) *types.Type {
|
||||
}
|
||||
|
||||
func (c *coster) getPath(e ast.Expr) []string {
|
||||
return c.exprPath[e.ID()]
|
||||
if e.Kind() == ast.IdentKind {
|
||||
if v, found := c.peekLocalVar(e.AsIdent()); found {
|
||||
return v.path[:]
|
||||
}
|
||||
}
|
||||
return c.exprPaths[e.ID()][:]
|
||||
}
|
||||
|
||||
func (c *coster) addPath(e ast.Expr, path []string) {
|
||||
c.exprPath[e.ID()] = path
|
||||
c.exprPaths[e.ID()] = path
|
||||
}
|
||||
|
||||
func isAccumulatorVar(name string) bool {
|
||||
return name == parser.AccumulatorName || name == parser.HiddenAccumulatorName
|
||||
}
|
||||
|
||||
func (c *coster) newAstNode(e ast.Expr) *astNode {
|
||||
path := c.getPath(e)
|
||||
if len(path) > 0 && path[0] == parser.AccumulatorName {
|
||||
if len(path) > 0 && isAccumulatorVar(path[0]) {
|
||||
// only provide paths to root vars; omit accumulator vars
|
||||
path = nil
|
||||
}
|
||||
var derivedSize *SizeEstimate
|
||||
if size, ok := c.computedSizes[e.ID()]; ok {
|
||||
derivedSize = &size
|
||||
}
|
||||
return &astNode{
|
||||
path: path,
|
||||
t: c.getType(e),
|
||||
expr: e,
|
||||
derivedSize: derivedSize}
|
||||
derivedSize: c.computeSize(e)}
|
||||
}
|
||||
|
||||
func (c *coster) setSize(e ast.Expr, size *SizeEstimate) {
|
||||
if size == nil {
|
||||
return
|
||||
}
|
||||
// Store the computed size with the expression
|
||||
c.computedSizes[e.ID()] = *size
|
||||
}
|
||||
|
||||
func (c *coster) sizeOrUnknown(node any) SizeEstimate {
|
||||
switch v := node.(type) {
|
||||
case ast.Expr:
|
||||
if sz := c.computeSize(v); sz != nil {
|
||||
return *sz
|
||||
}
|
||||
case AstNode:
|
||||
if sz := v.ComputedSize(); sz != nil {
|
||||
return *sz
|
||||
}
|
||||
}
|
||||
return UnknownSizeEstimate()
|
||||
}
|
||||
|
||||
func (c *coster) copySizeEstimates(dst, src ast.Expr) {
|
||||
c.setSize(dst, c.computeSize(src))
|
||||
c.setEntrySize(dst, c.computeEntrySize(src))
|
||||
}
|
||||
|
||||
func (c *coster) computeSize(e ast.Expr) *SizeEstimate {
|
||||
if size, ok := c.computedSizes[e.ID()]; ok {
|
||||
return &size
|
||||
}
|
||||
if size := computeExprSize(e); size != nil {
|
||||
return size
|
||||
}
|
||||
// Ensure size estimates are computed first as users may choose to override the costs that
|
||||
// CEL would otherwise ascribe to the type.
|
||||
node := astNode{expr: e, path: c.getPath(e), t: c.getType(e)}
|
||||
if size := c.estimator.EstimateSize(node); size != nil {
|
||||
// storing the computed size should reduce calls to EstimateSize()
|
||||
c.computedSizes[e.ID()] = *size
|
||||
return size
|
||||
}
|
||||
if size := computeTypeSize(c.getType(e)); size != nil {
|
||||
return size
|
||||
}
|
||||
if e.Kind() == ast.IdentKind {
|
||||
varName := e.AsIdent()
|
||||
if v, ok := c.peekLocalVar(varName); ok && v.size != nil {
|
||||
return v.size
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *coster) setEntrySize(e ast.Expr, size *entrySizeEstimate) {
|
||||
if size == nil {
|
||||
return
|
||||
}
|
||||
c.computedEntrySizes[e.ID()] = *size
|
||||
}
|
||||
|
||||
func (c *coster) computeEntrySize(e ast.Expr) *entrySizeEstimate {
|
||||
if sz, found := c.computedEntrySizes[e.ID()]; found {
|
||||
return &sz
|
||||
}
|
||||
if e.Kind() == ast.IdentKind {
|
||||
varName := e.AsIdent()
|
||||
if v, ok := c.peekLocalVar(varName); ok && v.entrySize != nil {
|
||||
return v.entrySize
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func computeExprSize(expr ast.Expr) *SizeEstimate {
|
||||
var v uint64
|
||||
switch expr.Kind() {
|
||||
case ast.LiteralKind:
|
||||
switch ck := expr.AsLiteral().(type) {
|
||||
case types.String:
|
||||
// converting to runes here is an O(n) operation, but
|
||||
// this is consistent with how size is computed at runtime,
|
||||
// and how the language definition defines string size
|
||||
v = uint64(len([]rune(ck)))
|
||||
case types.Bytes:
|
||||
v = uint64(len(ck))
|
||||
case types.Bool, types.Double, types.Duration,
|
||||
types.Int, types.Timestamp, types.Uint,
|
||||
types.Null:
|
||||
v = uint64(1)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case ast.ListKind:
|
||||
v = uint64(expr.AsList().Size())
|
||||
case ast.MapKind:
|
||||
v = uint64(expr.AsMap().Size())
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
cost := FixedSizeEstimate(v)
|
||||
return &cost
|
||||
}
|
||||
|
||||
func computeTypeSize(t *types.Type) *SizeEstimate {
|
||||
if isScalar(t) {
|
||||
cost := FixedSizeEstimate(1)
|
||||
return &cost
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isScalar returns true if the given type is known to be of a constant size at
|
||||
@ -696,10 +1018,24 @@ func isScalar(t *types.Type) bool {
|
||||
switch t.Kind() {
|
||||
case types.BoolKind, types.DoubleKind, types.DurationKind, types.IntKind, types.TimestampKind, types.UintKind:
|
||||
return true
|
||||
case types.OpaqueKind:
|
||||
if t.TypeName() == "optional_type" {
|
||||
return isScalar(t.Parameters()[0])
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var (
|
||||
doubleTwoTo64 = math.Ldexp(1.0, 64)
|
||||
|
||||
unknownSizeEstimate = SizeEstimate{Min: 0, Max: math.MaxUint64}
|
||||
unknownCostEstimate = unknownSizeEstimate.MultiplyByCostFactor(1)
|
||||
|
||||
selectAndIdentCost = FixedCostEstimate(common.SelectAndIdentCost)
|
||||
constCost = FixedCostEstimate(common.ConstCost)
|
||||
|
||||
createListBaseCost = FixedCostEstimate(common.ListCreateBaseCost)
|
||||
createMapBaseCost = FixedCostEstimate(common.MapCreateBaseCost)
|
||||
createMessageBaseCost = FixedCostEstimate(common.StructCreateBaseCost)
|
||||
)
|
||||
|
Reference in New Issue
Block a user