aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTing-Wei Lan <tingwei.lan@cobinhood.com>2019-03-27 15:14:11 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-05-06 10:44:04 +0800
commit21920a48d70856b29bdb390d12f2eb5cb10ef4b9 (patch)
tree1236f5a25748a4e8c478f7c39483e83737690e6a
parent997eb4d86724d7bc6c4b2784e6da54801d65cd7f (diff)
downloaddexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.tar
dexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.tar.gz
dexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.tar.bz2
dexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.tar.lz
dexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.tar.xz
dexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.tar.zst
dexon-21920a48d70856b29bdb390d12f2eb5cb10ef4b9.zip
core: vm: sqlvm: ast: don't calculate min and max values on demand
It is unsafe to write a map concurrently. Even if it is unlikely for us to call it from multiple threads, it is better to avoid providing an thread-unsafe API when it is possible.
-rw-r--r--core/vm/sqlvm/ast/types.go101
-rw-r--r--core/vm/sqlvm/ast/types_test.go30
-rw-r--r--core/vm/sqlvm/errors/errors.go2
3 files changed, 76 insertions, 57 deletions
diff --git a/core/vm/sqlvm/ast/types.go b/core/vm/sqlvm/ast/types.go
index d75d29294..41c35ac78 100644
--- a/core/vm/sqlvm/ast/types.go
+++ b/core/vm/sqlvm/ast/types.go
@@ -16,14 +16,6 @@ var (
bigIntTen = big.NewInt(10)
)
-type decPair struct {
- Min, Max decimal.Decimal
-}
-
-var (
- decPairMap = make(map[DataType]decPair)
-)
-
// DataTypeMajor defines type for high byte of DataType.
type DataTypeMajor uint8
@@ -152,6 +144,67 @@ func (dt DataType) GetNode() TypeNode {
return nil
}
+type decimalMinMaxPair struct {
+ Min, Max decimal.Decimal
+}
+
+var decimalMinMaxMap = func() map[DataType]decimalMinMaxPair {
+ m := make(map[DataType]decimalMinMaxPair)
+
+ // bool
+ {
+ dt := ComposeDataType(DataTypeMajorBool, DataTypeMinorDontCare)
+ m[dt] = decimalMinMaxPair{Min: dec.False, Max: dec.True}
+ }
+
+ // address
+ {
+ dt := ComposeDataType(DataTypeMajorAddress, DataTypeMinorDontCare)
+ bigMax := new(big.Int)
+ bigMax.Lsh(bigIntOne, common.AddressLength*8)
+ bigMax.Sub(bigMax, bigIntOne)
+ max := decimal.NewFromBigInt(bigMax, 0)
+ m[dt] = decimalMinMaxPair{Min: decimal.Zero, Max: max}
+ }
+
+ // uint, bytes{X}
+ for i := uint(0); i <= 0x1f; i++ {
+ dtUint := ComposeDataType(DataTypeMajorUint, DataTypeMinor(i))
+ dtBytes := ComposeDataType(DataTypeMajorFixedBytes, DataTypeMinor(i))
+ bigMax := new(big.Int)
+ bigMax.Lsh(bigIntOne, (i+1)*8)
+ bigMax.Sub(bigMax, bigIntOne)
+ max := decimal.NewFromBigInt(bigMax, 0)
+ m[dtUint] = decimalMinMaxPair{Min: decimal.Zero, Max: max}
+ m[dtBytes] = decimalMinMaxPair{Min: decimal.Zero, Max: max}
+ }
+
+ // int
+ for i := uint(0); i <= 0x1f; i++ {
+ dt := ComposeDataType(DataTypeMajorInt, DataTypeMinor(i))
+ bigMax := new(big.Int)
+ bigMax.Lsh(bigIntOne, (i+1)*8-1)
+ bigMin := new(big.Int)
+ bigMin.Neg(bigMax)
+ bigMax.Sub(bigMax, bigIntOne)
+ min := decimal.NewFromBigInt(bigMin, 0)
+ max := decimal.NewFromBigInt(bigMax, 0)
+ m[dt] = decimalMinMaxPair{Min: min, Max: max}
+ }
+
+ return m
+}()
+
+// GetMinMax returns min, max pair according to given data type.
+func (dt DataType) GetMinMax() (decimal.Decimal, decimal.Decimal, bool) {
+ var (
+ pair decimalMinMaxPair
+ ok bool
+ )
+ pair, ok = decimalMinMaxMap[dt]
+ return pair.Min, pair.Max, ok
+}
+
func decimalToBig(d decimal.Decimal) (b *big.Int) {
if exponent := int64(d.Exponent()); exponent >= 0 {
exp := new(big.Int).Exp(bigIntTen, big.NewInt(exponent), nil)
@@ -254,38 +307,6 @@ func DecimalDecode(dt DataType, b []byte) (decimal.Decimal, error) {
return decimal.Zero, se.ErrorCodeDecimalDecode
}
-// GetMinMax returns min, max pair according to given data type.
-func GetMinMax(dt DataType) (min, max decimal.Decimal, err error) {
- cached, ok := decPairMap[dt]
- if ok {
- min, max = cached.Min, cached.Max
- return
- }
-
- major, minor := DecomposeDataType(dt)
- switch major {
- case DataTypeMajorBool:
- min, max = dec.False, dec.True
- case DataTypeMajorAddress:
- bigUMax := new(big.Int).Lsh(bigIntOne, common.AddressLength*8)
- max = decimal.NewFromBigInt(bigUMax, 0).Sub(dec.One)
- case DataTypeMajorInt:
- bigMax := new(big.Int).Lsh(bigIntOne, (uint(minor)+1)*8-1)
- decMax := decimal.NewFromBigInt(bigMax, 0)
- min, max = decMax.Neg(), decMax.Sub(dec.One)
- case DataTypeMajorUint,
- DataTypeMajorFixedBytes:
- bigUMax := new(big.Int).Lsh(bigIntOne, (uint(minor)+1)*8)
- max = decimal.NewFromBigInt(bigUMax, 0).Sub(dec.One)
- default:
- err = se.ErrorCodeGetMinMax
- return
- }
-
- decPairMap[dt] = decPair{Max: max, Min: min}
- return
-}
-
// DecimalToUint64 convert decimal to uint64.
// Negative case will return error, and decimal part will be trancated.
func DecimalToUint64(d decimal.Decimal) (uint64, error) {
diff --git a/core/vm/sqlvm/ast/types_test.go b/core/vm/sqlvm/ast/types_test.go
index 3d0ecb395..000ae2cc8 100644
--- a/core/vm/sqlvm/ast/types_test.go
+++ b/core/vm/sqlvm/ast/types_test.go
@@ -153,33 +153,33 @@ func (s *TypesTestSuite) TestEncodeAndDecodeDecimal() {
3)
}
-func (s *TypesTestSuite) TestGetMinMax() {
+func (s *TypesTestSuite) TestDataTypeGetMinMax() {
decAddressMax := decimal.New(2, 0).Pow(decimal.New(common.AddressLength*8, 0)).Sub(dec.One)
testcases := []struct {
Name string
In DataType
Min, Max decimal.Decimal
- Err error
+ Ok bool
}{
- {"Bool", ComposeDataType(DataTypeMajorBool, 0), dec.False, dec.True, nil},
- {"Address", ComposeDataType(DataTypeMajorAddress, 0), decimal.Zero, decAddressMax, nil},
- {"Int8", ComposeDataType(DataTypeMajorInt, 0), decimal.New(-128, 0), decimal.New(127, 0), nil},
- {"Int16", ComposeDataType(DataTypeMajorInt, 1), decimal.New(-32768, 0), decimal.New(32767, 0), nil},
- {"UInt8", ComposeDataType(DataTypeMajorUint, 0), decimal.Zero, decimal.New(255, 0), nil},
- {"UInt16", ComposeDataType(DataTypeMajorUint, 1), decimal.Zero, decimal.New(65535, 0), nil},
- {"Bytes1", ComposeDataType(DataTypeMajorFixedBytes, 0), decimal.Zero, decimal.New(255, 0), nil},
- {"Bytes2", ComposeDataType(DataTypeMajorFixedBytes, 1), decimal.Zero, decimal.New(65535, 0), nil},
- {"Dynamic Bytes", ComposeDataType(DataTypeMajorDynamicBytes, 0), decimal.Zero, decimal.Zero, errors.ErrorCodeGetMinMax},
+ {"Bool", ComposeDataType(DataTypeMajorBool, 0), dec.False, dec.True, true},
+ {"Address", ComposeDataType(DataTypeMajorAddress, 0), decimal.Zero, decAddressMax, true},
+ {"Int8", ComposeDataType(DataTypeMajorInt, 0), decimal.New(-128, 0), decimal.New(127, 0), true},
+ {"Int16", ComposeDataType(DataTypeMajorInt, 1), decimal.New(-32768, 0), decimal.New(32767, 0), true},
+ {"UInt8", ComposeDataType(DataTypeMajorUint, 0), decimal.Zero, decimal.New(255, 0), true},
+ {"UInt16", ComposeDataType(DataTypeMajorUint, 1), decimal.Zero, decimal.New(65535, 0), true},
+ {"Bytes1", ComposeDataType(DataTypeMajorFixedBytes, 0), decimal.Zero, decimal.New(255, 0), true},
+ {"Bytes2", ComposeDataType(DataTypeMajorFixedBytes, 1), decimal.Zero, decimal.New(65535, 0), true},
+ {"Dynamic Bytes", ComposeDataType(DataTypeMajorDynamicBytes, 0), decimal.Zero, decimal.Zero, false},
}
var (
min, max decimal.Decimal
- err error
+ ok bool
)
for _, t := range testcases {
- min, max, err = GetMinMax(t.In)
- s.Require().Equal(t.Err, err, "Case: %v. Error not equal: %v != %v", t.Name, t.Err, err)
- if t.Err != nil {
+ min, max, ok = t.In.GetMinMax()
+ s.Require().Equal(t.Ok, ok, "Case: %v. Ok not equal: %v != %v", t.Name, t.Ok, ok)
+ if !ok {
continue
}
diff --git a/core/vm/sqlvm/errors/errors.go b/core/vm/sqlvm/errors/errors.go
index 40d4e0a8b..6f2b8ad93 100644
--- a/core/vm/sqlvm/errors/errors.go
+++ b/core/vm/sqlvm/errors/errors.go
@@ -110,7 +110,6 @@ const (
ErrorCodeInvalidUfixedFractionalDigits
ErrorCodeDecimalEncode
ErrorCodeDecimalDecode
- ErrorCodeGetMinMax
// Runtime Error
ErrorCodeInvalidDataType
@@ -142,7 +141,6 @@ var errorCodeMap = [...]string{
ErrorCodeInvalidUfixedFractionalDigits: "invalid ufixed fractional digits",
ErrorCodeDecimalEncode: "decimal encode failed",
ErrorCodeDecimalDecode: "decimal decode failed",
- ErrorCodeGetMinMax: "get (min, max) failed",
// Runtime Error
ErrorCodeInvalidDataType: "invalid data type",
ErrorCodeOverflow: "overflow",