-
Notifications
You must be signed in to change notification settings - Fork 147
/
indexer_utils.go
120 lines (108 loc) · 4.06 KB
/
indexer_utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package state
import (
"fmt"
"math/big"
"github.com/rollkit/rollkit/state/indexer"
)
// If the actual event value is a float, we get the condition and parse it as a float
// to compare against
func compareFloat(op1 *big.Float, op2 interface{}) (int, bool, error) {
switch opVal := op2.(type) {
case *big.Int:
vF := new(big.Float)
vF.SetInt(opVal)
cmp := op1.Cmp(vF)
return cmp, false, nil
case *big.Float:
return op1.Cmp(opVal), true, nil
default:
return -1, false, fmt.Errorf("unable to parse arguments, bad type: %T", op2)
}
}
// If the event value we compare against the condition (op2) is an integer
// we convert the int to float with a precision equal to the number of bits
// needed to represent the integer to avoid rounding issues with floats
// where 100 would equal to 100.2 because 100.2 is rounded to 100, while 100.7
// would be rounded to 101.
func compareInt(op1 *big.Int, op2 interface{}) (int, bool, error) {
switch opVal := op2.(type) {
case *big.Int:
return op1.Cmp(opVal), false, nil
case *big.Float:
vF := new(big.Float)
vF.SetInt(op1)
return vF.Cmp(opVal), true, nil
default:
return -1, false, fmt.Errorf("unable to parse arguments, unexpected type: %T", op2)
}
}
// CheckBounds checks if the given value falls within the specified query range.
func CheckBounds(ranges indexer.QueryRange, v interface{}) (bool, error) {
// These functions fetch the lower and upper bounds of the query
// It is expected that for x > 5, the value of lowerBound is 6.
// This is achieved by adding one to the actual lower bound.
// For a query of x < 5, the value of upper bound is 4.
// This is achieved by subtracting one from the actual upper bound.
// For integers this behavior will work. However, for floats, we cannot simply add/sub 1.
// Query :x < 5.5 ; x = 5 should match the query. If we subtracted one as for integers,
// the upperBound would be 4.5 and x would not match. Thus we do not subtract anything for
// floating point bounds.
// We can rewrite these functions to not add/sub 1 but the function handles also time arguments.
// To be sure we are not breaking existing queries that compare time, and as we are planning to replace
// the indexer in the future, we adapt the code here to handle floats as a special case.
lowerBound := ranges.LowerBoundValue()
upperBound := ranges.UpperBoundValue()
// *Explanation for the isFloat condition below.*
// In LowerBoundValue(), for floating points, we cannot simply add 1 due to the reasons explained in
// in the comment at the beginning. The same is true for subtracting one for UpperBoundValue().
// That means that for integers, if the condition is >=, cmp will be either 0 or 1
// ( cmp == -1 should always be false).
// But if the lowerBound is a float, we have not subtracted one, so returning a 0
// is correct only if ranges.IncludeLowerBound is true.
// example int: x < 100; upperBound = 99; if x.Cmp(99) == 0 the condition holds
// example float: x < 100.0; upperBound = 100.0; if x.Cmp(100) ==0 then returning x
// would be wrong.
switch vVal := v.(type) {
case *big.Int:
if lowerBound != nil {
cmp, isFloat, err := compareInt(vVal, lowerBound)
if err != nil {
return false, err
}
if cmp == -1 || (isFloat && cmp == 0 && !ranges.IncludeLowerBound) {
return false, err
}
}
if upperBound != nil {
cmp, isFloat, err := compareInt(vVal, upperBound)
if err != nil {
return false, err
}
if cmp == 1 || (isFloat && cmp == 0 && !ranges.IncludeUpperBound) {
return false, err
}
}
case *big.Float:
if lowerBound != nil {
cmp, isFloat, err := compareFloat(vVal, lowerBound)
if err != nil {
return false, err
}
if cmp == -1 || (cmp == 0 && isFloat && !ranges.IncludeLowerBound) {
return false, err
}
}
if upperBound != nil {
cmp, isFloat, err := compareFloat(vVal, upperBound)
if err != nil {
return false, err
}
if cmp == 1 || (cmp == 0 && isFloat && !ranges.IncludeUpperBound) {
return false, err
}
}
default:
return false, fmt.Errorf("invalid argument type in query: %T", v)
}
return true, nil
}