Skip to content

Commit

Permalink
internal/proto: Fix encoding of zero embedded messages
Browse files Browse the repository at this point in the history
Fixes the problem detected in d44ef3b
via `reflect` package as well as `neofs-api-go` module does.

Another way would be to check each field in the implementation of
`MarshalStable([]byte)` interface, but this would add a significant
amount of code. Benchmark showed almost no performance loss:
```
goos: linux
goarch: amd64
pkg: github.com/nspcc-dev/neofs-sdk-go/internal/proto
cpu: Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz
                  │   old.txt    │            new.txt            │
                  │    sec/op    │   sec/op     vs base          │
MarshalEmbedded-8   26.00n ± 31%   27.59n ± 2%  ~ (p=0.393 n=10)

                  │  old.txt   │            new.txt             │
                  │    B/op    │    B/op     vs base            │
MarshalEmbedded-8   0.000 ± 0%   0.000 ± 0%  ~ (p=1.000 n=10) ¹
¹ all samples are equal

                  │  old.txt   │            new.txt             │
                  │ allocs/op  │ allocs/op   vs base            │
MarshalEmbedded-8   0.000 ± 0%   0.000 ± 0%  ~ (p=1.000 n=10) ¹
¹ all samples are equal
```

Signed-off-by: Leonard Lyubich <[email protected]>
  • Loading branch information
cthulhu-rider committed Dec 17, 2024
1 parent d44ef3b commit 6cbf49a
Showing 1 changed file with 4 additions and 10 deletions.
14 changes: 4 additions & 10 deletions internal/proto/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package proto
import (
"encoding/binary"
"math"
"reflect"

"google.golang.org/protobuf/encoding/protowire"
)
Expand Down Expand Up @@ -159,27 +160,20 @@ func MarshalToDouble(b []byte, num protowire.Number, v float64) int {
// SizeEmbedded returns the encoded size of embedded message being a protobuf
// field with given number and value.
func SizeEmbedded(num protowire.Number, v Message) int {
if v == nil {
if v == nil || reflect.ValueOf(v).IsNil() {
return 0
}
sz := v.MarshaledSize()
if sz == 0 {
return 0
}
return protowire.SizeTag(num) + protowire.SizeBytes(sz)
return protowire.SizeTag(num) + protowire.SizeBytes(v.MarshaledSize())
}

// MarshalToEmbedded encodes embedded message being a protobuf field with given
// number and value into b and returns the number of bytes written. If the
// buffer is too small, MarshalToEmbedded will panic.
func MarshalToEmbedded(b []byte, num protowire.Number, v Message) int {
if v == nil {
if v == nil || reflect.ValueOf(v).IsNil() {
return 0
}
sz := v.MarshaledSize()
if sz == 0 {
return 0
}
off := binary.PutUvarint(b, protowire.EncodeTag(num, protowire.BytesType))
off += binary.PutUvarint(b[off:], uint64(sz))
v.MarshalStable(b[off:])
Expand Down

0 comments on commit 6cbf49a

Please sign in to comment.