From fed4998a5854e52a1fb91814aaff3ac790ee34c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Wed, 25 Sep 2024 19:12:36 +0200 Subject: [PATCH] cue: use append-like funcs inside Value.MarshalJSON MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So that we can reuse buffers where possible rather than making new ones only to copy the data over to the final buffer. Note that this doesn't happen everywhere yet, as the underlying API we use to marshal literals is json.Marshal, which is not append-like. │ old │ new │ │ sec/op │ sec/op vs base │ LargeValueMarshalJSON-8 12.240m ± 1% 7.772m ± 1% -36.50% (p=0.002 n=6) │ old │ new │ │ B/op │ B/op vs base │ LargeValueMarshalJSON-8 23.508Mi ± 0% 7.158Mi ± 0% -69.55% (p=0.002 n=6) │ old │ new │ │ allocs/op │ allocs/op vs base │ LargeValueMarshalJSON-8 78.36k ± 0% 70.28k ± 0% -10.32% (p=0.002 n=6) Updates #2470. Signed-off-by: Daniel Martí Change-Id: Idd806ce52f9726ffd87d6bf71c2759131d2553a5 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/1201808 Unity-Result: CUE porcuepine Reviewed-by: Roger Peppe TryBot-Result: CUEcueckoo --- cue/decode.go | 2 +- cue/types.go | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/cue/decode.go b/cue/decode.go index 07e7418302b..7286121b2f9 100644 --- a/cue/decode.go +++ b/cue/decode.go @@ -118,7 +118,7 @@ func (d *decoder) decode(x reflect.Value, v Value, isPtr bool) { ij, it, x := indirect(x, v.IsNull()) if ij != nil { - b, err := v.marshalJSON() + b, err := v.appendJSON(nil) d.addErr(err) d.addErr(ij.UnmarshalJSON(b)) return diff --git a/cue/types.go b/cue/types.go index b15d2a6f2bf..611c5503ec5 100644 --- a/cue/types.go +++ b/cue/types.go @@ -143,7 +143,7 @@ func (o *hiddenStructValue) Lookup(key string) Value { // MarshalJSON returns a valid JSON encoding or reports an error if any of the // fields is invalid. -func (o *structValue) marshalJSON() (b []byte, err error) { +func (o *structValue) appendJSON(b []byte) ([]byte, error) { b = append(b, '{') n := o.Len() for i := range n { @@ -154,11 +154,10 @@ func (o *structValue) marshalJSON() (b []byte, err error) { } b = append(b, s...) b = append(b, ':') - bb, err := v.marshalJSON() + b, err = v.appendJSON(b) if err != nil { return nil, err } - b = append(b, bb...) if i < n-1 { b = append(b, ',') } @@ -294,15 +293,15 @@ func (i *hiddenIterator) IsDefinition() bool { // marshalJSON iterates over the list and generates JSON output. HasNext // will return false after this operation. -func marshalList(l *Iterator) (b []byte, err error) { +func listAppendJSON(b []byte, l *Iterator) ([]byte, error) { b = append(b, '[') if l.Next() { for i := 0; ; i++ { - x, err := l.Value().marshalJSON() + var err error + b, err = l.Value().appendJSON(b) if err != nil { return nil, err } - b = append(b, x...) if !l.Next() { break } @@ -901,17 +900,17 @@ func (v Value) IncompleteKind() Kind { // MarshalJSON marshalls this value into valid JSON. func (v Value) MarshalJSON() (b []byte, err error) { - b, err = v.marshalJSON() + b, err = v.appendJSON(nil) if err != nil { return nil, unwrapJSONError(err) } return b, nil } -func (v Value) marshalJSON() (b []byte, err error) { +func (v Value) appendJSON(b []byte) ([]byte, error) { v, _ = v.Default() if v.v == nil { - return []byte("null"), nil + return append(b, "null"...), nil } ctx := newContext(v.idx) x := v.eval(ctx) @@ -926,26 +925,29 @@ func (v Value) marshalJSON() (b []byte, err error) { // TODO: implement marshalles in value. switch k := x.Kind(); k { case adt.NullKind: - return []byte("null"), nil + return append(b, "null"...), nil case adt.BoolKind: - return internaljson.Marshal(x.(*adt.Bool).B) + b2, err := internaljson.Marshal(x.(*adt.Bool).B) + return append(b, b2...), err case adt.IntKind, adt.FloatKind, adt.NumberKind: - b, err := x.(*adt.Num).X.MarshalText() - b = bytes.TrimLeft(b, "+") - return b, err + b2, err := x.(*adt.Num).X.MarshalText() + b2 = bytes.TrimLeft(b2, "+") + return append(b, b2...), err case adt.StringKind: - return internaljson.Marshal(x.(*adt.String).Str) + b2, err := internaljson.Marshal(x.(*adt.String).Str) + return append(b, b2...), err case adt.BytesKind: - return internaljson.Marshal(x.(*adt.Bytes).B) + b2, err := internaljson.Marshal(x.(*adt.Bytes).B) + return append(b, b2...), err case adt.ListKind: i, _ := v.List() - return marshalList(&i) + return listAppendJSON(b, &i) case adt.StructKind: obj, err := v.structValData(ctx) if err != nil { return nil, toMarshalErr(v, err) } - return obj.marshalJSON() + return obj.appendJSON(b) case adt.BottomKind: return nil, toMarshalErr(v, x.(*adt.Bottom)) default: