From 8fdc7e32f5e9051f6b48ba022cd62f8d008a2d55 Mon Sep 17 00:00:00 2001 From: Evgeniy Stratonikov Date: Fri, 9 Jul 2021 16:09:33 +0300 Subject: [PATCH] stackitem: cache serialized results in `EncodeBinary` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` name old time/op new time/op delta EncodeBinary-8 8.39ms ± 1% 0.05ms ±21% -99.44% (p=0.000 n=10+9) name old alloc/op new alloc/op delta EncodeBinary-8 2.24MB ± 0% 0.33MB ± 0% -85.29% (p=0.000 n=9+10) name old allocs/op new allocs/op delta EncodeBinary-8 65.6k ± 0% 0.0k ± 0% -99.95% (p=0.000 n=10+10) ``` Signed-off-by: Evgeniy Stratonikov --- pkg/vm/stackitem/serialization.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/pkg/vm/stackitem/serialization.go b/pkg/vm/stackitem/serialization.go index 60b98091c..4fb5c2b56 100644 --- a/pkg/vm/stackitem/serialization.go +++ b/pkg/vm/stackitem/serialization.go @@ -22,14 +22,14 @@ type serContext struct { uv [9]byte data []byte allowInvalid bool - seen map[Item]bool + seen map[Item]sliceNoPointer } // Serialize encodes given Item into the byte slice. func Serialize(item Item) ([]byte, error) { sc := serContext{ allowInvalid: false, - seen: make(map[Item]bool), + seen: make(map[Item]sliceNoPointer), } err := sc.serialize(item) if err != nil { @@ -58,7 +58,7 @@ func EncodeBinary(item Item, w *io.BinWriter) { func EncodeBinaryProtected(item Item, w *io.BinWriter) { sc := serContext{ allowInvalid: true, - seen: make(map[Item]bool), + seen: make(map[Item]sliceNoPointer), } err := sc.serialize(item) if err != nil { @@ -69,10 +69,18 @@ func EncodeBinaryProtected(item Item, w *io.BinWriter) { } func (w *serContext) serialize(item Item) error { - if w.seen[item] { - return ErrRecursive + 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 + } + w.data = append(w.data, w.data[v.start:v.end]...) + return nil } + start := len(w.data) switch t := item.(type) { case *ByteArray: w.data = append(w.data, byte(ByteArrayT)) @@ -103,7 +111,7 @@ func (w *serContext) serialize(item Item) error { return fmt.Errorf("%w: Interop", ErrUnserializable) } case *Array, *Struct: - w.seen[item] = true + w.seen[item] = sliceNoPointer{} _, isArray := t.(*Array) if isArray { @@ -119,9 +127,9 @@ func (w *serContext) serialize(item Item) error { return err } } - delete(w.seen, item) + w.seen[item] = sliceNoPointer{start, len(w.data)} case *Map: - w.seen[item] = true + w.seen[item] = sliceNoPointer{} elems := t.Value().([]MapElement) w.data = append(w.data, byte(MapT)) @@ -134,7 +142,7 @@ func (w *serContext) serialize(item Item) error { return err } } - delete(w.seen, item) + w.seen[item] = sliceNoPointer{start, len(w.data)} case Null: w.data = append(w.data, byte(AnyT)) case nil: