From 21920a48d70856b29bdb390d12f2eb5cb10ef4b9 Mon Sep 17 00:00:00 2001 From: Ting-Wei Lan Date: Wed, 27 Mar 2019 15:14:11 +0800 Subject: 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. --- core/vm/sqlvm/ast/types.go | 101 ++++++++++++++++++++++++---------------- core/vm/sqlvm/ast/types_test.go | 30 ++++++------ core/vm/sqlvm/errors/errors.go | 2 - 3 files changed, 76 insertions(+), 57 deletions(-) (limited to 'core') 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", -- cgit v1.2.3