diff options
author | wmin0 <wmin0@cobinhood.com> | 2019-01-29 18:40:35 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-05-06 10:44:03 +0800 |
commit | 490101975d5e365e2aa801972bc3b30bf9a65c91 (patch) | |
tree | 6ea36c504635ab5b46ae40106546475ce1eddfa9 | |
parent | f4b202770ceeb57a25e40d7714883f321cc5e3e1 (diff) | |
download | dexon-490101975d5e365e2aa801972bc3b30bf9a65c91.tar.gz dexon-490101975d5e365e2aa801972bc3b30bf9a65c91.tar.zst dexon-490101975d5e365e2aa801972bc3b30bf9a65c91.zip |
core: vm: sqlvm: ast: data type encoder and decoder
Implement encode & decode function to convert between type node and
2-bytes type described on spec.
-rw-r--r-- | core/vm/sqlvm/ast/type.go | 160 | ||||
-rw-r--r-- | core/vm/sqlvm/ast/type_test.go | 84 |
2 files changed, 244 insertions, 0 deletions
diff --git a/core/vm/sqlvm/ast/type.go b/core/vm/sqlvm/ast/type.go new file mode 100644 index 000000000..06f0c0207 --- /dev/null +++ b/core/vm/sqlvm/ast/type.go @@ -0,0 +1,160 @@ +package ast + +import ( + "errors" + "reflect" +) + +// Error defines. +var ( + ErrDataTypeEncode = errors.New("data type encode failed") + ErrDataTypeDecode = errors.New("data type decode failed") +) + +// DataTypeMajor defines type for high byte of DataType. +type DataTypeMajor uint8 + +// DataTypeMinor defines type for low byte of DataType. +type DataTypeMinor uint8 + +// DataType defines type for data type encoded. +type DataType uint16 + +// DataTypeMajor enums. +const ( + DataTypeMajorUnknown DataTypeMajor = iota + DataTypeMajorSpecial + DataTypeMajorBool + DataTypeMajorAddress + DataTypeMajorInt + DataTypeMajorUint + DataTypeMajorFixedBytes + DataTypeMajorDynamicBytes + DataTypeMajorFixed DataTypeMajor = 0x10 + DataTypeMajorUfixed DataTypeMajor = 0x30 +) + +// DataTypeUnknown for unknown data type. +const DataTypeUnknown DataType = 0 + +func decomposeDataType(t DataType) (DataTypeMajor, DataTypeMinor) { + return DataTypeMajor(t >> 8), DataTypeMinor(t & 0xff) +} + +func composeDataType(major DataTypeMajor, minor DataTypeMinor) DataType { + return (DataType(major) << 8) | DataType(minor) +} + +// DataTypeEncode encodes data type node into DataType. +func DataTypeEncode(n interface{}) (DataType, error) { + if n == nil { + return DataTypeUnknown, ErrDataTypeEncode + } + if reflect.TypeOf(n).Kind() == reflect.Ptr { + return DataTypeEncode(reflect.ValueOf(n).Elem()) + } + + switch t := n.(type) { + case BoolTypeNode: + return composeDataType(DataTypeMajorBool, 0), nil + + case AddressTypeNode: + return composeDataType(DataTypeMajorAddress, 0), nil + + case IntTypeNode: + if t.Size%8 != 0 || t.Size > 256 { + return DataTypeUnknown, ErrDataTypeEncode + } + + minor := DataTypeMinor((t.Size / 8) - 1) + if t.Unsigned { + return composeDataType(DataTypeMajorUint, minor), nil + } + return composeDataType(DataTypeMajorInt, minor), nil + + case FixedBytesTypeNode: + if t.Size%8 != 0 || t.Size > 256 { + return DataTypeUnknown, ErrDataTypeEncode + } + + minor := DataTypeMinor((t.Size / 8) - 1) + return composeDataType(DataTypeMajorFixedBytes, minor), nil + + case DynamicBytesTypeNode: + return composeDataType(DataTypeMajorDynamicBytes, 0), nil + + case FixedTypeNode: + if t.Size%8 != 0 || t.Size > 256 { + return DataTypeUnknown, ErrDataTypeEncode + } + + if t.FractionalDigits > 80 { + return DataTypeUnknown, ErrDataTypeEncode + } + + major := DataTypeMajor((t.Size / 8) - 1) + minor := DataTypeMinor(t.FractionalDigits) + if t.Unsigned { + return composeDataType(DataTypeMajorUfixed+major, minor), nil + } + return composeDataType(DataTypeMajorFixed+major, minor), nil + } + + return DataTypeUnknown, ErrDataTypeEncode +} + +// DataTypeDecode decodes DataType into data type node. +func DataTypeDecode(t DataType) (interface{}, error) { + major, minor := decomposeDataType(t) + switch major { + // TODO(wmin0): define unsupported error for special type. + case DataTypeMajorBool: + if minor == 0 { + return BoolTypeNode{}, nil + } + case DataTypeMajorAddress: + if minor == 0 { + return AddressTypeNode{}, nil + } + case DataTypeMajorInt: + if minor <= 0x1f { + size := (uint32(minor) + 1) * 8 + return IntTypeNode{Unsigned: false, Size: size}, nil + } + case DataTypeMajorUint: + if minor <= 0x1f { + size := (uint32(minor) + 1) * 8 + return IntTypeNode{Unsigned: true, Size: size}, nil + } + case DataTypeMajorFixedBytes: + if minor <= 0x1f { + size := (uint32(minor) + 1) * 8 + return FixedBytesTypeNode{Size: size}, nil + } + case DataTypeMajorDynamicBytes: + if minor == 0 { + return DynamicBytesTypeNode{}, nil + } + } + switch { + case major >= DataTypeMajorFixed && major-DataTypeMajorFixed <= 0x1f: + if minor <= 80 { + size := (uint32(major-DataTypeMajorFixed) + 1) * 8 + return FixedTypeNode{ + Unsigned: false, + Size: size, + FractionalDigits: uint32(minor), + }, nil + } + case major >= DataTypeMajorUfixed && major-DataTypeMajorUfixed <= 0x1f: + if minor <= 80 { + size := (uint32(major-DataTypeMajorUfixed) + 1) * 8 + return FixedTypeNode{ + Unsigned: true, + Size: size, + FractionalDigits: uint32(minor), + }, nil + } + } + return nil, ErrDataTypeDecode +} diff --git a/core/vm/sqlvm/ast/type_test.go b/core/vm/sqlvm/ast/type_test.go new file mode 100644 index 000000000..41c5d3a20 --- /dev/null +++ b/core/vm/sqlvm/ast/type_test.go @@ -0,0 +1,84 @@ +package ast + +import ( + "testing" + + "github.com/stretchr/testify/suite" +) + +type TypeTestSuite struct{ suite.Suite } + +func (s *TypeTestSuite) requireEncodeAndDecodeNoError( + d DataType, t interface{}) { + encode, err := DataTypeEncode(t) + s.Require().NoError(err) + s.Require().Equal(d, encode) + decode, err := DataTypeDecode(d) + s.Require().NoError(err) + s.Require().Equal(t, decode) +} + +func (s *TypeTestSuite) requireEncodeError(input interface{}) { + _, err := DataTypeEncode(input) + s.Require().Error(err) +} + +func (s *TypeTestSuite) requireDecodeError(input DataType) { + _, err := DataTypeDecode(input) + s.Require().Error(err) +} + +func (s *TypeTestSuite) TestEncodeAndDecode() { + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorBool, 0), + BoolTypeNode{}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorAddress, 0), + AddressTypeNode{}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorInt, 1), + IntTypeNode{Size: 16}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorUint, 2), + IntTypeNode{Unsigned: true, Size: 24}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorFixedBytes, 3), + FixedBytesTypeNode{Size: 32}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorDynamicBytes, 0), + DynamicBytesTypeNode{}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorFixed, 1), + FixedTypeNode{Size: 8, FractionalDigits: 1}) + s.requireEncodeAndDecodeNoError( + composeDataType(DataTypeMajorUfixed+1, 2), + FixedTypeNode{Unsigned: true, Size: 16, FractionalDigits: 2}) +} + +func (s *TypeTestSuite) TestEncodeError() { + s.requireEncodeError(struct{}{}) + s.requireEncodeError(IntTypeNode{Size: 1}) + s.requireEncodeError(IntTypeNode{Size: 257}) + s.requireEncodeError(FixedBytesTypeNode{Size: 1}) + s.requireEncodeError(FixedBytesTypeNode{Size: 257}) + s.requireEncodeError(FixedTypeNode{Size: 1, FractionalDigits: 0}) + s.requireEncodeError(FixedTypeNode{Size: 257, FractionalDigits: 0}) + s.requireEncodeError(FixedTypeNode{Size: 8, FractionalDigits: 81}) +} + +func (s *TypeTestSuite) TestDecodeError() { + s.requireDecodeError(DataTypeUnknown) + s.requireDecodeError(composeDataType(DataTypeMajorBool, 1)) + s.requireDecodeError(composeDataType(DataTypeMajorAddress, 1)) + s.requireDecodeError(composeDataType(DataTypeMajorInt, 0x20)) + s.requireDecodeError(composeDataType(DataTypeMajorUint, 0x20)) + s.requireDecodeError(composeDataType(DataTypeMajorFixedBytes, 0x20)) + s.requireDecodeError(composeDataType(DataTypeMajorDynamicBytes, 1)) + s.requireDecodeError(composeDataType(DataTypeMajorFixed, 81)) + s.requireDecodeError(composeDataType(DataTypeMajorUfixed, 81)) + s.requireDecodeError(composeDataType(DataTypeMajorUfixed+0x20, 80)) +} + +func TestType(t *testing.T) { + suite.Run(t, new(TypeTestSuite)) +} |