Skip to content

Commit bb0ab59

Browse files
committed
poc: avoid extra heap alloc per item in json list encoding special case
1 parent 4cbb621 commit bb0ab59

File tree

1 file changed

+52
-5
lines changed
  • staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json

1 file changed

+52
-5
lines changed

staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/collections.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -215,11 +215,13 @@ func encodeKeyValuePair(w io.Writer, key string, value any, suffix []byte) (err
215215
}
216216

217217
func encodeValue(w io.Writer, value any, suffix []byte) error {
218-
data, err := json.Marshal(value)
219-
if err != nil {
220-
return err
221-
}
222-
_, err = w.Write(data)
218+
// Use an Encoder to avoid the extra []byte allocated by Marshal. Encode, unlike Marshal,
219+
// appends a trailing newline to separate consecutive encodings of JSON values that aren't
220+
// self-delimiting, like numbers. Strip the newline to avoid the assumption that every
221+
// json.Unmarshaler implementation will accept trailing whitespace.
222+
enc := json.NewEncoder(&trailingLinefeedSuppressor{delegate: w})
223+
enc.SetIndent("", "")
224+
err := enc.Encode(value)
223225
if err != nil {
224226
return err
225227
}
@@ -228,3 +230,48 @@ func encodeValue(w io.Writer, value any, suffix []byte) error {
228230
}
229231
return err
230232
}
233+
234+
// trailingLinefeedSuppressor is an io.Writer that wraps another io.Writer, suppressing a single
235+
// trailing linefeed if it is the last byte written by the latest call to Write.
236+
type trailingLinefeedSuppressor struct {
237+
lf bool
238+
delegate io.Writer
239+
}
240+
241+
func (w *trailingLinefeedSuppressor) Write(p []byte) (int, error) {
242+
if len(p) == 0 {
243+
// Avoid flushing a buffered linefeeds on an empty write.
244+
return 0, nil
245+
}
246+
247+
if w.lf {
248+
// The previous write had a trailing linefeed that was buffered. That wasn't the
249+
// last Write call, so flush the buffered linefeed before continuing.
250+
n, err := w.delegate.Write([]byte{'\n'})
251+
if n > 0 {
252+
w.lf = false
253+
}
254+
if err != nil {
255+
return 0, err
256+
}
257+
}
258+
259+
if p[len(p)-1] != '\n' {
260+
return w.delegate.Write(p)
261+
}
262+
263+
p = p[:len(p)-1]
264+
265+
if len(p) == 0 { // []byte{'\n'}
266+
w.lf = true
267+
return 1, nil
268+
}
269+
270+
n, err := w.delegate.Write(p)
271+
if n == len(p) {
272+
// Everything up to the trailing linefeed has been flushed. Eat the linefeed.
273+
w.lf = true
274+
n++
275+
}
276+
return n, err
277+
}

0 commit comments

Comments
 (0)