slice: introduce common Copy helper

It's a bit more convenient to use.
This commit is contained in:
Roman Khimov 2021-07-18 16:32:10 +03:00
parent a54e3516d1
commit 19717dd9a8
14 changed files with 51 additions and 65 deletions

View file

@ -6,6 +6,7 @@ import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -35,32 +36,27 @@ func TestParseMultisigContract(t *testing.T) {
testParseMultisigContract(t, s, 1, pub) testParseMultisigContract(t, s, 1, pub)
}) })
t.Run("bad, no check multisig", func(t *testing.T) { t.Run("bad, no check multisig", func(t *testing.T) {
sBad := make([]byte, len(s)) sBad := slice.Copy(s)
copy(sBad, s)
sBad[len(sBad)-1] ^= 0xFF sBad[len(sBad)-1] ^= 0xFF
testParseMultisigContract(t, sBad, 0) testParseMultisigContract(t, sBad, 0)
}) })
t.Run("bad, invalid number of keys", func(t *testing.T) { t.Run("bad, invalid number of keys", func(t *testing.T) {
sBad := make([]byte, len(s)) sBad := slice.Copy(s)
copy(sBad, s)
sBad[len(sBad)-2] = opPush1 + 1 sBad[len(sBad)-2] = opPush1 + 1
testParseMultisigContract(t, sBad, 0) testParseMultisigContract(t, sBad, 0)
}) })
t.Run("bad, invalid first instruction", func(t *testing.T) { t.Run("bad, invalid first instruction", func(t *testing.T) {
sBad := make([]byte, len(s)) sBad := slice.Copy(s)
copy(sBad, s)
sBad[0] = 0xFF sBad[0] = 0xFF
testParseMultisigContract(t, sBad, 0) testParseMultisigContract(t, sBad, 0)
}) })
t.Run("bad, invalid public key", func(t *testing.T) { t.Run("bad, invalid public key", func(t *testing.T) {
sBad := make([]byte, len(s)) sBad := slice.Copy(s)
copy(sBad, s)
sBad[2] = 0xFF sBad[2] = 0xFF
testParseMultisigContract(t, sBad, 0) testParseMultisigContract(t, sBad, 0)
}) })
t.Run("bad, many sigs", func(t *testing.T) { t.Run("bad, many sigs", func(t *testing.T) {
sBad := make([]byte, len(s)) sBad := slice.Copy(s)
copy(sBad, s)
sBad[0] = opPush1 + 1 sBad[0] = opPush1 + 1
testParseMultisigContract(t, sBad, 0) testParseMultisigContract(t, sBad, 0)
}) })

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
) )
// HasTransaction errors. // HasTransaction errors.
@ -317,11 +318,9 @@ func (dao *Simple) GetStorageItemsWithPrefix(id int32, prefix []byte) (map[strin
saveToMap := func(k, v []byte) { saveToMap := func(k, v []byte) {
// Cut prefix and hash. // Cut prefix and hash.
// Must copy here, #1468. // Must copy here, #1468.
key := make([]byte, len(k)) key := slice.Copy(k)
copy(key, k) val := slice.Copy(v)
si := make(state.StorageItem, len(v)) siMap[string(key)] = state.StorageItem(val)
copy(si, v)
siMap[string(key)] = si
} }
dao.Seek(id, prefix, saveToMap) dao.Seek(id, prefix, saveToMap)
return siMap, nil return siMap, nil

View file

@ -31,13 +31,6 @@ func lcpMany(kv []keyValue) []byte {
return p return p
} }
// copySlice is a helper for copying slice if needed.
func copySlice(a []byte) []byte {
b := make([]byte, len(a))
copy(b, a)
return b
}
// toNibbles mangles path by splitting every byte into 2 containing low- and high- 4-byte part. // toNibbles mangles path by splitting every byte into 2 containing low- and high- 4-byte part.
func toNibbles(path []byte) []byte { func toNibbles(path []byte) []byte {
result := make([]byte, len(path)*2) result := make([]byte, len(path)*2)

View file

@ -6,6 +6,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
) )
// GetProof returns a proof that key belongs to t. // GetProof returns a proof that key belongs to t.
@ -25,11 +26,11 @@ func (t *Trie) getProof(curr Node, path []byte, proofs *[][]byte) (Node, error)
switch n := curr.(type) { switch n := curr.(type) {
case *LeafNode: case *LeafNode:
if len(path) == 0 { if len(path) == 0 {
*proofs = append(*proofs, copySlice(n.Bytes())) *proofs = append(*proofs, slice.Copy(n.Bytes()))
return n, nil return n, nil
} }
case *BranchNode: case *BranchNode:
*proofs = append(*proofs, copySlice(n.Bytes())) *proofs = append(*proofs, slice.Copy(n.Bytes()))
i, path := splitPath(path) i, path := splitPath(path)
r, err := t.getProof(n.Children[i], path, proofs) r, err := t.getProof(n.Children[i], path, proofs)
if err != nil { if err != nil {
@ -39,7 +40,7 @@ func (t *Trie) getProof(curr Node, path []byte, proofs *[][]byte) (Node, error)
return n, nil return n, nil
case *ExtensionNode: case *ExtensionNode:
if bytes.HasPrefix(path, n.key) { if bytes.HasPrefix(path, n.key) {
*proofs = append(*proofs, copySlice(n.Bytes())) *proofs = append(*proofs, slice.Copy(n.Bytes()))
r, err := t.getProof(n.next, path[len(n.key):], proofs) r, err := t.getProof(n.next, path[len(n.key):], proofs)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
) )
// Trie is an MPT trie storing all key-value pairs. // Trie is an MPT trie storing all key-value pairs.
@ -64,7 +65,7 @@ func (t *Trie) getWithPath(curr Node, path []byte) (Node, []byte, error) {
switch n := curr.(type) { switch n := curr.(type) {
case *LeafNode: case *LeafNode:
if len(path) == 0 { if len(path) == 0 {
return curr, copySlice(n.value), nil return curr, slice.Copy(n.value), nil
} }
case *BranchNode: case *BranchNode:
i, path := splitPath(path) i, path := splitPath(path)
@ -179,7 +180,7 @@ func (t *Trie) putIntoExtension(curr *ExtensionNode, path []byte, val Node) (Nod
t.addRef(b.Hash(), b.bytes) t.addRef(b.Hash(), b.bytes)
if lp > 0 { if lp > 0 {
e := NewExtensionNode(copySlice(pref), b) e := NewExtensionNode(slice.Copy(pref), b)
t.addRef(e.Hash(), e.bytes) t.addRef(e.Hash(), e.bytes)
return e, nil return e, nil
} }

View file

@ -27,6 +27,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
@ -135,9 +136,7 @@ func newOracle() *Oracle {
// GetOracleResponseScript returns script for transaction with oracle response. // GetOracleResponseScript returns script for transaction with oracle response.
func (o *Oracle) GetOracleResponseScript() []byte { func (o *Oracle) GetOracleResponseScript() []byte {
b := make([]byte, len(o.oracleScript)) return slice.Copy(o.oracleScript)
copy(b, o.oracleScript)
return b
} }
// OnPersist implements Contract interface. // OnPersist implements Contract interface.

View file

@ -4,6 +4,7 @@ import (
"os" "os"
"github.com/dgraph-io/badger/v2" "github.com/dgraph-io/badger/v2"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
) )
// BadgerDBOptions configuration for BadgerDB. // BadgerDBOptions configuration for BadgerDB.
@ -32,10 +33,8 @@ func (b *BadgerDBBatch) Delete(key []byte) {
// Put implements the Batch interface. // Put implements the Batch interface.
func (b *BadgerDBBatch) Put(key, value []byte) { func (b *BadgerDBBatch) Put(key, value []byte) {
keycopy := make([]byte, len(key)) keycopy := slice.Copy(key)
copy(keycopy, key) valuecopy := slice.Copy(value)
valuecopy := make([]byte, len(value))
copy(valuecopy, value)
err := b.batch.Set(keycopy, valuecopy) err := b.batch.Set(keycopy, valuecopy)
if err != nil { if err != nil {
panic(err) panic(err)

View file

@ -6,6 +6,7 @@ import (
"os" "os"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/syndtr/goleveldb/leveldb/util" "github.com/syndtr/goleveldb/leveldb/util"
"go.etcd.io/bbolt" "go.etcd.io/bbolt"
) )
@ -63,9 +64,7 @@ func (s *BoltDBStore) Get(key []byte) (val []byte, err error) {
val = b.Get(key) val = b.Get(key)
// Value from Get is only valid for the lifetime of transaction, #1482 // Value from Get is only valid for the lifetime of transaction, #1482
if val != nil { if val != nil {
var valcopy = make([]byte, len(val)) val = slice.Copy(val)
copy(valcopy, val)
val = valcopy
} }
return nil return nil
}) })

View file

@ -3,6 +3,8 @@ package storage
import ( import (
"strings" "strings"
"sync" "sync"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
) )
// MemoryStore is an in-memory implementation of a Store, mainly // MemoryStore is an in-memory implementation of a Store, mainly
@ -57,8 +59,7 @@ func (s *MemoryStore) put(key string, value []byte) {
// Put implements the Store interface. Never returns an error. // Put implements the Store interface. Never returns an error.
func (s *MemoryStore) Put(key, value []byte) error { func (s *MemoryStore) Put(key, value []byte) error {
newKey := string(key) newKey := string(key)
vcopy := make([]byte, len(value)) vcopy := slice.Copy(value)
copy(vcopy, value)
s.mut.Lock() s.mut.Lock()
s.put(newKey, vcopy) s.put(newKey, vcopy)
s.mut.Unlock() s.mut.Unlock()

View file

@ -5,6 +5,7 @@ import (
"runtime" "runtime"
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -55,10 +56,8 @@ func testStorePutBatch(t *testing.T, s Store) {
batch = s.Batch() batch = s.Batch()
) )
// Test that key and value are copied when batching. // Test that key and value are copied when batching.
keycopy := make([]byte, len(key)) keycopy := slice.Copy(key)
copy(keycopy, key) valuecopy := slice.Copy(value)
valuecopy := make([]byte, len(value))
copy(valuecopy, value)
batch.Put(keycopy, valuecopy) batch.Put(keycopy, valuecopy)
copy(valuecopy, key) copy(valuecopy, key)

View file

@ -14,6 +14,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -211,11 +212,8 @@ func (o *Oracle) Run() {
// UpdateNativeContract updates native oracle contract info for tx verification. // UpdateNativeContract updates native oracle contract info for tx verification.
func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) { func (o *Oracle) UpdateNativeContract(script, resp []byte, h util.Uint160, verifyOffset int) {
o.oracleScript = make([]byte, len(script)) o.oracleScript = slice.Copy(script)
copy(o.oracleScript, script) o.oracleResponse = slice.Copy(resp)
o.oracleResponse = make([]byte, len(resp))
copy(o.oracleResponse, resp)
o.oracleHash = h o.oracleHash = h
o.verifyOffset = verifyOffset o.verifyOffset = verifyOffset

View file

@ -21,3 +21,10 @@ func reverse(dst []byte, src []byte) {
dst[i], dst[j] = src[j], src[i] dst[i], dst[j] = src[j], src[i]
} }
} }
// Copy copies the byte slice into new slice (make/copy).
func Copy(b []byte) []byte {
d := make([]byte, len(b))
copy(d, b)
return d
}

View file

@ -30,8 +30,8 @@ var testCases = []struct {
func TestCopyReverse(t *testing.T) { func TestCopyReverse(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
arg := make([]byte, len(tc.arr)) arg := Copy(tc.arr)
copy(arg, tc.arr) require.Equal(t, tc.arr, arg)
have := CopyReverse(arg) have := CopyReverse(arg)
require.Equal(t, tc.rev, have) require.Equal(t, tc.rev, have)
@ -44,5 +44,8 @@ func TestCopyReverse(t *testing.T) {
Reverse(arg) Reverse(arg)
require.Equal(t, tc.rev, arg) require.Equal(t, tc.rev, arg)
if len(tc.arr) > 1 {
require.NotEqual(t, tc.arr, arg)
}
} }
} }

View file

@ -15,6 +15,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
) )
const ( const (
@ -188,9 +189,7 @@ func convertPrimitive(item Item, typ Type) (Item, error) {
return nil, err return nil, err
} }
if typ == BufferT { if typ == BufferT {
newb := make([]byte, len(b)) return NewBuffer(slice.Copy(b)), nil
copy(newb, b)
return NewBuffer(newb), nil
} }
// ByteArray can't really be changed, so it's OK to reuse `b`. // ByteArray can't really be changed, so it's OK to reuse `b`.
return NewByteArray(b), nil return NewByteArray(b), nil
@ -633,9 +632,7 @@ func (i *ByteArray) Equals(s Item) bool {
// Dup implements Item interface. // Dup implements Item interface.
func (i *ByteArray) Dup() Item { func (i *ByteArray) Dup() Item {
a := make([]byte, len(i.value)) return &ByteArray{slice.Copy(i.value)}
copy(a, i.value)
return &ByteArray{a}
} }
// Type implements Item interface. // Type implements Item interface.
@ -1110,9 +1107,7 @@ func (i *Buffer) Convert(typ Type) (Item, error) {
case BufferT: case BufferT:
return i, nil return i, nil
case ByteArrayT: case ByteArrayT:
val := make([]byte, len(i.value)) return NewByteArray(slice.Copy(i.value)), nil
copy(val, i.value)
return NewByteArray(val), nil
case IntegerT: case IntegerT:
if len(i.value) > MaxBigIntegerSizeBits/8 { if len(i.value) > MaxBigIntegerSizeBits/8 {
return nil, errTooBigInteger return nil, errTooBigInteger
@ -1173,13 +1168,9 @@ func deepCopy(item Item, seen map[Item]Item) Item {
} }
return NewBigInteger(bi) return NewBigInteger(bi)
case *ByteArray: case *ByteArray:
val := make([]byte, len(it.value)) return NewByteArray(slice.Copy(it.value))
copy(val, it.value)
return NewByteArray(val)
case *Buffer: case *Buffer:
val := make([]byte, len(it.value)) return NewBuffer(slice.Copy(it.value))
copy(val, it.value)
return NewBuffer(val)
case *Bool: case *Bool:
return NewBool(it.value) return NewBool(it.value)
case *Pointer: case *Pointer: