diff options
author | Meng-Ying Yang <garfield@dexon.org> | 2019-04-08 10:32:36 +0800 |
---|---|---|
committer | Jhih-Ming Huang <jm.huang@cobinhood.com> | 2019-05-06 10:44:05 +0800 |
commit | c07255382a10bdeaf7d5d2411e9c7c4cdc937694 (patch) | |
tree | 2a0fbb3fb3d6e0f546ee4cb09912c36bdb487953 | |
parent | 64c440d58f8eea970487b39bf231696932464159 (diff) | |
download | dexon-c07255382a10bdeaf7d5d2411e9c7c4cdc937694.tar.gz dexon-c07255382a10bdeaf7d5d2411e9c7c4cdc937694.tar.zst dexon-c07255382a10bdeaf7d5d2411e9c7c4cdc937694.zip |
core: vm: sqlvm: add opConcat
Add `opConcat` supports dynamic bytes (string) concating.
-rw-r--r-- | core/vm/sqlvm/runtime/instructions.go | 45 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_op_test.go | 80 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/instructions_tmpl_data.go | 68 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/jumptable.go | 11 | ||||
-rw-r--r-- | core/vm/sqlvm/runtime/opcodes.go | 1 |
5 files changed, 200 insertions, 5 deletions
diff --git a/core/vm/sqlvm/runtime/instructions.go b/core/vm/sqlvm/runtime/instructions.go index 353701f8c..4b03b6494 100644 --- a/core/vm/sqlvm/runtime/instructions.go +++ b/core/vm/sqlvm/runtime/instructions.go @@ -359,6 +359,13 @@ func value2ColIdx(v decimal.Decimal) (idx uint16) { return } +func metaDynBytes(dType ast.DataType) bool { + dMajor, _ := ast.DecomposeDataType(dType) + return dMajor == ast.DataTypeMajorDynamicBytes +} + +func metaAllDynBytes(op *Operand) bool { return metaAll(op, metaDynBytes) } + func flowCheck(ctx *common.Context, v decimal.Decimal, dType ast.DataType) (err error) { if !ctx.Opt.SafeMath { return @@ -1735,3 +1742,41 @@ func (r *Raw) shiftBytes(src []byte, l int, signed, rPadding bool) (tgr []byte) } return } + +func opConcat(ctx *common.Context, ops, registers []*Operand, output uint) (err error) { + if len(ops) != 2 { + err = se.ErrorCodeDataLengthNotMatch + return + } + op, op2 := ops[0], ops[1] + + if !metaAllEq(op, op2) { + err = se.ErrorCodeInvalidDataType + return + } + + if !metaAllDynBytes(op) { + err = se.ErrorCodeInvalidDataType + return + } + + op3 := op.clone(true) + op3.Data = make([]Tuple, len(op.Data)) + + for i := 0; i < len(op.Data); i++ { + op3.Data[i] = op.Data[i].concat(op2.Data[i]) + } + + registers[output] = op3 + return +} + +func (t Tuple) concat(t2 Tuple) (t3 Tuple) { + t3 = make(Tuple, len(t)) + for i := 0; i < len(t); i++ { + t3[i] = &Raw{Bytes: make([]byte, len(t[i].Bytes)+len(t2[i].Bytes))} + copy(t3[i].Bytes[:len(t[i].Bytes)], t[i].Bytes) + copy(t3[i].Bytes[len(t[i].Bytes):], t2[i].Bytes) + } + return +} diff --git a/core/vm/sqlvm/runtime/instructions_op_test.go b/core/vm/sqlvm/runtime/instructions_op_test.go index e0550006f..6c10a2e87 100644 --- a/core/vm/sqlvm/runtime/instructions_op_test.go +++ b/core/vm/sqlvm/runtime/instructions_op_test.go @@ -1321,6 +1321,86 @@ func (s *instructionSuite) TestOpMod() { s.run(testcases, opMod) } +func (s *instructionSuite) TestOpConcat() { + testcases := []opTestcase{ + { + "Concat bytes", + Instruction{ + Op: CONCAT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abc-1")}, &Raw{Bytes: []byte("xyz-1")}}, + {&Raw{Bytes: []byte("abc-2")}, &Raw{Bytes: []byte("xyz-2")}}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("ABC-1")}, &Raw{Bytes: []byte("XYZ-1")}}, + {&Raw{Bytes: []byte("ABC-2")}, &Raw{Bytes: []byte("XYZ-2")}}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abc-1ABC-1")}, &Raw{Bytes: []byte("xyz-1XYZ-1")}}, + {&Raw{Bytes: []byte("abc-2ABC-2")}, &Raw{Bytes: []byte("xyz-2XYZ-2")}}, + }, + ), + nil, + }, + { + "Invalid concat", + Instruction{ + Op: CONCAT, + Input: []*Operand{ + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), ast.ComposeDataType(ast.DataTypeMajorBool, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("abc-1")}, rawTrue}, + }, + ), + makeOperand( + false, + []ast.DataType{ + ast.ComposeDataType(ast.DataTypeMajorDynamicBytes, 0), ast.ComposeDataType(ast.DataTypeMajorBool, 0), + }, + []Tuple{ + {&Raw{Bytes: []byte("ABC-1")}, rawFalse}, + }, + ), + }, + Output: 0, + }, + makeOperand( + false, + []ast.DataType{}, + []Tuple{}, + ), + errors.ErrorCodeInvalidDataType, + }, + } + + s.run(testcases, opConcat) +} + func (s *instructionSuite) TestOpLt() { testcases := []opTestcase{ { diff --git a/core/vm/sqlvm/runtime/instructions_tmpl_data.go b/core/vm/sqlvm/runtime/instructions_tmpl_data.go index 7edc2c3e6..a55dab60a 100644 --- a/core/vm/sqlvm/runtime/instructions_tmpl_data.go +++ b/core/vm/sqlvm/runtime/instructions_tmpl_data.go @@ -773,6 +773,74 @@ var testData = &tmplData{ }, // -- end of MOD { + TestName: "OpConcat", OpFunc: "opConcat", + Cases: []*tmplTestCase{ + { + Name: "Concat bytes", + Error: "nil", OpCode: "CONCAT", + Inputs: []*tmplOp{ + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + {Major: "DynamicBytes", Minor: 0}, + }, + Data: []string{ + `{B: "abc-1", B: "xyz-1"}`, + `{B: "abc-2", B: "xyz-2"}`, + }, + }, + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + {Major: "DynamicBytes", Minor: 0}, + }, + Data: []string{ + `{B: "ABC-1", B: "XYZ-1"}`, + `{B: "ABC-2", B: "XYZ-2"}`, + }, + }, + }, + Output: &tmplOp{ + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + {Major: "DynamicBytes", Minor: 0}, + }, + Data: []string{ + `{B: "abc-1ABC-1", B: "xyz-1XYZ-1"}`, + `{B: "abc-2ABC-2", B: "xyz-2XYZ-2"}`, + }, + }, + }, + { + Name: "Invalid concat", + Error: "errors.ErrorCodeInvalidDataType", OpCode: "CONCAT", + Inputs: []*tmplOp{ + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + {Major: "Bool", Minor: 0}, + }, + Data: []string{`{B: "abc-1", T}`}, + }, + { + Im: false, + Metas: []*tmplOpMeta{ + {Major: "DynamicBytes", Minor: 0}, + {Major: "Bool", Minor: 0}, + }, + Data: []string{`{B: "ABC-1", F}`}, + }, + }, + Output: &tmplOp{}, + }, + }, + }, + // -- end of CONCAT + { TestName: "OpLt", OpFunc: "opLt", Cases: []*tmplTestCase{ { diff --git a/core/vm/sqlvm/runtime/jumptable.go b/core/vm/sqlvm/runtime/jumptable.go index aab57225d..f6cad57e7 100644 --- a/core/vm/sqlvm/runtime/jumptable.go +++ b/core/vm/sqlvm/runtime/jumptable.go @@ -2,11 +2,12 @@ package runtime var jumpTable = [256]OpFunction{ // 0x10 - ADD: opAdd, - MUL: opMul, - SUB: opSub, - DIV: opDiv, - MOD: opMod, + ADD: opAdd, + MUL: opMul, + SUB: opSub, + DIV: opDiv, + MOD: opMod, + CONCAT: opConcat, // 0x20 LT: opLt, diff --git a/core/vm/sqlvm/runtime/opcodes.go b/core/vm/sqlvm/runtime/opcodes.go index 8827f0531..12a4dfefb 100644 --- a/core/vm/sqlvm/runtime/opcodes.go +++ b/core/vm/sqlvm/runtime/opcodes.go @@ -15,6 +15,7 @@ const ( SUB DIV MOD + CONCAT ) // 0x20 range - comparison ops. |