stackitem: don't copy existing slices for TryBytes
Most often we only need to read them and it doesn't require copying. Make an explicit copy (and copy only things we need!) where needed. After the recent neo-vm tests update our vm package testing time jumped to ~12s, with this change it's now more like ~8s.
This commit is contained in:
parent
77ea3d361b
commit
324f4c265b
2 changed files with 22 additions and 13 deletions
|
@ -33,7 +33,8 @@ type Item interface {
|
||||||
Dup() Item
|
Dup() Item
|
||||||
// TryBool converts Item to a boolean value.
|
// TryBool converts Item to a boolean value.
|
||||||
TryBool() (bool, error)
|
TryBool() (bool, error)
|
||||||
// TryBytes converts Item to a byte slice.
|
// TryBytes converts Item to a byte slice. If the underlying type is a
|
||||||
|
// byte slice, it's returned as is without copying.
|
||||||
TryBytes() ([]byte, error)
|
TryBytes() ([]byte, error)
|
||||||
// TryInteger converts Item to an integer.
|
// TryInteger converts Item to an integer.
|
||||||
TryInteger() (*big.Int, error)
|
TryInteger() (*big.Int, error)
|
||||||
|
@ -151,8 +152,11 @@ func convertPrimitive(item Item, typ Type) (Item, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if typ == BufferT {
|
if typ == BufferT {
|
||||||
return NewBuffer(b), nil
|
newb := make([]byte, len(b))
|
||||||
|
copy(newb, b)
|
||||||
|
return NewBuffer(newb), nil
|
||||||
}
|
}
|
||||||
|
// ByteArray can't really be changed, so it's OK to reuse `b`.
|
||||||
return NewByteArray(b), nil
|
return NewByteArray(b), nil
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
b, err := item.TryBool()
|
b, err := item.TryBool()
|
||||||
|
@ -519,9 +523,7 @@ func (i *ByteArray) TryBool() (bool, error) {
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i *ByteArray) TryBytes() ([]byte, error) {
|
func (i *ByteArray) TryBytes() ([]byte, error) {
|
||||||
val := make([]byte, len(i.value))
|
return i.value, nil
|
||||||
copy(val, i.value)
|
|
||||||
return val, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
|
@ -982,9 +984,7 @@ func (i *Buffer) TryBool() (bool, error) {
|
||||||
|
|
||||||
// TryBytes implements Item interface.
|
// TryBytes implements Item interface.
|
||||||
func (i *Buffer) TryBytes() ([]byte, error) {
|
func (i *Buffer) TryBytes() ([]byte, error) {
|
||||||
val := make([]byte, len(i.value))
|
return i.value, nil
|
||||||
copy(val, i.value)
|
|
||||||
return val, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements Item interface.
|
// TryInteger implements Item interface.
|
||||||
|
|
19
pkg/vm/vm.go
19
pkg/vm/vm.go
|
@ -663,10 +663,13 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
case opcode.CAT:
|
case opcode.CAT:
|
||||||
b := v.estack.Pop().Bytes()
|
b := v.estack.Pop().Bytes()
|
||||||
a := v.estack.Pop().Bytes()
|
a := v.estack.Pop().Bytes()
|
||||||
if l := len(a) + len(b); l > stackitem.MaxSize {
|
l := len(a) + len(b)
|
||||||
|
if l > stackitem.MaxSize {
|
||||||
panic(fmt.Sprintf("too big item: %d", l))
|
panic(fmt.Sprintf("too big item: %d", l))
|
||||||
}
|
}
|
||||||
ab := append(a, b...)
|
ab := make([]byte, l)
|
||||||
|
copy(ab, a)
|
||||||
|
copy(ab[len(a):], b)
|
||||||
v.estack.PushVal(stackitem.NewBuffer(ab))
|
v.estack.PushVal(stackitem.NewBuffer(ab))
|
||||||
|
|
||||||
case opcode.SUBSTR:
|
case opcode.SUBSTR:
|
||||||
|
@ -683,7 +686,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if last > len(s) {
|
if last > len(s) {
|
||||||
panic("invalid offset")
|
panic("invalid offset")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(stackitem.NewBuffer(s[o:last]))
|
res := make([]byte, l)
|
||||||
|
copy(res, s[o:last])
|
||||||
|
v.estack.PushVal(stackitem.NewBuffer(res))
|
||||||
|
|
||||||
case opcode.LEFT:
|
case opcode.LEFT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -694,7 +699,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if t := len(s); l > t {
|
if t := len(s); l > t {
|
||||||
panic("size is too big")
|
panic("size is too big")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(stackitem.NewBuffer(s[:l]))
|
res := make([]byte, l)
|
||||||
|
copy(res, s[:l])
|
||||||
|
v.estack.PushVal(stackitem.NewBuffer(res))
|
||||||
|
|
||||||
case opcode.RIGHT:
|
case opcode.RIGHT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -702,7 +709,9 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("negative length")
|
panic("negative length")
|
||||||
}
|
}
|
||||||
s := v.estack.Pop().Bytes()
|
s := v.estack.Pop().Bytes()
|
||||||
v.estack.PushVal(stackitem.NewBuffer(s[len(s)-l:]))
|
res := make([]byte, l)
|
||||||
|
copy(res, s[len(s)-l:])
|
||||||
|
v.estack.PushVal(stackitem.NewBuffer(res))
|
||||||
|
|
||||||
case opcode.DEPTH:
|
case opcode.DEPTH:
|
||||||
v.estack.PushVal(v.estack.Len())
|
v.estack.PushVal(v.estack.Len())
|
||||||
|
|
Loading…
Reference in a new issue