aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwmin0 <wmin0@cobinhood.com>2019-01-29 18:40:35 +0800
committerJhih-Ming Huang <jm.huang@cobinhood.com>2019-05-06 10:44:03 +0800
commit490101975d5e365e2aa801972bc3b30bf9a65c91 (patch)
tree6ea36c504635ab5b46ae40106546475ce1eddfa9
parentf4b202770ceeb57a25e40d7714883f321cc5e3e1 (diff)
downloaddexon-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.go160
-rw-r--r--core/vm/sqlvm/ast/type_test.go84
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))
+}