From 01e130a74e957a2415edbcd99612c6c21b1f64ad Mon Sep 17 00:00:00 2001 From: Meng-Ying Yang Date: Tue, 16 Apr 2019 16:38:55 +0800 Subject: core: vm: sqlvm: add built-in function BITAND() --- core/vm/sqlvm/runtime/functions.go | 119 ++++++++++++++++++++++++ core/vm/sqlvm/runtime/instructions_op_test.go | 62 ++++++++++++ core/vm/sqlvm/runtime/instructions_tmpl_data.go | 62 ++++++++++++ 3 files changed, 243 insertions(+) (limited to 'core/vm') diff --git a/core/vm/sqlvm/runtime/functions.go b/core/vm/sqlvm/runtime/functions.go index 7aed05b19..6cdceb7fb 100644 --- a/core/vm/sqlvm/runtime/functions.go +++ b/core/vm/sqlvm/runtime/functions.go @@ -26,6 +26,7 @@ const ( TXORIGIN NOW RAND + BITAND ) type fn func(*common.Context, []*Operand, uint64) (*Operand, error) @@ -42,6 +43,7 @@ var ( MSGDATA: fnMsgData, TXORIGIN: fnTxOrigin, RAND: fnRand, + BITAND: fnBitAnd, } ) @@ -195,5 +197,122 @@ func fnRand(ctx *common.Context, ops []*Operand, length uint64) (result *Operand fn, length, ) return +} + +func metaBitOp(dType ast.DataType) bool { + dMajor, _ := ast.DecomposeDataType(dType) + switch dMajor { + case ast.DataTypeMajorUint, + ast.DataTypeMajorInt, + ast.DataTypeMajorFixedBytes: + return true + } + return false +} + +func metaAllBitOp(op *Operand) bool { return metaAll(op, metaBitOp) } + +func extractOps(ops []*Operand) (n int, op1, op2 *Operand, err error) { + if len(ops) < 2 { + err = se.ErrorCodeInvalidOperandNum + return + } + + n, err = findMaxDataLength(ops) + if err != nil { + return + } + op1, op2 = ops[0], ops[1] + + if !metaAllEq(op1, op2) || !metaAllBitOp(op1) { + err = se.ErrorCodeInvalidDataType + } + return +} + +func (r *Raw) toBytes(dType ast.DataType) []byte { + dMajor, _ := ast.DecomposeDataType(dType) + switch dMajor { + case ast.DataTypeMajorFixedBytes, + ast.DataTypeMajorAddress, + ast.DataTypeMajorDynamicBytes: + return r.Bytes + case ast.DataTypeMajorUint, + ast.DataTypeMajorInt, + ast.DataTypeMajorFixed: + bytes, err := ast.DecimalEncode(dType, r.Value) + if err != nil { + panic(err) + } + return bytes + default: + panic(fmt.Errorf("unrecognized data type: %v", dType)) + } +} + +func (r *Raw) fromBytes(bytes []byte, dType ast.DataType) { + dMajor, _ := ast.DecomposeDataType(dType) + switch dMajor { + case ast.DataTypeMajorFixedBytes, + ast.DataTypeMajorAddress, + ast.DataTypeMajorDynamicBytes: + r.Bytes = bytes + case ast.DataTypeMajorUint, + ast.DataTypeMajorInt, + ast.DataTypeMajorFixed: + var err error + r.Value, err = ast.DecimalDecode(dType, bytes) + if err != nil { + panic(err) + } + default: + panic(fmt.Errorf("unrecognized data type: %v", dType)) + } +} + +type bitBinFunc func(b1, b2 byte) byte + +func fnBitAnd(ctx *common.Context, ops []*Operand, length uint64) (result *Operand, err error) { + n, op1, op2, err := extractOps(ops) + if err != nil { + return + } + + result = op1.clone(true) + result.Data = make([]Tuple, n) + for i := 0; i < n; i++ { + result.Data[i] = op1.Data[i].bitBinOp( + op2.Data[i], + op1.Meta, + func(b1, b2 byte) byte { return b1 & b2 }, + ) + } + return +} + +func (t Tuple) bitBinOp(t2 Tuple, meta []ast.DataType, bFn bitBinFunc) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = t[i].bitBinOp(t2[i], meta[i], bFn) + } + return +} + +func (r *Raw) bitBinOp(r2 *Raw, dType ast.DataType, bFn bitBinFunc) (r3 *Raw) { + bytes1, bytes2 := r.toBytes(dType), r2.toBytes(dType) + + if len(bytes1) != len(bytes2) { + panic(fmt.Errorf("bitwise operand on different length bits")) + } + + n := len(bytes1) + bytes3 := make([]byte, n) + for i := 0; i < n; i++ { + bytes3[i] = bFn(bytes1[i], bytes2[i]) + } + + r3 = &Raw{} + r3.fromBytes(bytes3, dType) + return } diff --git a/core/vm/sqlvm/runtime/instructions_op_test.go b/core/vm/sqlvm/runtime/instructions_op_test.go index 994c508ff..b2fec064e 100644 --- a/core/vm/sqlvm/runtime/instructions_op_test.go +++ b/core/vm/sqlvm/runtime/instructions_op_test.go @@ -3693,3 +3693,65 @@ func (s *instructionSuite) TestOpRange() { s.run(testcases, opRange) } + +func (s *instructionSuite) TestOpFuncBitAnd() { + testcases := []opTestcase{ + { + "Func BitAnd", + Instruction{ + Op: FUNC, + Input: []*Operand{ + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorUint, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(2)}}, + }, + ), + makeOperand( + true, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorUint, 1), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(10)}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorUint, 0), ast.ComposeDataType(ast.DataTypeMajorUint, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-1)}, &Raw{Value: decimal.NewFromFloat(-128)}, &Raw{Bytes: []byte{0x12, 0x34}}, &Raw{Bytes: []byte{0x56, 0x78}}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorUint, 0), ast.ComposeDataType(ast.DataTypeMajorUint, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(5)}, &Raw{Value: decimal.NewFromFloat(6)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Bytes: []byte{0xff, 0xff}}, &Raw{Bytes: []byte{0x00, 0x00}}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorUint, 0), ast.ComposeDataType(ast.DataTypeMajorUint, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorInt, 0), ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), ast.ComposeDataType(ast.DataTypeMajorFixedBytes, 0), + }, + []Tuple{ + {&Raw{Value: decimal.NewFromFloat(1)}, &Raw{Value: decimal.NewFromFloat(2)}, &Raw{Value: decimal.NewFromFloat(-2)}, &Raw{Value: decimal.NewFromFloat(-128)}, &Raw{Bytes: []byte{0x12, 0x34}}, &Raw{Bytes: []byte{0x00, 0x00}}}, + }, + ), + nil, + }, + } + + s.run(testcases, opFunc) +} diff --git a/core/vm/sqlvm/runtime/instructions_tmpl_data.go b/core/vm/sqlvm/runtime/instructions_tmpl_data.go index 23669d97a..c7a718cbf 100644 --- a/core/vm/sqlvm/runtime/instructions_tmpl_data.go +++ b/core/vm/sqlvm/runtime/instructions_tmpl_data.go @@ -2598,5 +2598,67 @@ var testData = &tmplData{ }, }, // -- end of RANGE + { + TestName: "OpFuncBitAnd", OpFunc: "opFunc", + Cases: []*tmplTestCase{ + { + Name: "Func BitAnd", + Error: "nil", OpCode: "FUNC", + Inputs: []*tmplOp{ + { + Im: true, + Metas: []*tmplOpMeta{ + {Major: "Uint", Minor: 0}, + }, + Data: []string{`{V: 2}`}, + }, + { + Im: true, + Metas: []*tmplOpMeta{ + {Major: "Uint", Minor: 1}, + }, + Data: []string{`{V: 10}`}, + }, + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Uint", Minor: 0}, + {Major: "Uint", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "FixedBytes", Minor: 0}, + {Major: "FixedBytes", Minor: 0}, + }, + Data: []string{"{V: 1, V: 2, V: -1, V: -128, B: {0x12, 0x34}, B: {0x56, 0x78}}"}, + }, + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Uint", Minor: 0}, + {Major: "Uint", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "FixedBytes", Minor: 0}, + {Major: "FixedBytes", Minor: 0}, + }, + Data: []string{"{V: 5, V: 6, V: -2, V: -2, B: {0xff, 0xff}, B:{0x00, 0x00}}"}, + }, + }, + Output: &tmplOp{ + Im: false, + Metas: []*tmplOpMeta{ + {Major: "Uint", Minor: 0}, + {Major: "Uint", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "Int", Minor: 0}, + {Major: "FixedBytes", Minor: 0}, + {Major: "FixedBytes", Minor: 0}, + }, + Data: []string{`{V: 1, V: 2, V: -2, V: -128, B: {0x12, 0x34}, B: {0x00, 0x00}}`}, + }, + }, + }, + }, + // -- end of FUNC BITAND }, } -- cgit v1.2.3