cosmos-sdk/tx/textual/valuerenderer/bytes.go
Emmanuel T Odeke 8c23f6f957
perf: fix: tx/textual/valuerender: use io.WriteString to skip str->byteslice + fix negative sign dropping (#12815)
Noticed in an audit that the differeent value renderers perform an
expensive and unnecessary string->byteslice in cases where the output
write implements io.StringWriter. This change instead invokes

   io.WriteString(w, formatted)

instead of:

   w.Write([]byte(formatted))

and added benchmarks that show an improvement from just the 1 line change:

```shell
$ benchstat before.txt after.txt
name                        old time/op    new time/op    delta
IntValueRendererFormat-8      4.13µs ± 3%    3.95µs ± 6%   -4.55%  (p=0.000 n=15+14)
BytesValueRendererFormat-8    5.22ms ± 3%    4.77ms ± 5%   -8.60%  (p=0.000 n=15+14)

name                        old alloc/op   new alloc/op   delta
IntValueRendererFormat-8      3.64kB ± 0%    3.31kB ± 0%   -9.01%  (p=0.000 n=15+15)
BytesValueRendererFormat-8    12.6MB ± 0%     8.4MB ± 0%  -33.22%  (p=0.000 n=15+15)

name                        old allocs/op  new allocs/op  delta
IntValueRendererFormat-8        76.0 ± 0%      67.0 ± 0%  -11.84%  (p=0.000 n=15+15)
BytesValueRendererFormat-8      27.0 ± 0%      18.0 ± 0%  -33.33%  (p=0.000 n=15+15)
```

While here, implemented negative sign preservation because previously
the code wasn't tested for negative values so passing in negative values
such as:

  "-10000000.11"

would produce:

  "10'000'000.11"

instead of the proper value with the negative sign preserved:

  "-10'000'000.11"

Fixes #12810
Fixes #12812
2022-08-04 01:27:54 -07:00

35 lines
834 B
Go

package valuerenderer
import (
"context"
"encoding/base64"
"io"
"google.golang.org/protobuf/reflect/protoreflect"
)
// bytesValueRenderer implements ValueRenderer for bytes
type bytesValueRenderer struct {
}
var _ ValueRenderer = bytesValueRenderer{}
func (vr bytesValueRenderer) Format(ctx context.Context, v protoreflect.Value, w io.Writer) error {
_, err := io.WriteString(w, base64.StdEncoding.EncodeToString(v.Bytes()))
return err
}
func (vr bytesValueRenderer) Parse(_ context.Context, r io.Reader) (protoreflect.Value, error) {
formatted, err := io.ReadAll(r)
if err != nil {
return protoreflect.ValueOfBytes([]byte{}), err
}
data, err := base64.StdEncoding.DecodeString(string(formatted))
if err != nil {
return protoreflect.ValueOfBytes([]byte{}), err
}
return protoreflect.ValueOfBytes(data), nil
}