2020-06-03 12:55:06 +00:00
|
|
|
package stackitem
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2020-07-06 11:35:55 +00:00
|
|
|
"fmt"
|
2020-06-03 12:55:06 +00:00
|
|
|
"math/big"
|
|
|
|
|
2020-06-04 18:11:27 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
|
2020-06-03 12:55:06 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
2022-11-19 20:13:59 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-06-03 12:55:06 +00:00
|
|
|
)
|
|
|
|
|
2021-07-17 19:21:33 +00:00
|
|
|
// MaxDeserialized is the maximum number one deserialized item can contain
|
|
|
|
// (including itself).
|
|
|
|
const MaxDeserialized = 2048
|
|
|
|
|
2023-11-22 10:52:41 +00:00
|
|
|
// MaxSerialized is the maximum number one serialized item can contain
|
|
|
|
// (including itself).
|
|
|
|
const MaxSerialized = MaxDeserialized
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// typicalNumOfItems is the number of items covering most serialization needs.
|
|
|
|
// It's a hint used for map creation, so it does not limit anything, it's just
|
2021-11-30 16:51:52 +00:00
|
|
|
// a microoptimization to avoid excessive reallocations. Most of the serialized
|
|
|
|
// items are structs, so there is at least one of them.
|
|
|
|
const typicalNumOfItems = 4
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// ErrRecursive is returned upon an attempt to serialize some recursive stack item
|
|
|
|
// (like an array including an item with the reference to the same array).
|
2021-07-06 21:18:00 +00:00
|
|
|
var ErrRecursive = errors.New("recursive item")
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// ErrUnserializable is returned upon an attempt to serialize some item that can't
|
2021-07-06 21:18:00 +00:00
|
|
|
// be serialized (like Interop item or Pointer).
|
|
|
|
var ErrUnserializable = errors.New("unserializable")
|
|
|
|
|
2022-05-31 17:10:20 +00:00
|
|
|
// SerializationContext is a serialization context.
|
|
|
|
type SerializationContext struct {
|
2021-07-09 12:26:56 +00:00
|
|
|
uv [9]byte
|
|
|
|
data []byte
|
2021-07-06 16:32:52 +00:00
|
|
|
allowInvalid bool
|
2023-11-22 10:52:41 +00:00
|
|
|
limit int
|
2021-07-09 13:09:33 +00:00
|
|
|
seen map[Item]sliceNoPointer
|
2021-07-06 16:32:52 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 19:21:33 +00:00
|
|
|
// deserContext is an internal deserialization context.
|
|
|
|
type deserContext struct {
|
|
|
|
*io.BinReader
|
|
|
|
allowInvalid bool
|
|
|
|
limit int
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Serialize encodes the given Item into a byte slice.
|
2021-07-06 16:56:23 +00:00
|
|
|
func Serialize(item Item) ([]byte, error) {
|
2023-11-22 10:52:41 +00:00
|
|
|
return SerializeLimited(item, MaxSerialized)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SerializeLimited encodes the given Item into a byte slice using custom
|
|
|
|
// limit to restrict the maximum serialized number of elements.
|
|
|
|
func SerializeLimited(item Item, limit int) ([]byte, error) {
|
2022-05-31 17:10:20 +00:00
|
|
|
sc := SerializationContext{
|
2021-07-06 16:32:52 +00:00
|
|
|
allowInvalid: false,
|
2023-11-22 10:52:41 +00:00
|
|
|
limit: MaxSerialized,
|
2021-11-30 16:51:52 +00:00
|
|
|
seen: make(map[Item]sliceNoPointer, typicalNumOfItems),
|
2021-07-06 16:32:52 +00:00
|
|
|
}
|
2023-11-22 10:52:41 +00:00
|
|
|
if limit > 0 {
|
|
|
|
sc.limit = limit
|
|
|
|
}
|
2021-07-09 12:26:56 +00:00
|
|
|
err := sc.serialize(item)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
2021-07-09 12:26:56 +00:00
|
|
|
return sc.data, nil
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// EncodeBinary encodes the given Item into the given BinWriter. It's
|
|
|
|
// similar to io.Serializable's EncodeBinary but works with Item
|
2020-06-03 12:55:06 +00:00
|
|
|
// interface.
|
2021-07-06 16:56:23 +00:00
|
|
|
func EncodeBinary(item Item, w *io.BinWriter) {
|
2021-07-09 12:26:56 +00:00
|
|
|
data, err := Serialize(item)
|
|
|
|
if err != nil {
|
|
|
|
w.Err = err
|
|
|
|
return
|
2021-07-06 16:32:52 +00:00
|
|
|
}
|
2021-07-09 12:26:56 +00:00
|
|
|
w.WriteBytes(data)
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// EncodeBinaryProtected encodes the given Item into the given BinWriter. It's
|
2021-07-06 16:56:23 +00:00
|
|
|
// similar to EncodeBinary but allows to encode interop items (only type,
|
2022-04-20 18:30:09 +00:00
|
|
|
// value is lost) and doesn't return any errors in the w. Instead, if an error
|
|
|
|
// (like recursive array) is encountered, it just writes the special InvalidT
|
|
|
|
// type of an element to the w.
|
2021-07-06 16:56:23 +00:00
|
|
|
func EncodeBinaryProtected(item Item, w *io.BinWriter) {
|
2022-05-31 17:10:20 +00:00
|
|
|
sc := SerializationContext{
|
2021-07-06 16:32:52 +00:00
|
|
|
allowInvalid: true,
|
2023-11-22 10:52:41 +00:00
|
|
|
limit: MaxSerialized,
|
2021-11-30 16:51:52 +00:00
|
|
|
seen: make(map[Item]sliceNoPointer, typicalNumOfItems),
|
2021-07-06 16:32:52 +00:00
|
|
|
}
|
2021-07-09 12:26:56 +00:00
|
|
|
err := sc.serialize(item)
|
|
|
|
if err != nil {
|
2020-12-18 09:28:01 +00:00
|
|
|
w.WriteBytes([]byte{byte(InvalidT)})
|
|
|
|
return
|
|
|
|
}
|
2021-07-09 12:26:56 +00:00
|
|
|
w.WriteBytes(sc.data)
|
2020-12-18 09:28:01 +00:00
|
|
|
}
|
|
|
|
|
2022-05-31 17:10:20 +00:00
|
|
|
func (w *SerializationContext) writeArray(item Item, arr []Item, start int) error {
|
2021-11-30 19:30:44 +00:00
|
|
|
w.seen[item] = sliceNoPointer{}
|
2023-11-22 10:52:41 +00:00
|
|
|
limit := w.limit
|
2021-11-30 19:30:44 +00:00
|
|
|
w.appendVarUint(uint64(len(arr)))
|
|
|
|
for i := range arr {
|
|
|
|
if err := w.serialize(arr[i]); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2023-11-22 10:52:41 +00:00
|
|
|
w.seen[item] = sliceNoPointer{start, len(w.data), limit - w.limit + 1} // number of items including the array itself.
|
2021-11-30 19:30:44 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-31 17:10:20 +00:00
|
|
|
// NewSerializationContext returns reusable stack item serialization context.
|
|
|
|
func NewSerializationContext() *SerializationContext {
|
|
|
|
return &SerializationContext{
|
2023-11-22 10:52:41 +00:00
|
|
|
limit: MaxSerialized,
|
|
|
|
seen: make(map[Item]sliceNoPointer, typicalNumOfItems),
|
2022-05-31 17:10:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Serialize returns flat slice of bytes with the given item. The process can be protected
|
|
|
|
// from bad elements if appropriate flag is given (otherwise an error is returned on
|
|
|
|
// encountering any of them). The buffer returned is only valid until the call to Serialize.
|
2023-11-22 10:52:41 +00:00
|
|
|
// The number of serialized items is restricted with MaxSerialized.
|
2022-05-31 17:10:20 +00:00
|
|
|
func (w *SerializationContext) Serialize(item Item, protected bool) ([]byte, error) {
|
|
|
|
w.allowInvalid = protected
|
2023-11-22 10:52:41 +00:00
|
|
|
w.limit = MaxSerialized
|
2022-05-31 17:10:20 +00:00
|
|
|
if w.data != nil {
|
|
|
|
w.data = w.data[:0]
|
|
|
|
}
|
*: use clear() to clear maps
Supposedly more efficient since we can avoid some memory management dances.
Memory pool agrees:
goos: linux
goarch: amd64
pkg: github.com/nspcc-dev/neo-go/pkg/core/mempool
cpu: AMD Ryzen 7 PRO 7840U w/ Radeon 780M Graphics
│ pool.old │ pool.new │
│ sec/op │ sec/op vs base │
Pool/one,_incr_fee-16 12.44m ± 1% 12.51m ± 1% +0.55% (p=0.029 n=10)
Pool/many,_same_fee-16 4.960m ± 2% 3.100m ± 1% -37.50% (p=0.000 n=10)
Pool/many,_incr_fee-16 16.03m ± 2% 14.11m ± 1% -12.00% (p=0.000 n=10)
Pool/one,_same_fee-16 1.742m ± 1%
geomean 9.964m 5.556m -17.92%
│ pool.old │ pool.new │
│ B/op │ B/op vs base │
Pool/one,_incr_fee-16 8.117Ki ± 120% 7.101Ki ± 128% -12.52% (p=0.022 n=10)
Pool/many,_same_fee-16 3941.2Ki ± 0% 805.4Ki ± 0% -79.56% (p=0.000 n=10)
Pool/many,_incr_fee-16 3936.2Ki ± 0% 829.8Ki ± 0% -78.92% (p=0.000 n=10)
Pool/one,_same_fee-16 12.98Ki ± 10%
geomean 501.2Ki 88.59Ki -66.47%
│ pool.old │ pool.new │
│ allocs/op │ allocs/op vs base │
Pool/one,_incr_fee-16 28.00 ± 21% 24.00 ± 21% -14.29% (p=0.002 n=10)
Pool/many,_same_fee-16 40.38k ± 0% 40.03k ± 0% -0.86% (p=0.000 n=10)
Pool/many,_incr_fee-16 40.38k ± 0% 40.04k ± 0% -0.85% (p=0.000 n=10)
Pool/one,_same_fee-16 23.00 ± 4%
geomean 3.574k 969.8 -5.55%
Signed-off-by: Roman Khimov <roman@nspcc.ru>
2024-08-23 19:21:20 +00:00
|
|
|
clear(w.seen)
|
2022-05-31 17:10:20 +00:00
|
|
|
err := w.serialize(item)
|
|
|
|
if err != nil && protected {
|
|
|
|
if w.data == nil {
|
|
|
|
w.data = make([]byte, 0, 1)
|
|
|
|
}
|
|
|
|
w.data = append(w.data[:0], byte(InvalidT))
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
return w.data, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *SerializationContext) serialize(item Item) error {
|
2021-07-09 13:09:33 +00:00
|
|
|
if v, ok := w.seen[item]; ok {
|
|
|
|
if v.start == v.end {
|
|
|
|
return ErrRecursive
|
|
|
|
}
|
|
|
|
if len(w.data)+v.end-v.start > MaxSize {
|
|
|
|
return ErrTooBig
|
|
|
|
}
|
2023-11-22 10:52:41 +00:00
|
|
|
w.limit -= v.itemsCount
|
|
|
|
if w.limit < 0 {
|
|
|
|
return errTooBigElements
|
|
|
|
}
|
2021-07-09 13:09:33 +00:00
|
|
|
w.data = append(w.data, w.data[v.start:v.end]...)
|
|
|
|
return nil
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
2023-11-22 10:52:41 +00:00
|
|
|
w.limit--
|
|
|
|
if w.limit < 0 {
|
|
|
|
return errTooBigElements
|
|
|
|
}
|
2021-07-09 13:09:33 +00:00
|
|
|
start := len(w.data)
|
2020-06-03 12:55:06 +00:00
|
|
|
switch t := item.(type) {
|
|
|
|
case *ByteArray:
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(ByteArrayT))
|
2021-08-20 19:37:06 +00:00
|
|
|
w.appendVarUint(uint64(len(*t)))
|
|
|
|
w.data = append(w.data, *t...)
|
2020-06-03 12:55:06 +00:00
|
|
|
case *Buffer:
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(BufferT))
|
2021-08-20 19:37:06 +00:00
|
|
|
w.appendVarUint(uint64(len(*t)))
|
|
|
|
w.data = append(w.data, *t...)
|
2021-08-03 16:37:06 +00:00
|
|
|
case Bool:
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(BooleanT))
|
2021-08-03 16:37:06 +00:00
|
|
|
if t {
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, 1)
|
|
|
|
} else {
|
|
|
|
w.data = append(w.data, 0)
|
|
|
|
}
|
2020-06-03 12:55:06 +00:00
|
|
|
case *BigInteger:
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(IntegerT))
|
2021-08-03 13:29:21 +00:00
|
|
|
ln := len(w.data)
|
|
|
|
w.data = append(w.data, 0)
|
2021-08-20 19:37:06 +00:00
|
|
|
data := bigint.ToPreallocatedBytes((*big.Int)(t), w.data[len(w.data):])
|
2021-08-03 13:29:21 +00:00
|
|
|
w.data[ln] = byte(len(data))
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, data...)
|
2020-06-03 12:55:06 +00:00
|
|
|
case *Interop:
|
2021-07-06 16:32:52 +00:00
|
|
|
if w.allowInvalid {
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(InteropT))
|
2021-07-06 16:32:52 +00:00
|
|
|
} else {
|
2021-07-09 12:26:56 +00:00
|
|
|
return fmt.Errorf("%w: Interop", ErrUnserializable)
|
2020-12-18 09:28:01 +00:00
|
|
|
}
|
2022-11-19 20:13:59 +00:00
|
|
|
case *Pointer:
|
|
|
|
if w.allowInvalid {
|
|
|
|
w.data = append(w.data, byte(PointerT))
|
|
|
|
w.appendVarUint(uint64(t.pos))
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("%w: Pointer", ErrUnserializable)
|
|
|
|
}
|
2021-11-30 19:30:44 +00:00
|
|
|
case *Array:
|
|
|
|
w.data = append(w.data, byte(ArrayT))
|
|
|
|
if err := w.writeArray(item, t.value, start); err != nil {
|
|
|
|
return err
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
2021-11-30 19:30:44 +00:00
|
|
|
case *Struct:
|
|
|
|
w.data = append(w.data, byte(StructT))
|
|
|
|
if err := w.writeArray(item, t.value, start); err != nil {
|
|
|
|
return err
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
|
|
|
case *Map:
|
2021-07-09 13:09:33 +00:00
|
|
|
w.seen[item] = sliceNoPointer{}
|
2023-11-22 10:52:41 +00:00
|
|
|
limit := w.limit
|
2020-06-03 12:55:06 +00:00
|
|
|
|
2021-11-30 19:37:56 +00:00
|
|
|
elems := t.value
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(MapT))
|
|
|
|
w.appendVarUint(uint64(len(elems)))
|
|
|
|
for i := range elems {
|
|
|
|
if err := w.serialize(elems[i].Key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := w.serialize(elems[i].Value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
2023-11-22 10:52:41 +00:00
|
|
|
w.seen[item] = sliceNoPointer{start, len(w.data), limit - w.limit + 1} // number of items including Map itself.
|
2020-06-18 20:22:15 +00:00
|
|
|
case Null:
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(AnyT))
|
2021-07-06 16:34:02 +00:00
|
|
|
case nil:
|
|
|
|
if w.allowInvalid {
|
2021-07-09 12:26:56 +00:00
|
|
|
w.data = append(w.data, byte(InvalidT))
|
2021-07-06 16:34:02 +00:00
|
|
|
} else {
|
2021-07-09 12:26:56 +00:00
|
|
|
return fmt.Errorf("%w: nil", ErrUnserializable)
|
2021-07-06 16:34:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-09 12:26:56 +00:00
|
|
|
if len(w.data) > MaxSize {
|
|
|
|
return errTooBigSize
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
2021-07-09 12:26:56 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-05-31 17:10:20 +00:00
|
|
|
func (w *SerializationContext) appendVarUint(val uint64) {
|
2021-07-09 12:26:56 +00:00
|
|
|
n := io.PutVarUint(w.uv[:], val)
|
|
|
|
w.data = append(w.data, w.uv[:n]...)
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// Deserialize decodes the Item from the given byte slice.
|
2021-07-06 16:56:23 +00:00
|
|
|
func Deserialize(data []byte) (Item, error) {
|
2020-06-03 12:55:06 +00:00
|
|
|
r := io.NewBinReaderFromBuf(data)
|
2021-07-06 16:56:23 +00:00
|
|
|
item := DecodeBinary(r)
|
2020-06-03 12:55:06 +00:00
|
|
|
if r.Err != nil {
|
|
|
|
return nil, r.Err
|
|
|
|
}
|
|
|
|
return item, nil
|
|
|
|
}
|
|
|
|
|
2023-02-07 08:36:25 +00:00
|
|
|
// DeserializeLimited returns Item deserialized from the given byte slice. limit
|
|
|
|
// restricts the maximum number of items deserialized item can contain (including
|
|
|
|
// itself). The default limit of MaxDeserialized is used if non-positive limit is
|
|
|
|
// specified.
|
|
|
|
func DeserializeLimited(data []byte, limit int) (Item, error) {
|
|
|
|
r := io.NewBinReaderFromBuf(data)
|
|
|
|
dc := deserContext{
|
|
|
|
BinReader: r,
|
|
|
|
allowInvalid: false,
|
|
|
|
limit: MaxDeserialized,
|
|
|
|
}
|
|
|
|
if limit > 0 {
|
|
|
|
dc.limit = limit
|
|
|
|
}
|
|
|
|
item := dc.decodeBinary()
|
|
|
|
if r.Err != nil {
|
|
|
|
return nil, r.Err
|
|
|
|
}
|
|
|
|
return item, nil
|
|
|
|
}
|
|
|
|
|
2022-04-20 18:30:09 +00:00
|
|
|
// DecodeBinary decodes the previously serialized Item from the given
|
|
|
|
// reader. It's similar to the io.Serializable's DecodeBinary() but implemented
|
2020-06-03 12:55:06 +00:00
|
|
|
// as a function because Item itself is an interface. Caveat: always check
|
|
|
|
// reader's error value before using the returned Item.
|
2021-07-06 16:56:23 +00:00
|
|
|
func DecodeBinary(r *io.BinReader) Item {
|
2021-07-17 19:21:33 +00:00
|
|
|
dc := deserContext{
|
|
|
|
BinReader: r,
|
|
|
|
allowInvalid: false,
|
|
|
|
limit: MaxDeserialized,
|
|
|
|
}
|
|
|
|
return dc.decodeBinary()
|
2020-12-18 09:28:01 +00:00
|
|
|
}
|
|
|
|
|
2021-07-06 16:56:23 +00:00
|
|
|
// DecodeBinaryProtected is similar to DecodeBinary but allows Interop and
|
|
|
|
// Invalid values to be present (making it symmetric to EncodeBinaryProtected).
|
|
|
|
func DecodeBinaryProtected(r *io.BinReader) Item {
|
2021-07-17 19:21:33 +00:00
|
|
|
dc := deserContext{
|
|
|
|
BinReader: r,
|
|
|
|
allowInvalid: true,
|
|
|
|
limit: MaxDeserialized,
|
|
|
|
}
|
|
|
|
return dc.decodeBinary()
|
2020-12-18 09:28:01 +00:00
|
|
|
}
|
|
|
|
|
2021-07-17 19:21:33 +00:00
|
|
|
func (r *deserContext) decodeBinary() Item {
|
2020-06-03 12:55:06 +00:00
|
|
|
var t = Type(r.ReadB())
|
|
|
|
if r.Err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-07-17 19:21:33 +00:00
|
|
|
r.limit--
|
|
|
|
if r.limit < 0 {
|
|
|
|
r.Err = errTooBigElements
|
|
|
|
return nil
|
|
|
|
}
|
2020-06-03 12:55:06 +00:00
|
|
|
switch t {
|
2020-07-06 11:35:55 +00:00
|
|
|
case ByteArrayT, BufferT:
|
2020-10-07 20:08:20 +00:00
|
|
|
data := r.ReadVarBytes(MaxSize)
|
2021-06-28 15:07:25 +00:00
|
|
|
if t == ByteArrayT {
|
|
|
|
return NewByteArray(data)
|
|
|
|
}
|
|
|
|
return NewBuffer(data)
|
2020-06-03 12:55:06 +00:00
|
|
|
case BooleanT:
|
|
|
|
var b = r.ReadBool()
|
|
|
|
return NewBool(b)
|
|
|
|
case IntegerT:
|
2020-10-07 20:07:10 +00:00
|
|
|
data := r.ReadVarBytes(bigint.MaxBytesLen)
|
2020-06-04 18:11:27 +00:00
|
|
|
num := bigint.FromBytes(data)
|
2020-06-03 12:55:06 +00:00
|
|
|
return NewBigInteger(num)
|
|
|
|
case ArrayT, StructT:
|
|
|
|
size := int(r.ReadVarUint())
|
2023-02-07 08:39:17 +00:00
|
|
|
if size > r.limit {
|
2021-07-19 10:35:48 +00:00
|
|
|
r.Err = errTooBigElements
|
2021-07-16 09:28:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-06-03 12:55:06 +00:00
|
|
|
arr := make([]Item, size)
|
2024-08-30 18:41:02 +00:00
|
|
|
for i := range size {
|
2021-07-17 19:21:33 +00:00
|
|
|
arr[i] = r.decodeBinary()
|
2020-06-03 12:55:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if t == ArrayT {
|
|
|
|
return NewArray(arr)
|
|
|
|
}
|
|
|
|
return NewStruct(arr)
|
|
|
|
case MapT:
|
|
|
|
size := int(r.ReadVarUint())
|
2023-02-07 08:39:17 +00:00
|
|
|
if size > r.limit/2 {
|
2021-07-19 10:35:48 +00:00
|
|
|
r.Err = errTooBigElements
|
2021-07-16 09:28:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
2020-06-03 12:55:06 +00:00
|
|
|
m := NewMap()
|
2024-08-30 18:41:02 +00:00
|
|
|
for range size {
|
2021-07-17 19:21:33 +00:00
|
|
|
key := r.decodeBinary()
|
|
|
|
value := r.decodeBinary()
|
2020-06-03 12:55:06 +00:00
|
|
|
if r.Err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
m.Add(key, value)
|
|
|
|
}
|
|
|
|
return m
|
2020-06-18 20:22:15 +00:00
|
|
|
case AnyT:
|
|
|
|
return Null{}
|
2020-12-18 09:28:01 +00:00
|
|
|
case InteropT:
|
2021-07-17 19:21:33 +00:00
|
|
|
if r.allowInvalid {
|
2020-12-18 09:28:01 +00:00
|
|
|
return NewInterop(nil)
|
|
|
|
}
|
|
|
|
fallthrough
|
2022-11-19 20:13:59 +00:00
|
|
|
case PointerT:
|
|
|
|
if r.allowInvalid {
|
|
|
|
pos := int(r.ReadVarUint())
|
|
|
|
return NewPointerWithHash(pos, nil, util.Uint160{})
|
|
|
|
}
|
|
|
|
fallthrough
|
2020-06-03 12:55:06 +00:00
|
|
|
default:
|
2021-07-17 19:21:33 +00:00
|
|
|
if t == InvalidT && r.allowInvalid {
|
2020-12-18 09:28:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-07-06 21:18:00 +00:00
|
|
|
r.Err = fmt.Errorf("%w: %v", ErrInvalidType, t)
|
2020-06-03 12:55:06 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
2021-07-17 15:37:33 +00:00
|
|
|
|
|
|
|
// SerializeConvertible serializes Convertible into a slice of bytes.
|
|
|
|
func SerializeConvertible(conv Convertible) ([]byte, error) {
|
|
|
|
item, err := conv.ToStackItem()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return Serialize(item)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeserializeConvertible deserializes Convertible from a slice of bytes.
|
|
|
|
func DeserializeConvertible(data []byte, conv Convertible) error {
|
|
|
|
item, err := Deserialize(data)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return conv.FromStackItem(item)
|
|
|
|
}
|